1 import ../make-test-python.nix ({ pkgs, lib, extra ? {}, name ? "incus-container", incus ? pkgs.incus-lts, ... } :
4 releases = import ../../release.nix {
5 configuration = lib.recursiveUpdate {
6 # Building documentation makes the test unnecessarily take a longer time:
7 documentation.enable = lib.mkForce false;
9 boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
14 container-image-metadata = "${releases.incusContainerMeta.${pkgs.stdenv.hostPlatform.system}}/tarball/nixos-system-${pkgs.stdenv.hostPlatform.system}.tar.xz";
15 container-image-rootfs = "${releases.incusContainerImage.${pkgs.stdenv.hostPlatform.system}}/nixos-lxc-image-${pkgs.stdenv.hostPlatform.system}.squashfs";
21 maintainers = lib.teams.lxc.members;
24 nodes.machine = { ... }: {
26 # Ensure test VM has enough resources for creating and managing guests
36 networking.nftables.enable = true;
40 def instance_is_up(_) -> bool:
41 status, _ = machine.execute("incus exec container --disable-stdin --force-interactive /run/current-system/sw/bin/systemctl -- is-system-running")
44 def set_container(config):
45 machine.succeed(f"incus config set container {config}")
46 machine.succeed("incus restart container")
47 with machine.nested("Waiting for instance to start and be usable"):
50 def check_sysctl(instance):
51 with subtest("systemd sysctl settings are applied"):
52 machine.succeed(f"incus exec {instance} -- systemctl status systemd-sysctl")
53 sysctl = machine.succeed(f"incus exec {instance} -- sysctl net.ipv4.ip_forward").strip().split(" ")[-1]
54 assert "1" == sysctl, f"systemd-sysctl configuration not correctly applied, {sysctl} != 1"
56 machine.wait_for_unit("incus.service")
58 # no preseed should mean no service
59 machine.fail("systemctl status incus-preseed.service")
61 machine.succeed("incus admin init --minimal")
63 with subtest("Container image can be imported"):
64 machine.succeed("incus image import ${container-image-metadata} ${container-image-rootfs} --alias nixos")
66 with subtest("Container can be launched and managed"):
67 machine.succeed("incus launch nixos container")
68 with machine.nested("Waiting for instance to start and be usable"):
70 machine.succeed("echo true | incus exec container /run/current-system/sw/bin/bash -")
72 with subtest("Container mounts lxcfs overlays"):
73 machine.succeed("incus exec container mount | grep 'lxcfs on /proc/cpuinfo type fuse.lxcfs'")
74 machine.succeed("incus exec container mount | grep 'lxcfs on /proc/meminfo type fuse.lxcfs'")
76 with subtest("resource limits"):
77 with subtest("Container CPU limits can be managed"):
78 set_container("limits.cpu 1")
79 cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
80 assert cpuinfo == "1", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 1, got: {cpuinfo}"
82 set_container("limits.cpu 2")
83 cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
84 assert cpuinfo == "2", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 2, got: {cpuinfo}"
86 with subtest("Container memory limits can be managed"):
87 set_container("limits.memory 64MB")
88 meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
89 meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
90 assert meminfo_bytes == "62500 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '62500 kB', got: '{meminfo_bytes}'"
92 set_container("limits.memory 128MB")
93 meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
94 meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
95 assert meminfo_bytes == "125000 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '125000 kB', got: '{meminfo_bytes}'"
97 with subtest("lxc-generator"):
98 with subtest("lxc-container generator configures plain container"):
99 # reuse the existing container to save some time
100 machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
101 check_sysctl("container")
103 with subtest("lxc-container generator configures nested container"):
104 machine.execute("incus delete --force container")
105 machine.succeed("incus launch nixos container --config security.nesting=true")
106 with machine.nested("Waiting for instance to start and be usable"):
107 retry(instance_is_up)
109 machine.fail("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
110 target = machine.succeed("incus exec container readlink -- -f /run/systemd/system/systemd-binfmt.service").strip()
111 assert target == "/dev/null", "lxc generator did not correctly mask /run/systemd/system/systemd-binfmt.service"
113 check_sysctl("container")
115 with subtest("lxc-container generator configures privileged container"):
116 machine.execute("incus delete --force container")
117 machine.succeed("incus launch nixos container --config security.privileged=true")
118 with machine.nested("Waiting for instance to start and be usable"):
119 retry(instance_is_up)
121 machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
123 check_sysctl("container")
125 with subtest("softDaemonRestart"):
126 with subtest("Instance remains running when softDaemonRestart is enabled and services is stopped"):
127 pid = machine.succeed("incus info container | grep 'PID'").split(":")[1].strip()
128 machine.succeed(f"ps {pid}")
129 machine.succeed("systemctl stop incus")
130 machine.succeed(f"ps {pid}")