diff options
| author | tv <tv@krebsco.de> | 2026-11-12 01:19:54 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-12 01:19:54 +0000 |
| commit | ea6d6b16fa422d3fb0a7fea671d3637b89751721 (patch) | |
| tree | 43ed25bab755e8884d8b1f070464ebe63415ffb8 | |
| parent | 69f0ab4533a6b233dca1193ee558de8994df1ffb (diff) | |
krops writeDeploy: pass --use-remote-sudo only with a separate build target
For targets using sudo, writeDeploy runs nixos-rebuild on the target via
sudo, so --use-remote-sudo has no effect there. It only matters when
nixos-rebuild runs on a separate build target and activates the system
on the target, so writeDeploy passes it only then.
A new VM test, deploy, runs writeDeploy with a stand-in for
nixos-rebuild, which records the user it runs as, NIX_PATH, and its
arguments, for deploying flakes and other sources, with and without a
separate build target, to every kind of target.
| -rw-r--r-- | pkgs/krops/default.nix | 3 | ||||
| -rw-r--r-- | tests/default.nix | 5 | ||||
| -rwxr-xr-x | tests/nixos-rebuild.sh | 8 | ||||
| -rw-r--r-- | tests/scenarios.nix | 73 | ||||
| -rw-r--r-- | tests/vm.nix | 8 |
5 files changed, 85 insertions, 12 deletions
diff --git a/pkgs/krops/default.nix b/pkgs/krops/default.nix index dcda28f..429fc61 100644 --- a/pkgs/krops/default.nix +++ b/pkgs/krops/default.nix @@ -135,7 +135,8 @@ in ] ++ lib.optionals (buildTarget' != target') [ "--build-host" "${buildTarget'.user}@${buildTarget'.host}" "--target-host" "${target'.user}@${target'.host}" - ] ++ lib.optionals target'.sudo [ + ] ++ lib.optionals (buildTarget' != target' && target'.sudo) [ + # nixos-rebuild runs on the target itself via sudo otherwise. "--use-remote-sudo" ]) buildTarget'} '' diff --git a/tests/default.nix b/tests/default.nix index e9ea769..1f3de5c 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -1,5 +1,5 @@ -# NixOS VM tests for populate, one test per source type, plus one for -# features spanning sources. Build all with nix-build tests, or one with, +# NixOS VM tests for populate, one test per source type, one for features +# spanning sources, and one for writeDeploy's arguments to nixos-rebuild. Build all with nix-build tests, or one with, # e.g., nix-build tests -A flake. { pkgs ? import ../pkgs {} }: @@ -12,6 +12,7 @@ let inherit (pkgs) lib; krops = import ../lib; inherit (pkgs) populate; + inherit (pkgs.krops) writeDeploy; url = "http://deployer:8000"; gitUrl = "git://deployer/gitsrc.git"; remoteHost = "target"; diff --git a/tests/nixos-rebuild.sh b/tests/nixos-rebuild.sh new file mode 100755 index 0000000..9e2e47e --- /dev/null +++ b/tests/nixos-rebuild.sh @@ -0,0 +1,8 @@ +#! /bin/sh +# Stands in for nixos-rebuild: records the user it runs as, NIX_PATH, and its +# arguments in /tmp/nixos-rebuild.log. +{ + id -u + printf '%s\n' "${NIX_PATH-}" + printf '%s\n' "$*" +} > /tmp/nixos-rebuild.log diff --git a/tests/scenarios.nix b/tests/scenarios.nix index c9774bf..429cff8 100644 --- a/tests/scenarios.nix +++ b/tests/scenarios.nix @@ -3,9 +3,9 @@ # a remote target using sudo, plus scenarios for features spanning sources. # # A scenario prepares the target directory, then runs its phases. Each phase -# runs populate, expecting it to succeed or to fail with a message, and checks -# the result on the target. Phases expecting evaluation to fail get checked -# here and don't run. +# runs populate, or writeDeploy with a stand-in for nixos-rebuild, expecting +# it to succeed or to fail with a message, and checks the result on the +# target. Phases expecting evaluation to fail get checked here and don't run. { # nixpkgs' lib. lib, @@ -13,6 +13,8 @@ krops, # Function from populate's arguments to the command running populate. populate, + # krops' writeDeploy. + writeDeploy, # Where the deploying machine serves /fixtures/www via HTTP. url, # URL of the Git repository /fixtures/git/gitsrc.git. @@ -28,7 +30,7 @@ let inherit (lib) attrNames concatMap concatStringsSep foldl' isBool mapAttrsToList - optional optionalAttrs optionalString; + optional optionalAttrs optionals optionalString; prelude = ". ${./lib.sh}\n"; @@ -60,16 +62,22 @@ let alt = { alt.file = "/fixtures/alt"; }; # Builds a scenario from a name, a target kind, and phases with sources. - scenario = { name, targetKind, prepare ? "", phases }: let + scenario = { name, targetKind, node ? null, prepare ? "", phases }: let path = "/var/krops/${name}"; target = targets.${targetKind} path; vars = "T=${path}\n"; - phase = { sources, prune ? false, expect ? "success", check ? "", mutate ? null }: let - command = populate { + phase = { + sources, prune ? false, deploy ? null, + expect ? "success", check ? "", mutate ? null, + }: let + args = { source = krops.evalSource [ sources ]; inherit prune target; force = true; }; + command = + if deploy == null then populate args + else writeDeploy "deploy" (args // { useNixOutputMonitor = false; } // deploy); evaluated = builtins.tryEval (builtins.seq (toString command) true); in if expect == "eval-error" then @@ -85,7 +93,10 @@ let }; in { inherit name; - node = if targetKind == "local" then "deployer" else "target"; + node = + if node != null then node + else if targetKind == "local" then "deployer" + else "target"; prepare = prelude + vars + '' rm -fR "$T" '' + prepare; @@ -404,6 +415,52 @@ in { }; }); + # writeDeploy's arguments to nixos-rebuild, which a stand-in records. + deploy = matrix "deploy" { + flake = [ false true ]; + buildTarget = [ false true ]; + } (flags: ctx: let + target = targets.${ctx.targetKind} ctx.path; + buildTarget = targets.local "${ctx.path}-build"; + build = if flags.buildTarget then buildTarget else target; + expected = [ "switch" ] + ++ optionals flags.buildTarget [ + "--build-host" "${buildTarget.user}@${buildTarget.host}" + "--target-host" "${target.user}@${target.host}" + ] + ++ optional (flags.buildTarget && target.sudo) "--use-remote-sudo" + ++ optionals flags.flake [ "--flake" "${build.path}/x" ]; + in + # The local target can't have another build target. + if flags.buildTarget && ctx.targetKind == "local" then null else { + node = if flags.buildTarget then "deployer" else null; + prepare = '' + rm -f /tmp/nixos-rebuild.log + ''; + phases = twice { + sources.x = + if flags.flake then { + flake = { + flake = "/fixtures/top"; + fetchOnTarget = ctx.targetKind == "sudo"; + }; + } else { + file = "/fixtures/alt"; + }; + deploy = { + flake = if flags.flake then "x" else null; + } // optionalAttrs flags.buildTarget { + inherit buildTarget; + }; + check = '' + expect_eq user "$(sed -n 1p /tmp/nixos-rebuild.log)" 0 + expect_eq NIX_PATH "$(sed -n 2p /tmp/nixos-rebuild.log)" ${build.path} + expect_eq arguments "$(sed -n 3p /tmp/nixos-rebuild.log)" \ + ${lib.escapeShellArg (concatStringsSep " " expected)} + ''; + }; + }); + # Features spanning several sources. features = concatMap (targetKind: let s = name: args: scenario ({ name = "${name}-${targetKind}"; inherit targetKind; } // args); diff --git a/tests/vm.nix b/tests/vm.nix index cb6385d..b46b015 100644 --- a/tests/vm.nix +++ b/tests/vm.nix @@ -28,7 +28,13 @@ let # system, they survive the scenarios collecting garbage. system.extraDependencies = [ plan "${./fixtures.sh}" ]; nix.settings.substituters = lib.mkForce []; - environment.systemPackages = [ pkgs.git pkgs.rsync ]; + environment.systemPackages = [ + pkgs.git + pkgs.rsync + (pkgs.writeScriptBin "nixos-rebuild" (builtins.readFile ./nixos-rebuild.sh)) + ]; + # The stand-in replaces nixos-rebuild. + system.disableInstallerTools = true; # Only warnings and errors on the console, which ends up in the log. services.journald.extraConfig = lib.mkAfter "MaxLevelConsole=warning"; boot.initrd.systemd.contents."/etc/systemd/journald.conf".text = |
