incus: refactor tests and fix VM CSM support (#365778)

This commit is contained in:
Adam C. Stephens
2024-12-19 19:55:14 -05:00
committed by GitHub
14 changed files with 497 additions and 623 deletions

View File

@@ -108,6 +108,10 @@ let
name = "OVMF_VARS.4MB.ms.fd";
path = "${pkgs.OVMFFull.fd}/FV/${ovmf-prefix}_VARS.fd";
}
{
name = "seabios.bin";
path = "${pkgs.seabios-qemu}/share/seabios/bios.bin";
}
];
environment = lib.mkMerge [

View File

@@ -467,8 +467,8 @@ in {
iftop = handleTest ./iftop.nix {};
immich = handleTest ./web-apps/immich.nix {};
incron = handleTest ./incron.nix {};
incus = pkgs.recurseIntoAttrs (handleTest ./incus { inherit handleTestOn; inherit (pkgs) incus; });
incus-lts = pkgs.recurseIntoAttrs (handleTest ./incus { inherit handleTestOn; });
incus = pkgs.recurseIntoAttrs (handleTest ./incus { lts = false; });
incus-lts = pkgs.recurseIntoAttrs (handleTest ./incus { });
influxdb = handleTest ./influxdb.nix {};
influxdb2 = handleTest ./influxdb2.nix {};
initrd-network-openvpn = handleTestOn [ "x86_64-linux" "i686-linux" ] ./initrd-network-openvpn {};

View File

@@ -1,154 +0,0 @@
import ../make-test-python.nix (
{
pkgs,
lib,
extra ? { },
name ? "incus-container",
incus ? pkgs.incus-lts,
...
}:
let
releases = import ../../release.nix {
configuration = lib.recursiveUpdate {
# Building documentation makes the test unnecessarily take a longer time:
documentation.enable = lib.mkForce false;
boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
} extra;
};
container-image-metadata = "${
releases.incusContainerMeta.${pkgs.stdenv.hostPlatform.system}
}/tarball/nixos-image-lxc-*-${pkgs.stdenv.hostPlatform.system}.tar.xz";
container-image-rootfs = "${
releases.incusContainerImage.${pkgs.stdenv.hostPlatform.system}
}/nixos-lxc-image-${pkgs.stdenv.hostPlatform.system}.squashfs";
in
{
inherit name;
meta = {
maintainers = lib.teams.lxc.members;
};
nodes.machine =
{ ... }:
{
virtualisation = {
# Ensure test VM has enough resources for creating and managing guests
cores = 2;
memorySize = 1024;
diskSize = 4096;
incus = {
enable = true;
package = incus;
};
};
networking.nftables.enable = true;
};
testScript = # python
''
def instance_is_up(_) -> bool:
status, _ = machine.execute("incus exec container --disable-stdin --force-interactive /run/current-system/sw/bin/systemctl -- is-system-running")
return status == 0
def set_container(config):
machine.succeed(f"incus config set container {config}")
machine.succeed("incus restart container")
with machine.nested("Waiting for instance to start and be usable"):
retry(instance_is_up)
def check_sysctl(instance):
with subtest("systemd sysctl settings are applied"):
machine.succeed(f"incus exec {instance} -- systemctl status systemd-sysctl")
sysctl = machine.succeed(f"incus exec {instance} -- sysctl net.ipv4.ip_forward").strip().split(" ")[-1]
assert "1" == sysctl, f"systemd-sysctl configuration not correctly applied, {sysctl} != 1"
machine.wait_for_unit("incus.service")
# no preseed should mean no service
machine.fail("systemctl status incus-preseed.service")
machine.succeed("incus admin init --minimal")
with subtest("Container image can be imported"):
machine.succeed("incus image import ${container-image-metadata} ${container-image-rootfs} --alias nixos")
with subtest("Container can be launched and managed"):
machine.succeed("incus launch nixos container")
with machine.nested("Waiting for instance to start and be usable"):
retry(instance_is_up)
machine.succeed("echo true | incus exec container /run/current-system/sw/bin/bash -")
with subtest("Container mounts lxcfs overlays"):
machine.succeed("incus exec container mount | grep 'lxcfs on /proc/cpuinfo type fuse.lxcfs'")
machine.succeed("incus exec container mount | grep 'lxcfs on /proc/meminfo type fuse.lxcfs'")
with subtest("resource limits"):
with subtest("Container CPU limits can be managed"):
set_container("limits.cpu 1")
cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
assert cpuinfo == "1", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 1, got: {cpuinfo}"
set_container("limits.cpu 2")
cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
assert cpuinfo == "2", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 2, got: {cpuinfo}"
with subtest("Container memory limits can be managed"):
set_container("limits.memory 64MB")
meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
assert meminfo_bytes == "62500 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '62500 kB', got: '{meminfo_bytes}'"
set_container("limits.memory 128MB")
meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
assert meminfo_bytes == "125000 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '125000 kB', got: '{meminfo_bytes}'"
with subtest("virtual tpm can be configured"):
machine.succeed("incus config device add container vtpm tpm path=/dev/tpm0 pathrm=/dev/tpmrm0")
machine.succeed("incus exec container -- test -e /dev/tpm0")
machine.succeed("incus exec container -- test -e /dev/tpmrm0")
machine.succeed("incus config device remove container vtpm")
machine.fail("incus exec container -- test -e /dev/tpm0")
with subtest("lxc-generator"):
with subtest("lxc-container generator configures plain container"):
# reuse the existing container to save some time
machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
check_sysctl("container")
with subtest("lxc-container generator configures nested container"):
machine.execute("incus delete --force container")
machine.succeed("incus launch nixos container --config security.nesting=true")
with machine.nested("Waiting for instance to start and be usable"):
retry(instance_is_up)
machine.fail("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
target = machine.succeed("incus exec container readlink -- -f /run/systemd/system/systemd-binfmt.service").strip()
assert target == "/dev/null", "lxc generator did not correctly mask /run/systemd/system/systemd-binfmt.service"
check_sysctl("container")
with subtest("lxc-container generator configures privileged container"):
machine.execute("incus delete --force container")
machine.succeed("incus launch nixos container --config security.privileged=true")
with machine.nested("Waiting for instance to start and be usable"):
retry(instance_is_up)
machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
check_sysctl("container")
with subtest("softDaemonRestart"):
with subtest("Instance remains running when softDaemonRestart is enabled and services is stopped"):
pid = machine.succeed("incus info container | grep 'PID'").split(":")[1].strip()
machine.succeed(f"ps {pid}")
machine.succeed("systemctl stop incus")
machine.succeed(f"ps {pid}")
'';
}
)

View File

@@ -1,29 +1,42 @@
{
system ? builtins.currentSystem,
config ? { },
pkgs ? import ../../.. { inherit system config; },
handleTestOn,
incus ? pkgs.incus-lts,
lts ? true,
...
}:
let
incusTest = import ./incus-tests.nix;
in
{
container-legacy-init = import ./container.nix {
name = "container-legacy-init";
inherit incus system pkgs;
all = incusTest {
inherit lts;
allTests = true;
};
container-systemd-init = import ./container.nix {
name = "container-systemd-init";
inherit incus system pkgs;
extra = {
boot.initrd.systemd.enable = true;
};
container = incusTest {
inherit lts;
instanceContainer = true;
};
incusd-options = import ./incusd-options.nix { inherit incus system pkgs; };
lxd-to-incus = import ./lxd-to-incus.nix { inherit incus system pkgs; };
openvswitch = import ./openvswitch.nix { inherit incus system pkgs; };
socket-activated = import ./socket-activated.nix { inherit incus system pkgs; };
storage = import ./storage.nix { inherit incus system pkgs; };
ui = import ./ui.nix { inherit incus system pkgs; };
virtual-machine = handleTestOn [ "x86_64-linux" ] ./virtual-machine.nix {
inherit incus system pkgs;
lvm = incusTest {
inherit lts;
storageLvm = true;
};
lxd-to-incus = import ./lxd-to-incus.nix { };
openvswitch = incusTest {
inherit lts;
networkOvs = true;
};
ui = import ./ui.nix { };
virtual-machine = incusTest {
inherit lts;
instanceVm = true;
};
zfs = incusTest {
inherit lts;
storageLvm = true;
};
}

View File

@@ -0,0 +1,452 @@
import ../make-test-python.nix (
{
pkgs,
lib,
lts ? true,
allTests ? false,
featureUser ? allTests,
initLegacy ? true,
initSystemd ? true,
instanceContainer ? allTests,
instanceVm ? allTests,
networkOvs ? allTests,
storageLvm ? allTests,
storageZfs ? allTests,
...
}:
let
releases =
init:
import ../../release.nix {
configuration = {
# Building documentation makes the test unnecessarily take a longer time:
documentation.enable = lib.mkForce false;
boot.initrd.systemd.enable = init == "systemd";
# Arbitrary sysctl modification to ensure containers can update sysctl
boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
};
};
images = init: {
container = {
metadata =
(releases init).incusContainerMeta.${pkgs.stdenv.hostPlatform.system}
+ "/tarball/nixos-image-lxc-*-${pkgs.stdenv.hostPlatform.system}.tar.xz";
rootfs =
(releases init).incusContainerImage.${pkgs.stdenv.hostPlatform.system}
+ "/nixos-lxc-image-${pkgs.stdenv.hostPlatform.system}.squashfs";
};
virtual-machine = {
metadata =
(releases init).incusVirtualMachineImageMeta.${pkgs.stdenv.hostPlatform.system} + "/*/*.tar.xz";
disk = (releases init).incusVirtualMachineImage.${pkgs.stdenv.hostPlatform.system} + "/nixos.qcow2";
};
};
initVariants = lib.optionals initLegacy [ "legacy" ] ++ lib.optionals initSystemd [ "systemd" ];
canTestVm = instanceVm && pkgs.stdenv.isLinux && pkgs.stdenv.isx86_64;
in
{
name = "incus" + lib.optionalString lts "-lts";
meta = {
maintainers = lib.teams.lxc.members;
};
nodes.machine = {
virtualisation = {
cores = 2;
memorySize = 2048;
diskSize = 12 * 1024;
emptyDiskImages = [
# vdb for zfs
2048
# vdc for lvm
2048
];
incus = {
enable = true;
package = if lts then pkgs.incus-lts else pkgs.incus;
preseed = {
networks =
[
{
name = "incusbr0";
type = "bridge";
config = {
"ipv4.address" = "10.0.10.1/24";
"ipv4.nat" = "true";
};
}
]
++ lib.optionals networkOvs [
{
name = "ovsbr0";
type = "bridge";
config = {
"bridge.driver" = "openvswitch";
"ipv4.address" = "10.0.20.1/24";
"ipv4.nat" = "true";
};
}
];
profiles = [
{
name = "default";
devices = {
eth0 = {
name = "eth0";
network = "incusbr0";
type = "nic";
};
root = {
path = "/";
pool = "default";
size = "35GiB";
type = "disk";
};
};
}
];
storage_pools = [
{
name = "default";
driver = "dir";
}
];
};
};
vswitch.enable = networkOvs;
};
boot.supportedFilesystems = lib.optionals storageZfs [ "zfs" ];
boot.zfs.forceImportRoot = false;
environment.systemPackages = [ pkgs.parted ];
networking.hostId = "01234567";
networking.firewall.trustedInterfaces = [ "incusbr0" ];
services.lvm = {
boot.thin.enable = storageLvm;
dmeventd.enable = storageLvm;
};
networking.nftables.enable = true;
users.users.testuser = {
isNormalUser = true;
shell = pkgs.bashInteractive;
group = "incus";
uid = 1000;
};
};
testScript = # python
''
import json
def wait_for_instance(name: str, project: str = "default"):
machine.wait_until_succeeds(f"incus exec {name} --disable-stdin --force-interactive --project {project} -- /run/current-system/sw/bin/systemctl is-system-running")
def wait_incus_exec_success(name: str, command: str, timeout: int = 900, project: str = "default"):
def check_command(_) -> bool:
status, _ = machine.execute(f"incus exec {name} --disable-stdin --force-interactive --project {project} -- {command}")
return status == 0
with machine.nested(f"Waiting for successful exec: {command}"):
retry(check_command, timeout)
def set_config(name: str, config: str, restart: bool = False, unset: bool = False):
if restart:
machine.succeed(f"incus stop {name}")
if unset:
machine.succeed(f"incus config unset {name} {config}")
else:
machine.succeed(f"incus config set {name} {config}")
if restart:
machine.succeed(f"incus start {name}")
wait_for_instance(name)
else:
# give a moment to settle
machine.sleep(1)
def cleanup():
# avoid conflict between preseed and cleanup operations
machine.wait_for_unit("incus-preseed.service")
instances = json.loads(machine.succeed("incus list --format json --all-projects"))
with subtest("Stopping all running instances"):
for instance in [a for a in instances if a['status'] == 'Running']:
machine.execute(f"incus stop --force {instance['name']} --project {instance['project']}")
machine.execute(f"incus delete --force {instance['name']} --project {instance['project']}")
def check_sysctl(name: str):
with subtest("systemd sysctl settings are applied"):
machine.succeed(f"incus exec {name} -- systemctl status systemd-sysctl")
sysctl = machine.succeed(f"incus exec {name} -- sysctl net.ipv4.ip_forward").strip().split(" ")[-1]
assert "1" == sysctl, f"systemd-sysctl configuration not correctly applied, {sysctl} != 1"
with subtest("Wait for startup"):
machine.wait_for_unit("incus.service")
machine.wait_for_unit("incus-preseed.service")
with subtest("Verify preseed resources created"):
machine.succeed("incus profile show default")
machine.succeed("incus network info incusbr0")
machine.succeed("incus storage show default")
''
+ lib.optionalString instanceContainer (
lib.foldl (
acc: variant:
acc
# python
+ ''
metadata = "${(images variant).container.metadata}"
rootfs = "${(images variant).container.rootfs}"
alias = "nixos/container/${variant}"
variant = "${variant}"
with subtest("Container image can be imported"):
machine.succeed(f"incus image import {metadata} {rootfs} --alias {alias}")
with subtest("Container can be launched and managed"):
machine.succeed(f"incus launch {alias} container-{variant}1")
wait_for_instance(f"container-{variant}1")
with subtest("Container mounts lxcfs overlays"):
machine.succeed(f"incus exec container-{variant}1 mount | grep 'lxcfs on /proc/cpuinfo type fuse.lxcfs'")
machine.succeed(f"incus exec container-{variant}1 mount | grep 'lxcfs on /proc/meminfo type fuse.lxcfs'")
with subtest("resource limits"):
with subtest("Container CPU limits can be managed"):
set_config(f"container-{variant}1", "limits.cpu 1", restart=True)
wait_incus_exec_success(f"container-{variant}1", "nproc | grep '^1$'", timeout=15)
with subtest("Container CPU limits can be hotplug changed"):
set_config(f"container-{variant}1", "limits.cpu 2")
wait_incus_exec_success(f"container-{variant}1", "nproc | grep '^2$'", timeout=15)
with subtest("Container memory limits can be managed"):
set_config(f"container-{variant}1", "limits.memory 128MB", restart=True)
wait_incus_exec_success(f"container-{variant}1", "grep 'MemTotal:[[:space:]]*125000 kB' /proc/meminfo", timeout=15)
with subtest("Container memory limits can be hotplug changed"):
set_config(f"container-{variant}1", "limits.memory 256MB")
wait_incus_exec_success(f"container-{variant}1", "grep 'MemTotal:[[:space:]]*250000 kB' /proc/meminfo", timeout=15)
with subtest("virtual tpm can be configured"):
machine.succeed(f"incus config device add container-{variant}1 vtpm tpm path=/dev/tpm0 pathrm=/dev/tpmrm0")
machine.succeed(f"incus exec container-{variant}1 -- test -e /dev/tpm0")
machine.succeed(f"incus exec container-{variant}1 -- test -e /dev/tpmrm0")
machine.succeed(f"incus config device remove container-{variant}1 vtpm")
machine.fail(f"incus exec container-{variant}1 -- test -e /dev/tpm0")
with subtest("lxc-generator"):
with subtest("lxc-container generator configures plain container"):
# default container is plain
machine.succeed(f"incus exec container-{variant}1 test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
check_sysctl(f"container-{variant}1")
with subtest("lxc-container generator configures nested container"):
set_config(f"container-{variant}1", "security.nesting=true", restart=True)
machine.fail(f"incus exec container-{variant}1 test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
target = machine.succeed(f"incus exec container-{variant}1 readlink -- -f /run/systemd/system/systemd-binfmt.service").strip()
assert target == "/dev/null", "lxc generator did not correctly mask /run/systemd/system/systemd-binfmt.service"
check_sysctl(f"container-{variant}1")
with subtest("lxc-container generator configures privileged container"):
# Create a new instance for a clean state
machine.succeed(f"incus launch {alias} container-{variant}2")
wait_for_instance(f"container-{variant}2")
machine.succeed(f"incus exec container-{variant}2 test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
check_sysctl(f"container-{variant}2")
with subtest("Instance remains running when softDaemonRestart is enabled and service is stopped"):
pid = machine.succeed(f"incus info container-{variant}1 | grep 'PID'").split(":")[1].strip()
machine.succeed(f"ps {pid}")
machine.succeed("systemctl stop incus")
machine.succeed(f"ps {pid}")
machine.succeed("systemctl start incus")
cleanup()
''
) "" initVariants
)
+ lib.optionalString canTestVm (
(lib.foldl (
acc: variant:
acc
# python
+ ''
metadata = "${(images variant).virtual-machine.metadata}"
disk = "${(images variant).virtual-machine.disk}"
alias = "nixos/virtual-machine/${variant}"
variant = "${variant}"
with subtest("virtual-machine image can be imported"):
machine.succeed(f"incus image import {metadata} {disk} --alias {alias}")
with subtest("virtual-machine can be created"):
machine.succeed(f"incus create {alias} vm-{variant}1 --vm --config limits.memory=512MB --config security.secureboot=false")
with subtest("virtual tpm can be configured"):
machine.succeed(f"incus config device add vm-{variant}1 vtpm tpm path=/dev/tpm0")
with subtest("virtual-machine can be launched and become available"):
machine.succeed(f"incus start vm-{variant}1")
wait_for_instance(f"vm-{variant}1")
with subtest("incus-agent is started"):
machine.succeed(f"incus exec vm-{variant}1 systemctl is-active incus-agent")
with subtest("incus-agent has a valid path"):
machine.succeed(f"incus exec vm-{variant}1 -- bash -c 'true'")
with subtest("Container CPU limits can be managed"):
set_config(f"vm-{variant}1", "limits.cpu 1", restart=True)
wait_incus_exec_success(f"vm-{variant}1", "nproc | grep '^1$'", timeout=90)
with subtest("Container CPU limits can be hotplug changed"):
set_config(f"vm-{variant}1", "limits.cpu 2")
wait_incus_exec_success(f"vm-{variant}1", "nproc | grep '^2$'", timeout=15)
with subtest("Instance remains running when softDaemonRestart is enabled and service is stopped"):
pid = machine.succeed(f"incus info vm-{variant}1 | grep 'PID'").split(":")[1].strip()
machine.succeed(f"ps {pid}")
machine.succeed("systemctl stop incus")
machine.succeed(f"ps {pid}")
machine.succeed("systemctl start incus")
cleanup()
''
) "" initVariants)
+
# python
''
with subtest("Can launch CSM virtual machine"):
machine.succeed("incus init csm --vm --empty -c security.csm=true -c security.secureboot=false")
machine.succeed("incus start csm")
cleanup()
''
)
+
lib.optionalString featureUser # python
''
with subtest("incus-user allows restricted access for users"):
machine.fail("incus project show user-1000")
machine.succeed("su - testuser bash -c 'incus list'")
# a project is created dynamically for the user
machine.succeed("incus project show user-1000")
# users shouldn't be able to list storage pools
machine.fail("su - testuser bash -c 'incus storage list'")
with subtest("incus-user allows users to launch instances"):
machine.succeed("su - testuser bash -c 'incus image import ${(images "systemd").container.metadata} ${(images "systemd").container.rootfs} --alias nixos'")
machine.succeed("su - testuser bash -c 'incus launch nixos instance2'")
wait_for_instance("instance2", "user-1000")
cleanup()
''
+
lib.optionalString networkOvs # python
''
with subtest("Verify openvswitch bridge"):
machine.succeed("incus network info ovsbr0")
with subtest("Verify openvswitch bridge"):
machine.succeed("ovs-vsctl br-exists ovsbr0")
''
+
lib.optionalString storageZfs # python
''
with subtest("Verify zfs pool created and usable"):
machine.succeed(
"zpool status",
"parted --script /dev/vdb mklabel gpt",
"zpool create zfs_pool /dev/vdb",
)
machine.succeed("incus storage create zfs_pool zfs source=zfs_pool/incus")
machine.succeed("zfs list zfs_pool/incus")
machine.succeed("incus storage volume create zfs_pool test_fs --type filesystem")
machine.succeed("incus storage volume create zfs_pool test_vol --type block")
machine.succeed("incus storage show zfs_pool")
machine.succeed("incus storage volume list zfs_pool")
machine.succeed("incus storage volume show zfs_pool test_fs")
machine.succeed("incus storage volume show zfs_pool test_vol")
machine.succeed("incus create zfs1 --empty --storage zfs_pool")
machine.succeed("incus list zfs1")
''
+
lib.optionalString storageLvm # python
''
with subtest("Verify lvm pool created and usable"):
machine.succeed("incus storage create lvm_pool lvm source=/dev/vdc lvm.vg_name=incus_pool")
machine.succeed("vgs incus_pool")
machine.succeed("incus storage volume create lvm_pool test_fs --type filesystem")
machine.succeed("incus storage volume create lvm_pool test_vol --type block")
machine.succeed("incus storage show lvm_pool")
machine.succeed("incus storage volume list lvm_pool")
machine.succeed("incus storage volume show lvm_pool test_fs")
machine.succeed("incus storage volume show lvm_pool test_vol")
machine.succeed("incus create lvm1 --empty --storage zfs_pool")
machine.succeed("incus list lvm1")
'';
}
)

View File

@@ -1,140 +0,0 @@
# this is a set of tests for non-default options. typically the default options
# will be handled by the other tests
import ../make-test-python.nix (
{
pkgs,
lib,
incus ? pkgs.incus-lts,
...
}:
let
releases = import ../../release.nix {
configuration = {
# Building documentation makes the test unnecessarily take a longer time:
documentation.enable = lib.mkForce false;
};
};
container-image-metadata = "${
releases.incusContainerMeta.${pkgs.stdenv.hostPlatform.system}
}/tarball/nixos-image-lxc-*-${pkgs.stdenv.hostPlatform.system}.tar.xz";
container-image-rootfs = "${
releases.incusContainerImage.${pkgs.stdenv.hostPlatform.system}
}/nixos-lxc-image-${pkgs.stdenv.hostPlatform.system}.squashfs";
in
{
name = "incusd-options";
meta = {
maintainers = lib.teams.lxc.members;
};
nodes.machine = {
virtualisation = {
cores = 2;
memorySize = 1024;
diskSize = 4096;
incus = {
enable = true;
package = incus;
softDaemonRestart = false;
preseed = {
networks = [
{
name = "incusbr0";
type = "bridge";
config = {
"ipv4.address" = "10.0.100.1/24";
"ipv4.nat" = "true";
};
}
];
profiles = [
{
name = "default";
devices = {
eth0 = {
name = "eth0";
network = "incusbr0";
type = "nic";
};
root = {
path = "/";
pool = "default";
size = "35GiB";
type = "disk";
};
};
}
];
storage_pools = [
{
name = "default";
driver = "dir";
}
];
};
};
};
networking.nftables.enable = true;
users.users.testuser = {
isNormalUser = true;
shell = pkgs.bashInteractive;
group = "incus";
uid = 1000;
};
};
testScript = # python
''
def wait_for_instance(name: str, project: str = "default"):
def instance_is_up(_) -> bool:
status, _ = machine.execute(f"incus exec {name} --disable-stdin --force-interactive --project {project} -- /run/current-system/sw/bin/systemctl is-system-running")
return status == 0
with machine.nested(f"Waiting for instance {name} to start and be usable"):
retry(instance_is_up)
machine.wait_for_unit("incus.service")
machine.wait_for_unit("incus-preseed.service")
with subtest("Container image can be imported"):
machine.succeed("incus image import ${container-image-metadata} ${container-image-rootfs} --alias nixos")
with subtest("Container can be launched and managed"):
machine.succeed("incus launch nixos instance1")
wait_for_instance("instance1")
machine.succeed("echo true | incus exec instance1 /run/current-system/sw/bin/bash -")
with subtest("Verify preseed resources created"):
machine.succeed("incus profile show default")
machine.succeed("incus network info incusbr0")
machine.succeed("incus storage show default")
with subtest("Instance is stopped when softDaemonRestart is disabled and services is stopped"):
pid = machine.succeed("incus info instance1 | grep 'PID'").split(":")[1].strip()
machine.succeed(f"ps {pid}")
machine.succeed("systemctl stop incus")
machine.fail(f"ps {pid}")
with subtest("incus-user allows restricted access for users"):
machine.fail("incus project show user-1000")
machine.succeed("su - testuser bash -c 'incus list'")
# a project is created dynamically for the user
machine.succeed("incus project show user-1000")
# users shouldn't be able to list storage pools
machine.fail("su - testuser bash -c 'incus storage list'")
with subtest("incus-user allows users to launch instances"):
machine.succeed("su - testuser bash -c 'incus image import ${container-image-metadata} ${container-image-rootfs} --alias nixos'")
machine.succeed("su - testuser bash -c 'incus launch nixos instance2'")
wait_for_instance("instance2", "user-1000")
'';
}
)

View File

@@ -1,78 +0,0 @@
import ../make-test-python.nix (
{
pkgs,
lib,
incus ? pkgs.incus-lts,
...
}:
{
name = "incus-openvswitch";
meta = {
maintainers = lib.teams.lxc.members;
};
nodes.machine =
{ lib, ... }:
{
virtualisation = {
incus = {
enable = true;
package = incus;
};
vswitch.enable = true;
incus.preseed = {
networks = [
{
name = "nixostestbr0";
type = "bridge";
config = {
"bridge.driver" = "openvswitch";
"ipv4.address" = "10.0.100.1/24";
"ipv4.nat" = "true";
};
}
];
profiles = [
{
name = "nixostest_default";
devices = {
eth0 = {
name = "eth0";
network = "nixostestbr0";
type = "nic";
};
root = {
path = "/";
pool = "default";
size = "35GiB";
type = "disk";
};
};
}
];
storage_pools = [
{
name = "nixostest_pool";
driver = "dir";
}
];
};
};
networking.nftables.enable = true;
};
testScript = ''
machine.wait_for_unit("incus.service")
machine.wait_for_unit("incus-preseed.service")
with subtest("Verify openvswitch bridge"):
machine.succeed("incus network info nixostestbr0")
with subtest("Verify openvswitch bridge"):
machine.succeed("ovs-vsctl br-exists nixostestbr0")
'';
}
)

View File

@@ -1,41 +0,0 @@
import ../make-test-python.nix (
{
pkgs,
lib,
incus ? pkgs.incus-lts,
...
}:
{
name = "incus-socket-activated";
meta = {
maintainers = lib.teams.lxc.members;
};
nodes.machine =
{ lib, ... }:
{
virtualisation = {
incus = {
enable = true;
package = incus;
socketActivation = true;
};
};
networking.nftables.enable = true;
};
testScript = ''
machine.wait_for_unit("incus.socket")
# ensure service is not running by default
machine.fail("systemctl is-active incus.service")
machine.fail("systemctl is-active incus-preseed.service")
# access the socket and ensure the service starts
machine.succeed("incus list")
machine.wait_for_unit("incus.service")
'';
}
)

View File

@@ -1,84 +0,0 @@
import ../make-test-python.nix (
{
pkgs,
lib,
incus ? pkgs.incus-lts,
...
}:
{
name = "incus-storage";
meta = {
maintainers = lib.teams.lxc.members;
};
nodes.machine = {
boot.supportedFilesystems = [ "zfs" ];
boot.zfs.forceImportRoot = false;
environment.systemPackages = [ pkgs.parted ];
networking.hostId = "01234567";
networking.nftables.enable = true;
services.lvm = {
boot.thin.enable = true;
dmeventd.enable = true;
};
virtualisation = {
emptyDiskImages = [
2048
2048
];
incus = {
enable = true;
package = incus;
};
};
};
testScript = # python
''
machine.wait_for_unit("incus.service")
with subtest("Verify zfs pool created and usable"):
machine.succeed(
"zpool status",
"parted --script /dev/vdb mklabel gpt",
"zpool create zfs_pool /dev/vdb",
)
machine.succeed("incus storage create zfs_pool zfs source=zfs_pool/incus")
machine.succeed("zfs list zfs_pool/incus")
machine.succeed("incus storage volume create zfs_pool test_fs --type filesystem")
machine.succeed("incus storage volume create zfs_pool test_vol --type block")
machine.succeed("incus storage show zfs_pool")
machine.succeed("incus storage volume list zfs_pool")
machine.succeed("incus storage volume show zfs_pool test_fs")
machine.succeed("incus storage volume show zfs_pool test_vol")
machine.succeed("incus create zfs1 --empty --storage zfs_pool")
machine.succeed("incus list zfs1")
with subtest("Verify lvm pool created and usable"):
machine.succeed("incus storage create lvm_pool lvm source=/dev/vdc lvm.vg_name=incus_pool")
machine.succeed("vgs incus_pool")
machine.succeed("incus storage volume create lvm_pool test_fs --type filesystem")
machine.succeed("incus storage volume create lvm_pool test_vol --type block")
machine.succeed("incus storage show lvm_pool")
machine.succeed("incus storage volume list lvm_pool")
machine.succeed("incus storage volume show lvm_pool test_fs")
machine.succeed("incus storage volume show lvm_pool test_vol")
machine.succeed("incus create lvm1 --empty --storage zfs_pool")
machine.succeed("incus list lvm1")
'';
}
)

View File

@@ -1,95 +0,0 @@
import ../make-test-python.nix (
{
pkgs,
lib,
incus ? pkgs.incus-lts,
...
}:
let
releases = import ../../release.nix {
configuration = {
# Building documentation makes the test unnecessarily take a longer time:
documentation.enable = lib.mkForce false;
# Our tests require `grep` & friends:
environment.systemPackages = with pkgs; [ busybox ];
};
};
vm-image-metadata = releases.incusVirtualMachineImageMeta.${pkgs.stdenv.hostPlatform.system};
vm-image-disk = releases.incusVirtualMachineImage.${pkgs.stdenv.hostPlatform.system};
instance-name = "instance1";
in
{
name = "incus-virtual-machine";
meta = {
maintainers = lib.teams.lxc.members;
};
nodes.machine =
{ ... }:
{
virtualisation = {
# Ensure test VM has enough resources for creating and managing guests
cores = 2;
memorySize = 1024;
diskSize = 4096;
incus = {
enable = true;
package = incus;
};
};
networking.nftables.enable = true;
};
testScript = # python
''
def instance_is_up(_) -> bool:
status, _ = machine.execute("incus exec ${instance-name} --disable-stdin --force-interactive /run/current-system/sw/bin/systemctl -- is-system-running")
return status == 0
machine.wait_for_unit("incus.service")
machine.succeed("incus admin init --minimal")
with subtest("virtual-machine image can be imported"):
machine.succeed("incus image import ${vm-image-metadata}/*/*.tar.xz ${vm-image-disk}/nixos.qcow2 --alias nixos")
with subtest("virtual-machine can be created"):
machine.succeed("incus create nixos ${instance-name} --vm --config limits.memory=512MB --config security.secureboot=false")
with subtest("virtual tpm can be configured"):
machine.succeed("incus config device add ${instance-name} vtpm tpm path=/dev/tpm0")
with subtest("virtual-machine can be launched and become available"):
machine.succeed("incus start ${instance-name}")
with machine.nested("Waiting for instance to start and be usable"):
retry(instance_is_up)
with subtest("incus-agent is started"):
machine.succeed("incus exec ${instance-name} systemctl is-active incus-agent")
with subtest("incus-agent has a valid path"):
machine.succeed("incus exec ${instance-name} -- bash -c 'true'")
with subtest("guest supports cpu hotplug"):
machine.succeed("incus config set ${instance-name} limits.cpu=1")
count = int(machine.succeed("incus exec ${instance-name} -- nproc").strip())
assert count == 1, f"Wrong number of CPUs reported, want: 1, got: {count}"
machine.succeed("incus config set ${instance-name} limits.cpu=2")
count = int(machine.succeed("incus exec ${instance-name} -- nproc").strip())
assert count == 2, f"Wrong number of CPUs reported, want: 2, got: {count}"
with subtest("Instance remains running when softDaemonRestart is enabled and services is stopped"):
pid = machine.succeed("incus info ${instance-name} | grep 'PID'").split(":")[1].strip()
machine.succeed(f"ps {pid}")
machine.succeed("systemctl stop incus")
machine.succeed(f"ps {pid}")
'';
}
)

View File

@@ -63,8 +63,7 @@ buildGoModule rec {
passthru = {
tests = {
incus-legacy-init = nixosTests.incus.container-legacy-init;
incus-systemd-init = nixosTests.incus.container-systemd-init;
incus-lts = nixosTests.incus-lts.container;
};
generator = callPackage ./generator.nix { inherit src version; };

View File

@@ -124,7 +124,7 @@ buildGoModule rec {
;
};
tests = if lts then nixosTests.incus-lts else nixosTests.incus;
tests = if lts then nixosTests.incus-lts.all else nixosTests.incus.all;
ui = callPackage ./ui.nix { };

View File

@@ -85,8 +85,7 @@ stdenv.mkDerivation (finalAttrs: {
passthru = {
tests = {
incus-legacy-init = nixosTests.incus.container-legacy-init;
incus-systemd-init = nixosTests.incus.container-systemd-init;
incus-lts = nixosTests.incus-lts.container;
lxc = nixosTests.lxc;
lxd = nixosTests.lxd.container;
};

View File

@@ -68,8 +68,7 @@ stdenv.mkDerivation rec {
passthru = {
tests = {
incus-container-legacy-init = nixosTests.incus.container-legacy-init;
incus-container-systemd-init = nixosTests.incus.container-systemd-init;
incus-lts = nixosTests.incus-lts.container;
};
updateScript = nix-update-script { };