From b5a96a70b8d19d9ebf5c42baf53d13327a29b170 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Wed, 27 Aug 2025 20:40:49 +0200 Subject: [PATCH] add nixops4 data model test --- deployment/check/common/nixosTest.nix | 2 +- deployment/check/data-model/constants.nix | 4 +- deployment/check/data-model/default.nix | 7 +- deployment/check/data-model/deployment.nix | 114 ++++++++----- .../check/data-model/flake-under-test.nix | 26 +++ deployment/check/data-model/nixosTest.nix | 150 +++++++++--------- deployment/data-model-test.nix | 2 +- deployment/data-model.nix | 19 +++ 8 files changed, 210 insertions(+), 114 deletions(-) create mode 100644 deployment/check/data-model/flake-under-test.nix diff --git a/deployment/check/common/nixosTest.nix b/deployment/check/common/nixosTest.nix index 93bd3fef..574621c4 100644 --- a/deployment/check/common/nixosTest.nix +++ b/deployment/check/common/nixosTest.nix @@ -163,7 +163,7 @@ in '')} ${ - if config.useFlake then + if true then '' ## NOTE: This is super slow. It could probably be optimised in Nix, for ## instance by allowing to grab things directly from the host's store. diff --git a/deployment/check/data-model/constants.nix b/deployment/check/data-model/constants.nix index 0c8576a7..9cc81ea9 100644 --- a/deployment/check/data-model/constants.nix +++ b/deployment/check/data-model/constants.nix @@ -1,7 +1,9 @@ { targetMachines = [ - "hello" + "ssh" + "nixops4" ]; pathToRoot = ../../..; pathFromRoot = ./.; + enableAcme = true; } diff --git a/deployment/check/data-model/default.nix b/deployment/check/data-model/default.nix index 11c1fb37..1815f19a 100644 --- a/deployment/check/data-model/default.nix +++ b/deployment/check/data-model/default.nix @@ -12,5 +12,10 @@ runNixOSTest { ./nixosTest.nix ]; _module.args = { inherit inputs sources; }; - inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot; + inherit (import ./constants.nix) + targetMachines + pathToRoot + pathFromRoot + enableAcme + ; } diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index 69380519..769fea74 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -12,11 +12,12 @@ let inherit (pkgs) lib; deployment-config = config; inherit (lib) mkOption types; + inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot; eval = module: (lib.evalModules { specialArgs = { - inherit inputs; + inherit pkgs inputs; }; modules = [ module @@ -98,44 +99,67 @@ let }; }; }; - environments.single-nixos-vm = environment: { - resources."operator-environment".login-shell.username = "operator"; - implementation = requests: { - input = requests; - output.ssh-host = { - ssh = { - username = "root"; - inherit (deployment-config) host; - key-file = null; + environments = + let + mkNixosConfiguration = + environment: requests: + { ... }: + { + imports = [ + ./options.nix + ../common/sharedOptions.nix + ../common/targetNode.nix + "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + ]; + + users.users = environment.config.resources."operator-environment".login-shell.apply { + resources = lib.filterAttrs (_name: value: value ? login-shell) ( + lib.concatMapAttrs ( + k': req: lib.mapAttrs' (k: lib.nameValuePair "${k'}.${k}") req.resources + ) requests + ); + }; }; - nixos-configuration = - { ... }: - { - imports = [ - ./options.nix - ../common/sharedOptions.nix - ../common/targetNode.nix - "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" - ]; - - inherit (deployment-config) enableAcme; - acmeNodeIP = - if deployment-config.enableAcme then - deployment-config.nodes.acme.networking.primaryIPAddress - else - null; - - users.users = environment.config.resources."operator-environment".login-shell.apply { - resources = lib.filterAttrs (_name: value: value ? login-shell) ( - lib.concatMapAttrs ( - k': req: lib.mapAttrs' (k: lib.nameValuePair "${k'}.${k}") req.resources - ) requests - ); + in + { + single-nixos-vm-ssh = environment: { + resources."operator-environment".login-shell.username = "operator"; + implementation = requests: { + input = requests; + output.ssh-host = { + nixos-configuration = mkNixosConfiguration environment requests; + ssh = { + username = "root"; + inherit (deployment-config) host; + key-file = null; }; }; + }; + }; + single-nixos-vm-nixops4 = environment: { + resources."operator-environment".login-shell.username = "operator"; + implementation = requests: { + input = requests; + output.nixops4 = + { providers, ... }: + { + providers = { + inherit (inputs.nixops4.modules.nixops4Provider) local; + }; + resources = lib.genAttrs targetMachines (nodeName: { + type = providers.local.exec; + imports = [ + inputs.nixops4-nixos.modules.nixops4Resource.nixos + ../common/targetResource.nix + ]; + nixos.module = mkNixosConfiguration environment requests; + _module.args = { inherit inputs sources; }; + inherit nodeName pathToRoot pathFromRoot; + }); + }; + }; }; }; - }; }; options = { "example-configuration" = mkOption { @@ -145,12 +169,24 @@ let applications.hello.enable = true; }; }; - "example-deployment" = mkOption { - type = config.environments.single-nixos-vm.resource-mapping.output-type; - default = config.environments.single-nixos-vm.deployment config."example-configuration"; - }; + "ssh-deployment" = + let + env = config.environments."single-nixos-vm-ssh"; + in + mkOption { + type = env.resource-mapping.output-type; + default = env.deployment config."example-configuration"; + }; + "nixops4-deployment" = + let + env = config.environments."single-nixos-vm-nixops4"; + in + mkOption { + type = env.resource-mapping.output-type; + default = env.deployment config."example-configuration"; + }; }; } ); in -fediversity."example-deployment" +fediversity diff --git a/deployment/check/data-model/flake-under-test.nix b/deployment/check/data-model/flake-under-test.nix new file mode 100644 index 00000000..d6f4482d --- /dev/null +++ b/deployment/check/data-model/flake-under-test.nix @@ -0,0 +1,26 @@ +{ + inputs = { + nixops4.follows = "nixops4-nixos/nixops4"; + nixops4-nixos.url = "github:nixops4/nixops4-nixos"; + }; + + outputs = + inputs: + import ./mkFlake.nix inputs ( + { inputs, ... }: + let + system = "x86_64-linux"; + in + { + imports = [ + inputs.nixops4.modules.flake.default + ]; + + nixops4Deployments.check-deployment-model = + (import ./deployment/check/data-model/deployment.nix { + inherit system inputs; + config.host = "nixops4"; + })."nixops4-deployment".nixops4; + } + ); +} diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 11c0e3a6..019203c8 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -2,11 +2,28 @@ lib, config, pkgs, + inputs, ... }: let - inherit (import ./constants.nix) targetMachines pathToRoot; + inherit (import ./constants.nix) pathToRoot; escapedJson = v: lib.replaceStrings [ "\"" ] [ "\\\\\"" ] (lib.strings.toJSON v); + deployment-config = { + inherit (config) enableAcme; + acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; + host = "ssh"; + }; + inherit + ((import ./deployment.nix { + inherit (pkgs) system; + inherit inputs; + config = deployment-config; + })."ssh-deployment".ssh-host.ssh + ) + host + username + key-file + ; in { _class = "nixosTest"; @@ -20,14 +37,24 @@ in ../../function.nix ./constants.nix ./deployment.nix + ./options.nix + (config.pathToCwd + "/flake-under-test.nix") ]; nodes.deployer = { pkgs, ... }: { environment.systemPackages = with pkgs; [ + inputs.nixops4.packages.${system}.default jq ]; + + # FIXME: sad times + system.extraDependencies = with pkgs; [ + jq + jq.inputDerivation + ]; + system.extraDependenciesFromModule = { pkgs, ... }: { @@ -38,77 +65,58 @@ in }; extraTestScript = '' - ${lib.concatStringsSep "\n" ( - lib.lists.map ( - nodeName: - let - deployment-config = { - inherit (config) enableAcme; - acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; - host = nodeName; - }; - inherit - ((import ./deployment.nix { - inherit (pkgs) system; - config = deployment-config; - }).ssh-host.ssh - ) - host - username - key-file - ; - in - '' - with subtest("Check the status before deployment"): - ${nodeName}.fail("${nodeName} 1>&2") + with subtest("nixops4"): + nixops4.fail("hello 1>&2") + deployer.succeed("nixops4 apply check-deployment-model --show-trace --verbose --no-interactive 1>&2") + nixops4.succeed("su - operator -c hello 1>&2") - with subtest("Run the deployment for ${nodeName}"): - deployer.succeed(""" - set -euo pipefail + with subtest("ssh: Check the status before deployment"): + ssh.fail("hello 1>&2") - # INSTANTIATE - command=(nix-instantiate --show-trace --expr ' - let - system = "${pkgs.system}"; # FIXME: what system are we deploying to? - in - import ${pathToRoot}/deployment/nixos.nix { - inherit system; - configuration = ( - import ${pathToRoot}/deployment/check/data-model/deployment.nix { - inherit system; - config = builtins.fromJSON "${escapedJson deployment-config}"; - } - ).ssh-host.nixos-configuration; - } - ') - # DEPLOY - host="${lib.defaultTo "root" username}@${host}" - sshOpts=( - ${if key-file == null then "" else "-i ${key-file}"} - -o StrictHostKeyChecking=no - -o "ConnectTimeout=1" - -o "ServerAliveInterval=1" - ) - # instantiate the config in /nix/store - "''${command[@]}" --show-trace -A out_path - # get the realized derivation to deploy - outPath=$(nix-store --realize "$("''${command[@]}" --show-trace --eval --strict --json | jq -r '.drv_path')") - # deploy the config by nix-copy-closure - NIX_SSHOPTS="''${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes - # switch the remote host to the config - output=$(ssh "''${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; nohup $outPath/bin/switch-to-configuration switch &" 2>&1) || echo "status code: $?" - echo "output: $output" - if [[ $output != *"Timeout, server ${nodeName} not responding"* ]]; then - echo "non-timeout error: $output" - exit 1 - else - exit 0 - fi - """) - ${nodeName}.wait_for_unit("multi-user.target") - ${nodeName}.succeed("su - operator -c ${nodeName} 1>&2") - '' - ) targetMachines - )} + with subtest("ssh: Run the deployment"): + deployer.succeed(""" + set -euo pipefail + + # INSTANTIATE + command=(nix-instantiate --show-trace --expr ' + let + system = "${pkgs.system}"; # FIXME: what system are we deploying to? + in + import ${pathToRoot}/deployment/nixos.nix { + inherit system; + configuration = ( + import ${pathToRoot}/deployment/check/data-model/deployment.nix { + inherit system; + config = builtins.fromJSON "${escapedJson deployment-config}"; + } + )."ssh-deployment".ssh-host.nixos-configuration; + } + ') + # DEPLOY + host="${lib.defaultTo "root" username}@${host}" + sshOpts=( + ${if key-file == null then "" else "-i ${key-file}"} + -o StrictHostKeyChecking=no + -o "ConnectTimeout=1" + -o "ServerAliveInterval=1" + ) + # instantiate the config in /nix/store + "''${command[@]}" --show-trace -A out_path + # get the realized derivation to deploy + outPath=$(nix-store --realize "$("''${command[@]}" --show-trace --eval --strict --json | jq -r '.drv_path')") + # deploy the config by nix-copy-closure + NIX_SSHOPTS="''${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes + # switch the remote host to the config + output=$(ssh "''${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; nohup $outPath/bin/switch-to-configuration switch &" 2>&1) || echo "status code: $?" + echo "output: $output" + if [[ $output != *"Timeout, server ssh not responding"* ]]; then + echo "non-timeout error: $output" + exit 1 + else + exit 0 + fi + """) + ssh.wait_for_unit("multi-user.target") + ssh.succeed("su - operator -c hello 1>&2") ''; } diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index e754bc03..aad95026 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -6,7 +6,7 @@ let module: (lib.evalModules { specialArgs = { - inherit inputs; + inherit pkgs inputs; }; modules = [ module diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 90d3b0d2..89b7275e 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -1,6 +1,8 @@ { lib, config, + inputs, + pkgs, ... }: let @@ -28,6 +30,19 @@ let ); }; }; + nixops4Deployment = types.deferredModuleWith { + staticModules = [ + inputs.nixops4.modules.nixops4Deployment.default + + { + _class = "nixops4Deployment"; + _module.args = { + resourceProviderSystem = pkgs.system; + resources = { }; + }; + } + ]; + }; nixos-configuration = mkOption { description = "A NixOS configuration."; type = raw; @@ -63,6 +78,10 @@ let }; }; }; + nixops4 = mkOption { + description = "A NixOps4 NixOS deployment. For an example, see https://github.com/nixops4/nixops4-nixos/blob/main/example/deployment.nix."; + type = nixops4Deployment; + }; }; in {