Skip to content

Commit 0515f8d

Browse files
committed
add ubuntu kexec test
1 parent 1858c3a commit 0515f8d

File tree

4 files changed

+106
-58
lines changed

4 files changed

+106
-58
lines changed

flake.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
nixos-images.url = "github:nix-community/nixos-images";
1212
nixos-images.inputs.nixos-unstable.follows = "nixpkgs";
1313
nixos-images.inputs.nixos-stable.follows = "nixos-stable";
14+
# https://github.com/numtide/nix-vm-test/pull/105
15+
nix-vm-test = { url = "github:Mic92/nix-vm-test"; inputs.nixpkgs.follows = "nixpkgs"; };
1416

1517
# used for development
1618
treefmt-nix = { url = "github:numtide/treefmt-nix"; inputs.nixpkgs.follows = "nixpkgs"; };

tests/flake-module.nix

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
testInputsStable = testInputsUnstable // {
1818
kexec-installer = "${inputs'.nixos-images.packages.kexec-installer-nixos-stable-noninteractive}/nixos-kexec-installer-noninteractive-${system}.tar.gz";
1919
};
20+
ubuntuTestInputs = testInputsUnstable // {
21+
nix-vm-test = inputs.nix-vm-test;
22+
};
2023
in
2124
{
2225
from-nixos = import ./from-nixos.nix testInputsUnstable;
@@ -26,5 +29,6 @@
2629
from-nixos-with-generated-config = import ./from-nixos-generate-config.nix testInputsUnstable;
2730
from-nixos-build-on-remote = import ./from-nixos-build-on-remote.nix testInputsUnstable;
2831
from-nixos-separated-phases = import ./from-nixos-separated-phases.nix testInputsUnstable;
32+
ubuntu-kexec-test = import ./ubuntu-kexec-test.nix ubuntuTestInputs;
2933
});
3034
}

tests/ubuntu-kexec-test.nix

Lines changed: 79 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,97 @@
1-
{ pkgs, nixos-anywhere, kexec-installer, nix-vm-test, ... }:
2-
3-
let
4-
# Create dummy store paths for testing
5-
testPaths = pkgs.runCommand "test-store-paths" {} ''
6-
mkdir -p $out
7-
echo "echo 'Dummy disko script'" > $out/disko
8-
echo "echo 'Dummy system closure'" > $out/system
9-
chmod +x $out/disko $out/system
10-
'';
11-
in
12-
nix-vm-test.ubuntu."22_04" {
13-
memorySize = 2048;
14-
15-
# Forward SSH port to allow connection from the host
16-
virtualisation.forwardPorts = [
17-
{ from = "host"; host.port = 2222; guest.port = 22; }
18-
];
19-
20-
# Make the SSH keys available in the VM
21-
sharedDirs = {
22-
sshKeys = {
23-
source = "${nixos-anywhere}/tests/modules/ssh-keys";
24-
target = "/tmp/ssh-keys";
25-
};
1+
{ pkgs, nixos-anywhere, kexec-installer, nix-vm-test, system-to-install, ... }:
2+
3+
(nix-vm-test.lib.${pkgs.system}.ubuntu."24_04" {
4+
sharedDirs = { };
5+
6+
# Configure VM with 2GB memory
7+
machineConfigModule = { ... }: {
8+
nodes.vm.virtualisation.memorySize = 2048;
269
};
2710

2811
# The test script
2912
testScript = ''
13+
# Python imports
14+
import subprocess
15+
import tempfile
16+
import shutil
17+
import os
18+
3019
# Wait for the system to be fully booted
3120
vm.wait_for_unit("multi-user.target")
3221
3322
# Unmask SSH service (which is masked by default in the test VM)
34-
vm.succeed("systemctl unmask ssh.service")
35-
vm.succeed("systemctl unmask ssh.socket")
36-
vm.succeed("systemctl start ssh")
23+
vm.succeed("systemctl unmask ssh.service ssh.socket")
24+
25+
# Generate SSH host keys (required for SSH to start)
26+
vm.succeed("ssh-keygen -A")
3727
3828
# Setup SSH with the existing keys
3929
vm.succeed("mkdir -p /root/.ssh")
40-
vm.succeed("cp /tmp/ssh-keys/ssh.pub /root/.ssh/authorized_keys")
30+
vm.succeed(
31+
"echo '${builtins.replaceStrings ["\n"] [""] (builtins.readFile ./modules/ssh-keys/ssh.pub)}' > /root/.ssh/authorized_keys"
32+
)
4133
vm.succeed("chmod 644 /root/.ssh/authorized_keys")
4234
4335
# Setup SSH for connection from host
44-
vm.succeed("sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config")
45-
vm.succeed("systemctl restart ssh")
46-
36+
vm.succeed(
37+
"sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config"
38+
)
39+
40+
# Start SSH service
41+
vm.succeed("systemctl start ssh")
42+
4743
# Wait for SSH to be available
4844
vm.wait_for_open_port(22)
49-
print("SSH server is ready")
5045
51-
# Run nixos-anywhere from the host against the VM
52-
print("Starting kexec test phase...")
53-
54-
# Use Python's subprocess to run nixos-anywhere from the host
55-
import subprocess
56-
57-
cmd = f"""${nixos-anywhere}/bin/nixos-anywhere \\
58-
-i ${nixos-anywhere}/tests/modules/ssh-keys/ssh \\
59-
--ssh-port 2222 \\
60-
--phases kexec \\
61-
--kexec ${kexec-installer} \\
62-
--store-paths ${testPaths}/disko ${testPaths}/system \\
63-
--debug \\
64-
root@localhost
65-
"""
66-
67-
print(f"Running command: {cmd}")
68-
result = subprocess.run(cmd, shell=True, check=False)
69-
70-
if result.returncode == 0:
71-
print("kexec phase completed successfully")
72-
else:
73-
print(f"nixos-anywhere failed with exit code {result.returncode}")
74-
raise Exception("nixos-anywhere command failed")
46+
# Forward SSH port using vm.forward_port method
47+
ssh_port = 2222
48+
vm.forward_port(host_port=ssh_port, guest_port=22)
49+
50+
# Use temporary file for SSH key with automatic cleanup
51+
with tempfile.NamedTemporaryFile(mode='w', delete=True, suffix='_ssh_key') as temp_key:
52+
temp_key_path = temp_key.name
53+
54+
# Copy SSH private key to temp file with correct permissions
55+
shutil.copy2("${./modules/ssh-keys/ssh}", temp_key_path)
56+
os.chmod(temp_key_path, 0o600)
57+
58+
nixos_anywhere_cmd = [
59+
"${nixos-anywhere}/bin/nixos-anywhere",
60+
"-i", temp_key_path,
61+
"--ssh-port", str(ssh_port),
62+
"--post-kexec-ssh-port", "2222",
63+
"--phases", "kexec",
64+
"--kexec", "${kexec-installer}",
65+
"--store-paths", "${system-to-install.config.system.build.diskoScriptNoDeps}",
66+
"${system-to-install.config.system.build.toplevel}",
67+
"--debug",
68+
"root@localhost"
69+
]
70+
71+
result = subprocess.run(nixos_anywhere_cmd, check=False)
72+
73+
if result.returncode != 0:
74+
print(f"nixos-anywhere failed with exit code {result.returncode}")
75+
vm.succeed("dmesg | tail -n 50")
76+
vm.succeed("journalctl -n 50")
77+
raise Exception(f"nixos-anywhere command failed with exit code {result.returncode}")
78+
79+
# Test SSH connection to verify we're in NixOS kexec environment
80+
check_cmd = [
81+
"${pkgs.openssh}/bin/ssh", "-v",
82+
"-i", temp_key_path,
83+
"-p", "2222",
84+
"-o", "StrictHostKeyChecking=no",
85+
"-o", "UserKnownHostsFile=/dev/null",
86+
"root@localhost",
87+
"cat /etc/os-release"
88+
]
89+
90+
test_result = subprocess.run(check_cmd, check=True, stdout=subprocess.PIPE, text=True)
91+
assert "nixos" in test_result.stdout.lower(), f"Expected NixOS environment but got: {test_result.stdout}"
92+
93+
# After kexec we no longer have the machine driver,
94+
# so we need to let the VM crash because the test driver backdoor gets confused by the terminal output.
95+
vm.crash()
7596
'';
76-
}
97+
}).sandboxed

0 commit comments

Comments
 (0)