Fediversity/tests/rebuildableTest.nix
Taeer Bar-Yam 693e21b1a8 first stab at a nixos test
for now, had to get rid of vmVariant. we can figure out how to add it
back when we understand how we should actually distinguish between
real machines and VMs
2024-06-25 06:39:04 -04:00

150 lines
4.5 KiB
Nix

pkgs: test:
let
inherit (pkgs.lib) mapAttrsToList concatStringsSep genAttrs mkIf;
inherit (builtins) attrNames;
interactiveConfig = ({ config, ... }: {
# so we can run `nix shell nixpkgs#foo` on the machines
nix.extraOptions = ''
extra-experimental-features = nix-command flakes
'';
# so we can ssh in and rebuild them
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "yes";
PermitEmptyPasswords = "yes";
UsePAM = "no";
};
};
virtualisation = mkIf (config.networking.hostName == "jumphost") {
forwardPorts = [{
from = "host";
host.port = 2222;
guest.port = 22;
}];
};
});
sshConfig = pkgs.writeText "ssh-config" ''
Host *
User root
StrictHostKeyChecking no
BatchMode yes
ConnectTimeout 20
UserKnownHostsFile=/dev/null
LogLevel Error # no "added to known hosts"
Host jumphost
Port 2222
HostName localhost
Host * !jumphost
ProxyJump jumphost
'';
# one should first start up the interactive test driver, then start the
# machines, then update the config, and then redeploy with the `rebuildScript`
# associated with the new config.
rebuildScript = pkgs.writeShellScriptBin "rebuild" ''
# create an association array from machine names to the path to their
# configuration in the nix store
declare -A configPaths=(${
concatStringsSep " "
(mapAttrsToList
(n: v: ''["${n}"]="${v.system.build.toplevel}"'')
rebuildableTest.driverInteractive.nodes)
})
rebuild_one() {
machine="$1"
echo "pushing new config to $machine"
if [ -z ''${configPaths[$machine]+x} ]; then
echo 'No machine '"$machine"' in this test.'
exit 1
fi
if ! ssh -F ${sshConfig} $machine true; then
echo 'Couldn'"'"'t connect to '"$machine"'. Make sure you'"'"'ve started it with `'"$machine"'.start()` in the test interactive driver.'
exit 1
fi
# taken from nixos-rebuild (we only want to do the activate part)
cmd=(
"systemd-run"
"-E" "LOCALE_ARCHIVE"
"--collect"
"--no-ask-password"
"--pty"
"--quiet"
"--same-dir"
"--service-type=exec"
"--unit=nixos-rebuild-switch-to-configuration"
"--wait"
"''${configPaths[$machine]}/bin/switch-to-configuration"
"test"
)
if ! ssh -F ${sshConfig} $machine "''${cmd[@]}"; then
echo "warning: error(s) occurred while switching to the new configuration"
exit 1
fi
}
if ! ssh -F ${sshConfig} jumphost true; then
echo 'Couldn'"'"'t connect to jump host. Make sure you are running driverInteractive, and that you'"'"'ve run `jumphost.start()` and `jumphost.forward_port(2222,22)`'
exit 1
fi
if [ -n "$1" ]; then
rebuild_one "$1"
else
for machine in ${concatStringsSep " " (attrNames rebuildableTest.driverInteractive.nodes)}; do
rebuild_one $machine
done
fi
'';
# NOTE: This is awkward because NixOS does not expose the module interface
# that is used to build tests. When we upstream this, we can build it into the
# system more naturally (and expose more of the interface to end users while
# we're at it)
rebuildableTest =
let
preOverride = pkgs.nixosTest (test // {
interactive = (test.interactive or { }) // {
# no need to // with test.interactive.nodes here, since we are iterating
# over all of them, and adding back in the config via `imports`
nodes = genAttrs
(
attrNames test.nodes or { } ++
attrNames test.interactive.nodes or { } ++
[ "jumphost" ]
)
(n: {
imports = [
(test.interactive.${n} or { })
interactiveConfig
];
});
};
# override with test.passthru in case someone wants to overwrite us.
passthru = { inherit rebuildScript sshConfig; } // (test.passthru or { });
});
in
preOverride // {
driverInteractive = preOverride.driverInteractive.overrideAttrs (old: {
# this comes from runCommand, not mkDerivation, so this is the only
# hook we have to override
buildCommand = old.buildCommand + ''
ln -s ${sshConfig} $out/ssh-config
ln -s ${rebuildScript}/bin/rebuild $out/bin/rebuild
'';
});
};
in
rebuildableTest