forked from Fediversity/Fediversity
163 lines
5.4 KiB
Nix
163 lines
5.4 KiB
Nix
{
|
|
lib,
|
|
config,
|
|
hostPkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
sources = import ../../../npins;
|
|
inherit (builtins)
|
|
filter
|
|
;
|
|
inherit (lib)
|
|
fileset
|
|
mkOption
|
|
genAttrs
|
|
concatLists
|
|
attrNames
|
|
;
|
|
|
|
## Helpers to map over target machines and produce an attrset, a list, or a
|
|
## multiline string suitable for use in a Python script.
|
|
# forAllTargetMachines = lib.genAttrs config.targetMachines;
|
|
# forAllTargetMachines' = f: map f config.targetMachines;
|
|
forConcat = xs: f: builtins.concatStringsSep "\n" (map f xs);
|
|
|
|
inherit (config) targetMachines pathToRoot pathFromRoot;
|
|
|
|
## The whole repository.
|
|
## 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;
|
|
};
|
|
|
|
## 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.
|
|
extraDependenciesFromMachine =
|
|
machine:
|
|
[
|
|
machine.system.build.toplevel.inputDerivation
|
|
machine.system.build.etc.inputDerivation
|
|
machine.system.build.etcBasedir.inputDerivation
|
|
machine.system.build.etcMetadataImage.inputDerivation
|
|
machine.system.build.extraUtils.inputDerivation
|
|
machine.system.path.inputDerivation
|
|
machine.system.build.setEnvironment.inputDerivation
|
|
machine.system.build.vm.inputDerivation
|
|
# machine.system.build.vmWithBootLoader.inputDerivation
|
|
machine.system.build.bootStage1.inputDerivation
|
|
machine.system.build.bootStage2.inputDerivation
|
|
]
|
|
++ concatLists (
|
|
lib.mapAttrsToList (
|
|
_k: v: if v ? source.inputDerivation then [ v.source.inputDerivation ] else [ ]
|
|
) machine.environment.etc
|
|
);
|
|
|
|
allNodesButFake = filter (m: m != "fake") (attrNames config.nodes);
|
|
in
|
|
{
|
|
options = {
|
|
targetMachines = mkOption { };
|
|
|
|
pathToRoot = mkOption { };
|
|
## TODO: sanity check that (pathToRoot + "/${pathFromRoot}" == ./.)
|
|
pathFromRoot = mkOption { };
|
|
|
|
## 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 =
|
|
{ pkgs, nodes, ... }:
|
|
{
|
|
virtualisation = {
|
|
## Memory use is expected to be dominated by the NixOS evaluation,
|
|
## which happens on the deployer.
|
|
memorySize = 32 * 1024; # FIXME: trim down - maybe make it an option
|
|
diskSize = 50 * 1024; # FIXME: trim down - maybe make it an option
|
|
cores = 8; # FIXME: trim down - maybe make it an option
|
|
};
|
|
|
|
nix.settings = {
|
|
substituters = lib.mkForce [ ];
|
|
hashed-mirrors = null;
|
|
connect-timeout = 1;
|
|
};
|
|
|
|
system.extraDependencies =
|
|
lib.attrValues sources
|
|
++ [
|
|
# pkgs
|
|
pkgs.stdenv
|
|
pkgs.stdenvNoCC
|
|
]
|
|
++ concatLists (map (tm: extraDependenciesFromMachine nodes.${tm}) (targetMachines ++ [ "fake" ]));
|
|
};
|
|
|
|
## A “fake” node that can will not be started or used in the test, but
|
|
## whose configuration will be dumped in the deployer's extra
|
|
## dependencies. This is an indirect (and more pleasant) way of giving
|
|
## the deployer the right derivations to build the desired services.
|
|
fake.imports = [ ../basic/minimalTarget.nix ];
|
|
}
|
|
// genAttrs targetMachines (_: {
|
|
imports = [ ../basic/minimalTarget.nix ];
|
|
users.users.root.openssh.authorizedKeys.keyFiles = [
|
|
(pathToRoot + "/${pathFromRoot}/deployer.pub")
|
|
];
|
|
});
|
|
|
|
testScript = ''
|
|
${forConcat allNodesButFake (n: ''
|
|
${n}.start()
|
|
'')}
|
|
|
|
${forConcat allNodesButFake (n: ''
|
|
${n}.wait_for_unit("multi-user.target")
|
|
'')}
|
|
|
|
with subtest("Unpacking"):
|
|
deployer.succeed("cp -r --no-preserve=mode ${src} work")
|
|
|
|
with subtest("Configure the network"):
|
|
${forConcat targetMachines (
|
|
tm:
|
|
let
|
|
targetNetworkJSON = hostPkgs.writeText "target-network.json" (
|
|
builtins.toJSON config.nodes.${tm}.system.build.networkConfig
|
|
);
|
|
in
|
|
''
|
|
deployer.copy_from_host("${targetNetworkJSON}", "/root/target-network.json")
|
|
deployer.succeed("mv /root/target-network.json work/${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}' > work/${pathFromRoot}/deployer.pub")
|
|
${forConcat targetMachines (tm: ''
|
|
${tm}.succeed(f"mkdir -p /root/.ssh && echo '{deployer_key}' >> /root/.ssh/authorized_keys")
|
|
'')}
|
|
|
|
with subtest("Configure the target host key"):
|
|
${forConcat targetMachines (tm: ''
|
|
host_key = ${tm}.succeed("ssh-keyscan ${tm} | grep -v '^#' | cut -f 2- -d ' ' | head -n 1")
|
|
deployer.succeed(f"echo '{host_key}' > work/${pathFromRoot}/${tm}_host_key.pub")
|
|
'')}
|
|
|
|
${config.extraTestScript}
|
|
'';
|
|
};
|
|
}
|