{ testers, inputs, runCommandNoCC, nixops4-flake-in-a-bottle, ... }: testers.runNixOSTest ( { lib, config, hostPkgs, ... }: let vmSystem = config.node.pkgs.hostPlatform.system; pathToRoot = ../../..; pathFromRoot = "deployment/check/basic"; deploymentName = "check-deployment-basic"; ## TODO: sanity check the existence of (pathToRoot + "/flake.nix") ## TODO: sanity check that (pathToRoot + "/${pathFromRoot}" == ./.) ## The whole repository, with the flake at its root. src = lib.fileset.toSource { fileset = pathToRoot; root = pathToRoot; }; ## We will need to override some inputs by the empty flake, so we make one. emptyFlake = runCommandNoCC "empty-flake" { } '' mkdir $out echo "{ outputs = { self }: {}; }" > $out/flake.nix ''; targetNetworkJSON = hostPkgs.writeText "target-network.json" ( builtins.toJSON config.nodes.target.system.build.networkConfig ); in { name = "deployment-basic"; imports = [ inputs.nixops4-nixos.modules.nixosTest.static ]; nodes = { deployer = { pkgs, nodes, ... }: { environment.systemPackages = [ inputs.nixops4.packages.${vmSystem}.default ]; virtualisation = { ## Memory use is expected to be dominated by the NixOS evaluation, ## which happens on the deployer. memorySize = 4096; diskSize = 10 * 1024; cores = 2; }; nix.settings = { substituters = lib.mkForce [ ]; hashed-mirrors = null; connect-timeout = 1; }; system.extraDependencies = [ "${inputs.flake-parts}" "${inputs.flake-parts.inputs.nixpkgs-lib}" "${inputs.nixops4}" "${inputs.nixops4-nixos}" "${inputs.nixpkgs}" pkgs.stdenv pkgs.stdenvNoCC pkgs.cowsay pkgs.cowsay.inputDerivation # NOTE: Crucial!!! ## Some derivations will be different compared to target's initial ## state, so we'll need to be able to build something similar. ## Generally the derivation inputs aren't that different, so we ## use the initial state of the target as a base. nodes.target.system.build.toplevel.inputDerivation nodes.target.system.build.etc.inputDerivation nodes.target.system.path.inputDerivation nodes.target.system.build.bootStage1.inputDerivation nodes.target.system.build.bootStage2.inputDerivation ] ++ lib.concatLists ( lib.mapAttrsToList ( _k: v: if v ? source.inputDerivation then [ v.source.inputDerivation ] else [ ] ) nodes.target.environment.etc ); }; target.imports = [ ./minimalTarget.nix ]; }; testScript = '' start_all() target.wait_for_unit("multi-user.target") deployer.wait_for_unit("multi-user.target") with subtest("Unpacking"): deployer.succeed("cp -r --no-preserve=mode ${src} work") with subtest("Configure the network"): deployer.copy_from_host("${targetNetworkJSON}", "/root/target-network.json") deployer.succeed("mv /root/target-network.json work/${pathFromRoot}/target-network.json") with subtest("Configure the deployer key"): deployer.succeed("""mkdir -p ~/.ssh && ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa""") deployer_key = deployer.succeed("cat ~/.ssh/id_rsa.pub").strip() deployer.succeed(f"echo '{deployer_key}' > work/${pathFromRoot}/deployer.pub") target.succeed(f"mkdir -p /root/.ssh && echo '{deployer_key}' >> /root/.ssh/authorized_keys") with subtest("Configure the target host key"): target_host_key = target.succeed("ssh-keyscan target | grep -v '^#' | cut -f 2- -d ' ' | head -n 1") deployer.succeed(f"echo '{target_host_key}' > work/${pathFromRoot}/target_host_key.pub") ## 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. with subtest("Override the lock"): deployer.succeed(""" cd work nix flake lock --extra-experimental-features 'flakes nix-command' \ --offline -v \ --override-input flake-parts ${inputs.flake-parts} \ --override-input nixops4 ${nixops4-flake-in-a-bottle} \ \ --override-input nixops4-nixos ${inputs.nixops4-nixos} \ --override-input nixops4-nixos/flake-parts ${inputs.nixops4-nixos.inputs.flake-parts} \ --override-input nixops4-nixos/flake-parts/nixpkgs-lib ${inputs.nixops4-nixos.inputs.flake-parts.inputs.nixpkgs-lib} \ --override-input nixops4-nixos/nixops4-nixos ${emptyFlake} \ --override-input nixops4-nixos/nixpkgs ${inputs.nixops4-nixos.inputs.nixpkgs} \ --override-input nixops4-nixos/nixops4 ${nixops4-flake-in-a-bottle} \ --override-input nixops4-nixos/git-hooks-nix ${emptyFlake} \ \ --override-input nixpkgs ${inputs.nixpkgs} \ --override-input git-hooks ${inputs.git-hooks} \ ; """) with subtest("Check the status before deployment"): target.fail("cowsay hi 1>&2") with subtest("Run the deployment"): deployer.succeed("cd work && nixops4 apply ${deploymentName} --show-trace --no-interactive") with subtest("Check the deployment"): target.succeed("cowsay hi 1>&2") ''; } )