forked from Fediversity/Fediversity
Closes Fediversity/Fediversity#276 This PR adds a CLI deployment test. It builds on top of Fediversity/Fediversity#323. This test features a deployer node and four target nodes. The deployer node runs `nixops4 apply` on a deployment built with our actual code in `deployment/default.nix`, which pushes onto the four target machines combinations of Garage/Mastodon/Peertube/Pixelfed depending on a JSON payload. We check that the expected services are indeed deployed on the machines. Getting there involved reworking the existing basic test to extract common patterns, and adding support for ACME certificates negotiation inside the NixOS test. What works: - deployer successfully runs `nixops4 apply` with various payloads - target machines indeed get the right services pushed onto them and removed - services on target machines successfully negotiate ACME certificates What does not work: the services themselves depend a lot on DNS and that is not taken care of at all, so they are probably very broken. Still, this is a good milestone. Test it yourself by running `nix build .#checks.x86_64-linux.deployment-basic -vL` and `nix build .#checks.x86_64-linux.deployment-cli -vL`. On the very beefy machine that I am using, the basic test runs in ~4 minutes and the CLI test in ~17 minutes. We know from Fediversity/Fediversity#323 that the basic test runs in ~12 minutes on the CI runner, so maybe about an hour for the CLI test? Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io> Reviewed-on: Fediversity/Fediversity#329 Reviewed-by: kiara Grouwstra <kiara@procolix.eu> Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io> Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com> Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
164 lines
5.2 KiB
Nix
164 lines
5.2 KiB
Nix
{
|
|
inputs,
|
|
lib,
|
|
config,
|
|
hostPkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
inherit (builtins)
|
|
concatStringsSep
|
|
toJSON
|
|
;
|
|
inherit (lib)
|
|
fileset
|
|
mkOption
|
|
genAttrs
|
|
attrNames
|
|
optionalString
|
|
;
|
|
inherit (hostPkgs)
|
|
runCommandNoCC
|
|
writeText
|
|
system
|
|
;
|
|
|
|
forConcat = xs: f: concatStringsSep "\n" (map f xs);
|
|
|
|
## The whole repository, with the flake at its root.
|
|
## FIXME: We could probably have fileset be the union of ./. with flake.nix
|
|
## and flake.lock - I doubt we need anything else.
|
|
src = fileset.toSource {
|
|
fileset = config.pathToRoot;
|
|
root = config.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
|
|
'';
|
|
|
|
in
|
|
{
|
|
imports = [
|
|
./sharedOptions.nix
|
|
];
|
|
|
|
options = {
|
|
## FIXME: I wish I could just use `testScript` but with something like
|
|
## `mkOrder` to put this module's string before something else.
|
|
extraTestScript = mkOption { };
|
|
};
|
|
|
|
config = {
|
|
|
|
nodes =
|
|
{
|
|
deployer = {
|
|
imports = [ ./deployerNode.nix ];
|
|
_module.args.inputs = inputs;
|
|
enableAcme = config.enableAcme;
|
|
acmeNodeIP = config.nodes.acme.networking.primaryIPAddress;
|
|
};
|
|
}
|
|
|
|
//
|
|
|
|
(
|
|
if config.enableAcme then
|
|
{
|
|
acme = {
|
|
## FIXME: This makes `nodes.acme` into a local resolver. Maybe this will
|
|
## break things once we play with DNS?
|
|
imports = [ "${inputs.nixpkgs}/nixos/tests/common/acme/server" ];
|
|
## We aren't testing ACME - we just want certificates.
|
|
systemd.services.pebble.environment.PEBBLE_VA_ALWAYS_VALID = "1";
|
|
};
|
|
}
|
|
else
|
|
{ }
|
|
)
|
|
|
|
//
|
|
|
|
genAttrs config.targetMachines (_: {
|
|
imports = [ ./targetNode.nix ];
|
|
_module.args.inputs = inputs;
|
|
enableAcme = config.enableAcme;
|
|
acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null;
|
|
});
|
|
|
|
testScript = ''
|
|
${forConcat (attrNames config.nodes) (n: ''
|
|
${n}.start()
|
|
'')}
|
|
|
|
${forConcat (attrNames config.nodes) (n: ''
|
|
${n}.wait_for_unit("multi-user.target")
|
|
'')}
|
|
|
|
with subtest("Unpacking"):
|
|
deployer.succeed("cp -r --no-preserve=mode ${src}/* .")
|
|
|
|
with subtest("Configure the network"):
|
|
${forConcat config.targetMachines (
|
|
tm:
|
|
let
|
|
targetNetworkJSON = writeText "target-network.json" (
|
|
toJSON config.nodes.${tm}.system.build.networkConfig
|
|
);
|
|
in
|
|
''
|
|
deployer.copy_from_host("${targetNetworkJSON}", "${config.pathFromRoot}/${tm}-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}' > ${config.pathFromRoot}/deployer.pub")
|
|
${forConcat config.targetMachines (tm: ''
|
|
${tm}.succeed(f"mkdir -p /root/.ssh && echo '{deployer_key}' >> /root/.ssh/authorized_keys")
|
|
'')}
|
|
|
|
with subtest("Configure the target host key"):
|
|
${forConcat config.targetMachines (tm: ''
|
|
host_key = ${tm}.succeed("ssh-keyscan ${tm} | grep -v '^#' | cut -f 2- -d ' ' | head -n 1")
|
|
deployer.succeed(f"echo '{host_key}' > ${config.pathFromRoot}/${tm}_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("""
|
|
nix flake lock --extra-experimental-features 'flakes nix-command' \
|
|
--offline -v \
|
|
--override-input flake-parts ${inputs.flake-parts} \
|
|
--override-input nixops4 ${inputs.nixops4.packages.${system}.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 ${
|
|
inputs.nixops4-nixos.inputs.nixops4.packages.${system}.flake-in-a-bottle
|
|
} \
|
|
--override-input nixops4-nixos/git-hooks-nix ${emptyFlake} \
|
|
\
|
|
--override-input nixpkgs ${inputs.nixpkgs} \
|
|
--override-input git-hooks ${inputs.git-hooks} \
|
|
;
|
|
""")
|
|
|
|
${optionalString config.enableAcme ''
|
|
with subtest("Set up handmade DNS"):
|
|
deployer.succeed("echo '${config.nodes.acme.networking.primaryIPAddress}' > ${config.pathFromRoot}/acme_server_ip")
|
|
''}
|
|
|
|
${config.extraTestScript}
|
|
'';
|
|
};
|
|
}
|