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

This commit is contained in:
oddlama 2023-08-23 12:17:09 +02:00
parent 192a8b8ee0
commit 8b5b7def91
No known key found for this signature in database
GPG Key ID: 14EFE510775FE39A
3 changed files with 579 additions and 68 deletions

View File

@ -199,6 +199,8 @@ The module update takes care of the new config syntax and the data itself (user
- `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`. - `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`.
- `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.
- `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. - `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.
- `boot.initrd.network.udhcp.enable` allows control over dhcp during stage 1 regardless of what `networking.useDHCP` is set to. - `boot.initrd.network.udhcp.enable` allows control over dhcp during stage 1 regardless of what `networking.useDHCP` is set to.

View File

@ -3,34 +3,291 @@
let let
inherit inherit
(lib) (lib)
any
attrNames
attrValues
count
escapeShellArg escapeShellArg
filterAttrs
flatten
flip
getExe
hasAttr hasAttr
hasInfix
listToAttrs
literalExpression literalExpression
mapAttrsToList
mdDoc
mkEnableOption mkEnableOption
mkIf mkIf
mkOption mkOption
nameValuePair
optional
subtractLists
types types
unique
; ;
format = pkgs.formats.json { }; format = pkgs.formats.json { };
cfg = config.services.influxdb2; cfg = config.services.influxdb2;
configFile = format.generate "config.json" cfg.settings; configFile = format.generate "config.json" cfg.settings;
validPermissions = [
"authorizations"
"buckets"
"dashboards"
"orgs"
"tasks"
"telegrafs"
"users"
"variables"
"secrets"
"labels"
"views"
"documents"
"notificationRules"
"notificationEndpoints"
"checks"
"dbrp"
"annotations"
"sources"
"scrapers"
"notebooks"
"remotes"
"replications"
];
# Determines whether at least one active api token is defined
anyAuthDefined =
flip any (attrValues cfg.provision.organizations)
(o: o.present && flip any (attrValues o.auths)
(a: a.present && a.tokenFile != null));
provisionState = pkgs.writeText "provision_state.json" (builtins.toJSON {
inherit (cfg.provision) organizations users;
});
provisioningScript = pkgs.writeShellScript "post-start-provision" ''
set -euo pipefail
export INFLUX_HOST="http://"${escapeShellArg (
if ! hasAttr "http-bind-address" cfg.settings
|| hasInfix "0.0.0.0" cfg.settings.http-bind-address
then "localhost:8086"
else cfg.settings.http-bind-address
)}
# Wait for the influxdb server to come online
count=0
while ! influx ping &>/dev/null; do
if [ "$count" -eq 300 ]; then
echo "Tried for 30 seconds, giving up..."
exit 1
fi
if ! kill -0 "$MAINPID"; then
echo "Main server died, giving up..."
exit 1
fi
sleep 0.1
count=$((count++))
done
# Do the initial database setup. Pass /dev/null as configs-path to
# avoid saving the token as the active config.
if test -e "$STATE_DIRECTORY/.first_startup"; then
influx setup \
--configs-path /dev/null \
--org ${escapeShellArg cfg.provision.initialSetup.organization} \
--bucket ${escapeShellArg cfg.provision.initialSetup.bucket} \
--username ${escapeShellArg cfg.provision.initialSetup.username} \
--password "$(< "$CREDENTIALS_DIRECTORY/admin-password")" \
--token "$(< "$CREDENTIALS_DIRECTORY/admin-token")" \
--retention ${toString cfg.provision.initialSetup.retention}s \
--force >/dev/null
rm -f "$STATE_DIRECTORY/.first_startup"
fi
provision_result=$(${getExe pkgs.influxdb2-provision} ${provisionState} "$INFLUX_HOST" "$(< "$CREDENTIALS_DIRECTORY/admin-token")")
if [[ "$(jq '[.auths[] | select(.action == "created")] | length' <<< "$provision_result")" -gt 0 ]]; then
echo "Created at least one new token, queueing service restart so we can manipulate secrets"
touch "$STATE_DIRECTORY/.needs_restart"
fi
'';
restarterScript = pkgs.writeShellScript "post-start-restarter" ''
set -euo pipefail
if test -e "$STATE_DIRECTORY/.needs_restart"; then
rm -f "$STATE_DIRECTORY/.needs_restart"
/run/current-system/systemd/bin/systemctl restart influxdb2
fi
'';
organizationSubmodule = types.submodule (organizationSubmod: let
org = organizationSubmod.config._module.args.name;
in {
options = {
present = mkOption {
description = mdDoc "Whether to ensure that this organization is present or absent.";
type = types.bool;
default = true;
};
description = mkOption {
description = mdDoc "Optional description for the organization.";
default = null;
type = types.nullOr types.str;
};
buckets = mkOption {
description = mdDoc "Buckets to provision in this organization.";
default = {};
type = types.attrsOf (types.submodule (bucketSubmod: let
bucket = bucketSubmod.config._module.args.name;
in {
options = {
present = mkOption {
description = mdDoc "Whether to ensure that this bucket is present or absent.";
type = types.bool;
default = true;
};
description = mkOption {
description = mdDoc "Optional description for the bucket.";
default = null;
type = types.nullOr types.str;
};
retention = mkOption {
type = types.ints.unsigned;
default = 0;
description = mdDoc "The duration in seconds for which the bucket will retain data (0 is infinite).";
};
};
}));
};
auths = mkOption {
description = mdDoc "API tokens to provision for the user in this organization.";
default = {};
type = types.attrsOf (types.submodule (authSubmod: let
auth = authSubmod.config._module.args.name;
in {
options = {
id = mkOption {
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.";
readOnly = true;
default = builtins.substring 0 32 (builtins.hashString "sha256" "${org}:${auth}");
defaultText = "<a hash derived from org and name>";
type = types.str;
};
present = mkOption {
description = mdDoc "Whether to ensure that this user is present or absent.";
type = types.bool;
default = true;
};
description = mkOption {
description = ''
Optional description for the API token.
Note that the actual token will always be created with a descriptionregardless
of whether this is given or not. The name is always added plus a unique suffix
to later identify the token to track whether it has already been created.
'';
default = null;
type = types.nullOr types.str;
};
tokenFile = mkOption {
type = types.nullOr types.path;
default = null;
description = mdDoc "The token value. If not given, influx will automatically generate one.";
};
operator = mkOption {
description = mdDoc "Grants all permissions in all organizations.";
default = false;
type = types.bool;
};
allAccess = mkOption {
description = mdDoc "Grants all permissions in the associated organization.";
default = false;
type = types.bool;
};
readPermissions = mkOption {
description = mdDoc ''
The read permissions to include for this token. Access is usually granted only
for resources in the associated organization.
Available permissions are `authorizations`, `buckets`, `dashboards`,
`orgs`, `tasks`, `telegrafs`, `users`, `variables`, `secrets`, `labels`, `views`,
`documents`, `notificationRules`, `notificationEndpoints`, `checks`, `dbrp`,
`annotations`, `sources`, `scrapers`, `notebooks`, `remotes`, `replications`.
Refer to `influx auth create --help` for a full list with descriptions.
`buckets` grants read access to all associated buckets. Use `readBuckets` to define
more granular access permissions.
'';
default = [];
type = types.listOf (types.enum validPermissions);
};
writePermissions = mkOption {
description = mdDoc ''
The read permissions to include for this token. Access is usually granted only
for resources in the associated organization.
Available permissions are `authorizations`, `buckets`, `dashboards`,
`orgs`, `tasks`, `telegrafs`, `users`, `variables`, `secrets`, `labels`, `views`,
`documents`, `notificationRules`, `notificationEndpoints`, `checks`, `dbrp`,
`annotations`, `sources`, `scrapers`, `notebooks`, `remotes`, `replications`.
Refer to `influx auth create --help` for a full list with descriptions.
`buckets` grants write access to all associated buckets. Use `writeBuckets` to define
more granular access permissions.
'';
default = [];
type = types.listOf (types.enum validPermissions);
};
readBuckets = mkOption {
description = mdDoc "The organization's buckets which should be allowed to be read";
default = [];
type = types.listOf types.str;
};
writeBuckets = mkOption {
description = mdDoc "The organization's buckets which should be allowed to be written";
default = [];
type = types.listOf types.str;
};
};
}));
};
};
});
in in
{ {
options = { options = {
services.influxdb2 = { services.influxdb2 = {
enable = mkEnableOption (lib.mdDoc "the influxdb2 server"); enable = mkEnableOption (mdDoc "the influxdb2 server");
package = mkOption { package = mkOption {
default = pkgs.influxdb2-server; default = pkgs.influxdb2-server;
defaultText = literalExpression "pkgs.influxdb2"; defaultText = literalExpression "pkgs.influxdb2";
description = lib.mdDoc "influxdb2 derivation to use."; description = mdDoc "influxdb2 derivation to use.";
type = types.package; type = types.package;
}; };
settings = mkOption { settings = mkOption {
default = { }; default = { };
description = lib.mdDoc ''configuration options for influxdb2, see <https://docs.influxdata.com/influxdb/v2.0/reference/config-options> for details.''; description = mdDoc ''configuration options for influxdb2, see <https://docs.influxdata.com/influxdb/v2.0/reference/config-options> for details.'';
type = format.type; type = format.type;
}; };
@ -41,52 +298,135 @@ in
organization = mkOption { organization = mkOption {
type = types.str; type = types.str;
example = "main"; example = "main";
description = "Primary organization name"; description = mdDoc "Primary organization name";
}; };
bucket = mkOption { bucket = mkOption {
type = types.str; type = types.str;
example = "example"; example = "example";
description = "Primary bucket name"; description = mdDoc "Primary bucket name";
}; };
username = mkOption { username = mkOption {
type = types.str; type = types.str;
default = "admin"; default = "admin";
description = "Primary username"; description = mdDoc "Primary username";
}; };
retention = mkOption { retention = mkOption {
type = types.str; type = types.ints.unsigned;
default = "0"; default = 0;
description = '' description = mdDoc "The duration in seconds for which the bucket will retain data (0 is infinite).";
The duration for which the bucket will retain data (0 is infinite).
Accepted units are `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds),
`s` (seconds), `m` (minutes), `h` (hours), `d` (days) and `w` (weeks).
'';
}; };
passwordFile = mkOption { passwordFile = mkOption {
type = types.path; type = types.path;
description = "Password for primary user. Don't use a file from the nix store!"; description = mdDoc "Password for primary user. Don't use a file from the nix store!";
}; };
tokenFile = mkOption { tokenFile = mkOption {
type = types.path; type = types.path;
description = "API Token to set for the admin user. Don't use a file from the nix store!"; description = mdDoc "API Token to set for the admin user. Don't use a file from the nix store!";
}; };
}; };
organizations = mkOption {
description = mdDoc "Organizations to provision.";
example = literalExpression ''
{
myorg = {
description = "My organization";
buckets.mybucket = {
description = "My bucket";
retention = 31536000; # 1 year
};
auths.mytoken = {
readBuckets = ["mybucket"];
tokenFile = "/run/secrets/mytoken";
};
};
}
'';
default = {};
type = types.attrsOf organizationSubmodule;
};
users = mkOption {
description = mdDoc "Users to provision.";
default = {};
example = literalExpression ''
{
# admin = {}; /* The initialSetup.username will automatically be added. */
myuser.passwordFile = "/run/secrets/myuser_password";
}
'';
type = types.attrsOf (types.submodule (userSubmod: let
user = userSubmod.config._module.args.name;
org = userSubmod.config.org;
in {
options = {
present = mkOption {
description = mdDoc "Whether to ensure that this user is present or absent.";
type = types.bool;
default = true;
};
passwordFile = mkOption {
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!";
default = null;
type = types.nullOr types.path;
};
};
}));
};
}; };
}; };
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
assertions = [ assertions =
{ [
assertion = !(hasAttr "bolt-path" cfg.settings) && !(hasAttr "engine-path" cfg.settings); {
message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd"; assertion = !(hasAttr "bolt-path" cfg.settings) && !(hasAttr "engine-path" cfg.settings);
} message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd";
]; }
]
++ flatten (flip mapAttrsToList cfg.provision.organizations (orgName: org:
flip mapAttrsToList org.auths (authName: auth:
[
{
assertion = 1 == count (x: x) [
auth.operator
auth.allAccess
(auth.readPermissions != []
|| auth.writePermissions != []
|| auth.readBuckets != []
|| auth.writeBuckets != [])
];
message = "influxdb2: provision.organizations.${orgName}.auths.${authName}: The `operator` and `allAccess` options are mutually exclusive with each other and the granular permission settings.";
}
(let unknownBuckets = subtractLists (attrNames org.buckets) auth.readBuckets; in {
assertion = unknownBuckets == [];
message = "influxdb2: provision.organizations.${orgName}.auths.${authName}: Refers to invalid buckets in readBuckets: ${toString unknownBuckets}";
})
(let unknownBuckets = subtractLists (attrNames org.buckets) auth.writeBuckets; in {
assertion = unknownBuckets == [];
message = "influxdb2: provision.organizations.${orgName}.auths.${authName}: Refers to invalid buckets in writeBuckets: ${toString unknownBuckets}";
})
]
)
));
services.influxdb2.provision = mkIf cfg.provision.enable {
organizations.${cfg.provision.initialSetup.organization} = {
buckets.${cfg.provision.initialSetup.bucket} = {
inherit (cfg.provision.initialSetup) retention;
};
};
users.${cfg.provision.initialSetup.username} = {
inherit (cfg.provision.initialSetup) passwordFile;
};
};
systemd.services.influxdb2 = { systemd.services.influxdb2 = {
description = "InfluxDB is an open-source, distributed, time series database"; description = "InfluxDB is an open-source, distributed, time series database";
@ -111,58 +451,38 @@ in
"admin-password:${cfg.provision.initialSetup.passwordFile}" "admin-password:${cfg.provision.initialSetup.passwordFile}"
"admin-token:${cfg.provision.initialSetup.tokenFile}" "admin-token:${cfg.provision.initialSetup.tokenFile}"
]; ];
ExecStartPost = mkIf cfg.provision.enable (
[provisioningScript] ++
# Only the restarter runs with elevated privileges
optional anyAuthDefined "+${restarterScript}"
);
}; };
path = [pkgs.influxdb2-cli]; path = [
pkgs.influxdb2-cli
pkgs.jq
];
# Mark if this is the first startup so postStart can do the initial setup # Mark if this is the first startup so postStart can do the initial setup.
preStart = mkIf cfg.provision.enable '' # Also extract any token secret mappings and apply them if this isn't the first start.
preStart = let
tokenPaths = listToAttrs (flatten
# For all organizations
(flip mapAttrsToList cfg.provision.organizations
# For each contained token that has a token file
(_: org: flip mapAttrsToList (filterAttrs (_: x: x.tokenFile != null) org.auths)
# Collect id -> tokenFile for the mapping
(_: auth: nameValuePair auth.id auth.tokenFile))));
tokenMappings = pkgs.writeText "token_mappings.json" (builtins.toJSON tokenPaths);
in mkIf cfg.provision.enable ''
if ! test -e "$STATE_DIRECTORY/influxd.bolt"; then if ! test -e "$STATE_DIRECTORY/influxd.bolt"; then
touch "$STATE_DIRECTORY/.first_startup" touch "$STATE_DIRECTORY/.first_startup"
else
# Manipulate provisioned api tokens if necessary
${getExe pkgs.influxdb2-token-manipulator} "$STATE_DIRECTORY/influxd.bolt" ${tokenMappings}
fi fi
''; '';
postStart = let
initCfg = cfg.provision.initialSetup;
in mkIf cfg.provision.enable (
''
set -euo pipefail
export INFLUX_HOST="http://"${escapeShellArg (cfg.settings.http-bind-address or "localhost:8086")}
# Wait for the influxdb server to come online
count=0
while ! influx ping &>/dev/null; do
if [ "$count" -eq 300 ]; then
echo "Tried for 30 seconds, giving up..."
exit 1
fi
if ! kill -0 "$MAINPID"; then
echo "Main server died, giving up..."
exit 1
fi
sleep 0.1
count=$((count++))
done
# Do the initial database setup. Pass /dev/null as configs-path to
# avoid saving the token as the active config.
if test -e "$STATE_DIRECTORY/.first_startup"; then
influx setup \
--configs-path /dev/null \
--org ${escapeShellArg initCfg.organization} \
--bucket ${escapeShellArg initCfg.bucket} \
--username ${escapeShellArg initCfg.username} \
--password "$(< "$CREDENTIALS_DIRECTORY/admin-password")" \
--token "$(< "$CREDENTIALS_DIRECTORY/admin-token")" \
--retention ${escapeShellArg initCfg.retention} \
--force >/dev/null
rm -f "$STATE_DIRECTORY/.first_startup"
fi
''
);
}; };
users.extraUsers.influxdb2 = { users.extraUsers.influxdb2 = {

View File

@ -6,6 +6,9 @@ import ./make-test-python.nix ({ pkgs, ...} : {
nodes.machine = { lib, ... }: { nodes.machine = { lib, ... }: {
environment.systemPackages = [ pkgs.influxdb2-cli ]; environment.systemPackages = [ pkgs.influxdb2-cli ];
# Make sure that the service is restarted immediately if tokens need to be rewritten
# without relying on any Restart=on-failure behavior
systemd.services.influxdb2.serviceConfig.RestartSec = 6000;
services.influxdb2.enable = true; services.influxdb2.enable = true;
services.influxdb2.provision = { services.influxdb2.provision = {
enable = true; enable = true;
@ -15,22 +18,208 @@ import ./make-test-python.nix ({ pkgs, ...} : {
passwordFile = pkgs.writeText "admin-pw" "ExAmPl3PA55W0rD"; passwordFile = pkgs.writeText "admin-pw" "ExAmPl3PA55W0rD";
tokenFile = pkgs.writeText "admin-token" "verysecureadmintoken"; tokenFile = pkgs.writeText "admin-token" "verysecureadmintoken";
}; };
organizations.someorg = {
buckets.somebucket = {};
auths.sometoken = {
description = "some auth token";
readBuckets = ["somebucket"];
writeBuckets = ["somebucket"];
};
};
users.someuser.passwordFile = pkgs.writeText "tmp-pw" "abcgoiuhaoga";
};
specialisation.withModifications.configuration = { ... }: {
services.influxdb2.provision = {
organizations.someorg.buckets.somebucket.present = false;
organizations.someorg.auths.sometoken.present = false;
users.someuser.present = false;
organizations.myorg = {
description = "Myorg description";
buckets.mybucket = {
description = "Mybucket description";
};
auths.mytoken = {
operator = true;
description = "operator token";
tokenFile = pkgs.writeText "tmp-tok" "someusertoken";
};
};
users.myuser.passwordFile = pkgs.writeText "tmp-pw" "abcgoiuhaoga";
};
};
specialisation.withParentDelete.configuration = { ... }: {
services.influxdb2.provision = {
organizations.someorg.present = false;
# Deleting the parent implies:
#organizations.someorg.buckets.somebucket.present = false;
#organizations.someorg.auths.sometoken.present = false;
};
};
specialisation.withNewTokens.configuration = { ... }: {
services.influxdb2.provision = {
organizations.default = {
auths.operator = {
operator = true;
description = "new optoken";
tokenFile = pkgs.writeText "tmp-tok" "newoptoken";
};
auths.allaccess = {
operator = true;
description = "new allaccess";
tokenFile = pkgs.writeText "tmp-tok" "newallaccess";
};
auths.specifics = {
description = "new specifics";
readPermissions = ["users" "tasks"];
writePermissions = ["tasks"];
tokenFile = pkgs.writeText "tmp-tok" "newspecificstoken";
};
};
};
}; };
}; };
testScript = { nodes, ... }: testScript = { nodes, ... }:
let let
specialisations = "${nodes.machine.system.build.toplevel}/specialisation";
tokenArg = "--token verysecureadmintoken"; tokenArg = "--token verysecureadmintoken";
in '' in ''
def assert_contains(haystack, needle):
if needle not in haystack:
print("The haystack that will cause the following exception is:")
print("---")
print(haystack)
print("---")
raise Exception(f"Expected string '{needle}' was not found")
def assert_lacks(haystack, needle):
if needle in haystack:
print("The haystack that will cause the following exception is:")
print("---")
print(haystack, end="")
print("---")
raise Exception(f"Unexpected string '{needle}' was found")
machine.wait_for_unit("influxdb2.service") machine.wait_for_unit("influxdb2.service")
machine.fail("curl --fail -X POST 'http://localhost:8086/api/v2/signin' -u admin:wrongpassword") machine.fail("curl --fail -X POST 'http://localhost:8086/api/v2/signin' -u admin:wrongpassword")
machine.succeed("curl --fail -X POST 'http://localhost:8086/api/v2/signin' -u admin:ExAmPl3PA55W0rD") machine.succeed("curl --fail -X POST 'http://localhost:8086/api/v2/signin' -u admin:ExAmPl3PA55W0rD")
out = machine.succeed("influx org list ${tokenArg}") out = machine.succeed("influx org list ${tokenArg}")
assert "default" in out assert_contains(out, "default")
assert_lacks(out, "myorg")
assert_contains(out, "someorg")
out = machine.succeed("influx bucket list ${tokenArg} --org default") out = machine.succeed("influx bucket list ${tokenArg} --org default")
assert "default" in out assert_contains(out, "default")
machine.fail("influx bucket list ${tokenArg} --org myorg")
out = machine.succeed("influx bucket list ${tokenArg} --org someorg")
assert_contains(out, "somebucket")
out = machine.succeed("influx user list ${tokenArg}")
assert_contains(out, "admin")
assert_lacks(out, "myuser")
assert_contains(out, "someuser")
out = machine.succeed("influx auth list ${tokenArg}")
assert_lacks(out, "operator token")
assert_contains(out, "some auth token")
with subtest("withModifications"):
machine.succeed('${specialisations}/withModifications/bin/switch-to-configuration test')
machine.wait_for_unit("influxdb2.service")
out = machine.succeed("influx org list ${tokenArg}")
assert_contains(out, "default")
assert_contains(out, "myorg")
assert_contains(out, "someorg")
out = machine.succeed("influx bucket list ${tokenArg} --org myorg")
assert_contains(out, "mybucket")
out = machine.succeed("influx bucket list ${tokenArg} --org someorg")
assert_lacks(out, "somebucket")
out = machine.succeed("influx user list ${tokenArg}")
assert_contains(out, "admin")
assert_contains(out, "myuser")
assert_lacks(out, "someuser")
out = machine.succeed("influx auth list ${tokenArg}")
assert_contains(out, "operator token")
assert_lacks(out, "some auth token")
# Make sure the user token is also usable
machine.succeed("influx auth list --token someusertoken")
with subtest("keepsUnrelated"):
machine.succeed('${nodes.machine.system.build.toplevel}/bin/switch-to-configuration test')
machine.wait_for_unit("influxdb2.service")
out = machine.succeed("influx org list ${tokenArg}")
assert_contains(out, "default")
assert_contains(out, "myorg")
assert_contains(out, "someorg")
out = machine.succeed("influx bucket list ${tokenArg} --org default")
assert_contains(out, "default")
out = machine.succeed("influx bucket list ${tokenArg} --org myorg")
assert_contains(out, "mybucket")
out = machine.succeed("influx bucket list ${tokenArg} --org someorg")
assert_contains(out, "somebucket")
out = machine.succeed("influx user list ${tokenArg}")
assert_contains(out, "admin")
assert_contains(out, "myuser")
assert_contains(out, "someuser")
out = machine.succeed("influx auth list ${tokenArg}")
assert_contains(out, "operator token")
assert_contains(out, "some auth token")
with subtest("withParentDelete"):
machine.succeed('${specialisations}/withParentDelete/bin/switch-to-configuration test')
machine.wait_for_unit("influxdb2.service")
out = machine.succeed("influx org list ${tokenArg}")
assert_contains(out, "default")
assert_contains(out, "myorg")
assert_lacks(out, "someorg")
out = machine.succeed("influx bucket list ${tokenArg} --org default")
assert_contains(out, "default")
out = machine.succeed("influx bucket list ${tokenArg} --org myorg")
assert_contains(out, "mybucket")
machine.fail("influx bucket list ${tokenArg} --org someorg")
out = machine.succeed("influx user list ${tokenArg}")
assert_contains(out, "admin")
assert_contains(out, "myuser")
assert_contains(out, "someuser")
out = machine.succeed("influx auth list ${tokenArg}")
assert_contains(out, "operator token")
assert_lacks(out, "some auth token")
with subtest("withNewTokens"):
machine.succeed('${specialisations}/withNewTokens/bin/switch-to-configuration test')
machine.wait_for_unit("influxdb2.service")
out = machine.succeed("influx auth list ${tokenArg}")
assert_contains(out, "operator token")
assert_contains(out, "some auth token")
assert_contains(out, "new optoken")
assert_contains(out, "new allaccess")
assert_contains(out, "new specifics")
''; '';
}) })