Skip to content

Commit 2bad218

Browse files
authored
Merge pull request #264 from serokell/sereja/OPS-1384-add-nixos-vm-test
[OPS-1384] Introduce NixOS VM tests
2 parents 0a01877 + a928352 commit 2bad218

File tree

6 files changed

+293
-10
lines changed

6 files changed

+293
-10
lines changed

.github/workflows/check.yml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,26 @@ name: Nix flake check
22
on: pull_request
33

44
jobs:
5+
get-matrix:
6+
runs-on: [self-hosted, nix]
7+
outputs:
8+
check-matrix: ${{ steps.set-check-matrix.outputs.matrix }}
9+
steps:
10+
- uses: actions/checkout@v4
11+
12+
- id: set-check-matrix
13+
run: echo "matrix=$(nix eval --json .#check-matrix.x86_64-linux)" >> $GITHUB_OUTPUT
14+
515
check:
6-
runs-on: self-hosted
16+
needs: get-matrix
17+
name: check ${{ matrix.check }}
18+
runs-on: [self-hosted, nix]
19+
strategy:
20+
fail-fast: false
21+
# this matrix consists of the names of all checks defined in flake.nix
22+
matrix: ${{fromJson(needs.get-matrix.outputs.check-matrix)}}
723
steps:
824
- uses: actions/checkout@v4
925

10-
- name: check flake
11-
run: nix flake check -L
26+
- name: check
27+
run: nix build -L .#checks.x86_64-linux.${{ matrix.check }}

flake.nix

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@
1515
};
1616
};
1717

18-
outputs = { self, nixpkgs, utils, ... }:
18+
outputs = { self, nixpkgs, utils, ... }@inputs:
1919
{
20-
overlay = final: prev:
21-
let
20+
overlays.default = final: prev: let
2221
system = final.stdenv.hostPlatform.system;
2322
darwinOptions = final.lib.optionalAttrs final.stdenv.isDarwin {
2423
buildInputs = with final.darwin.apple_sdk.frameworks; [
@@ -34,7 +33,13 @@
3433
pname = "deploy-rs";
3534
version = "0.1.0";
3635

37-
src = ./.;
36+
src = final.lib.sourceByRegex ./. [
37+
"Cargo\.lock"
38+
"Cargo\.toml"
39+
"src"
40+
"src/bin"
41+
".*\.rs$"
42+
];
3843

3944
cargoLock.lockFile = ./Cargo.lock;
4045
}) // { meta.description = "A Simple multi-profile Nix-flake deploy tool"; };
@@ -145,7 +150,15 @@
145150
} //
146151
utils.lib.eachSystem (utils.lib.defaultSystems ++ ["aarch64-darwin"]) (system:
147152
let
148-
pkgs = import nixpkgs { inherit system; overlays = [ self.overlay ]; };
153+
pkgs = import nixpkgs {
154+
inherit system;
155+
overlays = [ self.overlays.default ];
156+
};
157+
158+
# make a matrix to use in GitHub pipeline
159+
mkMatrix = name: attrs: {
160+
include = map (v: { ${name} = v; }) (pkgs.lib.attrNames attrs);
161+
};
149162
in
150163
{
151164
defaultPackage = self.packages."${system}".deploy-rs;
@@ -176,8 +189,12 @@
176189

177190
checks = {
178191
deploy-rs = self.packages.${system}.default.overrideAttrs (super: { doCheck = true; });
179-
};
192+
} // (pkgs.lib.optionalAttrs (pkgs.lib.elem system ["x86_64-linux"]) (import ./nix/tests {
193+
inherit inputs pkgs;
194+
}));
195+
196+
inherit (pkgs.deploy-rs) lib;
180197

181-
lib = pkgs.deploy-rs.lib;
198+
check-matrix = mkMatrix "check" self.checks.${system};
182199
});
183200
}

nix/tests/common.nix

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# SPDX-FileCopyrightText: 2024 Serokell <https://serokell.io/>
2+
#
3+
# SPDX-License-Identifier: MPL-2.0
4+
5+
{inputs, pkgs, ...}: {
6+
nix = {
7+
registry.nixpkgs.flake = inputs.nixpkgs;
8+
extraOptions = ''
9+
experimental-features = nix-command flakes
10+
'';
11+
settings = {
12+
trusted-users = [ "root" "@wheel" ];
13+
substituters = pkgs.lib.mkForce [];
14+
};
15+
};
16+
17+
virtualisation.graphics = false;
18+
virtualisation.memorySize = 1536;
19+
boot.loader.grub.enable = false;
20+
documentation.enable = false;
21+
}

nix/tests/default.nix

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# SPDX-FileCopyrightText: 2024 Serokell <https://serokell.io/>
2+
#
3+
# SPDX-License-Identifier: MPL-2.0
4+
5+
{ pkgs , inputs , ... }:
6+
let
7+
inherit (pkgs) system lib;
8+
9+
inherit (import "${pkgs.path}/nixos/tests/ssh-keys.nix" pkgs) snakeOilPrivateKey;
10+
11+
# Include all build dependencies to be able to build profiles offline
12+
allDrvOutputs = pkg: pkgs.runCommand "allDrvOutputs" { refs = pkgs.writeReferencesToFile pkg.drvPath; } ''
13+
touch $out
14+
while read ref; do
15+
case $ref in
16+
*.drv)
17+
cat $ref >>$out
18+
;;
19+
esac
20+
done <$refs
21+
'';
22+
23+
mkTest = { name ? "", user ? "root", isLocal ? true, deployArgs }: let
24+
nodes = {
25+
server = { nodes, ... }: {
26+
imports = [
27+
./server.nix
28+
(import ./common.nix { inherit inputs pkgs; })
29+
];
30+
virtualisation.additionalPaths = lib.optionals (!isLocal) [
31+
pkgs.hello
32+
pkgs.figlet
33+
(allDrvOutputs nodes.server.system.build.toplevel)
34+
pkgs.deploy-rs.deploy-rs
35+
];
36+
};
37+
client = { nodes, ... }: {
38+
imports = [ (import ./common.nix { inherit inputs pkgs; }) ];
39+
environment.systemPackages = [ pkgs.deploy-rs.deploy-rs ];
40+
virtualisation.additionalPaths = lib.optionals isLocal [
41+
pkgs.hello
42+
pkgs.figlet
43+
(allDrvOutputs nodes.server.system.build.toplevel)
44+
];
45+
};
46+
};
47+
48+
flakeInputs = ''
49+
deploy-rs.url = "${../..}";
50+
deploy-rs.inputs.utils.follows = "utils";
51+
deploy-rs.inputs.flake-compat.follows = "flake-compat";
52+
53+
nixpkgs.url = "${inputs.nixpkgs}";
54+
utils.url = "${inputs.utils}";
55+
utils.inputs.systems.follows = "systems";
56+
systems.url = "${inputs.utils.inputs.systems}";
57+
flake-compat.url = "${inputs.flake-compat}";
58+
flake-compat.flake = false;
59+
'';
60+
61+
flake = builtins.toFile "flake.nix"
62+
(lib.replaceStrings [ "##inputs##" ] [ flakeInputs ] (builtins.readFile ./deploy-flake.nix));
63+
64+
in pkgs.nixosTest {
65+
inherit nodes name;
66+
67+
testScript = { nodes }: let
68+
serverNetworkJSON = pkgs.writeText "server-network.json"
69+
(builtins.toJSON nodes.server.system.build.networkConfig);
70+
in ''
71+
start_all()
72+
73+
# Prepare
74+
client.succeed("mkdir tmp && cd tmp")
75+
client.succeed("cp ${flake} ./flake.nix")
76+
client.succeed("cp ${./server.nix} ./server.nix")
77+
client.succeed("cp ${./common.nix} ./common.nix")
78+
client.succeed("cp ${serverNetworkJSON} ./network.json")
79+
client.succeed("nix flake lock")
80+
81+
82+
# Setup SSH key
83+
client.succeed("mkdir -m 700 /root/.ssh")
84+
client.succeed('cp --no-preserve=mode ${snakeOilPrivateKey} /root/.ssh/id_ed25519')
85+
client.succeed("chmod 600 /root/.ssh/id_ed25519")
86+
87+
# Test SSH connection
88+
server.wait_for_open_port(22)
89+
client.wait_for_unit("network.target")
90+
client.succeed(
91+
"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'echo hello world' >&2",
92+
timeout=30
93+
)
94+
95+
# Make sure the hello and figlet packages are missing
96+
server.fail("su ${user} -l -c 'hello | figlet'")
97+
98+
# Deploy to the server
99+
client.succeed("deploy ${deployArgs}")
100+
101+
# Make sure packages are present after deployment
102+
server.succeed("su ${user} -l -c 'hello | figlet' >&2")
103+
'';
104+
};
105+
in {
106+
# Deployment with client-side build
107+
local-build = mkTest {
108+
name = "local-build";
109+
deployArgs = "-s .#server -- --offline";
110+
};
111+
# Deployment with server-side build
112+
remote-build = mkTest {
113+
name = "remote-build";
114+
isLocal = false;
115+
deployArgs = "-s .#server --remote-build -- --offline";
116+
};
117+
# Deployment with overridden options
118+
options-overriding = mkTest {
119+
name = "options-overriding";
120+
deployArgs = lib.concatStrings [
121+
"-s .#server-override"
122+
" --hostname server --profile-user root --ssh-user root --sudo 'sudo -u'"
123+
" --ssh-opts='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'"
124+
" --confirm-timeout 30 --activation-timeout 30"
125+
" -- --offline"
126+
];
127+
};
128+
# User profile deployment
129+
profile = mkTest {
130+
name = "profile";
131+
user = "deploy";
132+
deployArgs = "-s .#profile -- --offline";
133+
};
134+
}

nix/tests/deploy-flake.nix

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# SPDX-FileCopyrightText: 2024 Serokell <https://serokell.io/>
2+
#
3+
# SPDX-License-Identifier: MPL-2.0
4+
5+
{
6+
inputs = {
7+
# real inputs are substituted in ./default.nix
8+
##inputs##
9+
};
10+
11+
outputs = { self, nixpkgs, deploy-rs, ... }@inputs: let
12+
system = "x86_64-linux";
13+
pkgs = inputs.nixpkgs.legacyPackages.${system};
14+
user = "deploy";
15+
in {
16+
nixosConfigurations.server = nixpkgs.lib.nixosSystem {
17+
inherit system pkgs;
18+
specialArgs = { inherit inputs; };
19+
modules = [
20+
./server.nix
21+
./common.nix
22+
# Import the base config used by nixos tests
23+
(pkgs.path + "/nixos/lib/testing/nixos-test-base.nix")
24+
# Deployment breaks the network settings, so we need to restore them
25+
(pkgs.lib.importJSON ./network.json)
26+
# Deploy packages
27+
{ environment.systemPackages = [ pkgs.figlet pkgs.hello ]; }
28+
];
29+
};
30+
31+
deploy.nodes = {
32+
server = {
33+
hostname = "server";
34+
sshUser = "root";
35+
sshOpts = [
36+
"-o" "StrictHostKeyChecking=no"
37+
"-o" "StrictHostKeyChecking=no"
38+
];
39+
profiles.system.path = deploy-rs.lib."${system}".activate.nixos self.nixosConfigurations.server;
40+
};
41+
server-override = {
42+
hostname = "override";
43+
sshUser = "override";
44+
user = "override";
45+
sudo = "override";
46+
sshOpts = [ ];
47+
confirmTimeout = 0;
48+
activationTimeout = 0;
49+
profiles.system.path = deploy-rs.lib."${system}".activate.nixos self.nixosConfigurations.server;
50+
};
51+
profile = {
52+
hostname = "server";
53+
sshUser = "${user}";
54+
sshOpts = [
55+
"-o" "UserKnownHostsFile=/dev/null"
56+
"-o" "StrictHostKeyChecking=no"
57+
];
58+
profiles = {
59+
"hello-world".path = let
60+
activateProfile = pkgs.writeShellScriptBin "activate" ''
61+
set -euo pipefail
62+
mkdir -p /home/${user}/.nix-profile/bin
63+
rm -f -- /home/${user}/.nix-profile/bin/hello /home/${user}/.nix-profile/bin/figlet
64+
ln -s ${pkgs.hello}/bin/hello /home/${user}/.nix-profile/bin/hello
65+
ln -s ${pkgs.figlet}/bin/figlet /home/${user}/.nix-profile/bin/figlet
66+
'';
67+
in deploy-rs.lib.${system}.activate.custom activateProfile "$PROFILE/bin/activate";
68+
};
69+
};
70+
};
71+
};
72+
}

nix/tests/server.nix

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# SPDX-FileCopyrightText: 2024 Serokell <https://serokell.io/>
2+
#
3+
# SPDX-License-Identifier: MPL-2.0
4+
{ pkgs, ... }:
5+
{
6+
nix.settings.trusted-users = [ "deploy" ];
7+
users = let
8+
inherit (import "${pkgs.path}/nixos/tests/ssh-keys.nix" pkgs) snakeOilPublicKey;
9+
in {
10+
mutableUsers = false;
11+
users = {
12+
deploy = {
13+
password = "";
14+
isNormalUser = true;
15+
createHome = true;
16+
openssh.authorizedKeys.keys = [ snakeOilPublicKey ];
17+
};
18+
root.openssh.authorizedKeys.keys = [ snakeOilPublicKey ];
19+
};
20+
};
21+
services.openssh.enable = true;
22+
virtualisation.writableStore = true;
23+
}

0 commit comments

Comments
 (0)