Introduce a framework for automated VM tests.

This commit is contained in:
r-vdp 2023-04-23 03:00:58 +02:00 committed by Ramses
parent 2f8a9ba967
commit 22684b6ed6
17 changed files with 2194 additions and 350 deletions

256
test/nix/flake.lock generated
View file

@ -1,256 +0,0 @@
{
"nodes": {
"crane": {
"inputs": {
"flake-compat": [
"system-manager",
"pre-commit-hooks",
"flake-compat"
],
"flake-utils": [
"system-manager",
"flake-utils"
],
"nixpkgs": [
"system-manager",
"nixpkgs"
],
"rust-overlay": [
"system-manager",
"rust-overlay"
]
},
"locked": {
"lastModified": 1679811681,
"narHash": "sha256-gc0lwuQxtKnUzdyfmHT6wKEuSdvn5KlRcXNkWT1/4hs=",
"owner": "ipetkov",
"repo": "crane",
"rev": "72fa29510a9ce61ea7455b4469507808684f5673",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"devshell": {
"inputs": {
"flake-utils": [
"system-manager",
"flake-utils"
],
"nixpkgs": [
"system-manager",
"nixpkgs"
]
},
"locked": {
"lastModified": 1678957337,
"narHash": "sha256-Gw4nVbuKRdTwPngeOZQOzH/IFowmz4LryMPDiJN/ah4=",
"owner": "numtide",
"repo": "devshell",
"rev": "3e0e60ab37cd0bf7ab59888f5c32499d851edb47",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"locked": {
"lastModified": 1678901627,
"narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"system-manager",
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1660459072,
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1679944645,
"narHash": "sha256-e5Qyoe11UZjVfgRfwNoSU57ZeKuEmjYb77B9IVW7L/M=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4bb072f0a8b267613c127684e099a70e1f6ff106",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1678872516,
"narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": [
"system-manager",
"flake-utils"
],
"gitignore": "gitignore",
"nixpkgs": [
"system-manager",
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1678976941,
"narHash": "sha256-skNr08frCwN9NO+7I77MjOHHAw+L410/37JknNld+W4=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "32b1dbedfd77892a6e375737ef04d8efba634e9e",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"system-manager": "system-manager"
}
},
"rust-overlay": {
"inputs": {
"flake-utils": [
"system-manager",
"flake-utils"
],
"nixpkgs": [
"system-manager",
"nixpkgs"
]
},
"locked": {
"lastModified": 1679970108,
"narHash": "sha256-8OfySbY1hhBzj0Iz90k4se6oFCGS3+ke31vkd0d4k/o=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "26ef1a2029239e204e51ab3402f8aae5aa1187ed",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"system-manager": {
"inputs": {
"crane": "crane",
"devshell": "devshell",
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
],
"pre-commit-hooks": "pre-commit-hooks",
"rust-overlay": "rust-overlay",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1680080819,
"narHash": "sha256-3p/85AhtTgS3DIctGMExHvf3ozStujge7E8/N1rxJQc=",
"owner": "numtide",
"repo": "system-manager",
"rev": "01073b251d1fc565174ff8d89324a5709dafda97",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "system-manager",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"system-manager",
"nixpkgs"
]
},
"locked": {
"lastModified": 1679588014,
"narHash": "sha256-URkRSunu8HAp2vH2KgLogjAXckiufCDFrBs59g9uiLY=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "af75d6efe437858f9ca5535e622cfbedad1ba717",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,19 +0,0 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
system-manager = {
url = "github:numtide/system-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { system-manager, ... }:
{
systemConfigs.default = system-manager.lib.makeSystemConfig {
modules = [
./modules
];
};
};
}

View file

@ -1,84 +1,121 @@
{ lib
, pkgs
, ...
{ system-manager
, system
}:
{
config = {
nixpkgs.hostPlatform = "x86_64-linux";
services.nginx.enable = true;
let
testConfig = { lib, pkgs, ... }: {
config = {
nixpkgs.hostPlatform = "x86_64-linux";
environment.etc = {
foo = {
text = ''
This is just a test!
services.nginx.enable = true;
environment.etc = {
foo = {
text = ''
This is just a test!
'';
target = "foo_test";
};
"foo.conf".text = ''
launch_the_rockets = true
'';
target = "foo_test";
};
"foo.conf".text = ''
launch_the_rockets = true
'';
"baz/bar/foo2" = {
text = ''
Another test!
'';
mode = "symlink";
};
"baz/bar/foo2" = {
text = ''
Another test!
'';
mode = "symlink";
};
foo3 = {
text = "boo!";
mode = "0700";
user = "root";
group = "root";
};
foo3 = {
text = "boo!";
mode = "0700";
user = "root";
group = "root";
};
"a/nested/example/foo3" = {
text = "boo!";
mode = "0764";
user = "root";
group = "root";
};
"a/nested/example/foo3" = {
text = "boo!";
mode = "0764";
user = "root";
group = "root";
};
"a/nested/example2/foo3" = {
text = "boo!";
mode = "0764";
user = "root";
group = "root";
};
"a/nested/example2/foo3" = {
text = "boo!";
mode = "0764";
user = "root";
group = "root";
};
out-of-store = {
source = "/run/systemd/system/";
out-of-store = {
source = "/run/systemd/system/";
};
};
systemd.services =
lib.listToAttrs
(lib.flip lib.genList 10 (ix:
lib.nameValuePair "service-${toString ix}"
{
enable = true;
description = "service-${toString ix}";
wants = [ "network-online.target" ];
after = [
"network-online.target"
];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecReload = "${lib.getBin pkgs.coreutils}/bin/true";
};
wantedBy = [ "system-manager.target" ];
requiredBy = lib.mkIf (ix > 5) [ "service-0.service" ];
script = ''
sleep ${if ix > 5 then "2" else "1"}
'';
})
);
};
systemd.services =
lib.listToAttrs
(lib.flip lib.genList 10 (ix:
lib.nameValuePair "service-${toString ix}"
{
enable = true;
description = "service-${toString ix}";
wants = [ "network-online.target" ];
after = [
"network-online.target"
"avahi-daemon.service"
"chrony.service"
"nss-lookup.target"
"tinc.service"
"pulseaudio.service"
];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecReload = "${lib.getBin pkgs.coreutils}/bin/true";
};
wantedBy = [ "multi-user.target" ];
requiredBy = lib.mkIf (ix > 5) [ "service-0.service" ];
script = ''
sleep ${if ix > 5 then "2" else "1"}
'';
})
);
};
in
system-manager.lib.make-vm-test {
inherit system;
modules = [
({ config, ... }:
let
inherit (config) hostPkgs;
in
{
nodes = {
node1 = { config, ... }: {
modules = [
testConfig
];
virtualisation.rootImage = system-manager.lib.prepareUbuntuImage {
inherit hostPkgs;
nodeConfig = config;
image = system-manager.lib.images.ubuntu_22_10_cloudimg;
};
};
};
testScript = ''
# Start all machines in parallel
start_all()
node1.wait_for_unit("default.target")
node1.execute("/system-manager-profile/bin/activate")
node1.wait_for_unit("system-manager.target")
node1.wait_for_unit("service-9.service")
node1.wait_for_file("/etc/baz/bar/foo2")
node1.wait_for_file("/etc/foo.conf")
node1.succeed("grep -F 'launch_the_rockets = true' /etc/foo.conf")
node1.fail("grep -F 'launch_the_rockets = false' /etc/foo.conf")
'';
})
];
}

View file

@ -0,0 +1,107 @@
{ lib, system-manager, ... }:
let
inherit (lib) types;
pkgsType = lib.mkOptionType {
name = "nixpkgs";
description = "An evaluation of Nixpkgs; the top level attribute set of packages";
check = builtins.isAttrs;
};
nodeOptions = { config, name, ... }: {
options = {
system.name = lib.mkOption {
type = types.str;
default = name;
};
modules = lib.mkOption {
# TODO: can we give a better type here?
# We want a list of system-manager modules
type = types.listOf types.raw;
};
systemConfig = lib.mkOption {
# TODO figure out correct type
type = types.raw;
internal = true;
readOnly = true;
};
virtualisation = {
rootImage = lib.mkOption {
# TODO: figure out the correct type.
type = types.raw;
};
memorySize = lib.mkOption {
type = types.ints.between 256 (1024 * 128);
default = 1024;
};
cpus = lib.mkOption {
type = types.ints.between 1 1024;
default = 2;
};
vlans = lib.mkOption {
type = types.ints.between 1 1024;
default = 1;
};
sharedDirectories = lib.mkOption {
type = types.attrsOf
(types.submodule {
options = {
source = lib.mkOption {
type = types.str;
};
target = lib.mkOption {
type = types.str;
};
};
});
default = { };
};
};
};
config = {
# Include these shared directories by default, they are used by the test driver.
virtualisation.sharedDirectories = {
xchg = {
source = ''"$TMPDIR"/xchg'';
target = "/tmp/xchg";
};
shared = {
source = ''"''${SHARED_DIR:-$TMPDIR/xchg}"'';
target = "/tmp/shared";
};
};
systemConfig = system-manager.lib.makeSystemConfig {
inherit (config) modules;
};
};
};
in
{
options = {
# TODO: figure out correct type
hostPkgs = lib.mkOption {
type = pkgsType;
};
nodes = lib.mkOption {
type = types.attrsOf (types.submodule nodeOptions);
default = { };
};
testScript = lib.mkOption {
type = types.str;
};
};
}