forked from fediversity/fediversity
Compare commits
3 commits
478c07df31
...
ba3d3adf73
| Author | SHA1 | Date | |
|---|---|---|---|
| ba3d3adf73 | |||
| a75442c940 | |||
| 69dad083b9 |
9 changed files with 354 additions and 22 deletions
|
|
@ -56,3 +56,9 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: nix build .#checks.x86_64-linux.deployment-panel -L
|
||||
|
||||
check-deployment-model:
|
||||
runs-on: native
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: nix build .#checks.x86_64-linux.deployment-model -L
|
||||
|
|
|
|||
|
|
@ -54,11 +54,8 @@ in
|
|||
|
||||
system.extraDependencies =
|
||||
[
|
||||
inputs.nixops4
|
||||
inputs.nixops4-nixos
|
||||
inputs.nixpkgs
|
||||
sources.nixpkgs
|
||||
|
||||
sources.flake-parts
|
||||
sources.flake-inputs
|
||||
sources.git-hooks
|
||||
|
||||
|
|
|
|||
168
deployment/check/data-model/common-nixosTest.nix
Normal file
168
deployment/check/data-model/common-nixosTest.nix
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
{
|
||||
inputs,
|
||||
lib,
|
||||
config,
|
||||
hostPkgs,
|
||||
sources,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (builtins)
|
||||
concatStringsSep
|
||||
toJSON
|
||||
;
|
||||
inherit (lib)
|
||||
types
|
||||
fileset
|
||||
mkOption
|
||||
genAttrs
|
||||
attrNames
|
||||
optionalString
|
||||
;
|
||||
inherit (hostPkgs)
|
||||
writeText
|
||||
;
|
||||
|
||||
forConcat = xs: f: concatStringsSep "\n" (map f xs);
|
||||
|
||||
in
|
||||
{
|
||||
_class = "nixosTest";
|
||||
|
||||
imports = [
|
||||
../common/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 { };
|
||||
|
||||
sourceFileset = mkOption {
|
||||
## REVIEW: Upstream to nixpkgs?
|
||||
type = types.mkOptionType {
|
||||
name = "fileset";
|
||||
description = "fileset";
|
||||
descriptionClass = "noun";
|
||||
check = (x: (builtins.tryEval (fileset.unions [ x ])).success);
|
||||
merge = (_: defs: fileset.unions (map (x: x.value) defs));
|
||||
};
|
||||
description = ''
|
||||
A fileset that will be copied to the deployer node in the current
|
||||
working directory. This should contain all the files that are
|
||||
necessary to run that particular test, such as the NixOS
|
||||
modules necessary to evaluate a deployment.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
sourceFileset = fileset.unions [
|
||||
../../../mkFlake.nix
|
||||
../../../flake.lock
|
||||
../../../npins
|
||||
../../data-model.nix
|
||||
../../function.nix
|
||||
|
||||
../common/sharedOptions.nix
|
||||
../common/targetNode.nix
|
||||
../common/targetResource.nix
|
||||
];
|
||||
|
||||
acmeNodeIP = config.nodes.acme.networking.primaryIPAddress;
|
||||
|
||||
nodes =
|
||||
{
|
||||
deployer = {
|
||||
imports = [ ../common/deployerNode.nix ];
|
||||
_module.args = { inherit inputs sources; };
|
||||
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 = [ ../common/targetNode.nix ];
|
||||
_module.args = { inherit inputs sources; };
|
||||
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")
|
||||
'')}
|
||||
|
||||
## A subset of the repository that is necessary for this test. It will be
|
||||
## copied inside the test. The smaller this set, the faster our CI, because we
|
||||
## won't need to re-run when things change outside of it.
|
||||
with subtest("Unpacking"):
|
||||
deployer.succeed("cp -r --no-preserve=mode ${
|
||||
fileset.toSource {
|
||||
root = ../../..;
|
||||
fileset = config.sourceFileset;
|
||||
}
|
||||
}/* .")
|
||||
|
||||
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()
|
||||
${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")
|
||||
'')}
|
||||
|
||||
# with subtest("Override the flake and its lock"):
|
||||
# deployer.succeed("cp ${config.pathFromRoot}/flake-under-test.nix flake.nix")
|
||||
|
||||
${optionalString config.enableAcme ''
|
||||
with subtest("Set up handmade DNS"):
|
||||
deployer.succeed("echo '${config.nodes.acme.networking.primaryIPAddress}' > ${config.pathFromRoot}/acme_server_ip")
|
||||
''}
|
||||
|
||||
${config.extraTestScript}
|
||||
'';
|
||||
};
|
||||
}
|
||||
8
deployment/check/data-model/constants.nix
Normal file
8
deployment/check/data-model/constants.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
targetMachines = [
|
||||
"hello"
|
||||
"cowsay"
|
||||
];
|
||||
pathToRoot = ../../..;
|
||||
pathFromRoot = ./.;
|
||||
}
|
||||
16
deployment/check/data-model/default.nix
Normal file
16
deployment/check/data-model/default.nix
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
runNixOSTest,
|
||||
inputs,
|
||||
sources,
|
||||
}:
|
||||
|
||||
runNixOSTest {
|
||||
imports = [
|
||||
../../data-model.nix
|
||||
../../function.nix
|
||||
./common-nixosTest.nix
|
||||
./nixosTest.nix
|
||||
];
|
||||
_module.args = { inherit inputs sources; };
|
||||
inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot;
|
||||
}
|
||||
53
deployment/check/data-model/deployment.nix
Normal file
53
deployment/check/data-model/deployment.nix
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
inputs,
|
||||
# sources,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
# inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot;
|
||||
eval =
|
||||
module:
|
||||
(lib.evalModules {
|
||||
specialArgs = {
|
||||
inherit inputs;
|
||||
};
|
||||
modules = [
|
||||
module
|
||||
../../data-model.nix
|
||||
];
|
||||
}).config;
|
||||
fediversity = eval (
|
||||
{ ... }:
|
||||
{
|
||||
config = {
|
||||
environments.single-nixos-vm =
|
||||
{ ... }:
|
||||
{
|
||||
implementation = requests: {
|
||||
input = requests;
|
||||
output.ssh-host = {
|
||||
ssh = {
|
||||
host = "localhost";
|
||||
username = "root";
|
||||
authentication.password = "password";
|
||||
};
|
||||
nixos-configuration =
|
||||
{ ... }:
|
||||
{
|
||||
users.users = config.resources.shell.login-shell.apply (
|
||||
lib.filterAttrs (_name: value: value ? login-shell) requests
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
in
|
||||
fediversity.environments.single-nixos-vm.deployment {
|
||||
enable = true;
|
||||
}
|
||||
50
deployment/check/data-model/nixosTest.nix
Normal file
50
deployment/check/data-model/nixosTest.nix
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
_class = "nixosTest";
|
||||
|
||||
name = "deployment-model";
|
||||
|
||||
sourceFileset = lib.fileset.unions [
|
||||
../../data-model.nix
|
||||
../../function.nix
|
||||
./constants.nix
|
||||
./deployment.nix
|
||||
];
|
||||
|
||||
nodes.deployer =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
|
||||
# FIXME: sad times
|
||||
system.extraDependencies = with pkgs; [
|
||||
jq
|
||||
jq.inputDerivation
|
||||
];
|
||||
|
||||
system.extraDependenciesFromModule =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
environment.systemPackages = with pkgs; [
|
||||
hello
|
||||
cowsay
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
extraTestScript = ''
|
||||
with subtest("Check the status before deployment"):
|
||||
hello.fail("hello 1>&2")
|
||||
cowsay.fail("cowsay 1>&2")
|
||||
|
||||
with subtest("Run the deployment"):
|
||||
deployer.succeed("nixops4 apply check-deployment-basic --show-trace --no-interactive 1>&2")
|
||||
|
||||
with subtest("Check the deployment"):
|
||||
hello.succeed("hello 1>&2")
|
||||
cowsay.succeed("cowsay hi 1>&2")
|
||||
'';
|
||||
}
|
||||
|
|
@ -1,18 +1,20 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (lib.types)
|
||||
attrsOf
|
||||
attrTag
|
||||
attrsOf
|
||||
deferredModuleWith
|
||||
submodule
|
||||
optionType
|
||||
functionTo
|
||||
nullOr
|
||||
optionType
|
||||
raw
|
||||
str
|
||||
submodule
|
||||
;
|
||||
|
||||
functionType = import ./function.nix;
|
||||
|
|
@ -26,23 +28,50 @@ let
|
|||
);
|
||||
};
|
||||
};
|
||||
nixops4Deployment = types.deferredModuleWith {
|
||||
staticModules = [
|
||||
inputs.nixops4.modules.nixops4Deployment.default
|
||||
|
||||
{
|
||||
_class = "nixops4Deployment";
|
||||
_module.args = {
|
||||
resourceProviderSystem = builtins.currentSystem;
|
||||
resources = { };
|
||||
nixos-configuration = mkOption {
|
||||
description = "A NixOS configuration.";
|
||||
type = raw;
|
||||
};
|
||||
host-ssh = mkOption {
|
||||
description = "SSH connection info to connect to a single host.";
|
||||
type = submodule {
|
||||
options = {
|
||||
host = mkOption {
|
||||
description = "the host to access by SSH";
|
||||
type = str;
|
||||
};
|
||||
}
|
||||
];
|
||||
username = mkOption {
|
||||
description = "the SSH user to use";
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
};
|
||||
authentication = mkOption {
|
||||
description = "authentication method";
|
||||
type = attrTag {
|
||||
private-key = mkOption {
|
||||
description = "path to the user's SSH private key";
|
||||
type = str;
|
||||
example = "/root/.ssh/id_ed25519";
|
||||
};
|
||||
password = mkOption {
|
||||
description = "SSH password";
|
||||
# TODO: mark as sensitive
|
||||
type = str;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
deployment = attrTag {
|
||||
nixops4 = mkOption {
|
||||
description = "A NixOps4 NixOS deployment. For an example, see https://github.com/nixops4/nixops4-nixos/blob/main/example/deployment.nix.";
|
||||
type = nixops4Deployment;
|
||||
ssh-host = {
|
||||
description = "A Terraform deployment by SSH to update a single existing NixOS host.";
|
||||
type = submodule {
|
||||
options = {
|
||||
inherit nixos-configuration;
|
||||
ssh = host-ssh;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
|
|
|
|||
|
|
@ -21,6 +21,11 @@
|
|||
inherit (pkgs.testers) runNixOSTest;
|
||||
inherit inputs sources;
|
||||
};
|
||||
|
||||
deployment-model = import ./check/data-model {
|
||||
inherit (pkgs.testers) runNixOSTest;
|
||||
inherit inputs sources;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue