From b4d718f14a00c5cad817406f08052a52c2fef4ed Mon Sep 17 00:00:00 2001 From: oddlama Date: Tue, 15 Aug 2023 00:26:41 +0200 Subject: [PATCH] nixos/influxdb2: add initial setup automation and nixos tests --- .../modules/services/databases/influxdb2.nix | 124 +++++++++++++++++- nixos/tests/all-tests.nix | 1 + nixos/tests/influxdb2.nix | 36 +++++ pkgs/servers/nosql/influxdb2/default.nix | 3 + 4 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 nixos/tests/influxdb2.nix diff --git a/nixos/modules/services/databases/influxdb2.nix b/nixos/modules/services/databases/influxdb2.nix index e74de66ddc2f..8e3135c9e235 100644 --- a/nixos/modules/services/databases/influxdb2.nix +++ b/nixos/modules/services/databases/influxdb2.nix @@ -1,8 +1,17 @@ { config, lib, pkgs, ... }: -with lib; - let + inherit + (lib) + escapeShellArg + hasAttr + literalExpression + mkEnableOption + mkIf + mkOption + types + ; + format = pkgs.formats.json { }; cfg = config.services.influxdb2; configFile = format.generate "config.json" cfg.settings; @@ -24,14 +33,60 @@ in description = lib.mdDoc ''configuration options for influxdb2, see for details.''; type = format.type; }; + + provision = { + enable = mkEnableOption "initial database setup and provisioning"; + + initialSetup = { + organization = mkOption { + type = types.str; + example = "main"; + description = "Primary organization name"; + }; + + bucket = mkOption { + type = types.str; + example = "example"; + description = "Primary bucket name"; + }; + + username = mkOption { + type = types.str; + default = "admin"; + description = "Primary username"; + }; + + retention = mkOption { + type = types.str; + default = "0"; + description = '' + 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 { + type = types.path; + description = "Password for primary user. Don't use a file from the nix store!"; + }; + + tokenFile = mkOption { + type = types.path; + description = "API Token to set for the admin user. Don't use a file from the nix store!"; + }; + }; + }; }; }; config = mkIf cfg.enable { - assertions = [{ - assertion = !(builtins.hasAttr "bolt-path" cfg.settings) && !(builtins.hasAttr "engine-path" cfg.settings); - message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd"; - }]; + 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"; + } + ]; systemd.services.influxdb2 = { description = "InfluxDB is an open-source, distributed, time series database"; @@ -52,7 +107,62 @@ in LimitNOFILE = 65536; KillMode = "control-group"; Restart = "on-failure"; + LoadCredential = [ + "admin-password:${cfg.provision.initialSetup.passwordFile}" + "admin-token:${cfg.provision.initialSetup.tokenFile}" + ]; }; + + path = [pkgs.influxdb2-cli]; + + # Mark if this is the first startup so postStart can do the initial setup + preStart = mkIf cfg.provision.enable '' + if ! test -e "$STATE_DIRECTORY/influxd.bolt"; then + touch "$STATE_DIRECTORY/.first_startup" + 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 = { @@ -63,5 +173,5 @@ in users.extraGroups.influxdb2 = {}; }; - meta.maintainers = with lib.maintainers; [ nickcao ]; + meta.maintainers = with lib.maintainers; [ nickcao oddlama ]; } diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 530447b99786..9fcb92a9e4f3 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -367,6 +367,7 @@ in { iftop = handleTest ./iftop.nix {}; incron = handleTest ./incron.nix {}; influxdb = handleTest ./influxdb.nix {}; + influxdb2 = handleTest ./influxdb2.nix {}; initrd-network-openvpn = handleTest ./initrd-network-openvpn {}; initrd-network-ssh = handleTest ./initrd-network-ssh {}; initrd-luks-empty-passphrase = handleTest ./initrd-luks-empty-passphrase.nix {}; diff --git a/nixos/tests/influxdb2.nix b/nixos/tests/influxdb2.nix new file mode 100644 index 000000000000..c9c54b788cc0 --- /dev/null +++ b/nixos/tests/influxdb2.nix @@ -0,0 +1,36 @@ +import ./make-test-python.nix ({ pkgs, ...} : { + name = "influxdb2"; + meta = with pkgs.lib.maintainers; { + maintainers = [ offline ]; + }; + + nodes.machine = { lib, ... }: { + environment.systemPackages = [ pkgs.influxdb2-cli ]; + services.influxdb2.enable = true; + services.influxdb2.provision = { + enable = true; + initialSetup = { + organization = "default"; + bucket = "default"; + passwordFile = pkgs.writeText "admin-pw" "ExAmPl3PA55W0rD"; + tokenFile = pkgs.writeText "admin-token" "verysecureadmintoken"; + }; + }; + }; + + testScript = { nodes, ... }: + let + tokenArg = "--token verysecureadmintoken"; + in '' + machine.wait_for_unit("influxdb2.service") + + 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") + + out = machine.succeed("influx org list ${tokenArg}") + assert "default" in out + + out = machine.succeed("influx bucket list ${tokenArg} --org default") + assert "default" in out + ''; +}) diff --git a/pkgs/servers/nosql/influxdb2/default.nix b/pkgs/servers/nosql/influxdb2/default.nix index a5cfa5ee55be..6a78aa70ee60 100644 --- a/pkgs/servers/nosql/influxdb2/default.nix +++ b/pkgs/servers/nosql/influxdb2/default.nix @@ -8,6 +8,7 @@ , rustPlatform , stdenv , libiconv +, nixosTests }: let @@ -107,6 +108,8 @@ in buildGoModule { ldflags = [ "-X main.commit=v${version}" "-X main.version=${version}" ]; + passthru.tests = { inherit (nixosTests) influxdb2; }; + meta = with lib; { description = "An open-source distributed time series database"; license = licenses.mit;