diff options
Diffstat (limited to 'nixos/tests')
72 files changed, 1695 insertions, 867 deletions
diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index d63a77fcdd23c..511d3c589faef 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -99,7 +99,14 @@ serverAliases = [ "${server}-wildcard-alias.example.test" ]; useACMEHost = "example.test"; }; - }; + } // (lib.optionalAttrs (server == "nginx") { + # The nginx module supports using a different key than the hostname + different-key = vhostBaseData // { + serverName = "${server}-different-key.example.test"; + serverAliases = [ "${server}-different-key-alias.example.test" ]; + enableACME = true; + }; + }); }; # Used to determine if service reload was triggered @@ -653,20 +660,20 @@ in { webserver.succeed("systemctl restart caddy.service") check_connection_key_bits(client, "a.example.test", "384") - domains = ["http", "dns", "wildcard"] - for server, logsrc in [ - ("nginx", "journalctl -n 30 -u nginx.service"), - ("httpd", "tail -n 30 /var/log/httpd/*.log"), + common_domains = ["http", "dns", "wildcard"] + for server, logsrc, domains in [ + ("nginx", "journalctl -n 30 -u nginx.service", common_domains + ["different-key"]), + ("httpd", "tail -n 30 /var/log/httpd/*.log", common_domains), ]: wait_for_server = lambda: webserver.wait_for_unit(f"{server}.service") with subtest(f"Works with {server}"): try: switch_to(webserver, server) - # Skip wildcard domain for this check ([:-1]) - for domain in domains[:-1]: - webserver.wait_for_unit( - f"acme-finished-{server}-{domain}.example.test.target" - ) + for domain in domains: + if domain != "wildcard": + webserver.wait_for_unit( + f"acme-finished-{server}-{domain}.example.test.target" + ) except Exception as err: _, output = webserver.execute( f"{logsrc} && ls -al /var/lib/acme/acme-challenge" @@ -676,8 +683,9 @@ in { wait_for_server() - for domain in domains[:-1]: - check_issuer(webserver, f"{server}-{domain}.example.test", "pebble") + for domain in domains: + if domain != "wildcard": + check_issuer(webserver, f"{server}-{domain}.example.test", "pebble") for domain in domains: check_connection(client, f"{server}-{domain}.example.test") check_connection(client, f"{server}-{domain}-alias.example.test") diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index d4da32c44990f..c6ec2474e6052 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -243,6 +243,7 @@ in { deepin = handleTest ./deepin.nix {}; deluge = handleTest ./deluge.nix {}; dendrite = handleTest ./matrix/dendrite.nix {}; + devpi-server = handleTest ./devpi-server.nix {}; dex-oidc = handleTest ./dex-oidc.nix {}; dhparams = handleTest ./dhparams.nix {}; disable-installer-tools = handleTest ./disable-installer-tools.nix {}; @@ -424,7 +425,8 @@ in { icingaweb2 = handleTest ./icingaweb2.nix {}; iftop = handleTest ./iftop.nix {}; incron = handleTest ./incron.nix {}; - incus = pkgs.recurseIntoAttrs (handleTest ./incus { inherit handleTestOn; }); + incus = pkgs.recurseIntoAttrs (handleTest ./incus { inherit handleTestOn; inherit (pkgs) incus; }); + incus-lts = pkgs.recurseIntoAttrs (handleTest ./incus { inherit handleTestOn; }); influxdb = handleTest ./influxdb.nix {}; influxdb2 = handleTest ./influxdb2.nix {}; initrd-network-openvpn = handleTestOn [ "x86_64-linux" "i686-linux" ] ./initrd-network-openvpn {}; @@ -756,6 +758,7 @@ in { pretix = runTest ./web-apps/pretix.nix; printing-socket = handleTest ./printing.nix { socket = true; }; printing-service = handleTest ./printing.nix { socket = false; }; + private-gpt = handleTest ./private-gpt.nix {}; privoxy = handleTest ./privoxy.nix {}; prometheus = handleTest ./prometheus.nix {}; prometheus-exporters = handleTest ./prometheus-exporters.nix {}; @@ -870,7 +873,8 @@ in { swap-random-encryption = handleTest ./swap-random-encryption.nix {}; sway = handleTest ./sway.nix {}; swayfx = handleTest ./swayfx.nix {}; - switchTest = handleTest ./switch-test.nix {}; + switchTest = handleTest ./switch-test.nix { ng = false; }; + switchTestNg = handleTest ./switch-test.nix { ng = true; }; sympa = handleTest ./sympa.nix {}; syncthing = handleTest ./syncthing.nix {}; syncthing-no-settings = handleTest ./syncthing-no-settings.nix {}; @@ -883,7 +887,7 @@ in { systemd-binfmt = handleTestOn ["x86_64-linux"] ./systemd-binfmt.nix {}; systemd-boot = handleTest ./systemd-boot.nix {}; systemd-bpf = handleTest ./systemd-bpf.nix {}; - systemd-confinement = handleTest ./systemd-confinement.nix {}; + systemd-confinement = handleTest ./systemd-confinement {}; systemd-coredump = handleTest ./systemd-coredump.nix {}; systemd-cryptenroll = handleTest ./systemd-cryptenroll.nix {}; systemd-credentials-tpm2 = handleTest ./systemd-credentials-tpm2.nix {}; @@ -994,6 +998,7 @@ in { v2ray = handleTest ./v2ray.nix {}; varnish60 = handleTest ./varnish.nix { package = pkgs.varnish60; }; varnish74 = handleTest ./varnish.nix { package = pkgs.varnish74; }; + varnish75 = handleTest ./varnish.nix { package = pkgs.varnish75; }; vault = handleTest ./vault.nix {}; vault-agent = handleTest ./vault-agent.nix {}; vault-dev = handleTest ./vault-dev.nix {}; @@ -1035,7 +1040,9 @@ in { xterm = handleTest ./xterm.nix {}; xxh = handleTest ./xxh.nix {}; yabar = handleTest ./yabar.nix {}; + ydotool = handleTest ./ydotool.nix {}; yggdrasil = handleTest ./yggdrasil.nix {}; + your_spotify = handleTest ./your_spotify.nix {}; zammad = handleTest ./zammad.nix {}; zeronet-conservancy = handleTest ./zeronet-conservancy.nix {}; zfs = handleTest ./zfs.nix {}; diff --git a/nixos/tests/archi.nix b/nixos/tests/archi.nix index 59f2e940c0050..a8cb1c503d4f7 100644 --- a/nixos/tests/archi.nix +++ b/nixos/tests/archi.nix @@ -24,7 +24,9 @@ import ./make-test-python.nix ({ lib, ... }: { machine.wait_for_window("Archi") # wait till main UI is open - machine.wait_for_text("Welcome to Archi") + # since OCR seems to be buggy wait_for_text was replaced by sleep, issue: #302965 + # machine.wait_for_text("Welcome to Archi") + machine.sleep(20) machine.screenshot("welcome-screen") ''; diff --git a/nixos/tests/avahi.nix b/nixos/tests/avahi.nix index d8f4d13340fbc..4ae2f919f2f7d 100644 --- a/nixos/tests/avahi.nix +++ b/nixos/tests/avahi.nix @@ -9,7 +9,7 @@ import ./make-test-python.nix { name = "avahi"; meta = with pkgs.lib.maintainers; { - maintainers = [ eelco ]; + maintainers = [ ]; }; nodes = let diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix index 473b05d4c98e8..b5f5982743a13 100644 --- a/nixos/tests/bittorrent.nix +++ b/nixos/tests/bittorrent.nix @@ -36,7 +36,7 @@ in { name = "bittorrent"; meta = with pkgs.lib.maintainers; { - maintainers = [ domenkozar eelco rob bobvanderlinden ]; + maintainers = [ domenkozar rob bobvanderlinden ]; }; nodes = { diff --git a/nixos/tests/containers-bridge.nix b/nixos/tests/containers-bridge.nix index d2e16299edaad..3001db33ba5a3 100644 --- a/nixos/tests/containers-bridge.nix +++ b/nixos/tests/containers-bridge.nix @@ -8,7 +8,7 @@ in import ./make-test-python.nix ({ pkgs, lib, ... }: { name = "containers-bridge"; meta = { - maintainers = with lib.maintainers; [ aristid aszlig eelco kampfschlaefer ]; + maintainers = with lib.maintainers; [ aristid aszlig kampfschlaefer ]; }; nodes.machine = diff --git a/nixos/tests/containers-imperative.nix b/nixos/tests/containers-imperative.nix index fff00e4f73a85..ea1046b40354e 100644 --- a/nixos/tests/containers-imperative.nix +++ b/nixos/tests/containers-imperative.nix @@ -1,7 +1,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { name = "containers-imperative"; meta = { - maintainers = with lib.maintainers; [ aristid aszlig eelco kampfschlaefer ]; + maintainers = with lib.maintainers; [ aristid aszlig kampfschlaefer ]; }; nodes.machine = diff --git a/nixos/tests/containers-ip.nix b/nixos/tests/containers-ip.nix index ecff99a3f0c25..034e5d660415a 100644 --- a/nixos/tests/containers-ip.nix +++ b/nixos/tests/containers-ip.nix @@ -14,7 +14,7 @@ let in import ./make-test-python.nix ({ pkgs, lib, ... }: { name = "containers-ipv4-ipv6"; meta = { - maintainers = with lib.maintainers; [ aristid aszlig eelco kampfschlaefer ]; + maintainers = with lib.maintainers; [ aristid aszlig kampfschlaefer ]; }; nodes.machine = diff --git a/nixos/tests/containers-portforward.nix b/nixos/tests/containers-portforward.nix index b8c7aabc5a50b..1a9880fe93133 100644 --- a/nixos/tests/containers-portforward.nix +++ b/nixos/tests/containers-portforward.nix @@ -8,7 +8,7 @@ in import ./make-test-python.nix ({ pkgs, lib, ... }: { name = "containers-portforward"; meta = { - maintainers = with lib.maintainers; [ aristid aszlig eelco kampfschlaefer ianwookim ]; + maintainers = with lib.maintainers; [ aristid aszlig kampfschlaefer ianwookim ]; }; nodes.machine = diff --git a/nixos/tests/devpi-server.nix b/nixos/tests/devpi-server.nix new file mode 100644 index 0000000000000..2a16d49724dbc --- /dev/null +++ b/nixos/tests/devpi-server.nix @@ -0,0 +1,35 @@ +import ./make-test-python.nix ({pkgs, ...}: let + server-port = 3141; +in { + name = "devpi-server"; + meta = with pkgs.lib.maintainers; { + maintainers = [cafkafk]; + }; + + nodes = { + devpi = {...}: { + services.devpi-server = { + enable = true; + host = "0.0.0.0"; + port = server-port; + openFirewall = true; + secretFile = pkgs.writeText "devpi-secret" "v263P+V3YGDYUyfYL/RBURw+tCPMDw94R/iCuBNJrDhaYrZYjpA6XPFVDDH8ViN20j77y2PHoMM/U0opNkVQ2g=="; + }; + }; + + client1 = {...}: { + environment.systemPackages = with pkgs; [ + devpi-client + jq + ]; + }; + }; + + testScript = '' + start_all() + devpi.wait_for_unit("devpi-server.service") + devpi.wait_for_open_port(${builtins.toString server-port}) + + client1.succeed("devpi getjson http://devpi:${builtins.toString server-port}") + ''; +}) diff --git a/nixos/tests/elk.nix b/nixos/tests/elk.nix index b5a8cb532ae0a..87c82877fe109 100644 --- a/nixos/tests/elk.nix +++ b/nixos/tests/elk.nix @@ -16,7 +16,7 @@ let import ./make-test-python.nix ({ inherit name; meta = with pkgs.lib.maintainers; { - maintainers = [ eelco offline basvandijk ]; + maintainers = [ offline basvandijk ]; }; nodes = { one = diff --git a/nixos/tests/fcitx5/default.nix b/nixos/tests/fcitx5/default.nix index c113f2e2c052c..feea621f6b5b2 100644 --- a/nixos/tests/fcitx5/default.nix +++ b/nixos/tests/fcitx5/default.nix @@ -89,10 +89,13 @@ rec { machine.succeed("xauth merge ${xauth}") machine.sleep(5) + machine.wait_until_succeeds("pgrep fcitx5") machine.succeed("su - ${user.name} -c 'kill $(pgrep fcitx5)'") machine.sleep(1) machine.succeed("su - ${user.name} -c 'alacritty >&2 &'") + machine.wait_for_window("alice@machine") + machine.succeed("su - ${user.name} -c 'fcitx5 >&2 &'") machine.sleep(10) diff --git a/nixos/tests/firefox.nix b/nixos/tests/firefox.nix index fbea95dc75235..6418e029f80d9 100644 --- a/nixos/tests/firefox.nix +++ b/nixos/tests/firefox.nix @@ -1,9 +1,9 @@ -import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }: +import ./make-test-python.nix ({ lib, pkgs, firefoxPackage, ... }: { name = firefoxPackage.pname; meta = with pkgs.lib.maintainers; { - maintainers = [ eelco shlevy ]; + maintainers = [ shlevy ]; }; nodes.machine = @@ -55,7 +55,7 @@ import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }: }; testScript = let - exe = firefoxPackage.unwrapped.binaryName; + exe = lib.getExe firefoxPackage; in '' from contextlib import contextmanager diff --git a/nixos/tests/firewall.nix b/nixos/tests/firewall.nix index dd7551f143a5e..34e8bda60eef5 100644 --- a/nixos/tests/firewall.nix +++ b/nixos/tests/firewall.nix @@ -3,7 +3,7 @@ import ./make-test-python.nix ( { pkgs, nftables, ... } : { name = "firewall" + pkgs.lib.optionalString nftables "-nftables"; meta = with pkgs.lib.maintainers; { - maintainers = [ eelco ]; + maintainers = [ ]; }; nodes = diff --git a/nixos/tests/fish.nix b/nixos/tests/fish.nix index 3d9b13c6af70a..c9a1bef51478e 100644 --- a/nixos/tests/fish.nix +++ b/nixos/tests/fish.nix @@ -10,6 +10,8 @@ import ./make-test-python.nix ({ pkgs, ... }: { coreutils procps # kill collides with coreutils' to test https://github.com/NixOS/nixpkgs/issues/56432 ]; + # TODO: remove if/when #267880 is merged and this is a default + services.logrotate.enable = false; }; testScript = diff --git a/nixos/tests/garage/default.nix b/nixos/tests/garage/default.nix index a42236e9a5bbe..b7f9bb4b865bd 100644 --- a/nixos/tests/garage/default.nix +++ b/nixos/tests/garage/default.nix @@ -51,4 +51,5 @@ in [ "0_8" "0_9" + "1_x" ] diff --git a/nixos/tests/garage/with-3node-replication.nix b/nixos/tests/garage/with-3node-replication.nix index d4387b198d976..266a1082893f7 100644 --- a/nixos/tests/garage/with-3node-replication.nix +++ b/nixos/tests/garage/with-3node-replication.nix @@ -7,10 +7,10 @@ args@{ mkNode, ver, ... }: }; nodes = { - node1 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::1"; }; - node2 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::2"; }; - node3 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::3"; }; - node4 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::4"; }; + node1 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::1"; }; + node2 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::2"; }; + node3 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::3"; }; + node4 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::4"; }; }; testScript = '' diff --git a/nixos/tests/incus/container.nix b/nixos/tests/incus/container.nix index a71c5355046a5..10262cf2132b8 100644 --- a/nixos/tests/incus/container.nix +++ b/nixos/tests/incus/container.nix @@ -1,4 +1,4 @@ -import ../make-test-python.nix ({ pkgs, lib, extra ? {}, name ? "incus-container", ... } : +import ../make-test-python.nix ({ pkgs, lib, extra ? {}, name ? "incus-container", incus ? pkgs.incus-lts, ... } : let releases = import ../../release.nix { @@ -28,7 +28,10 @@ in memorySize = 1024; diskSize = 4096; - incus.enable = true; + incus = { + enable = true; + package = incus; + }; }; networking.nftables.enable = true; }; @@ -70,51 +73,60 @@ in 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("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("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("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("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}") ''; }) diff --git a/nixos/tests/incus/default.nix b/nixos/tests/incus/default.nix index b850c4fba018d..c33bf1600f27a 100644 --- a/nixos/tests/incus/default.nix +++ b/nixos/tests/incus/default.nix @@ -3,24 +3,27 @@ config ? { }, pkgs ? import ../../.. { inherit system config; }, handleTestOn, + incus ? pkgs.incus-lts, }: { container-legacy-init = import ./container.nix { name = "container-legacy-init"; - inherit system pkgs; + inherit incus system pkgs; }; container-systemd-init = import ./container.nix { name = "container-systemd-init"; - inherit system pkgs; + inherit incus system pkgs; extra = { boot.initrd.systemd.enable = true; }; }; - lxd-to-incus = import ./lxd-to-incus.nix { inherit system pkgs; }; - openvswitch = import ./openvswitch.nix { inherit system pkgs; }; - preseed = import ./preseed.nix { inherit system pkgs; }; - socket-activated = import ./socket-activated.nix { inherit system pkgs; }; - storage = import ./storage.nix { inherit system pkgs; }; - ui = import ./ui.nix { inherit system pkgs; }; - virtual-machine = handleTestOn [ "x86_64-linux" ] ./virtual-machine.nix { inherit system pkgs; }; + 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; + }; } diff --git a/nixos/tests/incus/incusd-options.nix b/nixos/tests/incus/incusd-options.nix new file mode 100644 index 0000000000000..7b3a4d726e38e --- /dev/null +++ b/nixos/tests/incus/incusd-options.nix @@ -0,0 +1,110 @@ +# 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.lxdContainerMeta.${pkgs.stdenv.hostPlatform.system}; + container-image-rootfs = releases.lxdContainerImage.${pkgs.stdenv.hostPlatform.system}; + 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 = "nixostestbr0"; + type = "bridge"; + config = { + "ipv4.address" = "10.0.100.1/24"; + "ipv4.nat" = "true"; + }; + } + ]; + profiles = [ + { + name = "default"; + devices = { + eth0 = { + name = "eth0"; + network = "nixostestbr0"; + type = "nic"; + }; + root = { + path = "/"; + pool = "nixostest_pool"; + size = "35GiB"; + type = "disk"; + }; + }; + } + ]; + storage_pools = [ + { + name = "nixostest_pool"; + driver = "dir"; + } + ]; + }; + }; + }; + networking.nftables.enable = true; + }; + + testScript = '' + 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 + + 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}/*/*.tar.xz ${container-image-rootfs}/*/*.tar.xz --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("Verify preseed resources created"): + machine.succeed("incus profile show default") + machine.succeed("incus network info nixostestbr0") + machine.succeed("incus storage show nixostest_pool") + + with subtest("Instance is stopped when softDaemonRestart is disabled 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.fail(f"ps {pid}") + ''; + } +) diff --git a/nixos/tests/incus/lxd-to-incus.nix b/nixos/tests/incus/lxd-to-incus.nix index e93b76591eca4..66f78cbd33b40 100644 --- a/nixos/tests/incus/lxd-to-incus.nix +++ b/nixos/tests/incus/lxd-to-incus.nix @@ -1,6 +1,11 @@ import ../make-test-python.nix ( - { pkgs, lib, ... }: + { + pkgs, + lib, + incus ? pkgs.incus-lts, + ... + }: let releases = import ../../release.nix { configuration.documentation.enable = lib.mkForce false; }; @@ -65,7 +70,10 @@ import ../make-test-python.nix ( ]; }; - incus.enable = true; + incus = { + enable = true; + package = incus; + }; }; networking.nftables.enable = true; }; diff --git a/nixos/tests/incus/openvswitch.nix b/nixos/tests/incus/openvswitch.nix index 5d4aef031ad0a..1cead99080e7a 100644 --- a/nixos/tests/incus/openvswitch.nix +++ b/nixos/tests/incus/openvswitch.nix @@ -1,4 +1,4 @@ -import ../make-test-python.nix ({ pkgs, lib, ... } : +import ../make-test-python.nix ({ pkgs, lib, incus ? pkgs.incus-lts, ... } : { name = "incus-openvswitch"; @@ -9,7 +9,11 @@ import ../make-test-python.nix ({ pkgs, lib, ... } : nodes.machine = { lib, ... }: { virtualisation = { - incus.enable = true; + incus = { + enable = true; + package = incus; + }; + vswitch.enable = true; incus.preseed = { networks = [ diff --git a/nixos/tests/incus/preseed.nix b/nixos/tests/incus/preseed.nix deleted file mode 100644 index f2d928115f3ec..0000000000000 --- a/nixos/tests/incus/preseed.nix +++ /dev/null @@ -1,63 +0,0 @@ -import ../make-test-python.nix ({ pkgs, lib, ... } : - -{ - name = "incus-preseed"; - - meta = { - maintainers = lib.teams.lxc.members; - }; - - nodes.machine = { lib, ... }: { - virtualisation = { - incus.enable = true; - - incus.preseed = { - networks = [ - { - name = "nixostestbr0"; - type = "bridge"; - config = { - "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 preseed resources created"): - machine.succeed("incus profile show nixostest_default") - machine.succeed("incus network info nixostestbr0") - machine.succeed("incus storage show nixostest_pool") - ''; -}) diff --git a/nixos/tests/incus/socket-activated.nix b/nixos/tests/incus/socket-activated.nix index 59caf1090fbd8..55c5496396e91 100644 --- a/nixos/tests/incus/socket-activated.nix +++ b/nixos/tests/incus/socket-activated.nix @@ -1,4 +1,4 @@ -import ../make-test-python.nix ({ pkgs, lib, ... } : +import ../make-test-python.nix ({ pkgs, lib, incus ? pkgs.incus-lts, ... } : { name = "incus-socket-activated"; @@ -9,8 +9,11 @@ import ../make-test-python.nix ({ pkgs, lib, ... } : nodes.machine = { lib, ... }: { virtualisation = { - incus.enable = true; - incus.socketActivation = true; + incus = { + enable = true; + package = incus; + socketActivation = true; + }; }; networking.nftables.enable = true; }; diff --git a/nixos/tests/incus/storage.nix b/nixos/tests/incus/storage.nix index 190f4f7451c20..05ea6ba996eb2 100644 --- a/nixos/tests/incus/storage.nix +++ b/nixos/tests/incus/storage.nix @@ -1,5 +1,10 @@ import ../make-test-python.nix ( - { pkgs, lib, ... }: + { + pkgs, + lib, + incus ? pkgs.incus-lts, + ... + }: { name = "incus-storage"; @@ -19,7 +24,10 @@ import ../make-test-python.nix ( virtualisation = { emptyDiskImages = [ 2048 ]; - incus.enable = true; + incus = { + enable = true; + package = incus; + }; }; }; diff --git a/nixos/tests/incus/ui.nix b/nixos/tests/incus/ui.nix index 837eb14844cea..a255d6fabe839 100644 --- a/nixos/tests/incus/ui.nix +++ b/nixos/tests/incus/ui.nix @@ -1,4 +1,4 @@ -import ../make-test-python.nix ({ pkgs, lib, ... }: { +import ../make-test-python.nix ({ pkgs, lib, incus ? pkgs.incus-lts, ... }: { name = "incus-ui"; meta = { @@ -7,7 +7,10 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: { nodes.machine = { lib, ... }: { virtualisation = { - incus.enable = true; + incus = { + enable = true; + package = incus; + }; incus.ui.enable = true; }; networking.nftables.enable = true; diff --git a/nixos/tests/incus/virtual-machine.nix b/nixos/tests/incus/virtual-machine.nix index eebbbd113ed16..70e54191d3304 100644 --- a/nixos/tests/incus/virtual-machine.nix +++ b/nixos/tests/incus/virtual-machine.nix @@ -1,4 +1,4 @@ -import ../make-test-python.nix ({ pkgs, lib, ... }: +import ../make-test-python.nix ({ pkgs, lib, incus ? pkgs.incus-lts, ... }: let releases = import ../../release.nix { @@ -33,7 +33,10 @@ in # Provide a TPM to test vTPM support for guests tpm.enable = true; - incus.enable = true; + incus = { + enable = true; + package = incus; + }; }; networking.nftables.enable = true; }; @@ -75,5 +78,11 @@ in 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}") ''; }) diff --git a/nixos/tests/initrd-network.nix b/nixos/tests/initrd-network.nix index f2483b7393de4..abbc3d0fce822 100644 --- a/nixos/tests/initrd-network.nix +++ b/nixos/tests/initrd-network.nix @@ -1,7 +1,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { name = "initrd-network"; - meta.maintainers = [ pkgs.lib.maintainers.eelco ]; + meta.maintainers = [ ]; nodes.machine = { ... }: { imports = [ ../modules/profiles/minimal.nix ]; diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index 7e835041eb39f..b034d21146f35 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -249,12 +249,11 @@ let with subtest("Check whether nixos-rebuild works"): target.succeed("nixos-rebuild switch >&2") - # FIXME: Nix 2.4 broke nixos-option, someone has to fix it. - # with subtest("Test nixos-option"): - # kernel_modules = target.succeed("nixos-option boot.initrd.kernelModules") - # assert "virtio_console" in kernel_modules - # assert "List of modules" in kernel_modules - # assert "qemu-guest.nix" in kernel_modules + with subtest("Test nixos-option"): + kernel_modules = target.succeed("nixos-option boot.initrd.kernelModules") + assert "virtio_console" in kernel_modules + assert "List of modules" in kernel_modules + assert "qemu-guest.nix" in kernel_modules target.shutdown() diff --git a/nixos/tests/ipv6.nix b/nixos/tests/ipv6.nix index 75faa6f602010..7f91457fa5ea8 100644 --- a/nixos/tests/ipv6.nix +++ b/nixos/tests/ipv6.nix @@ -4,7 +4,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { name = "ipv6"; meta = with pkgs.lib.maintainers; { - maintainers = [ eelco ]; + maintainers = [ ]; }; nodes = diff --git a/nixos/tests/jenkins.nix b/nixos/tests/jenkins.nix index a8f6210006547..d7394c866c143 100644 --- a/nixos/tests/jenkins.nix +++ b/nixos/tests/jenkins.nix @@ -7,7 +7,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { name = "jenkins"; meta = with pkgs.lib.maintainers; { - maintainers = [ bjornfor coconnor domenkozar eelco ]; + maintainers = [ bjornfor coconnor domenkozar ]; }; nodes = { diff --git a/nixos/tests/jotta-cli.nix b/nixos/tests/jotta-cli.nix index 5eefe65c1d385..0df23ee2cba5c 100644 --- a/nixos/tests/jotta-cli.nix +++ b/nixos/tests/jotta-cli.nix @@ -4,7 +4,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { meta.maintainers = with pkgs.lib.maintainers; [ evenbrenden ]; nodes.machine = { pkgs, ... }: { - user.services.jotta-cli.enable = true; + services.jotta-cli.enable = true; imports = [ ./common/user-account.nix ]; }; diff --git a/nixos/tests/k3s/default.nix b/nixos/tests/k3s/default.nix index 512dc06ee77ec..297b05a4e4a74 100644 --- a/nixos/tests/k3s/default.nix +++ b/nixos/tests/k3s/default.nix @@ -1,16 +1,20 @@ -{ system ? builtins.currentSystem -, pkgs ? import ../../.. { inherit system; } -, lib ? pkgs.lib +{ + system ? builtins.currentSystem, + pkgs ? import ../../.. { inherit system; }, + lib ? pkgs.lib, }: let allK3s = lib.filterAttrs (n: _: lib.strings.hasPrefix "k3s_" n) pkgs; in { # Testing K3s with Etcd backend - etcd = lib.mapAttrs (_: k3s: import ./etcd.nix { - inherit system pkgs k3s; - inherit (pkgs) etcd; - }) allK3s; + etcd = lib.mapAttrs ( + _: k3s: + import ./etcd.nix { + inherit system pkgs k3s; + inherit (pkgs) etcd; + } + ) allK3s; # Run a single node k3s cluster and verify a pod can run single-node = lib.mapAttrs (_: k3s: import ./single-node.nix { inherit system pkgs k3s; }) allK3s; # Run a multi-node k3s cluster and verify pod networking works across nodes diff --git a/nixos/tests/k3s/etcd.nix b/nixos/tests/k3s/etcd.nix index d6e9a294adb13..ac0aa90472516 100644 --- a/nixos/tests/k3s/etcd.nix +++ b/nixos/tests/k3s/etcd.nix @@ -1,100 +1,130 @@ -import ../make-test-python.nix ({ pkgs, lib, k3s, etcd, ... }: - -{ - name = "${k3s.name}-etcd"; - - nodes = { - - etcd = { ... }: { - services.etcd = { - enable = true; - openFirewall = true; - listenClientUrls = [ "http://192.168.1.1:2379" "http://127.0.0.1:2379" ]; - listenPeerUrls = [ "http://192.168.1.1:2380" ]; - initialAdvertisePeerUrls = [ "http://192.168.1.1:2380" ]; - initialCluster = [ "etcd=http://192.168.1.1:2380" ]; - }; - networking = { - useDHCP = false; - defaultGateway = "192.168.1.1"; - interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ - { address = "192.168.1.1"; prefixLength = 24; } - ]; - }; - }; +import ../make-test-python.nix ( + { + pkgs, + lib, + k3s, + etcd, + ... + }: + + { + name = "${k3s.name}-etcd"; + + nodes = { + + etcd = + { ... }: + { + services.etcd = { + enable = true; + openFirewall = true; + listenClientUrls = [ + "http://192.168.1.1:2379" + "http://127.0.0.1:2379" + ]; + listenPeerUrls = [ "http://192.168.1.1:2380" ]; + initialAdvertisePeerUrls = [ "http://192.168.1.1:2380" ]; + initialCluster = [ "etcd=http://192.168.1.1:2380" ]; + }; + networking = { + useDHCP = false; + defaultGateway = "192.168.1.1"; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ + { + address = "192.168.1.1"; + prefixLength = 24; + } + ]; + }; + }; - k3s = { pkgs, ... }: { - environment.systemPackages = with pkgs; [ jq ]; - # k3s uses enough resources the default vm fails. - virtualisation.memorySize = 1536; - virtualisation.diskSize = 4096; - - services.k3s = { - enable = true; - role = "server"; - extraFlags = builtins.toString [ - "--datastore-endpoint=\"http://192.168.1.1:2379\"" - "--disable" "coredns" - "--disable" "local-storage" - "--disable" "metrics-server" - "--disable" "servicelb" - "--disable" "traefik" - "--node-ip" "192.168.1.2" - ]; - }; - - networking = { - firewall = { - allowedTCPPorts = [ 2379 2380 6443 ]; - allowedUDPPorts = [ 8472 ]; + k3s = + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ jq ]; + # k3s uses enough resources the default vm fails. + virtualisation.memorySize = 1536; + virtualisation.diskSize = 4096; + + services.k3s = { + enable = true; + role = "server"; + extraFlags = builtins.toString [ + "--datastore-endpoint=\"http://192.168.1.1:2379\"" + "--disable" + "coredns" + "--disable" + "local-storage" + "--disable" + "metrics-server" + "--disable" + "servicelb" + "--disable" + "traefik" + "--node-ip" + "192.168.1.2" + ]; + }; + + networking = { + firewall = { + allowedTCPPorts = [ + 2379 + 2380 + 6443 + ]; + allowedUDPPorts = [ 8472 ]; + }; + useDHCP = false; + defaultGateway = "192.168.1.2"; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ + { + address = "192.168.1.2"; + prefixLength = 24; + } + ]; + }; }; - useDHCP = false; - defaultGateway = "192.168.1.2"; - interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ - { address = "192.168.1.2"; prefixLength = 24; } - ]; - }; }; - }; - - testScript = '' - with subtest("should start etcd"): - etcd.start() - etcd.wait_for_unit("etcd.service") + testScript = '' + with subtest("should start etcd"): + etcd.start() + etcd.wait_for_unit("etcd.service") - with subtest("should wait for etcdctl endpoint status to succeed"): - etcd.wait_until_succeeds("etcdctl endpoint status") + with subtest("should wait for etcdctl endpoint status to succeed"): + etcd.wait_until_succeeds("etcdctl endpoint status") - with subtest("should start k3s"): - k3s.start() - k3s.wait_for_unit("k3s") + with subtest("should start k3s"): + k3s.start() + k3s.wait_for_unit("k3s") - with subtest("should test if kubectl works"): - k3s.wait_until_succeeds("k3s kubectl get node") + with subtest("should test if kubectl works"): + k3s.wait_until_succeeds("k3s kubectl get node") - with subtest("should wait for service account to show up; takes a sec"): - k3s.wait_until_succeeds("k3s kubectl get serviceaccount default") + with subtest("should wait for service account to show up; takes a sec"): + k3s.wait_until_succeeds("k3s kubectl get serviceaccount default") - with subtest("should create a sample secret object"): - k3s.succeed("k3s kubectl create secret generic nixossecret --from-literal thesecret=abacadabra") + with subtest("should create a sample secret object"): + k3s.succeed("k3s kubectl create secret generic nixossecret --from-literal thesecret=abacadabra") - with subtest("should check if secret is correct"): - k3s.wait_until_succeeds("[[ $(kubectl get secrets nixossecret -o json | jq -r .data.thesecret | base64 -d) == abacadabra ]]") + with subtest("should check if secret is correct"): + k3s.wait_until_succeeds("[[ $(kubectl get secrets nixossecret -o json | jq -r .data.thesecret | base64 -d) == abacadabra ]]") - with subtest("should have a secret in database"): - etcd.wait_until_succeeds("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]") + with subtest("should have a secret in database"): + etcd.wait_until_succeeds("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]") - with subtest("should delete the secret"): - k3s.succeed("k3s kubectl delete secret nixossecret") + with subtest("should delete the secret"): + k3s.succeed("k3s kubectl delete secret nixossecret") - with subtest("should not have a secret in database"): - etcd.wait_until_fails("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]") + with subtest("should not have a secret in database"): + etcd.wait_until_fails("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]") - with subtest("should shutdown k3s and etcd"): - k3s.shutdown() - etcd.shutdown() - ''; + with subtest("should shutdown k3s and etcd"): + k3s.shutdown() + etcd.shutdown() + ''; - meta.maintainers = etcd.meta.maintainers ++ k3s.meta.maintainers; -}) + meta.maintainers = etcd.meta.maintainers ++ k3s.meta.maintainers; + } +) diff --git a/nixos/tests/k3s/multi-node.nix b/nixos/tests/k3s/multi-node.nix index 20279f3ca4b93..b618d2aff34c4 100644 --- a/nixos/tests/k3s/multi-node.nix +++ b/nixos/tests/k3s/multi-node.nix @@ -1,14 +1,30 @@ -import ../make-test-python.nix ({ pkgs, lib, k3s, ... }: +import ../make-test-python.nix ( + { + pkgs, + lib, + k3s, + ... + }: let imageEnv = pkgs.buildEnv { name = "k3s-pause-image-env"; - paths = with pkgs; [ tini bashInteractive coreutils socat ]; + paths = with pkgs; [ + tini + bashInteractive + coreutils + socat + ]; }; pauseImage = pkgs.dockerTools.streamLayeredImage { name = "test.local/pause"; tag = "local"; contents = imageEnv; - config.Entrypoint = [ "/bin/tini" "--" "/bin/sleep" "inf" ]; + config.Entrypoint = [ + "/bin/tini" + "--" + "/bin/sleep" + "inf" + ]; }; # A daemonset that responds 'server' on port 8000 networkTestDaemonset = pkgs.writeText "test.yml" '' @@ -42,90 +58,135 @@ import ../make-test-python.nix ({ pkgs, lib, k3s, ... }: name = "${k3s.name}-multi-node"; nodes = { - server = { pkgs, ... }: { - environment.systemPackages = with pkgs; [ gzip jq ]; - # k3s uses enough resources the default vm fails. - virtualisation.memorySize = 1536; - virtualisation.diskSize = 4096; - - services.k3s = { - inherit tokenFile; - enable = true; - role = "server"; - package = k3s; - clusterInit = true; - extraFlags = builtins.toString [ - "--disable" "coredns" - "--disable" "local-storage" - "--disable" "metrics-server" - "--disable" "servicelb" - "--disable" "traefik" - "--node-ip" "192.168.1.1" - "--pause-image" "test.local/pause:local" + server = + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + gzip + jq + ]; + # k3s uses enough resources the default vm fails. + virtualisation.memorySize = 1536; + virtualisation.diskSize = 4096; + + services.k3s = { + inherit tokenFile; + enable = true; + role = "server"; + package = k3s; + clusterInit = true; + extraFlags = builtins.toString [ + "--disable" + "coredns" + "--disable" + "local-storage" + "--disable" + "metrics-server" + "--disable" + "servicelb" + "--disable" + "traefik" + "--node-ip" + "192.168.1.1" + "--pause-image" + "test.local/pause:local" + ]; + }; + networking.firewall.allowedTCPPorts = [ + 2379 + 2380 + 6443 + ]; + networking.firewall.allowedUDPPorts = [ 8472 ]; + networking.firewall.trustedInterfaces = [ "flannel.1" ]; + networking.useDHCP = false; + networking.defaultGateway = "192.168.1.1"; + networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ + { + address = "192.168.1.1"; + prefixLength = 24; + } ]; }; - networking.firewall.allowedTCPPorts = [ 2379 2380 6443 ]; - networking.firewall.allowedUDPPorts = [ 8472 ]; - networking.firewall.trustedInterfaces = [ "flannel.1" ]; - networking.useDHCP = false; - networking.defaultGateway = "192.168.1.1"; - networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ - { address = "192.168.1.1"; prefixLength = 24; } - ]; - }; - - server2 = { pkgs, ... }: { - environment.systemPackages = with pkgs; [ gzip jq ]; - virtualisation.memorySize = 1536; - virtualisation.diskSize = 4096; - - services.k3s = { - inherit tokenFile; - enable = true; - serverAddr = "https://192.168.1.1:6443"; - clusterInit = false; - extraFlags = builtins.toString [ - "--disable" "coredns" - "--disable" "local-storage" - "--disable" "metrics-server" - "--disable" "servicelb" - "--disable" "traefik" - "--node-ip" "192.168.1.3" - "--pause-image" "test.local/pause:local" + + server2 = + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + gzip + jq + ]; + virtualisation.memorySize = 1536; + virtualisation.diskSize = 4096; + + services.k3s = { + inherit tokenFile; + enable = true; + serverAddr = "https://192.168.1.1:6443"; + clusterInit = false; + extraFlags = builtins.toString [ + "--disable" + "coredns" + "--disable" + "local-storage" + "--disable" + "metrics-server" + "--disable" + "servicelb" + "--disable" + "traefik" + "--node-ip" + "192.168.1.3" + "--pause-image" + "test.local/pause:local" + ]; + }; + networking.firewall.allowedTCPPorts = [ + 2379 + 2380 + 6443 + ]; + networking.firewall.allowedUDPPorts = [ 8472 ]; + networking.firewall.trustedInterfaces = [ "flannel.1" ]; + networking.useDHCP = false; + networking.defaultGateway = "192.168.1.3"; + networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ + { + address = "192.168.1.3"; + prefixLength = 24; + } ]; }; - networking.firewall.allowedTCPPorts = [ 2379 2380 6443 ]; - networking.firewall.allowedUDPPorts = [ 8472 ]; - networking.firewall.trustedInterfaces = [ "flannel.1" ]; - networking.useDHCP = false; - networking.defaultGateway = "192.168.1.3"; - networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ - { address = "192.168.1.3"; prefixLength = 24; } - ]; - }; - - agent = { pkgs, ... }: { - virtualisation.memorySize = 1024; - virtualisation.diskSize = 2048; - services.k3s = { - inherit tokenFile; - enable = true; - role = "agent"; - serverAddr = "https://192.168.1.3:6443"; - extraFlags = lib.concatStringsSep " " [ - "--pause-image" "test.local/pause:local" - "--node-ip" "192.168.1.2" + + agent = + { pkgs, ... }: + { + virtualisation.memorySize = 1024; + virtualisation.diskSize = 2048; + services.k3s = { + inherit tokenFile; + enable = true; + role = "agent"; + serverAddr = "https://192.168.1.3:6443"; + extraFlags = lib.concatStringsSep " " [ + "--pause-image" + "test.local/pause:local" + "--node-ip" + "192.168.1.2" + ]; + }; + networking.firewall.allowedTCPPorts = [ 6443 ]; + networking.firewall.allowedUDPPorts = [ 8472 ]; + networking.firewall.trustedInterfaces = [ "flannel.1" ]; + networking.useDHCP = false; + networking.defaultGateway = "192.168.1.2"; + networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ + { + address = "192.168.1.2"; + prefixLength = 24; + } ]; }; - networking.firewall.allowedTCPPorts = [ 6443 ]; - networking.firewall.allowedUDPPorts = [ 8472 ]; - networking.firewall.trustedInterfaces = [ "flannel.1" ]; - networking.useDHCP = false; - networking.defaultGateway = "192.168.1.2"; - networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ - { address = "192.168.1.2"; prefixLength = 24; } - ]; - }; }; meta.maintainers = k3s.meta.maintainers; @@ -178,4 +239,5 @@ import ../make-test-python.nix ({ pkgs, lib, k3s, ... }: for m in machines: m.shutdown() ''; - }) + } +) diff --git a/nixos/tests/k3s/single-node.nix b/nixos/tests/k3s/single-node.nix index fd64a050e61ef..80d80a55ddf41 100644 --- a/nixos/tests/k3s/single-node.nix +++ b/nixos/tests/k3s/single-node.nix @@ -1,14 +1,29 @@ -import ../make-test-python.nix ({ pkgs, lib, k3s, ... }: +import ../make-test-python.nix ( + { + pkgs, + lib, + k3s, + ... + }: let imageEnv = pkgs.buildEnv { name = "k3s-pause-image-env"; - paths = with pkgs; [ tini (hiPrio coreutils) busybox ]; + paths = with pkgs; [ + tini + (hiPrio coreutils) + busybox + ]; }; pauseImage = pkgs.dockerTools.streamLayeredImage { name = "test.local/pause"; tag = "local"; contents = imageEnv; - config.Entrypoint = [ "/bin/tini" "--" "/bin/sleep" "inf" ]; + config.Entrypoint = [ + "/bin/tini" + "--" + "/bin/sleep" + "inf" + ]; }; testPodYaml = pkgs.writeText "test.yml" '' apiVersion: v1 @@ -27,57 +42,83 @@ import ../make-test-python.nix ({ pkgs, lib, k3s, ... }: name = "${k3s.name}-single-node"; meta.maintainers = k3s.meta.maintainers; - nodes.machine = { pkgs, ... }: { - environment.systemPackages = with pkgs; [ k3s gzip ]; + nodes.machine = + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + k3s + gzip + ]; - # k3s uses enough resources the default vm fails. - virtualisation.memorySize = 1536; - virtualisation.diskSize = 4096; + # k3s uses enough resources the default vm fails. + virtualisation.memorySize = 1536; + virtualisation.diskSize = 4096; - services.k3s.enable = true; - services.k3s.role = "server"; - services.k3s.package = k3s; - # Slightly reduce resource usage - services.k3s.extraFlags = builtins.toString [ - "--disable" "coredns" - "--disable" "local-storage" - "--disable" "metrics-server" - "--disable" "servicelb" - "--disable" "traefik" - "--pause-image" "test.local/pause:local" - ]; + services.k3s.enable = true; + services.k3s.role = "server"; + services.k3s.package = k3s; + # Slightly reduce resource usage + services.k3s.extraFlags = builtins.toString [ + "--disable" + "coredns" + "--disable" + "local-storage" + "--disable" + "metrics-server" + "--disable" + "servicelb" + "--disable" + "traefik" + "--pause-image" + "test.local/pause:local" + ]; - users.users = { - noprivs = { - isNormalUser = true; - description = "Can't access k3s by default"; - password = "*"; + users.users = { + noprivs = { + isNormalUser = true; + description = "Can't access k3s by default"; + password = "*"; + }; }; }; - }; - testScript = '' - start_all() + testScript = + '' + start_all() - machine.wait_for_unit("k3s") - machine.succeed("kubectl cluster-info") - machine.fail("sudo -u noprivs kubectl cluster-info") + machine.wait_for_unit("k3s") + machine.succeed("kubectl cluster-info") + machine.fail("sudo -u noprivs kubectl cluster-info") '' # Fix-Me: Tests fail for 'aarch64-linux' as: "CONFIG_CGROUP_FREEZER: missing (fail)" - + lib.optionalString (!pkgs.stdenv.isAarch64) ''machine.succeed("k3s check-config")'' + '' + + lib.optionalString (!pkgs.stdenv.isAarch64) ''machine.succeed("k3s check-config")'' + + '' - machine.succeed( - "${pauseImage} | ctr image import -" - ) + machine.succeed( + "${pauseImage} | ctr image import -" + ) - # Also wait for our service account to show up; it takes a sec - machine.wait_until_succeeds("kubectl get serviceaccount default") - machine.succeed("kubectl apply -f ${testPodYaml}") - machine.succeed("kubectl wait --for 'condition=Ready' pod/test") - machine.succeed("kubectl delete -f ${testPodYaml}") + # Also wait for our service account to show up; it takes a sec + machine.wait_until_succeeds("kubectl get serviceaccount default") + machine.succeed("kubectl apply -f ${testPodYaml}") + machine.succeed("kubectl wait --for 'condition=Ready' pod/test") + machine.succeed("kubectl delete -f ${testPodYaml}") - # regression test for #176445 - machine.fail("journalctl -o cat -u k3s.service | grep 'ipset utility not found'") + # regression test for #176445 + machine.fail("journalctl -o cat -u k3s.service | grep 'ipset utility not found'") - machine.shutdown() - ''; - }) + with subtest("Run k3s-killall"): + # Call the killall script with a clean path to assert that + # all required commands are wrapped + output = machine.succeed("PATH= ${k3s}/bin/k3s-killall.sh 2>&1 | tee /dev/stderr") + assert "command not found" not in output, "killall script contains unknown command" + + # Check that killall cleaned up properly + machine.fail("systemctl is-active k3s.service") + machine.fail("systemctl list-units | grep containerd") + machine.fail("ip link show | awk -F': ' '{print $2}' | grep -e flannel -e cni0") + machine.fail("ip netns show | grep cni-") + + machine.shutdown() + ''; + } +) diff --git a/nixos/tests/knot.nix b/nixos/tests/knot.nix index eec94a22f2fa7..4441fed6ef507 100644 --- a/nixos/tests/knot.nix +++ b/nixos/tests/knot.nix @@ -190,6 +190,10 @@ in { primary.wait_for_unit("knot.service") secondary.wait_for_unit("knot.service") + for zone in ("example.com.", "sub.example.com."): + secondary.wait_until_succeeds( + f"knotc zone-status {zone} | grep -q 'serial: 2019031302'" + ) def test(host, query_type, query, pattern): out = client.succeed(f"khost -t {query_type} {query} {host}").strip() diff --git a/nixos/tests/login.nix b/nixos/tests/login.nix index 67f5764a0a162..bcaee03175ad3 100644 --- a/nixos/tests/login.nix +++ b/nixos/tests/login.nix @@ -3,7 +3,7 @@ import ./make-test-python.nix ({ pkgs, latestKernel ? false, ... }: { name = "login"; meta = with pkgs.lib.maintainers; { - maintainers = [ eelco ]; + maintainers = [ ]; }; nodes.machine = diff --git a/nixos/tests/logrotate.nix b/nixos/tests/logrotate.nix index bcbe89c259ae5..f9c5e90609709 100644 --- a/nixos/tests/logrotate.nix +++ b/nixos/tests/logrotate.nix @@ -16,52 +16,60 @@ import ./make-test-python.nix ({ pkgs, ... }: rec { }; nodes = { - defaultMachine = { ... }: { }; + defaultMachine = { ... }: { + services.logrotate.enable = true; + }; failingMachine = { ... }: { - services.logrotate.configFile = pkgs.writeText "logrotate.conf" '' - # self-written config file - su notarealuser notagroupeither - ''; + services.logrotate = { + enable = true; + configFile = pkgs.writeText "logrotate.conf" '' + # self-written config file + su notarealuser notagroupeither + ''; + }; }; machine = { config, ... }: { imports = [ importTest ]; - services.logrotate.settings = { - # remove default frequency header and add another - header = { - frequency = null; - delaycompress = true; - }; - # extra global setting... affecting nothing - last_line = { - global = true; - priority = 2000; - shred = true; - }; - # using mail somewhere should add --mail to logrotate invocation - sendmail = { - mail = "user@domain.tld"; - }; - # postrotate should be suffixed by 'endscript' - postrotate = { - postrotate = "touch /dev/null"; - }; - # check checkConfig works as expected: there is nothing to check here - # except that the file build passes - checkConf = { - su = "root utmp"; - createolddir = "0750 root utmp"; - create = "root utmp"; - "create " = "0750 root utmp"; - }; - # multiple paths should be aggregated - multipath = { - files = [ "file1" "file2" ]; - }; - # overriding imported path should keep existing attributes - # (e.g. olddir is still set) - import = { - notifempty = true; + services.logrotate = { + enable = true; + settings = { + # remove default frequency header and add another + header = { + frequency = null; + delaycompress = true; + }; + # extra global setting... affecting nothing + last_line = { + global = true; + priority = 2000; + shred = true; + }; + # using mail somewhere should add --mail to logrotate invocation + sendmail = { + mail = "user@domain.tld"; + }; + # postrotate should be suffixed by 'endscript' + postrotate = { + postrotate = "touch /dev/null"; + }; + # check checkConfig works as expected: there is nothing to check here + # except that the file build passes + checkConf = { + su = "root utmp"; + createolddir = "0750 root utmp"; + create = "root utmp"; + "create " = "0750 root utmp"; + }; + # multiple paths should be aggregated + multipath = { + files = [ "file1" "file2" ]; + }; + # overriding imported path should keep existing attributes + # (e.g. olddir is still set) + import = { + notifempty = true; + }; }; }; }; diff --git a/nixos/tests/mediamtx.nix b/nixos/tests/mediamtx.nix index 8cacd02631d95..f40c4a8cb5832 100644 --- a/nixos/tests/mediamtx.nix +++ b/nixos/tests/mediamtx.nix @@ -1,57 +1,60 @@ -import ./make-test-python.nix ({ pkgs, lib, ...} : +import ./make-test-python.nix ( + { pkgs, lib, ... }: -{ - name = "mediamtx"; - meta.maintainers = with lib.maintainers; [ fpletz ]; + { + name = "mediamtx"; + meta.maintainers = with lib.maintainers; [ fpletz ]; - nodes = { - machine = { config, ... }: { - services.mediamtx = { - enable = true; - settings = { - metrics = true; - paths.all.source = "publisher"; + nodes = { + machine = { + services.mediamtx = { + enable = true; + settings = { + metrics = true; + paths.all.source = "publisher"; + }; }; - }; - systemd.services.rtmp-publish = { - description = "Publish an RTMP stream to mediamtx"; - after = [ "mediamtx.service" ]; - bindsTo = [ "mediamtx.service" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - DynamicUser = true; - Restart = "on-failure"; - RestartSec = "1s"; - TimeoutStartSec = "10s"; - ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -re -f lavfi -i smptebars=size=800x600:rate=10 -c libx264 -f flv rtmp://localhost:1935/test"; + systemd.services.rtmp-publish = { + description = "Publish an RTMP stream to mediamtx"; + after = [ "mediamtx.service" ]; + bindsTo = [ "mediamtx.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + DynamicUser = true; + Restart = "on-failure"; + RestartSec = "1s"; + TimeoutStartSec = "10s"; + ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -re -f lavfi -i smptebars=size=800x600:rate=10 -c libx264 -f flv rtmp://localhost:1935/test"; + }; }; - }; - systemd.services.rtmp-receive = { - description = "Receive an RTMP stream from mediamtx"; - after = [ "rtmp-publish.service" ]; - bindsTo = [ "rtmp-publish.service" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - DynamicUser = true; - Restart = "on-failure"; - RestartSec = "1s"; - TimeoutStartSec = "10s"; - ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -y -re -i rtmp://localhost:1935/test -f flv /dev/null"; + systemd.services.rtmp-receive = { + description = "Receive an RTMP stream from mediamtx"; + after = [ "rtmp-publish.service" ]; + bindsTo = [ "rtmp-publish.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + DynamicUser = true; + Restart = "on-failure"; + RestartSec = "1s"; + TimeoutStartSec = "10s"; + ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -y -re -i rtmp://localhost:1935/test -f flv /dev/null"; + }; }; }; }; - }; - testScript = '' - start_all() + testScript = '' + start_all() - machine.wait_for_unit("mediamtx.service") - machine.wait_for_unit("rtmp-publish.service") - machine.wait_for_unit("rtmp-receive.service") - machine.wait_for_open_port(9998) - machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"publish\".*1$'") - machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"read\".*1$'") - ''; -}) + machine.wait_for_unit("mediamtx.service") + machine.wait_for_unit("rtmp-publish.service") + machine.sleep(10) + machine.wait_for_unit("rtmp-receive.service") + machine.wait_for_open_port(9998) + machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"publish\".*1$'") + machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"read\".*1$'") + ''; + } +) diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix index e7842debba7a2..83e0f46be3ecf 100644 --- a/nixos/tests/misc.nix +++ b/nixos/tests/misc.nix @@ -1,164 +1,185 @@ # Miscellaneous small tests that don't warrant their own VM run. - -import ./make-test-python.nix ({ lib, pkgs, ...} : let - foo = pkgs.writeText "foo" "Hello World"; -in { - name = "misc"; - meta.maintainers = with lib.maintainers; [ eelco ]; - - nodes.machine = - { lib, ... }: - { swapDevices = lib.mkOverride 0 - [ { device = "/root/swapfile"; size = 128; } ]; - environment.variables.EDITOR = lib.mkOverride 0 "emacs"; - documentation.nixos.enable = lib.mkOverride 0 true; - systemd.tmpfiles.rules = [ "d /tmp 1777 root root 10d" ]; - systemd.tmpfiles.settings."10-test"."/tmp/somefile".d = {}; - virtualisation.fileSystems = { "/tmp2" = - { fsType = "tmpfs"; - options = [ "mode=1777" "noauto" ]; - }; - # Tests https://discourse.nixos.org/t/how-to-make-a-derivations-executables-have-the-s-permission/8555 - "/user-mount/point" = { - device = "/user-mount/source"; - fsType = "none"; - options = [ "bind" "rw" "user" "noauto" ]; - }; - "/user-mount/denied-point" = { - device = "/user-mount/denied-source"; - fsType = "none"; - options = [ "bind" "rw" "noauto" ]; +{ pkgs, ... }: + +let + inherit (pkgs) lib; + tests = { + default = testsForPackage { nixPackage = pkgs.nix; }; + lix = testsForPackage { nixPackage = pkgs.lix; }; + }; + + testsForPackage = args: lib.recurseIntoAttrs { + # If the attribute is not named 'test' + # You will break all the universe on the release-*.nix side of things. + # `discoverTests` relies on `test` existence to perform a `callTest`. + test = testMiscFeatures args; + passthru.override = args': testsForPackage (args // args'); + }; + + testMiscFeatures = { nixPackage, ... }: pkgs.testers.nixosTest ( + let + foo = pkgs.writeText "foo" "Hello World"; + in { + name = "misc"; + meta.maintainers = with lib.maintainers; [ raitobezarius ]; + + nodes.machine = + { lib, ... }: + { swapDevices = lib.mkOverride 0 + [ { device = "/root/swapfile"; size = 128; } ]; + environment.variables.EDITOR = lib.mkOverride 0 "emacs"; + documentation.nixos.enable = lib.mkOverride 0 true; + systemd.tmpfiles.rules = [ "d /tmp 1777 root root 10d" ]; + systemd.tmpfiles.settings."10-test"."/tmp/somefile".d = {}; + virtualisation.fileSystems = { "/tmp2" = + { fsType = "tmpfs"; + options = [ "mode=1777" "noauto" ]; + }; + # Tests https://discourse.nixos.org/t/how-to-make-a-derivations-executables-have-the-s-permission/8555 + "/user-mount/point" = { + device = "/user-mount/source"; + fsType = "none"; + options = [ "bind" "rw" "user" "noauto" ]; + }; + "/user-mount/denied-point" = { + device = "/user-mount/denied-source"; + fsType = "none"; + options = [ "bind" "rw" "noauto" ]; + }; }; + systemd.automounts = lib.singleton + { wantedBy = [ "multi-user.target" ]; + where = "/tmp2"; + }; + users.users.sybil = { isNormalUser = true; group = "wheel"; }; + users.users.alice = { isNormalUser = true; }; + security.sudo = { enable = true; wheelNeedsPassword = false; }; + boot.kernel.sysctl."vm.swappiness" = 1; + boot.kernelParams = [ "vsyscall=emulate" ]; + system.extraDependencies = [ foo ]; + + nix.package = nixPackage; }; - systemd.automounts = lib.singleton - { wantedBy = [ "multi-user.target" ]; - where = "/tmp2"; - }; - users.users.sybil = { isNormalUser = true; group = "wheel"; }; - users.users.alice = { isNormalUser = true; }; - security.sudo = { enable = true; wheelNeedsPassword = false; }; - boot.kernel.sysctl."vm.swappiness" = 1; - boot.kernelParams = [ "vsyscall=emulate" ]; - system.extraDependencies = [ foo ]; - }; - - testScript = - '' - import json - - - def get_path_info(path): - result = machine.succeed(f"nix --option experimental-features nix-command path-info --json {path}") - parsed = json.loads(result) - return parsed - - - with subtest("nix-db"): - info = get_path_info("${foo}") - print(info) - - if ( - info[0]["narHash"] - != "sha256-BdMdnb/0eWy3EddjE83rdgzWWpQjfWPAj3zDIFMD3Ck=" - ): - raise Exception("narHash not set") - - if info[0]["narSize"] != 128: - raise Exception("narSize not set") - - with subtest("nixos-version"): - machine.succeed("[ `nixos-version | wc -w` = 2 ]") - - with subtest("nixos-rebuild"): - assert "NixOS module" in machine.succeed("nixos-rebuild --help") - - with subtest("Sanity check for uid/gid assignment"): - assert "4" == machine.succeed("id -u messagebus").strip() - assert "4" == machine.succeed("id -g messagebus").strip() - assert "users:x:100:" == machine.succeed("getent group users").strip() - - with subtest("Regression test for GMP aborts on QEMU."): - machine.succeed("expr 1 + 2") - - with subtest("the swap file got created"): - machine.wait_for_unit("root-swapfile.swap") - machine.succeed("ls -l /root/swapfile | grep 134217728") - - with subtest("whether kernel.poweroff_cmd is set"): - machine.succeed('[ -x "$(cat /proc/sys/kernel/poweroff_cmd)" ]') - - with subtest("whether the io cgroupv2 controller is properly enabled"): - machine.succeed("grep -q '\\bio\\b' /sys/fs/cgroup/cgroup.controllers") - - with subtest("whether we have a reboot record in wtmp"): - machine.shutdown - machine.wait_for_unit("multi-user.target") - machine.succeed("last | grep reboot >&2") - - with subtest("whether we can override environment variables"): - machine.succeed('[ "$EDITOR" = emacs ]') - - with subtest("whether hostname (and by extension nss_myhostname) works"): - assert "machine" == machine.succeed("hostname").strip() - assert "machine" == machine.succeed("hostname -s").strip() - - with subtest("whether systemd-udevd automatically loads modules for our hardware"): - machine.succeed("systemctl start systemd-udev-settle.service") - machine.wait_for_unit("systemd-udev-settle.service") - assert "mousedev" in machine.succeed("lsmod") - - with subtest("whether systemd-tmpfiles-clean works"): - machine.succeed( - "touch /tmp/foo", "systemctl start systemd-tmpfiles-clean", "[ -e /tmp/foo ]" - ) - # move into the future - machine.succeed( - 'date -s "@$(($(date +%s) + 1000000))"', - "systemctl start systemd-tmpfiles-clean", - ) - machine.fail("[ -e /tmp/foo ]") - - with subtest("whether systemd-tmpfiles settings works"): - machine.succeed("[ -e /tmp/somefile ]") - - with subtest("whether automounting works"): - machine.fail("grep '/tmp2 tmpfs' /proc/mounts") - machine.succeed("touch /tmp2/x") - machine.succeed("grep '/tmp2 tmpfs' /proc/mounts") - - with subtest( - "Whether mounting by a user is possible with the `user` option in fstab (#95444)" - ): - machine.succeed("mkdir -p /user-mount/source") - machine.succeed("touch /user-mount/source/file") - machine.succeed("chmod -R a+Xr /user-mount/source") - machine.succeed("mkdir /user-mount/point") - machine.succeed("chown alice:users /user-mount/point") - machine.succeed("su - alice -c 'mount /user-mount/point'") - machine.succeed("su - alice -c 'ls /user-mount/point/file'") - with subtest( - "Whether mounting by a user is denied without the `user` option in fstab" - ): - machine.succeed("mkdir -p /user-mount/denied-source") - machine.succeed("touch /user-mount/denied-source/file") - machine.succeed("chmod -R a+Xr /user-mount/denied-source") - machine.succeed("mkdir /user-mount/denied-point") - machine.succeed("chown alice:users /user-mount/denied-point") - machine.fail("su - alice -c 'mount /user-mount/denied-point'") - - with subtest("shell-vars"): - machine.succeed('[ -n "$NIX_PATH" ]') - - with subtest("nix-db"): - machine.succeed("nix-store -qR /run/current-system | grep nixos-") - - with subtest("Test sysctl"): - machine.wait_for_unit("systemd-sysctl.service") - assert "1" == machine.succeed("sysctl -ne vm.swappiness").strip() - machine.execute("sysctl vm.swappiness=60") - assert "60" == machine.succeed("sysctl -ne vm.swappiness").strip() - - with subtest("Test boot parameters"): - assert "vsyscall=emulate" in machine.succeed("cat /proc/cmdline") - ''; -}) + + testScript = + '' + import json + + + def get_path_info(path): + result = machine.succeed(f"nix --option experimental-features nix-command path-info --json {path}") + parsed = json.loads(result) + return parsed + + + with subtest("nix-db"): + info = get_path_info("${foo}") + print(info) + + if ( + info[0]["narHash"] + != "sha256-BdMdnb/0eWy3EddjE83rdgzWWpQjfWPAj3zDIFMD3Ck=" + ): + raise Exception("narHash not set") + + if info[0]["narSize"] != 128: + raise Exception("narSize not set") + + with subtest("nixos-version"): + machine.succeed("[ `nixos-version | wc -w` = 2 ]") + + with subtest("nixos-rebuild"): + assert "NixOS module" in machine.succeed("nixos-rebuild --help") + + with subtest("Sanity check for uid/gid assignment"): + assert "4" == machine.succeed("id -u messagebus").strip() + assert "4" == machine.succeed("id -g messagebus").strip() + assert "users:x:100:" == machine.succeed("getent group users").strip() + + with subtest("Regression test for GMP aborts on QEMU."): + machine.succeed("expr 1 + 2") + + with subtest("the swap file got created"): + machine.wait_for_unit("root-swapfile.swap") + machine.succeed("ls -l /root/swapfile | grep 134217728") + + with subtest("whether kernel.poweroff_cmd is set"): + machine.succeed('[ -x "$(cat /proc/sys/kernel/poweroff_cmd)" ]') + + with subtest("whether the io cgroupv2 controller is properly enabled"): + machine.succeed("grep -q '\\bio\\b' /sys/fs/cgroup/cgroup.controllers") + + with subtest("whether we have a reboot record in wtmp"): + machine.shutdown + machine.wait_for_unit("multi-user.target") + machine.succeed("last | grep reboot >&2") + + with subtest("whether we can override environment variables"): + machine.succeed('[ "$EDITOR" = emacs ]') + + with subtest("whether hostname (and by extension nss_myhostname) works"): + assert "machine" == machine.succeed("hostname").strip() + assert "machine" == machine.succeed("hostname -s").strip() + + with subtest("whether systemd-udevd automatically loads modules for our hardware"): + machine.succeed("systemctl start systemd-udev-settle.service") + machine.wait_for_unit("systemd-udev-settle.service") + assert "mousedev" in machine.succeed("lsmod") + + with subtest("whether systemd-tmpfiles-clean works"): + machine.succeed( + "touch /tmp/foo", "systemctl start systemd-tmpfiles-clean", "[ -e /tmp/foo ]" + ) + # move into the future + machine.succeed( + 'date -s "@$(($(date +%s) + 1000000))"', + "systemctl start systemd-tmpfiles-clean", + ) + machine.fail("[ -e /tmp/foo ]") + + with subtest("whether systemd-tmpfiles settings works"): + machine.succeed("[ -e /tmp/somefile ]") + + with subtest("whether automounting works"): + machine.fail("grep '/tmp2 tmpfs' /proc/mounts") + machine.succeed("touch /tmp2/x") + machine.succeed("grep '/tmp2 tmpfs' /proc/mounts") + + with subtest( + "Whether mounting by a user is possible with the `user` option in fstab (#95444)" + ): + machine.succeed("mkdir -p /user-mount/source") + machine.succeed("touch /user-mount/source/file") + machine.succeed("chmod -R a+Xr /user-mount/source") + machine.succeed("mkdir /user-mount/point") + machine.succeed("chown alice:users /user-mount/point") + machine.succeed("su - alice -c 'mount /user-mount/point'") + machine.succeed("su - alice -c 'ls /user-mount/point/file'") + with subtest( + "Whether mounting by a user is denied without the `user` option in fstab" + ): + machine.succeed("mkdir -p /user-mount/denied-source") + machine.succeed("touch /user-mount/denied-source/file") + machine.succeed("chmod -R a+Xr /user-mount/denied-source") + machine.succeed("mkdir /user-mount/denied-point") + machine.succeed("chown alice:users /user-mount/denied-point") + machine.fail("su - alice -c 'mount /user-mount/denied-point'") + + with subtest("shell-vars"): + machine.succeed('[ -n "$NIX_PATH" ]') + + with subtest("nix-db"): + machine.succeed("nix-store -qR /run/current-system | grep nixos-") + + with subtest("Test sysctl"): + machine.wait_for_unit("systemd-sysctl.service") + assert "1" == machine.succeed("sysctl -ne vm.swappiness").strip() + machine.execute("sysctl vm.swappiness=60") + assert "60" == machine.succeed("sysctl -ne vm.swappiness").strip() + + with subtest("Test boot parameters"): + assert "vsyscall=emulate" in machine.succeed("cat /proc/cmdline") + ''; + }); + in + tests diff --git a/nixos/tests/mumble.nix b/nixos/tests/mumble.nix index 8eee454721a13..12fa00b79bbf8 100644 --- a/nixos/tests/mumble.nix +++ b/nixos/tests/mumble.nix @@ -15,7 +15,7 @@ in { name = "mumble"; meta = with pkgs.lib.maintainers; { - maintainers = [ thoughtpolice eelco ]; + maintainers = [ thoughtpolice ]; }; nodes = { diff --git a/nixos/tests/munin.nix b/nixos/tests/munin.nix index e371b2dffa6b8..7b7bf6f41c046 100644 --- a/nixos/tests/munin.nix +++ b/nixos/tests/munin.nix @@ -4,7 +4,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { name = "munin"; meta = with pkgs.lib.maintainers; { - maintainers = [ domenkozar eelco ]; + maintainers = [ domenkozar ]; }; nodes = { diff --git a/nixos/tests/mysql/common.nix b/nixos/tests/mysql/common.nix index 1cf52347f4c74..ad54b0e00c1b3 100644 --- a/nixos/tests/mysql/common.nix +++ b/nixos/tests/mysql/common.nix @@ -4,7 +4,7 @@ inherit (pkgs) mysql80; }; perconaPackages = { - inherit (pkgs) percona-server_8_0; + inherit (pkgs) percona-server_lts percona-server_innovation; }; mkTestName = pkg: "mariadb_${builtins.replaceStrings ["."] [""] (lib.versions.majorMinor pkg.version)}"; } diff --git a/nixos/tests/mysql/mysql.nix b/nixos/tests/mysql/mysql.nix index 0a61f9d38fe2e..093da4f46aa10 100644 --- a/nixos/tests/mysql/mysql.nix +++ b/nixos/tests/mysql/mysql.nix @@ -146,6 +146,6 @@ in }) mariadbPackages) // (lib.mapAttrs (_: package: makeMySQLTest { inherit package; - name = "percona_8_0"; + name = builtins.replaceStrings ["-"] ["_"] package.pname; hasMroonga = false; useSocketAuth = false; }) perconaPackages) diff --git a/nixos/tests/nat.nix b/nixos/tests/nat.nix index 0b617cea7774c..8b682a8b3aa7c 100644 --- a/nixos/tests/nat.nix +++ b/nixos/tests/nat.nix @@ -22,7 +22,7 @@ import ./make-test-python.nix ({ pkgs, lib, withFirewall, nftables ? false, ... name = "nat" + (lib.optionalString nftables "Nftables") + (if withFirewall then "WithFirewall" else "Standalone"); meta = with pkgs.lib.maintainers; { - maintainers = [ eelco rob ]; + maintainers = [ rob ]; }; nodes = diff --git a/nixos/tests/nfs/simple.nix b/nixos/tests/nfs/simple.nix index 026da9563bc03..077c1d4109356 100644 --- a/nixos/tests/nfs/simple.nix +++ b/nixos/tests/nfs/simple.nix @@ -20,7 +20,7 @@ in { name = "nfs"; meta = with pkgs.lib.maintainers; { - maintainers = [ eelco ]; + maintainers = [ ]; }; nodes = diff --git a/nixos/tests/openssh.nix b/nixos/tests/openssh.nix index 2684b6f45e84e..140723a2df810 100644 --- a/nixos/tests/openssh.nix +++ b/nixos/tests/openssh.nix @@ -5,7 +5,7 @@ let inherit (import ./ssh-keys.nix pkgs) in { name = "openssh"; meta = with pkgs.lib.maintainers; { - maintainers = [ aszlig eelco ]; + maintainers = [ aszlig ]; }; nodes = { diff --git a/nixos/tests/patroni.nix b/nixos/tests/patroni.nix index 1f15cd59677ad..68fce4051553e 100644 --- a/nixos/tests/patroni.nix +++ b/nixos/tests/patroni.nix @@ -155,7 +155,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: print(node.succeed("patronictl list cluster1")) node.wait_until_succeeds(f"[ $(patronictl list -f json cluster1 | jq 'length') == {expected_replicas + 1} ]") node.wait_until_succeeds("[ $(patronictl list -f json cluster1 | jq 'map(select(.Role | test(\"^Leader$\"))) | map(select(.State | test(\"^running$\"))) | length') == 1 ]") - node.wait_until_succeeds(f"[ $(patronictl list -f json cluster1 | jq 'map(select(.Role | test(\"^Replica$\"))) | map(select(.State | test(\"^running$\"))) | length') == {expected_replicas} ]") + node.wait_until_succeeds(f"[ $(patronictl list -f json cluster1 | jq 'map(select(.Role | test(\"^Replica$\"))) | map(select(.State | test(\"^streaming$\"))) | length') == {expected_replicas} ]") print(node.succeed("patronictl list cluster1")) client.wait_until_succeeds("psql -h 127.0.0.1 -U postgres --command='select 1;'") diff --git a/nixos/tests/pgvecto-rs.nix b/nixos/tests/pgvecto-rs.nix index cd871dab6a0f1..8d9d6c0b88f51 100644 --- a/nixos/tests/pgvecto-rs.nix +++ b/nixos/tests/pgvecto-rs.nix @@ -66,7 +66,7 @@ let ''; }; - applicablePostgresqlVersions = filterAttrs (_: value: versionAtLeast value.version "12") postgresql-versions; + applicablePostgresqlVersions = filterAttrs (_: value: versionAtLeast value.version "14") postgresql-versions; in mapAttrs' (name: package: { diff --git a/nixos/tests/printing.nix b/nixos/tests/printing.nix index 29c5d810f215a..b413996c67db8 100644 --- a/nixos/tests/printing.nix +++ b/nixos/tests/printing.nix @@ -9,7 +9,7 @@ import ./make-test-python.nix ( { name = "printing"; meta = with pkgs.lib.maintainers; { - maintainers = [ domenkozar eelco matthewbauer ]; + maintainers = [ domenkozar matthewbauer ]; }; nodes.server = { ... }: { diff --git a/nixos/tests/private-gpt.nix b/nixos/tests/private-gpt.nix new file mode 100644 index 0000000000000..d19e167cc303c --- /dev/null +++ b/nixos/tests/private-gpt.nix @@ -0,0 +1,27 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: +let + mainPort = "8001"; +in +{ + name = "private-gpt"; + meta = with lib.maintainers; { + maintainers = [ drupol ]; + }; + + nodes = { + machine = { ... }: { + services.private-gpt = { + enable = true; + }; + }; + }; + + testScript = '' + machine.start() + + machine.wait_for_unit("private-gpt.service") + machine.wait_for_open_port(${mainPort}) + + machine.succeed("curl http://127.0.0.1:${mainPort}") + ''; +}) diff --git a/nixos/tests/proxy.nix b/nixos/tests/proxy.nix index f8a3d576903e3..ce7131b09a8ab 100644 --- a/nixos/tests/proxy.nix +++ b/nixos/tests/proxy.nix @@ -12,7 +12,7 @@ let in { name = "proxy"; meta = with pkgs.lib.maintainers; { - maintainers = [ eelco ]; + maintainers = [ ]; }; nodes = { diff --git a/nixos/tests/quake3.nix b/nixos/tests/quake3.nix index 2d8c5207001cb..4b7ca03b365b5 100644 --- a/nixos/tests/quake3.nix +++ b/nixos/tests/quake3.nix @@ -32,7 +32,7 @@ in rec { name = "quake3"; meta = with lib.maintainers; { - maintainers = [ domenkozar eelco ]; + maintainers = [ domenkozar ]; }; # TODO: lcov doesn't work atm diff --git a/nixos/tests/rabbitmq.nix b/nixos/tests/rabbitmq.nix index 040679e68d989..4b8921662b7f4 100644 --- a/nixos/tests/rabbitmq.nix +++ b/nixos/tests/rabbitmq.nix @@ -9,7 +9,7 @@ in { name = "rabbitmq"; meta = with pkgs.lib.maintainers; { - maintainers = [ eelco offline ]; + maintainers = [ offline ]; }; nodes.machine = { diff --git a/nixos/tests/samba.nix b/nixos/tests/samba.nix index 252c3dd9c76e9..53cdbbe1c40f1 100644 --- a/nixos/tests/samba.nix +++ b/nixos/tests/samba.nix @@ -3,7 +3,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { name = "samba"; - meta.maintainers = [ pkgs.lib.maintainers.eelco ]; + meta.maintainers = [ ]; nodes = { client = diff --git a/nixos/tests/simple.nix b/nixos/tests/simple.nix index c36287b4e843b..afd49d481a65d 100644 --- a/nixos/tests/simple.nix +++ b/nixos/tests/simple.nix @@ -1,7 +1,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { name = "simple"; meta = with pkgs.lib.maintainers; { - maintainers = [ eelco ]; + maintainers = [ ]; }; nodes.machine = { ... }: { diff --git a/nixos/tests/smokeping.nix b/nixos/tests/smokeping.nix index 04f8139642918..fe1ecad9969b0 100644 --- a/nixos/tests/smokeping.nix +++ b/nixos/tests/smokeping.nix @@ -11,7 +11,6 @@ import ./make-test-python.nix ({ pkgs, ...} : { networking.domain = "example.com"; # FQDN: sm.example.com services.smokeping = { enable = true; - port = 8081; mailHost = "127.0.0.2"; probeConfig = '' + FPing @@ -25,12 +24,19 @@ import ./make-test-python.nix ({ pkgs, ...} : { testScript = '' start_all() sm.wait_for_unit("smokeping") - sm.wait_for_unit("thttpd") + sm.wait_for_unit("nginx") sm.wait_for_file("/var/lib/smokeping/data/Local/LocalMachine.rrd") - sm.succeed("curl -s -f localhost:8081/smokeping.fcgi?target=Local") + sm.succeed("curl -s -f localhost/smokeping.fcgi?target=Local") # Check that there's a helpful page without explicit path as well. - sm.succeed("curl -s -f localhost:8081") + sm.succeed("curl -s -f localhost") sm.succeed("ls /var/lib/smokeping/cache/Local/LocalMachine_mini.png") sm.succeed("ls /var/lib/smokeping/cache/index.html") + + # stop and start the service like nixos-rebuild would do + # see https://github.com/NixOS/nixpkgs/issues/265953) + sm.succeed("systemctl stop smokeping") + sm.succeed("systemctl start smokeping") + # ensure all services restarted properly + sm.succeed("systemctl --failed | grep -q '0 loaded units listed'") ''; }) diff --git a/nixos/tests/stalwart-mail.nix b/nixos/tests/stalwart-mail.nix index 634c0e2e39261..581090cd70f48 100644 --- a/nixos/tests/stalwart-mail.nix +++ b/nixos/tests/stalwart-mail.nix @@ -40,12 +40,14 @@ in import ./make-test-python.nix ({ lib, ... }: { }; }; - session.auth.mechanisms = [ "PLAIN" ]; - session.auth.directory = "in-memory"; - storage.directory = "in-memory"; # shared with imap + resolver.public-suffix = [ ]; # do not fetch from web in sandbox - session.rcpt.directory = "in-memory"; - queue.outbound.next-hop = [ "local" ]; + session.auth.mechanisms = "[plain]"; + session.auth.directory = "'in-memory'"; + storage.directory = "in-memory"; + + session.rcpt.directory = "'in-memory'"; + queue.outbound.next-hop = "'local'"; directory."in-memory" = { type = "memory"; diff --git a/nixos/tests/stub-ld.nix b/nixos/tests/stub-ld.nix index 25161301741b7..72b0aebf3e6ce 100644 --- a/nixos/tests/stub-ld.nix +++ b/nixos/tests/stub-ld.nix @@ -45,10 +45,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { ${if32 "machine.succeed('test -L /${libDir32}/${ldsoBasename32}')"} with subtest("Try FHS executable"): - machine.copy_from_host('${test-exec.${pkgs.system}}','test-exec') + machine.copy_from_host('${test-exec.${pkgs.stdenv.hostPlatform.system}}','test-exec') machine.succeed('if test-exec/${exec-name} 2>outfile; then false; else [ $? -eq 127 ];fi') machine.succeed('grep -qi nixos outfile') - ${if32 "machine.copy_from_host('${test-exec.${pkgs32.system}}','test-exec32')"} + ${if32 "machine.copy_from_host('${test-exec.${pkgs32.stdenv.hostPlatform.system}}','test-exec32')"} ${if32 "machine.succeed('if test-exec32/${exec-name} 2>outfile32; then false; else [ $? -eq 127 ];fi')"} ${if32 "machine.succeed('grep -qi nixos outfile32')"} diff --git a/nixos/tests/switch-test.nix b/nixos/tests/switch-test.nix index 4a7bcd5a82264..d90e5bb088cee 100644 --- a/nixos/tests/switch-test.nix +++ b/nixos/tests/switch-test.nix @@ -1,6 +1,6 @@ # Test configuration switching. -import ./make-test-python.nix ({ lib, pkgs, ...} : let +import ./make-test-python.nix ({ lib, pkgs, ng, ...} : let # Simple service that can either be socket-activated or that will # listen on port 1234 if not socket-activated. @@ -48,6 +48,11 @@ in { nodes = { machine = { pkgs, lib, ... }: { + system.switch = { + enable = !ng; + enableNg = ng; + }; + environment.systemPackages = [ pkgs.socat ]; # for the socket activation stuff users.mutableUsers = false; diff --git a/nixos/tests/systemd-confinement.nix b/nixos/tests/systemd-confinement.nix deleted file mode 100644 index bde5b770ea50d..0000000000000 --- a/nixos/tests/systemd-confinement.nix +++ /dev/null @@ -1,184 +0,0 @@ -import ./make-test-python.nix { - name = "systemd-confinement"; - - nodes.machine = { pkgs, lib, ... }: let - testServer = pkgs.writeScript "testserver.sh" '' - #!${pkgs.runtimeShell} - export PATH=${lib.escapeShellArg "${pkgs.coreutils}/bin"} - ${lib.escapeShellArg pkgs.runtimeShell} 2>&1 - echo "exit-status:$?" - ''; - - testClient = pkgs.writeScriptBin "chroot-exec" '' - #!${pkgs.runtimeShell} -e - output="$(echo "$@" | nc -NU "/run/test$(< /teststep).sock")" - ret="$(echo "$output" | sed -nre '$s/^exit-status:([0-9]+)$/\1/p')" - echo "$output" | head -n -1 - exit "''${ret:-1}" - ''; - - mkTestStep = num: { - testScript, - config ? {}, - serviceName ? "test${toString num}", - }: { - systemd.sockets.${serviceName} = { - description = "Socket for Test Service ${toString num}"; - wantedBy = [ "sockets.target" ]; - socketConfig.ListenStream = "/run/test${toString num}.sock"; - socketConfig.Accept = true; - }; - - systemd.services."${serviceName}@" = { - description = "Confined Test Service ${toString num}"; - confinement = (config.confinement or {}) // { enable = true; }; - serviceConfig = (config.serviceConfig or {}) // { - ExecStart = testServer; - StandardInput = "socket"; - }; - } // removeAttrs config [ "confinement" "serviceConfig" ]; - - __testSteps = lib.mkOrder num ('' - machine.succeed("echo ${toString num} > /teststep") - '' + testScript); - }; - - in { - imports = lib.imap1 mkTestStep [ - { config.confinement.mode = "chroot-only"; - testScript = '' - with subtest("chroot-only confinement"): - paths = machine.succeed('chroot-exec ls -1 / | paste -sd,').strip() - assert_eq(paths, "bin,nix,run") - uid = machine.succeed('chroot-exec id -u').strip() - assert_eq(uid, "0") - machine.succeed("chroot-exec chown 65534 /bin") - ''; - } - { testScript = '' - with subtest("full confinement with APIVFS"): - machine.fail("chroot-exec ls -l /etc") - machine.fail("chroot-exec chown 65534 /bin") - assert_eq(machine.succeed('chroot-exec id -u').strip(), "0") - machine.succeed("chroot-exec chown 0 /bin") - ''; - } - { config.serviceConfig.BindReadOnlyPaths = [ "/etc" ]; - testScript = '' - with subtest("check existence of bind-mounted /etc"): - passwd = machine.succeed('chroot-exec cat /etc/passwd').strip() - assert len(passwd) > 0, "/etc/passwd must not be empty" - ''; - } - { config.serviceConfig.User = "chroot-testuser"; - config.serviceConfig.Group = "chroot-testgroup"; - testScript = '' - with subtest("check if User/Group really runs as non-root"): - machine.succeed("chroot-exec ls -l /dev") - uid = machine.succeed('chroot-exec id -u').strip() - assert uid != "0", "UID of chroot-testuser shouldn't be 0" - machine.fail("chroot-exec touch /bin/test") - ''; - } - (let - symlink = pkgs.runCommand "symlink" { - target = pkgs.writeText "symlink-target" "got me\n"; - } "ln -s \"$target\" \"$out\""; - in { - config.confinement.packages = lib.singleton symlink; - testScript = '' - with subtest("check if symlinks are properly bind-mounted"): - machine.fail("chroot-exec test -e /etc") - text = machine.succeed('chroot-exec cat ${symlink}').strip() - assert_eq(text, "got me") - ''; - }) - { config.serviceConfig.User = "chroot-testuser"; - config.serviceConfig.Group = "chroot-testgroup"; - config.serviceConfig.StateDirectory = "testme"; - testScript = '' - with subtest("check if StateDirectory works"): - machine.succeed("chroot-exec touch /tmp/canary") - machine.succeed('chroot-exec "echo works > /var/lib/testme/foo"') - machine.succeed('test "$(< /var/lib/testme/foo)" = works') - machine.succeed("test ! -e /tmp/canary") - ''; - } - { testScript = '' - with subtest("check if /bin/sh works"): - machine.succeed( - "chroot-exec test -e /bin/sh", - 'test "$(chroot-exec \'/bin/sh -c "echo bar"\')" = bar', - ) - ''; - } - { config.confinement.binSh = null; - testScript = '' - with subtest("check if suppressing /bin/sh works"): - machine.succeed("chroot-exec test ! -e /bin/sh") - machine.succeed('test "$(chroot-exec \'/bin/sh -c "echo foo"\')" != foo') - ''; - } - { config.confinement.binSh = "${pkgs.hello}/bin/hello"; - testScript = '' - with subtest("check if we can set /bin/sh to something different"): - machine.succeed("chroot-exec test -e /bin/sh") - machine.succeed('test "$(chroot-exec /bin/sh -g foo)" = foo') - ''; - } - { config.environment.FOOBAR = pkgs.writeText "foobar" "eek\n"; - testScript = '' - with subtest("check if only Exec* dependencies are included"): - machine.succeed('test "$(chroot-exec \'cat "$FOOBAR"\')" != eek') - ''; - } - { config.environment.FOOBAR = pkgs.writeText "foobar" "eek\n"; - config.confinement.fullUnit = true; - testScript = '' - with subtest("check if all unit dependencies are included"): - machine.succeed('test "$(chroot-exec \'cat "$FOOBAR"\')" = eek') - ''; - } - { serviceName = "shipped-unitfile"; - config.confinement.mode = "chroot-only"; - testScript = '' - with subtest("check if shipped unit file still works"): - machine.succeed( - 'chroot-exec \'kill -9 $$ 2>&1 || :\' | ' - 'grep -q "Too many levels of symbolic links"' - ) - ''; - } - ]; - - options.__testSteps = lib.mkOption { - type = lib.types.lines; - description = "All of the test steps combined as a single script."; - }; - - config.environment.systemPackages = lib.singleton testClient; - config.systemd.packages = lib.singleton (pkgs.writeTextFile { - name = "shipped-unitfile"; - destination = "/etc/systemd/system/shipped-unitfile@.service"; - text = '' - [Service] - SystemCallFilter=~kill - SystemCallErrorNumber=ELOOP - ''; - }); - - config.users.groups.chroot-testgroup = {}; - config.users.users.chroot-testuser = { - isSystemUser = true; - description = "Chroot Test User"; - group = "chroot-testgroup"; - }; - }; - - testScript = { nodes, ... }: '' - def assert_eq(a, b): - assert a == b, f"{a} != {b}" - - machine.wait_for_unit("multi-user.target") - '' + nodes.machine.config.__testSteps; -} diff --git a/nixos/tests/systemd-confinement/checkperms.py b/nixos/tests/systemd-confinement/checkperms.py new file mode 100644 index 0000000000000..3c7ba279a3d20 --- /dev/null +++ b/nixos/tests/systemd-confinement/checkperms.py @@ -0,0 +1,187 @@ +import errno +import os + +from enum import IntEnum +from pathlib import Path + + +class Accessibility(IntEnum): + """ + The level of accessibility we have on a file or directory. + + This is needed to assess the attack surface on the file system namespace we + have within a confined service. Higher levels mean more permissions for the + user and thus a bigger attack surface. + """ + NONE = 0 + + # Directories can be listed or files can be read. + READABLE = 1 + + # This is for special file systems such as procfs and for stuff such as + # FIFOs or character special files. The reason why this has a lower value + # than WRITABLE is because those files are more restricted on what and how + # they can be written to. + SPECIAL = 2 + + # Another special case are sticky directories, which do allow write access + # but restrict deletion. This does *not* apply to sticky directories that + # are read-only. + STICKY = 3 + + # Essentially full permissions, the kind of accessibility we want to avoid + # in most cases. + WRITABLE = 4 + + def assert_on(self, path: Path) -> None: + """ + Raise an AssertionError if the given 'path' allows for more + accessibility than 'self'. + """ + actual = self.NONE + + if path.is_symlink(): + actual = self.READABLE + elif path.is_dir(): + writable = True + + dummy_file = path / 'can_i_write' + try: + dummy_file.touch() + except OSError as e: + if e.errno in [errno.EROFS, errno.EACCES]: + writable = False + else: + raise + else: + dummy_file.unlink() + + if writable: + # The reason why we test this *after* we made sure it's + # writable is because we could have a sticky directory where + # the current user doesn't have write access. + if path.stat().st_mode & 0o1000 == 0o1000: + actual = self.STICKY + else: + actual = self.WRITABLE + else: + actual = self.READABLE + elif path.is_file(): + try: + with path.open('rb') as fp: + fp.read(1) + actual = self.READABLE + except PermissionError: + pass + + writable = True + try: + with path.open('ab') as fp: + fp.write('x') + size = fp.tell() + fp.truncate(size) + except PermissionError: + writable = False + except OSError as e: + if e.errno == errno.ETXTBSY: + writable = os.access(path, os.W_OK) + elif e.errno == errno.EROFS: + writable = False + else: + raise + + # Let's always try to fail towards being writable, so if *either* + # access(2) or a real write is successful it's writable. This is to + # make sure we don't accidentally introduce no-ops if we have bugs + # in the more complicated real write code above. + if writable or os.access(path, os.W_OK): + actual = self.WRITABLE + else: + # We need to be very careful when writing to or reading from + # special files (eg. FIFOs), since they can possibly block. So if + # it's not a file, just trust that access(2) won't lie. + if os.access(path, os.R_OK): + actual = self.READABLE + + if os.access(path, os.W_OK): + actual = self.SPECIAL + + if actual > self: + stat = path.stat() + details = ', '.join([ + f'permissions: {stat.st_mode & 0o7777:o}', + f'uid: {stat.st_uid}', + f'group: {stat.st_gid}', + ]) + + raise AssertionError( + f'Expected at most {self!r} but got {actual!r} for path' + f' {path} ({details}).' + ) + + +def is_special_fs(path: Path) -> bool: + """ + Check whether the given path truly is a special file system such as procfs + or sysfs. + """ + try: + if path == Path('/proc'): + return (path / 'version').read_text().startswith('Linux') + elif path == Path('/sys'): + return b'Linux' in (path / 'kernel' / 'notes').read_bytes() + except FileNotFoundError: + pass + return False + + +def is_empty_dir(path: Path) -> bool: + try: + next(path.iterdir()) + return False + except (StopIteration, PermissionError): + return True + + +def _assert_permissions_in_directory( + directory: Path, + accessibility: Accessibility, + subdirs: dict[Path, Accessibility], +) -> None: + accessibility.assert_on(directory) + + for file in directory.iterdir(): + if is_special_fs(file): + msg = f'Got unexpected special filesystem at {file}.' + assert subdirs.pop(file) == Accessibility.SPECIAL, msg + elif not file.is_symlink() and file.is_dir(): + subdir_access = subdirs.pop(file, accessibility) + if is_empty_dir(file): + # Whenever we got an empty directory, we check the permission + # constraints on the current directory (except if specified + # explicitly in subdirs) because for example if we're non-root + # (the constraints of the current directory are thus + # Accessibility.READABLE), we really have to make sure that + # empty directories are *never* writable. + subdir_access.assert_on(file) + else: + _assert_permissions_in_directory(file, subdir_access, subdirs) + else: + subdirs.pop(file, accessibility).assert_on(file) + + +def assert_permissions(subdirs: dict[str, Accessibility]) -> None: + """ + Recursively check whether the file system conforms to the accessibility + specification we specified via 'subdirs'. + """ + root = Path('/') + absolute_subdirs = {root / p: a for p, a in subdirs.items()} + _assert_permissions_in_directory( + root, + Accessibility.WRITABLE if os.getuid() == 0 else Accessibility.READABLE, + absolute_subdirs, + ) + for file in absolute_subdirs.keys(): + msg = f'Expected {file} to exist, but it was nowwhere to be found.' + raise AssertionError(msg) diff --git a/nixos/tests/systemd-confinement/default.nix b/nixos/tests/systemd-confinement/default.nix new file mode 100644 index 0000000000000..15d442d476b08 --- /dev/null +++ b/nixos/tests/systemd-confinement/default.nix @@ -0,0 +1,274 @@ +import ../make-test-python.nix { + name = "systemd-confinement"; + + nodes.machine = { pkgs, lib, ... }: let + testLib = pkgs.python3Packages.buildPythonPackage { + name = "confinement-testlib"; + unpackPhase = '' + cat > setup.py <<EOF + from setuptools import setup + setup(name='confinement-testlib', py_modules=["checkperms"]) + EOF + cp ${./checkperms.py} checkperms.py + ''; + }; + + mkTest = name: testScript: pkgs.writers.writePython3 "${name}.py" { + libraries = [ pkgs.python3Packages.pytest testLib ]; + } '' + # This runs our test script by using pytest's assertion rewriting, so + # that whenever we use "assert <something>", the actual values are + # printed rather than getting a generic AssertionError or the need to + # pass an explicit assertion error message. + import ast + from pathlib import Path + from _pytest.assertion.rewrite import rewrite_asserts + + script = Path('${pkgs.writeText "${name}-main.py" '' + import errno, os, pytest, signal + from subprocess import run + from checkperms import Accessibility, assert_permissions + + ${testScript} + ''}') # noqa + filename = str(script) + source = script.read_bytes() + + tree = ast.parse(source, filename=filename) + rewrite_asserts(tree, source, filename) + exec(compile(tree, filename, 'exec', dont_inherit=True)) + ''; + + mkTestStep = num: { + description, + testScript, + config ? {}, + serviceName ? "test${toString num}", + rawUnit ? null, + }: { + systemd.packages = lib.optional (rawUnit != null) (pkgs.writeTextFile { + name = serviceName; + destination = "/etc/systemd/system/${serviceName}.service"; + text = rawUnit; + }); + + systemd.services.${serviceName} = { + inherit description; + requiredBy = [ "multi-user.target" ]; + confinement = (config.confinement or {}) // { enable = true; }; + serviceConfig = (config.serviceConfig or {}) // { + ExecStart = mkTest serviceName testScript; + Type = "oneshot"; + }; + } // removeAttrs config [ "confinement" "serviceConfig" ]; + }; + + parametrisedTests = lib.concatMap ({ user, privateTmp }: let + withTmp = if privateTmp then "with PrivateTmp" else "without PrivateTmp"; + + serviceConfig = if user == "static-user" then { + User = "chroot-testuser"; + Group = "chroot-testgroup"; + } else if user == "dynamic-user" then { + DynamicUser = true; + } else {}; + + in [ + { description = "${user}, chroot-only confinement ${withTmp}"; + config = { + confinement.mode = "chroot-only"; + # Only set if privateTmp is true to ensure that the default is false. + serviceConfig = serviceConfig // lib.optionalAttrs privateTmp { + PrivateTmp = true; + }; + }; + testScript = if user == "root" then '' + assert os.getuid() == 0 + assert os.getgid() == 0 + + assert_permissions({ + 'bin': Accessibility.READABLE, + 'nix': Accessibility.READABLE, + 'run': Accessibility.READABLE, + ${lib.optionalString privateTmp "'tmp': Accessibility.STICKY,"} + ${lib.optionalString privateTmp "'var': Accessibility.READABLE,"} + ${lib.optionalString privateTmp "'var/tmp': Accessibility.STICKY,"} + }) + '' else '' + assert os.getuid() != 0 + assert os.getgid() != 0 + + assert_permissions({ + 'bin': Accessibility.READABLE, + 'nix': Accessibility.READABLE, + 'run': Accessibility.READABLE, + ${lib.optionalString privateTmp "'tmp': Accessibility.STICKY,"} + ${lib.optionalString privateTmp "'var': Accessibility.READABLE,"} + ${lib.optionalString privateTmp "'var/tmp': Accessibility.STICKY,"} + }) + ''; + } + { description = "${user}, full APIVFS confinement ${withTmp}"; + config = { + # Only set if privateTmp is false to ensure that the default is true. + serviceConfig = serviceConfig // lib.optionalAttrs (!privateTmp) { + PrivateTmp = false; + }; + }; + testScript = if user == "root" then '' + assert os.getuid() == 0 + assert os.getgid() == 0 + + assert_permissions({ + 'bin': Accessibility.READABLE, + 'nix': Accessibility.READABLE, + ${lib.optionalString privateTmp "'tmp': Accessibility.STICKY,"} + 'run': Accessibility.WRITABLE, + + 'proc': Accessibility.SPECIAL, + 'sys': Accessibility.SPECIAL, + 'dev': Accessibility.WRITABLE, + + ${lib.optionalString privateTmp "'var': Accessibility.READABLE,"} + ${lib.optionalString privateTmp "'var/tmp': Accessibility.STICKY,"} + }) + '' else '' + assert os.getuid() != 0 + assert os.getgid() != 0 + + assert_permissions({ + 'bin': Accessibility.READABLE, + 'nix': Accessibility.READABLE, + ${lib.optionalString privateTmp "'tmp': Accessibility.STICKY,"} + 'run': Accessibility.STICKY, + + 'proc': Accessibility.SPECIAL, + 'sys': Accessibility.SPECIAL, + 'dev': Accessibility.SPECIAL, + 'dev/shm': Accessibility.STICKY, + 'dev/mqueue': Accessibility.STICKY, + + ${lib.optionalString privateTmp "'var': Accessibility.READABLE,"} + ${lib.optionalString privateTmp "'var/tmp': Accessibility.STICKY,"} + }) + ''; + } + ]) (lib.cartesianProductOfSets { + user = [ "root" "dynamic-user" "static-user" ]; + privateTmp = [ true false ]; + }); + + in { + imports = lib.imap1 mkTestStep (parametrisedTests ++ [ + { description = "existence of bind-mounted /etc"; + config.serviceConfig.BindReadOnlyPaths = [ "/etc" ]; + testScript = '' + assert Path('/etc/passwd').read_text() + ''; + } + (let + symlink = pkgs.runCommand "symlink" { + target = pkgs.writeText "symlink-target" "got me"; + } "ln -s \"$target\" \"$out\""; + in { + description = "check if symlinks are properly bind-mounted"; + config.confinement.packages = lib.singleton symlink; + testScript = '' + assert Path('${symlink}').read_text() == 'got me' + ''; + }) + { description = "check if StateDirectory works"; + config.serviceConfig.User = "chroot-testuser"; + config.serviceConfig.Group = "chroot-testgroup"; + config.serviceConfig.StateDirectory = "testme"; + + # We restart on purpose here since we want to check whether the state + # directory actually persists. + config.serviceConfig.Restart = "on-failure"; + config.serviceConfig.RestartMode = "direct"; + + testScript = '' + assert not Path('/tmp/canary').exists() + Path('/tmp/canary').touch() + + if (foo := Path('/var/lib/testme/foo')).exists(): + assert Path('/var/lib/testme/foo').read_text() == 'works' + else: + Path('/var/lib/testme/foo').write_text('works') + print('<4>Exiting with failure to check persistence on restart.') + raise SystemExit(1) + ''; + } + { description = "check if /bin/sh works"; + testScript = '' + assert Path('/bin/sh').exists() + + result = run( + ['/bin/sh', '-c', 'echo -n bar'], + capture_output=True, + check=True, + ) + assert result.stdout == b'bar' + ''; + } + { description = "check if suppressing /bin/sh works"; + config.confinement.binSh = null; + testScript = '' + assert not Path('/bin/sh').exists() + with pytest.raises(FileNotFoundError): + run(['/bin/sh', '-c', 'echo foo']) + ''; + } + { description = "check if we can set /bin/sh to something different"; + config.confinement.binSh = "${pkgs.hello}/bin/hello"; + testScript = '' + assert Path('/bin/sh').exists() + result = run( + ['/bin/sh', '-g', 'foo'], + capture_output=True, + check=True, + ) + assert result.stdout == b'foo\n' + ''; + } + { description = "check if only Exec* dependencies are included"; + config.environment.FOOBAR = pkgs.writeText "foobar" "eek"; + testScript = '' + with pytest.raises(FileNotFoundError): + Path(os.environ['FOOBAR']).read_text() + ''; + } + { description = "check if fullUnit includes all dependencies"; + config.environment.FOOBAR = pkgs.writeText "foobar" "eek"; + config.confinement.fullUnit = true; + testScript = '' + assert Path(os.environ['FOOBAR']).read_text() == 'eek' + ''; + } + { description = "check if shipped unit file still works"; + config.confinement.mode = "chroot-only"; + rawUnit = '' + [Service] + SystemCallFilter=~kill + SystemCallErrorNumber=ELOOP + ''; + testScript = '' + with pytest.raises(OSError) as excinfo: + os.kill(os.getpid(), signal.SIGKILL) + assert excinfo.value.errno == errno.ELOOP + ''; + } + ]); + + config.users.groups.chroot-testgroup = {}; + config.users.users.chroot-testuser = { + isSystemUser = true; + description = "Chroot Test User"; + group = "chroot-testgroup"; + }; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + ''; +} diff --git a/nixos/tests/systemd-initrd-modprobe.nix b/nixos/tests/systemd-initrd-modprobe.nix index 0f93492176b44..e563552a645fd 100644 --- a/nixos/tests/systemd-initrd-modprobe.nix +++ b/nixos/tests/systemd-initrd-modprobe.nix @@ -4,21 +4,21 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { nodes.machine = { pkgs, ... }: { testing.initrdBackdoor = true; boot.initrd.systemd.enable = true; - boot.initrd.kernelModules = [ "loop" ]; # Load module in initrd. + boot.initrd.kernelModules = [ "tcp_hybla" ]; # Load module in initrd. boot.extraModprobeConfig = '' - options loop max_loop=42 + options tcp_hybla rtt0=42 ''; }; testScript = '' machine.wait_for_unit("initrd.target") - max_loop = machine.succeed("cat /sys/module/loop/parameters/max_loop") - assert int(max_loop) == 42, "Parameter should be respected for initrd kernel modules" + rtt = machine.succeed("cat /sys/module/tcp_hybla/parameters/rtt0") + assert int(rtt) == 42, "Parameter should be respected for initrd kernel modules" # Make sure it sticks in stage 2 machine.switch_root() machine.wait_for_unit("multi-user.target") - max_loop = machine.succeed("cat /sys/module/loop/parameters/max_loop") - assert int(max_loop) == 42, "Parameter should be respected for initrd kernel modules" + rtt = machine.succeed("cat /sys/module/tcp_hybla/parameters/rtt0") + assert int(rtt) == 42, "Parameter should be respected for initrd kernel modules" ''; }) diff --git a/nixos/tests/tayga.nix b/nixos/tests/tayga.nix index 4aade67d74d0d..204391d1312f2 100644 --- a/nixos/tests/tayga.nix +++ b/nixos/tests/tayga.nix @@ -59,6 +59,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: ]; }; }; + programs.mtr.enable = true; }; # The router is configured with static IPv4 addresses towards the server @@ -120,6 +121,9 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: prefixLength = 96; }; }; + mappings = { + "192.0.2.42" = "2001:db8::2"; + }; }; }; @@ -171,6 +175,9 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: prefixLength = 96; }; }; + mappings = { + "192.0.2.42" = "2001:db8::2"; + }; }; }; @@ -199,7 +206,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: ]; }; }; - environment.systemPackages = [ pkgs.mtr ]; + programs.mtr.enable = true; }; }; @@ -225,10 +232,16 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: with subtest("Wait for tayga"): router.wait_for_unit("tayga.service") - with subtest("Test ICMP"): + with subtest("Test ICMP server -> client"): + server.wait_until_succeeds("ping -c 3 192.0.2.42 >&2") + + with subtest("Test ICMP and show a traceroute server -> client"): + server.wait_until_succeeds("mtr --show-ips --report-wide 192.0.2.42 >&2") + + with subtest("Test ICMP client -> server"): client.wait_until_succeeds("ping -c 3 64:ff9b::100.64.0.2 >&2") - with subtest("Test ICMP and show a traceroute"): + with subtest("Test ICMP and show a traceroute client -> server"): client.wait_until_succeeds("mtr --show-ips --report-wide 64:ff9b::100.64.0.2 >&2") router.log(router.execute("systemd-analyze security tayga.service")[1]) diff --git a/nixos/tests/udisks2.nix b/nixos/tests/udisks2.nix index 8cc148750c7bc..b934f0b951562 100644 --- a/nixos/tests/udisks2.nix +++ b/nixos/tests/udisks2.nix @@ -2,6 +2,7 @@ import ./make-test-python.nix ({ pkgs, ... }: let + # FIXME: 404s stick = pkgs.fetchurl { url = "https://nixos.org/~eelco/nix/udisks-test.img.xz"; sha256 = "0was1xgjkjad91nipzclaz5biv3m4b2nk029ga6nk7iklwi19l8b"; @@ -12,7 +13,7 @@ in { name = "udisks2"; meta = with pkgs.lib.maintainers; { - maintainers = [ eelco ]; + maintainers = [ ]; }; nodes.machine = diff --git a/nixos/tests/vector.nix b/nixos/tests/vector.nix index a55eb4e012c5b..9c0d7e84fab33 100644 --- a/nixos/tests/vector.nix +++ b/nixos/tests/vector.nix @@ -14,15 +14,27 @@ with pkgs.lib; enable = true; journaldAccess = true; settings = { - sources.journald.type = "journald"; + sources = { + journald.type = "journald"; + + vector_metrics.type = "internal_metrics"; + + vector_logs.type = "internal_logs"; + }; sinks = { file = { type = "file"; - inputs = [ "journald" ]; + inputs = [ "journald" "vector_logs" ]; path = "/var/lib/vector/logs.log"; encoding = { codec = "json"; }; }; + + prometheus_exporter = { + type = "prometheus_exporter"; + inputs = [ "vector_metrics" ]; + address = "[::]:9598"; + }; }; }; }; @@ -31,6 +43,10 @@ with pkgs.lib; # ensure vector is forwarding the messages appropriately testScript = '' machine.wait_for_unit("vector.service") + machine.wait_for_open_port(9598) + machine.wait_until_succeeds("curl -sSf http://localhost:9598/metrics | grep vector_build_info") + machine.wait_until_succeeds("curl -sSf http://localhost:9598/metrics | grep vector_component_received_bytes_total | grep journald") + machine.wait_until_succeeds("curl -sSf http://localhost:9598/metrics | grep vector_utilization | grep prometheus_exporter") machine.wait_for_file("/var/lib/vector/logs.log") ''; }; diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix index 3c2a391233dbd..5fce3ba548123 100644 --- a/nixos/tests/virtualbox.nix +++ b/nixos/tests/virtualbox.nix @@ -98,7 +98,6 @@ let cfg = (import ../lib/eval-config.nix { system = if use64bitGuest then "x86_64-linux" else "i686-linux"; modules = [ - ../modules/profiles/minimal.nix (testVMConfig vmName vmScript) ]; }).config; diff --git a/nixos/tests/web-apps/pretalx.nix b/nixos/tests/web-apps/pretalx.nix index a226639b076b4..cbb6580aa0515 100644 --- a/nixos/tests/web-apps/pretalx.nix +++ b/nixos/tests/web-apps/pretalx.nix @@ -5,13 +5,16 @@ meta.maintainers = lib.teams.c3d2.members; nodes = { - pretalx = { + pretalx = { config, ... }: { networking.extraHosts = '' 127.0.0.1 talks.local ''; services.pretalx = { enable = true; + plugins = with config.services.pretalx.package.plugins; [ + pages + ]; nginx.domain = "talks.local"; settings = { site.url = "http://talks.local"; @@ -27,5 +30,9 @@ pretalx.wait_for_unit("pretalx-worker.service") pretalx.wait_until_succeeds("curl -q --fail http://talks.local/orga/") + + pretalx.succeed("pretalx-manage --help") + + pretalx.log(pretalx.succeed("systemd-analyze security pretalx-web.service")) ''; } diff --git a/nixos/tests/ydotool.nix b/nixos/tests/ydotool.nix new file mode 100644 index 0000000000000..818ac6f2d50de --- /dev/null +++ b/nixos/tests/ydotool.nix @@ -0,0 +1,115 @@ +import ./make-test-python.nix ( + { pkgs, lib, ... }: + let + textInput = "This works."; + inputBoxText = "Enter input"; + inputBox = pkgs.writeShellScript "zenity-input" '' + ${lib.getExe pkgs.gnome.zenity} --entry --text '${inputBoxText}:' > /tmp/output & + ''; + in + { + name = "ydotool"; + + meta = { + maintainers = with lib.maintainers; [ + OPNA2608 + quantenzitrone + ]; + }; + + nodes = { + headless = + { config, ... }: + { + imports = [ ./common/user-account.nix ]; + + users.users.alice.extraGroups = [ "ydotool" ]; + + programs.ydotool.enable = true; + + services.getty.autologinUser = "alice"; + }; + + x11 = + { config, ... }: + { + imports = [ + ./common/user-account.nix + ./common/auto.nix + ./common/x11.nix + ]; + + users.users.alice.extraGroups = [ "ydotool" ]; + + programs.ydotool.enable = true; + + test-support.displayManager.auto = { + enable = true; + user = "alice"; + }; + + services.xserver.windowManager.dwm.enable = true; + services.displayManager.defaultSession = lib.mkForce "none+dwm"; + }; + + wayland = + { config, ... }: + { + imports = [ ./common/user-account.nix ]; + + services.cage = { + enable = true; + user = "alice"; + }; + + programs.ydotool.enable = true; + + services.cage.program = inputBox; + }; + }; + + enableOCR = true; + + testScript = + { nodes, ... }: + '' + def as_user(cmd: str): + """ + Return a shell command for running a shell command as a specific user. + """ + return f"sudo -u alice -i {cmd}" + + start_all() + + # Headless + headless.wait_for_unit("multi-user.target") + headless.wait_for_text("alice") + headless.succeed(as_user("ydotool type 'echo ${textInput} > /tmp/output'")) # text input + headless.succeed(as_user("ydotool key 28:1 28:0")) # text input + headless.screenshot("headless_input") + headless.wait_for_file("/tmp/output") + headless.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input + + # X11 + x11.wait_for_x() + x11.execute(as_user("${inputBox}")) + x11.wait_for_text("${inputBoxText}") + x11.succeed(as_user("ydotool type '${textInput}'")) # text input + x11.screenshot("x11_input") + x11.succeed(as_user("ydotool mousemove -a 400 110")) # mouse input + x11.succeed(as_user("ydotool click 0xC0")) # mouse input + x11.wait_for_file("/tmp/output") + x11.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input + + # Wayland + wayland.wait_for_unit("graphical.target") + wayland.wait_for_text("${inputBoxText}") + wayland.succeed("ydotool type '${textInput}'") # text input + wayland.screenshot("wayland_input") + wayland.succeed("ydotool mousemove -a 100 100") # mouse input + wayland.succeed("ydotool click 0xC0") # mouse input + wayland.wait_for_file("/tmp/output") + wayland.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input + ''; + } +) diff --git a/nixos/tests/your_spotify.nix b/nixos/tests/your_spotify.nix new file mode 100644 index 0000000000000..a1fa0e459a8e1 --- /dev/null +++ b/nixos/tests/your_spotify.nix @@ -0,0 +1,33 @@ +import ./make-test-python.nix ({pkgs, ...}: { + name = "your_spotify"; + meta = with pkgs.lib.maintainers; { + maintainers = [patrickdag]; + }; + + nodes.machine = { + services.your_spotify = { + enable = true; + spotifySecretFile = pkgs.writeText "spotifySecretFile" "deadbeef"; + settings = { + CLIENT_ENDPOINT = "http://localhost"; + API_ENDPOINT = "http://localhost:3000"; + SPOTIFY_PUBLIC = "beefdead"; + }; + enableLocalDB = true; + nginxVirtualHost = "localhost"; + }; + }; + + testScript = '' + machine.wait_for_unit("your_spotify.service") + + machine.wait_for_open_port(3000) + machine.wait_for_open_port(80) + + out = machine.succeed("curl --fail -X GET 'http://localhost:3000/'") + assert "Hello !" in out + + out = machine.succeed("curl --fail -X GET 'http://localhost:80/'") + assert "<title>Your Spotify</title>" in out + ''; +}) |