lol

nixos/influxdb2: add org, bucket, users and auth provisioning

oddlama 8b5b7def 192a8b8e

+579 -68
+2
nixos/doc/manual/release-notes/rl-2311.section.md
··· 199 199 200 200 - `programs.gnupg.agent.pinentryFlavor` is now set in `/etc/gnupg/gpg-agent.conf`, and will no longer take precedence over a `pinentry-program` set in `~/.gnupg/gpg-agent.conf`. 201 201 202 + - `services.influxdb2` now supports doing an automatic initial setup and provisioning of users, organizations, buckets and authentication tokens, see [#249502](https://github.com/NixOS/nixpkgs/pull/249502) for more details. 203 + 202 204 - `wrapHelm` now exposes `passthru.pluginsDir` which can be passed to `helmfile`. For convenience, a top-level package `helmfile-wrapped` has been added, which inherits `passthru.pluginsDir` from `kubernetes-helm-wrapped`. See [#217768](https://github.com/NixOS/nixpkgs/issues/217768) for details. 203 205 204 206 - `boot.initrd.network.udhcp.enable` allows control over dhcp during stage 1 regardless of what `networking.useDHCP` is set to.
+386 -66
nixos/modules/services/databases/influxdb2.nix
··· 3 3 let 4 4 inherit 5 5 (lib) 6 + any 7 + attrNames 8 + attrValues 9 + count 6 10 escapeShellArg 11 + filterAttrs 12 + flatten 13 + flip 14 + getExe 7 15 hasAttr 16 + hasInfix 17 + listToAttrs 8 18 literalExpression 19 + mapAttrsToList 20 + mdDoc 9 21 mkEnableOption 10 22 mkIf 11 23 mkOption 24 + nameValuePair 25 + optional 26 + subtractLists 12 27 types 28 + unique 13 29 ; 14 30 15 31 format = pkgs.formats.json { }; 16 32 cfg = config.services.influxdb2; 17 33 configFile = format.generate "config.json" cfg.settings; 34 + 35 + validPermissions = [ 36 + "authorizations" 37 + "buckets" 38 + "dashboards" 39 + "orgs" 40 + "tasks" 41 + "telegrafs" 42 + "users" 43 + "variables" 44 + "secrets" 45 + "labels" 46 + "views" 47 + "documents" 48 + "notificationRules" 49 + "notificationEndpoints" 50 + "checks" 51 + "dbrp" 52 + "annotations" 53 + "sources" 54 + "scrapers" 55 + "notebooks" 56 + "remotes" 57 + "replications" 58 + ]; 59 + 60 + # Determines whether at least one active api token is defined 61 + anyAuthDefined = 62 + flip any (attrValues cfg.provision.organizations) 63 + (o: o.present && flip any (attrValues o.auths) 64 + (a: a.present && a.tokenFile != null)); 65 + 66 + provisionState = pkgs.writeText "provision_state.json" (builtins.toJSON { 67 + inherit (cfg.provision) organizations users; 68 + }); 69 + 70 + provisioningScript = pkgs.writeShellScript "post-start-provision" '' 71 + set -euo pipefail 72 + export INFLUX_HOST="http://"${escapeShellArg ( 73 + if ! hasAttr "http-bind-address" cfg.settings 74 + || hasInfix "0.0.0.0" cfg.settings.http-bind-address 75 + then "localhost:8086" 76 + else cfg.settings.http-bind-address 77 + )} 78 + 79 + # Wait for the influxdb server to come online 80 + count=0 81 + while ! influx ping &>/dev/null; do 82 + if [ "$count" -eq 300 ]; then 83 + echo "Tried for 30 seconds, giving up..." 84 + exit 1 85 + fi 86 + 87 + if ! kill -0 "$MAINPID"; then 88 + echo "Main server died, giving up..." 89 + exit 1 90 + fi 91 + 92 + sleep 0.1 93 + count=$((count++)) 94 + done 95 + 96 + # Do the initial database setup. Pass /dev/null as configs-path to 97 + # avoid saving the token as the active config. 98 + if test -e "$STATE_DIRECTORY/.first_startup"; then 99 + influx setup \ 100 + --configs-path /dev/null \ 101 + --org ${escapeShellArg cfg.provision.initialSetup.organization} \ 102 + --bucket ${escapeShellArg cfg.provision.initialSetup.bucket} \ 103 + --username ${escapeShellArg cfg.provision.initialSetup.username} \ 104 + --password "$(< "$CREDENTIALS_DIRECTORY/admin-password")" \ 105 + --token "$(< "$CREDENTIALS_DIRECTORY/admin-token")" \ 106 + --retention ${toString cfg.provision.initialSetup.retention}s \ 107 + --force >/dev/null 108 + 109 + rm -f "$STATE_DIRECTORY/.first_startup" 110 + fi 111 + 112 + provision_result=$(${getExe pkgs.influxdb2-provision} ${provisionState} "$INFLUX_HOST" "$(< "$CREDENTIALS_DIRECTORY/admin-token")") 113 + if [[ "$(jq '[.auths[] | select(.action == "created")] | length' <<< "$provision_result")" -gt 0 ]]; then 114 + echo "Created at least one new token, queueing service restart so we can manipulate secrets" 115 + touch "$STATE_DIRECTORY/.needs_restart" 116 + fi 117 + ''; 118 + 119 + restarterScript = pkgs.writeShellScript "post-start-restarter" '' 120 + set -euo pipefail 121 + if test -e "$STATE_DIRECTORY/.needs_restart"; then 122 + rm -f "$STATE_DIRECTORY/.needs_restart" 123 + /run/current-system/systemd/bin/systemctl restart influxdb2 124 + fi 125 + ''; 126 + 127 + organizationSubmodule = types.submodule (organizationSubmod: let 128 + org = organizationSubmod.config._module.args.name; 129 + in { 130 + options = { 131 + present = mkOption { 132 + description = mdDoc "Whether to ensure that this organization is present or absent."; 133 + type = types.bool; 134 + default = true; 135 + }; 136 + 137 + description = mkOption { 138 + description = mdDoc "Optional description for the organization."; 139 + default = null; 140 + type = types.nullOr types.str; 141 + }; 142 + 143 + buckets = mkOption { 144 + description = mdDoc "Buckets to provision in this organization."; 145 + default = {}; 146 + type = types.attrsOf (types.submodule (bucketSubmod: let 147 + bucket = bucketSubmod.config._module.args.name; 148 + in { 149 + options = { 150 + present = mkOption { 151 + description = mdDoc "Whether to ensure that this bucket is present or absent."; 152 + type = types.bool; 153 + default = true; 154 + }; 155 + 156 + description = mkOption { 157 + description = mdDoc "Optional description for the bucket."; 158 + default = null; 159 + type = types.nullOr types.str; 160 + }; 161 + 162 + retention = mkOption { 163 + type = types.ints.unsigned; 164 + default = 0; 165 + description = mdDoc "The duration in seconds for which the bucket will retain data (0 is infinite)."; 166 + }; 167 + }; 168 + })); 169 + }; 170 + 171 + auths = mkOption { 172 + description = mdDoc "API tokens to provision for the user in this organization."; 173 + default = {}; 174 + type = types.attrsOf (types.submodule (authSubmod: let 175 + auth = authSubmod.config._module.args.name; 176 + in { 177 + options = { 178 + id = mkOption { 179 + description = mdDoc "A unique identifier for this authentication token. Since influx doesn't store names for tokens, this will be hashed and appended to the description to identify the token."; 180 + readOnly = true; 181 + default = builtins.substring 0 32 (builtins.hashString "sha256" "${org}:${auth}"); 182 + defaultText = "<a hash derived from org and name>"; 183 + type = types.str; 184 + }; 185 + 186 + present = mkOption { 187 + description = mdDoc "Whether to ensure that this user is present or absent."; 188 + type = types.bool; 189 + default = true; 190 + }; 191 + 192 + description = mkOption { 193 + description = '' 194 + Optional description for the API token. 195 + Note that the actual token will always be created with a descriptionregardless 196 + of whether this is given or not. The name is always added plus a unique suffix 197 + to later identify the token to track whether it has already been created. 198 + ''; 199 + default = null; 200 + type = types.nullOr types.str; 201 + }; 202 + 203 + tokenFile = mkOption { 204 + type = types.nullOr types.path; 205 + default = null; 206 + description = mdDoc "The token value. If not given, influx will automatically generate one."; 207 + }; 208 + 209 + operator = mkOption { 210 + description = mdDoc "Grants all permissions in all organizations."; 211 + default = false; 212 + type = types.bool; 213 + }; 214 + 215 + allAccess = mkOption { 216 + description = mdDoc "Grants all permissions in the associated organization."; 217 + default = false; 218 + type = types.bool; 219 + }; 220 + 221 + readPermissions = mkOption { 222 + description = mdDoc '' 223 + The read permissions to include for this token. Access is usually granted only 224 + for resources in the associated organization. 225 + 226 + Available permissions are `authorizations`, `buckets`, `dashboards`, 227 + `orgs`, `tasks`, `telegrafs`, `users`, `variables`, `secrets`, `labels`, `views`, 228 + `documents`, `notificationRules`, `notificationEndpoints`, `checks`, `dbrp`, 229 + `annotations`, `sources`, `scrapers`, `notebooks`, `remotes`, `replications`. 230 + 231 + Refer to `influx auth create --help` for a full list with descriptions. 232 + 233 + `buckets` grants read access to all associated buckets. Use `readBuckets` to define 234 + more granular access permissions. 235 + ''; 236 + default = []; 237 + type = types.listOf (types.enum validPermissions); 238 + }; 239 + 240 + writePermissions = mkOption { 241 + description = mdDoc '' 242 + The read permissions to include for this token. Access is usually granted only 243 + for resources in the associated organization. 244 + 245 + Available permissions are `authorizations`, `buckets`, `dashboards`, 246 + `orgs`, `tasks`, `telegrafs`, `users`, `variables`, `secrets`, `labels`, `views`, 247 + `documents`, `notificationRules`, `notificationEndpoints`, `checks`, `dbrp`, 248 + `annotations`, `sources`, `scrapers`, `notebooks`, `remotes`, `replications`. 249 + 250 + Refer to `influx auth create --help` for a full list with descriptions. 251 + 252 + `buckets` grants write access to all associated buckets. Use `writeBuckets` to define 253 + more granular access permissions. 254 + ''; 255 + default = []; 256 + type = types.listOf (types.enum validPermissions); 257 + }; 258 + 259 + readBuckets = mkOption { 260 + description = mdDoc "The organization's buckets which should be allowed to be read"; 261 + default = []; 262 + type = types.listOf types.str; 263 + }; 264 + 265 + writeBuckets = mkOption { 266 + description = mdDoc "The organization's buckets which should be allowed to be written"; 267 + default = []; 268 + type = types.listOf types.str; 269 + }; 270 + }; 271 + })); 272 + }; 273 + }; 274 + }); 18 275 in 19 276 { 20 277 options = { 21 278 services.influxdb2 = { 22 - enable = mkEnableOption (lib.mdDoc "the influxdb2 server"); 279 + enable = mkEnableOption (mdDoc "the influxdb2 server"); 23 280 24 281 package = mkOption { 25 282 default = pkgs.influxdb2-server; 26 283 defaultText = literalExpression "pkgs.influxdb2"; 27 - description = lib.mdDoc "influxdb2 derivation to use."; 284 + description = mdDoc "influxdb2 derivation to use."; 28 285 type = types.package; 29 286 }; 30 287 31 288 settings = mkOption { 32 289 default = { }; 33 - description = lib.mdDoc ''configuration options for influxdb2, see <https://docs.influxdata.com/influxdb/v2.0/reference/config-options> for details.''; 290 + description = mdDoc ''configuration options for influxdb2, see <https://docs.influxdata.com/influxdb/v2.0/reference/config-options> for details.''; 34 291 type = format.type; 35 292 }; 36 293 ··· 41 298 organization = mkOption { 42 299 type = types.str; 43 300 example = "main"; 44 - description = "Primary organization name"; 301 + description = mdDoc "Primary organization name"; 45 302 }; 46 303 47 304 bucket = mkOption { 48 305 type = types.str; 49 306 example = "example"; 50 - description = "Primary bucket name"; 307 + description = mdDoc "Primary bucket name"; 51 308 }; 52 309 53 310 username = mkOption { 54 311 type = types.str; 55 312 default = "admin"; 56 - description = "Primary username"; 313 + description = mdDoc "Primary username"; 57 314 }; 58 315 59 316 retention = mkOption { 60 - type = types.str; 61 - default = "0"; 62 - description = '' 63 - The duration for which the bucket will retain data (0 is infinite). 64 - Accepted units are `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), 65 - `s` (seconds), `m` (minutes), `h` (hours), `d` (days) and `w` (weeks). 66 - ''; 317 + type = types.ints.unsigned; 318 + default = 0; 319 + description = mdDoc "The duration in seconds for which the bucket will retain data (0 is infinite)."; 67 320 }; 68 321 69 322 passwordFile = mkOption { 70 323 type = types.path; 71 - description = "Password for primary user. Don't use a file from the nix store!"; 324 + description = mdDoc "Password for primary user. Don't use a file from the nix store!"; 72 325 }; 73 326 74 327 tokenFile = mkOption { 75 328 type = types.path; 76 - description = "API Token to set for the admin user. Don't use a file from the nix store!"; 329 + description = mdDoc "API Token to set for the admin user. Don't use a file from the nix store!"; 77 330 }; 78 331 }; 332 + 333 + organizations = mkOption { 334 + description = mdDoc "Organizations to provision."; 335 + example = literalExpression '' 336 + { 337 + myorg = { 338 + description = "My organization"; 339 + buckets.mybucket = { 340 + description = "My bucket"; 341 + retention = 31536000; # 1 year 342 + }; 343 + auths.mytoken = { 344 + readBuckets = ["mybucket"]; 345 + tokenFile = "/run/secrets/mytoken"; 346 + }; 347 + }; 348 + } 349 + ''; 350 + default = {}; 351 + type = types.attrsOf organizationSubmodule; 352 + }; 353 + 354 + users = mkOption { 355 + description = mdDoc "Users to provision."; 356 + default = {}; 357 + example = literalExpression '' 358 + { 359 + # admin = {}; /* The initialSetup.username will automatically be added. */ 360 + myuser.passwordFile = "/run/secrets/myuser_password"; 361 + } 362 + ''; 363 + type = types.attrsOf (types.submodule (userSubmod: let 364 + user = userSubmod.config._module.args.name; 365 + org = userSubmod.config.org; 366 + in { 367 + options = { 368 + present = mkOption { 369 + description = mdDoc "Whether to ensure that this user is present or absent."; 370 + type = types.bool; 371 + default = true; 372 + }; 373 + 374 + passwordFile = mkOption { 375 + description = mdDoc "Password for the user. If unset, the user will not be able to log in until a password is set by an operator! Don't use a file from the nix store!"; 376 + default = null; 377 + type = types.nullOr types.path; 378 + }; 379 + }; 380 + })); 381 + }; 79 382 }; 80 383 }; 81 384 }; 82 385 83 386 config = mkIf cfg.enable { 84 - assertions = [ 85 - { 86 - assertion = !(hasAttr "bolt-path" cfg.settings) && !(hasAttr "engine-path" cfg.settings); 87 - message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd"; 88 - } 89 - ]; 387 + assertions = 388 + [ 389 + { 390 + assertion = !(hasAttr "bolt-path" cfg.settings) && !(hasAttr "engine-path" cfg.settings); 391 + message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd"; 392 + } 393 + ] 394 + ++ flatten (flip mapAttrsToList cfg.provision.organizations (orgName: org: 395 + flip mapAttrsToList org.auths (authName: auth: 396 + [ 397 + { 398 + assertion = 1 == count (x: x) [ 399 + auth.operator 400 + auth.allAccess 401 + (auth.readPermissions != [] 402 + || auth.writePermissions != [] 403 + || auth.readBuckets != [] 404 + || auth.writeBuckets != []) 405 + ]; 406 + message = "influxdb2: provision.organizations.${orgName}.auths.${authName}: The `operator` and `allAccess` options are mutually exclusive with each other and the granular permission settings."; 407 + } 408 + (let unknownBuckets = subtractLists (attrNames org.buckets) auth.readBuckets; in { 409 + assertion = unknownBuckets == []; 410 + message = "influxdb2: provision.organizations.${orgName}.auths.${authName}: Refers to invalid buckets in readBuckets: ${toString unknownBuckets}"; 411 + }) 412 + (let unknownBuckets = subtractLists (attrNames org.buckets) auth.writeBuckets; in { 413 + assertion = unknownBuckets == []; 414 + message = "influxdb2: provision.organizations.${orgName}.auths.${authName}: Refers to invalid buckets in writeBuckets: ${toString unknownBuckets}"; 415 + }) 416 + ] 417 + ) 418 + )); 419 + 420 + services.influxdb2.provision = mkIf cfg.provision.enable { 421 + organizations.${cfg.provision.initialSetup.organization} = { 422 + buckets.${cfg.provision.initialSetup.bucket} = { 423 + inherit (cfg.provision.initialSetup) retention; 424 + }; 425 + }; 426 + users.${cfg.provision.initialSetup.username} = { 427 + inherit (cfg.provision.initialSetup) passwordFile; 428 + }; 429 + }; 90 430 91 431 systemd.services.influxdb2 = { 92 432 description = "InfluxDB is an open-source, distributed, time series database"; ··· 111 451 "admin-password:${cfg.provision.initialSetup.passwordFile}" 112 452 "admin-token:${cfg.provision.initialSetup.tokenFile}" 113 453 ]; 454 + 455 + ExecStartPost = mkIf cfg.provision.enable ( 456 + [provisioningScript] ++ 457 + # Only the restarter runs with elevated privileges 458 + optional anyAuthDefined "+${restarterScript}" 459 + ); 114 460 }; 115 461 116 - path = [pkgs.influxdb2-cli]; 462 + path = [ 463 + pkgs.influxdb2-cli 464 + pkgs.jq 465 + ]; 117 466 118 - # Mark if this is the first startup so postStart can do the initial setup 119 - preStart = mkIf cfg.provision.enable '' 467 + # Mark if this is the first startup so postStart can do the initial setup. 468 + # Also extract any token secret mappings and apply them if this isn't the first start. 469 + preStart = let 470 + tokenPaths = listToAttrs (flatten 471 + # For all organizations 472 + (flip mapAttrsToList cfg.provision.organizations 473 + # For each contained token that has a token file 474 + (_: org: flip mapAttrsToList (filterAttrs (_: x: x.tokenFile != null) org.auths) 475 + # Collect id -> tokenFile for the mapping 476 + (_: auth: nameValuePair auth.id auth.tokenFile)))); 477 + tokenMappings = pkgs.writeText "token_mappings.json" (builtins.toJSON tokenPaths); 478 + in mkIf cfg.provision.enable '' 120 479 if ! test -e "$STATE_DIRECTORY/influxd.bolt"; then 121 480 touch "$STATE_DIRECTORY/.first_startup" 481 + else 482 + # Manipulate provisioned api tokens if necessary 483 + ${getExe pkgs.influxdb2-token-manipulator} "$STATE_DIRECTORY/influxd.bolt" ${tokenMappings} 122 484 fi 123 485 ''; 124 - 125 - postStart = let 126 - initCfg = cfg.provision.initialSetup; 127 - in mkIf cfg.provision.enable ( 128 - '' 129 - set -euo pipefail 130 - export INFLUX_HOST="http://"${escapeShellArg (cfg.settings.http-bind-address or "localhost:8086")} 131 - 132 - # Wait for the influxdb server to come online 133 - count=0 134 - while ! influx ping &>/dev/null; do 135 - if [ "$count" -eq 300 ]; then 136 - echo "Tried for 30 seconds, giving up..." 137 - exit 1 138 - fi 139 - 140 - if ! kill -0 "$MAINPID"; then 141 - echo "Main server died, giving up..." 142 - exit 1 143 - fi 144 - 145 - sleep 0.1 146 - count=$((count++)) 147 - done 148 - 149 - # Do the initial database setup. Pass /dev/null as configs-path to 150 - # avoid saving the token as the active config. 151 - if test -e "$STATE_DIRECTORY/.first_startup"; then 152 - influx setup \ 153 - --configs-path /dev/null \ 154 - --org ${escapeShellArg initCfg.organization} \ 155 - --bucket ${escapeShellArg initCfg.bucket} \ 156 - --username ${escapeShellArg initCfg.username} \ 157 - --password "$(< "$CREDENTIALS_DIRECTORY/admin-password")" \ 158 - --token "$(< "$CREDENTIALS_DIRECTORY/admin-token")" \ 159 - --retention ${escapeShellArg initCfg.retention} \ 160 - --force >/dev/null 161 - 162 - rm -f "$STATE_DIRECTORY/.first_startup" 163 - fi 164 - '' 165 - ); 166 486 }; 167 487 168 488 users.extraUsers.influxdb2 = {
+191 -2
nixos/tests/influxdb2.nix
··· 6 6 7 7 nodes.machine = { lib, ... }: { 8 8 environment.systemPackages = [ pkgs.influxdb2-cli ]; 9 + # Make sure that the service is restarted immediately if tokens need to be rewritten 10 + # without relying on any Restart=on-failure behavior 11 + systemd.services.influxdb2.serviceConfig.RestartSec = 6000; 9 12 services.influxdb2.enable = true; 10 13 services.influxdb2.provision = { 11 14 enable = true; ··· 15 18 passwordFile = pkgs.writeText "admin-pw" "ExAmPl3PA55W0rD"; 16 19 tokenFile = pkgs.writeText "admin-token" "verysecureadmintoken"; 17 20 }; 21 + organizations.someorg = { 22 + buckets.somebucket = {}; 23 + auths.sometoken = { 24 + description = "some auth token"; 25 + readBuckets = ["somebucket"]; 26 + writeBuckets = ["somebucket"]; 27 + }; 28 + }; 29 + users.someuser.passwordFile = pkgs.writeText "tmp-pw" "abcgoiuhaoga"; 30 + }; 31 + 32 + specialisation.withModifications.configuration = { ... }: { 33 + services.influxdb2.provision = { 34 + organizations.someorg.buckets.somebucket.present = false; 35 + organizations.someorg.auths.sometoken.present = false; 36 + users.someuser.present = false; 37 + 38 + organizations.myorg = { 39 + description = "Myorg description"; 40 + buckets.mybucket = { 41 + description = "Mybucket description"; 42 + }; 43 + auths.mytoken = { 44 + operator = true; 45 + description = "operator token"; 46 + tokenFile = pkgs.writeText "tmp-tok" "someusertoken"; 47 + }; 48 + }; 49 + users.myuser.passwordFile = pkgs.writeText "tmp-pw" "abcgoiuhaoga"; 50 + }; 51 + }; 52 + 53 + specialisation.withParentDelete.configuration = { ... }: { 54 + services.influxdb2.provision = { 55 + organizations.someorg.present = false; 56 + # Deleting the parent implies: 57 + #organizations.someorg.buckets.somebucket.present = false; 58 + #organizations.someorg.auths.sometoken.present = false; 59 + }; 60 + }; 61 + 62 + specialisation.withNewTokens.configuration = { ... }: { 63 + services.influxdb2.provision = { 64 + organizations.default = { 65 + auths.operator = { 66 + operator = true; 67 + description = "new optoken"; 68 + tokenFile = pkgs.writeText "tmp-tok" "newoptoken"; 69 + }; 70 + auths.allaccess = { 71 + operator = true; 72 + description = "new allaccess"; 73 + tokenFile = pkgs.writeText "tmp-tok" "newallaccess"; 74 + }; 75 + auths.specifics = { 76 + description = "new specifics"; 77 + readPermissions = ["users" "tasks"]; 78 + writePermissions = ["tasks"]; 79 + tokenFile = pkgs.writeText "tmp-tok" "newspecificstoken"; 80 + }; 81 + }; 82 + }; 18 83 }; 19 84 }; 20 85 21 86 testScript = { nodes, ... }: 22 87 let 88 + specialisations = "${nodes.machine.system.build.toplevel}/specialisation"; 23 89 tokenArg = "--token verysecureadmintoken"; 24 90 in '' 91 + def assert_contains(haystack, needle): 92 + if needle not in haystack: 93 + print("The haystack that will cause the following exception is:") 94 + print("---") 95 + print(haystack) 96 + print("---") 97 + raise Exception(f"Expected string '{needle}' was not found") 98 + 99 + def assert_lacks(haystack, needle): 100 + if needle in haystack: 101 + print("The haystack that will cause the following exception is:") 102 + print("---") 103 + print(haystack, end="") 104 + print("---") 105 + raise Exception(f"Unexpected string '{needle}' was found") 106 + 25 107 machine.wait_for_unit("influxdb2.service") 26 108 27 109 machine.fail("curl --fail -X POST 'http://localhost:8086/api/v2/signin' -u admin:wrongpassword") 28 110 machine.succeed("curl --fail -X POST 'http://localhost:8086/api/v2/signin' -u admin:ExAmPl3PA55W0rD") 29 111 30 112 out = machine.succeed("influx org list ${tokenArg}") 31 - assert "default" in out 113 + assert_contains(out, "default") 114 + assert_lacks(out, "myorg") 115 + assert_contains(out, "someorg") 32 116 33 117 out = machine.succeed("influx bucket list ${tokenArg} --org default") 34 - assert "default" in out 118 + assert_contains(out, "default") 119 + 120 + machine.fail("influx bucket list ${tokenArg} --org myorg") 121 + 122 + out = machine.succeed("influx bucket list ${tokenArg} --org someorg") 123 + assert_contains(out, "somebucket") 124 + 125 + out = machine.succeed("influx user list ${tokenArg}") 126 + assert_contains(out, "admin") 127 + assert_lacks(out, "myuser") 128 + assert_contains(out, "someuser") 129 + 130 + out = machine.succeed("influx auth list ${tokenArg}") 131 + assert_lacks(out, "operator token") 132 + assert_contains(out, "some auth token") 133 + 134 + with subtest("withModifications"): 135 + machine.succeed('${specialisations}/withModifications/bin/switch-to-configuration test') 136 + machine.wait_for_unit("influxdb2.service") 137 + 138 + out = machine.succeed("influx org list ${tokenArg}") 139 + assert_contains(out, "default") 140 + assert_contains(out, "myorg") 141 + assert_contains(out, "someorg") 142 + 143 + out = machine.succeed("influx bucket list ${tokenArg} --org myorg") 144 + assert_contains(out, "mybucket") 145 + 146 + out = machine.succeed("influx bucket list ${tokenArg} --org someorg") 147 + assert_lacks(out, "somebucket") 148 + 149 + out = machine.succeed("influx user list ${tokenArg}") 150 + assert_contains(out, "admin") 151 + assert_contains(out, "myuser") 152 + assert_lacks(out, "someuser") 153 + 154 + out = machine.succeed("influx auth list ${tokenArg}") 155 + assert_contains(out, "operator token") 156 + assert_lacks(out, "some auth token") 157 + 158 + # Make sure the user token is also usable 159 + machine.succeed("influx auth list --token someusertoken") 160 + 161 + with subtest("keepsUnrelated"): 162 + machine.succeed('${nodes.machine.system.build.toplevel}/bin/switch-to-configuration test') 163 + machine.wait_for_unit("influxdb2.service") 164 + 165 + out = machine.succeed("influx org list ${tokenArg}") 166 + assert_contains(out, "default") 167 + assert_contains(out, "myorg") 168 + assert_contains(out, "someorg") 169 + 170 + out = machine.succeed("influx bucket list ${tokenArg} --org default") 171 + assert_contains(out, "default") 172 + 173 + out = machine.succeed("influx bucket list ${tokenArg} --org myorg") 174 + assert_contains(out, "mybucket") 175 + 176 + out = machine.succeed("influx bucket list ${tokenArg} --org someorg") 177 + assert_contains(out, "somebucket") 178 + 179 + out = machine.succeed("influx user list ${tokenArg}") 180 + assert_contains(out, "admin") 181 + assert_contains(out, "myuser") 182 + assert_contains(out, "someuser") 183 + 184 + out = machine.succeed("influx auth list ${tokenArg}") 185 + assert_contains(out, "operator token") 186 + assert_contains(out, "some auth token") 187 + 188 + with subtest("withParentDelete"): 189 + machine.succeed('${specialisations}/withParentDelete/bin/switch-to-configuration test') 190 + machine.wait_for_unit("influxdb2.service") 191 + 192 + out = machine.succeed("influx org list ${tokenArg}") 193 + assert_contains(out, "default") 194 + assert_contains(out, "myorg") 195 + assert_lacks(out, "someorg") 196 + 197 + out = machine.succeed("influx bucket list ${tokenArg} --org default") 198 + assert_contains(out, "default") 199 + 200 + out = machine.succeed("influx bucket list ${tokenArg} --org myorg") 201 + assert_contains(out, "mybucket") 202 + 203 + machine.fail("influx bucket list ${tokenArg} --org someorg") 204 + 205 + out = machine.succeed("influx user list ${tokenArg}") 206 + assert_contains(out, "admin") 207 + assert_contains(out, "myuser") 208 + assert_contains(out, "someuser") 209 + 210 + out = machine.succeed("influx auth list ${tokenArg}") 211 + assert_contains(out, "operator token") 212 + assert_lacks(out, "some auth token") 213 + 214 + with subtest("withNewTokens"): 215 + machine.succeed('${specialisations}/withNewTokens/bin/switch-to-configuration test') 216 + machine.wait_for_unit("influxdb2.service") 217 + 218 + out = machine.succeed("influx auth list ${tokenArg}") 219 + assert_contains(out, "operator token") 220 + assert_contains(out, "some auth token") 221 + assert_contains(out, "new optoken") 222 + assert_contains(out, "new allaccess") 223 + assert_contains(out, "new specifics") 35 224 ''; 36 225 })