diff options
Diffstat (limited to 'nixos/tests')
82 files changed, 1900 insertions, 306 deletions
diff --git a/nixos/tests/all-terminfo.nix b/nixos/tests/all-terminfo.nix index dd47c66ee1c1..2f5e56f09f26 100644 --- a/nixos/tests/all-terminfo.nix +++ b/nixos/tests/all-terminfo.nix @@ -10,7 +10,11 @@ import ./make-test-python.nix ({ pkgs, ... }: rec { let o = builtins.tryEval drv; in - o.success && lib.isDerivation o.value && o.value ? outputs && builtins.elem "terminfo" o.value.outputs; + o.success && + lib.isDerivation o.value && + o.value ? outputs && + builtins.elem "terminfo" o.value.outputs && + !o.value.meta.broken; terminfos = lib.filterAttrs infoFilter pkgs; excludedTerminfos = lib.filterAttrs (_: drv: !(builtins.elem drv.terminfo config.environment.systemPackages)) terminfos; includedOuts = lib.filterAttrs (_: drv: builtins.elem drv.out config.environment.systemPackages) terminfos; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index f44fcfcf54ab..e0572e3bed9c 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -117,13 +117,16 @@ in { allTerminfo = handleTest ./all-terminfo.nix {}; alps = handleTest ./alps.nix {}; amazon-init-shell = handleTest ./amazon-init-shell.nix {}; + amazon-ssm-agent = handleTest ./amazon-ssm-agent.nix {}; amd-sev = runTest ./amd-sev.nix; anbox = runTest ./anbox.nix; + anki-sync-server = handleTest ./anki-sync-server.nix {}; anuko-time-tracker = handleTest ./anuko-time-tracker.nix {}; apcupsd = handleTest ./apcupsd.nix {}; apfs = runTest ./apfs.nix; appliance-repart-image = runTest ./appliance-repart-image.nix; apparmor = handleTest ./apparmor.nix {}; + archi = handleTest ./archi.nix {}; atd = handleTest ./atd.nix {}; atop = handleTest ./atop.nix {}; atuin = handleTest ./atuin.nix {}; @@ -132,6 +135,7 @@ in { authelia = handleTest ./authelia.nix {}; avahi = handleTest ./avahi.nix {}; avahi-with-resolved = handleTest ./avahi.nix { networkd = true; }; + ayatana-indicators = handleTest ./ayatana-indicators.nix {}; babeld = handleTest ./babeld.nix {}; bazarr = handleTest ./bazarr.nix {}; bcachefs = handleTestOn ["x86_64-linux" "aarch64-linux"] ./bcachefs.nix {}; @@ -172,6 +176,7 @@ in { cassandra_3_0 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_3_0; }; cassandra_3_11 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_3_11; }; cassandra_4 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_4; }; + centrifugo = runTest ./centrifugo.nix; ceph-multi-node = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./ceph-multi-node.nix {}; ceph-single-node = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./ceph-single-node.nix {}; ceph-single-node-bluestore = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./ceph-single-node-bluestore.nix {}; @@ -183,6 +188,7 @@ in { chrony = handleTestOn ["aarch64-linux" "x86_64-linux"] ./chrony.nix {}; chrony-ptp = handleTestOn ["aarch64-linux" "x86_64-linux"] ./chrony-ptp.nix {}; cinnamon = handleTest ./cinnamon.nix {}; + cinnamon-wayland = handleTest ./cinnamon-wayland.nix {}; cjdns = handleTest ./cjdns.nix {}; clickhouse = handleTest ./clickhouse.nix {}; cloud-init = handleTest ./cloud-init.nix {}; @@ -191,7 +197,6 @@ in { cntr = handleTestOn ["aarch64-linux" "x86_64-linux"] ./cntr.nix {}; cockpit = handleTest ./cockpit.nix {}; cockroachdb = handleTestOn ["x86_64-linux"] ./cockroachdb.nix {}; - code-server = handleTest ./code-server.nix {}; coder = handleTest ./coder.nix {}; collectd = handleTest ./collectd.nix {}; connman = handleTest ./connman.nix {}; @@ -251,6 +256,7 @@ in { domination = handleTest ./domination.nix {}; dovecot = handleTest ./dovecot.nix {}; drbd = handleTest ./drbd.nix {}; + dublin-traceroute = handleTest ./dublin-traceroute.nix {}; earlyoom = handleTestOn ["x86_64-linux"] ./earlyoom.nix {}; early-mount-options = handleTest ./early-mount-options.nix {}; ec2-config = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-config or {}; @@ -319,6 +325,7 @@ in { mimir = handleTest ./mimir.nix {}; garage = handleTest ./garage {}; gemstash = handleTest ./gemstash.nix {}; + geoserver = runTest ./geoserver.nix; gerrit = handleTest ./gerrit.nix {}; geth = handleTest ./geth.nix {}; ghostunnel = handleTest ./ghostunnel.nix {}; @@ -330,6 +337,7 @@ in { gitolite-fcgiwrap = handleTest ./gitolite-fcgiwrap.nix {}; glusterfs = handleTest ./glusterfs.nix {}; gnome = handleTest ./gnome.nix {}; + gnome-extensions = handleTest ./gnome-extensions.nix {}; gnome-flashback = handleTest ./gnome-flashback.nix {}; gnome-xorg = handleTest ./gnome-xorg.nix {}; gnupg = handleTest ./gnupg.nix {}; @@ -351,6 +359,7 @@ in { grow-partition = runTest ./grow-partition.nix; grub = handleTest ./grub.nix {}; guacamole-server = handleTest ./guacamole-server.nix {}; + guix = handleTest ./guix {}; gvisor = handleTest ./gvisor.nix {}; hadoop = import ./hadoop { inherit handleTestOn; package=pkgs.hadoop; }; hadoop_3_2 = import ./hadoop { inherit handleTestOn; package=pkgs.hadoop_3_2; }; @@ -616,6 +625,7 @@ in { openstack-image-userdata = (handleTestOn ["x86_64-linux"] ./openstack-image.nix {}).userdata or {}; opentabletdriver = handleTest ./opentabletdriver.nix {}; opentelemetry-collector = handleTest ./opentelemetry-collector.nix {}; + ocsinventory-agent = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./ocsinventory-agent.nix {}; owncast = handleTest ./owncast.nix {}; outline = handleTest ./outline.nix {}; image-contents = handleTest ./image-contents.nix {}; @@ -656,6 +666,7 @@ in { phylactery = handleTest ./web-apps/phylactery.nix {}; pict-rs = handleTest ./pict-rs.nix {}; pinnwand = handleTest ./pinnwand.nix {}; + plantuml-server = handleTest ./plantuml-server.nix {}; plasma-bigscreen = handleTest ./plasma-bigscreen.nix {}; plasma5 = handleTest ./plasma5.nix {}; plasma5-systemd-start = handleTest ./plasma5-systemd-start.nix {}; @@ -737,6 +748,7 @@ in { sddm = handleTest ./sddm.nix {}; seafile = handleTest ./seafile.nix {}; searx = handleTest ./searx.nix {}; + seatd = handleTest ./seatd.nix {}; service-runner = handleTest ./service-runner.nix {}; sftpgo = runTest ./sftpgo.nix; sfxr-qt = handleTest ./sfxr-qt.nix {}; @@ -748,6 +760,7 @@ in { signal-desktop = handleTest ./signal-desktop.nix {}; simple = handleTest ./simple.nix {}; sing-box = handleTest ./sing-box.nix {}; + slimserver = handleTest ./slimserver.nix {}; slurm = handleTest ./slurm.nix {}; smokeping = handleTest ./smokeping.nix {}; snapcast = handleTest ./snapcast.nix {}; @@ -758,6 +771,7 @@ in { sogo = handleTest ./sogo.nix {}; solanum = handleTest ./solanum.nix {}; sonarr = handleTest ./sonarr.nix {}; + sonic-server = handleTest ./sonic-server.nix {}; sourcehut = handleTest ./sourcehut.nix {}; spacecookie = handleTest ./spacecookie.nix {}; spark = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./spark {}; @@ -804,6 +818,7 @@ in { systemd-initrd-luks-empty-passphrase = handleTest ./initrd-luks-empty-passphrase.nix { systemdStage1 = true; }; systemd-initrd-luks-password = handleTest ./systemd-initrd-luks-password.nix {}; systemd-initrd-luks-tpm2 = handleTest ./systemd-initrd-luks-tpm2.nix {}; + systemd-initrd-luks-unl0kr = handleTest ./systemd-initrd-luks-unl0kr.nix {}; systemd-initrd-modprobe = handleTest ./systemd-initrd-modprobe.nix {}; systemd-initrd-shutdown = handleTest ./systemd-shutdown.nix { systemdStage1 = true; }; systemd-initrd-simple = handleTest ./systemd-initrd-simple.nix {}; @@ -920,6 +935,7 @@ in { xmonad-xdg-autostart = handleTest ./xmonad-xdg-autostart.nix {}; xpadneo = handleTest ./xpadneo.nix {}; xrdp = handleTest ./xrdp.nix {}; + xscreensaver = handleTest ./xscreensaver.nix {}; xss-lock = handleTest ./xss-lock.nix {}; xterm = handleTest ./xterm.nix {}; xxh = handleTest ./xxh.nix {}; diff --git a/nixos/tests/amazon-ssm-agent.nix b/nixos/tests/amazon-ssm-agent.nix new file mode 100644 index 000000000000..957e9e0e02c5 --- /dev/null +++ b/nixos/tests/amazon-ssm-agent.nix @@ -0,0 +1,17 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: { + name = "amazon-ssm-agent"; + meta.maintainers = [ lib.maintainers.anthonyroussel ]; + + nodes.machine = { config, pkgs, ... }: { + services.amazon-ssm-agent.enable = true; + }; + + testScript = '' + start_all() + + machine.wait_for_file("/etc/amazon/ssm/seelog.xml") + machine.wait_for_file("/etc/amazon/ssm/amazon-ssm-agent.json") + + machine.wait_for_unit("amazon-ssm-agent.service") + ''; +}) diff --git a/nixos/tests/anki-sync-server.nix b/nixos/tests/anki-sync-server.nix new file mode 100644 index 000000000000..7d08cc9cb878 --- /dev/null +++ b/nixos/tests/anki-sync-server.nix @@ -0,0 +1,71 @@ +import ./make-test-python.nix ({ pkgs, ... }: + let + ankiSyncTest = pkgs.writeScript "anki-sync-test.py" '' + #!${pkgs.python3}/bin/python + + import sys + + # get site paths from anki itself + from runpy import run_path + run_path("${pkgs.anki}/bin/.anki-wrapped") + import anki + + col = anki.collection.Collection('test_collection') + endpoint = 'http://localhost:27701' + + # Sanity check: verify bad login fails + try: + col.sync_login('baduser', 'badpass', endpoint) + print("bad user login worked?!") + sys.exit(1) + except anki.errors.SyncError: + pass + + # test logging in to users + col.sync_login('user', 'password', endpoint) + col.sync_login('passfileuser', 'passfilepassword', endpoint) + + # Test actual sync. login apparently doesn't remember the endpoint... + login = col.sync_login('user', 'password', endpoint) + login.endpoint = endpoint + sync = col.sync_collection(login, False) + assert sync.required == sync.NO_CHANGES + # TODO: create an archive with server content including a test card + # and check we got it? + ''; + testPasswordFile = pkgs.writeText "anki-password" "passfilepassword"; + in + { + name = "anki-sync-server"; + meta = with pkgs.lib.maintainers; { + maintainers = [ martinetd ]; + }; + + nodes.machine = { pkgs, ...}: { + services.anki-sync-server = { + enable = true; + users = [ + { username = "user"; + password = "password"; + } + { username = "passfileuser"; + passwordFile = testPasswordFile; + } + ]; + }; + }; + + + testScript = + '' + start_all() + + with subtest("Server starts successfully"): + # service won't start without users + machine.wait_for_unit("anki-sync-server.service") + machine.wait_for_open_port(27701) + + with subtest("Can sync"): + machine.succeed("${ankiSyncTest}") + ''; +}) diff --git a/nixos/tests/archi.nix b/nixos/tests/archi.nix new file mode 100644 index 000000000000..59f2e940c005 --- /dev/null +++ b/nixos/tests/archi.nix @@ -0,0 +1,31 @@ +import ./make-test-python.nix ({ lib, ... }: { + name = "archi"; + meta.maintainers = with lib.maintainers; [ paumr ]; + + nodes.machine = { pkgs, ... }: { + imports = [ + ./common/x11.nix + ]; + + environment.systemPackages = with pkgs; [ archi ]; + }; + + enableOCR = true; + + testScript = '' + machine.wait_for_x() + + with subtest("createEmptyModel via CLI"): + machine.succeed("Archi -application com.archimatetool.commandline.app -consoleLog -nosplash --createEmptyModel --saveModel smoke.archimate") + machine.copy_from_vm("smoke.archimate", "") + + with subtest("UI smoketest"): + machine.succeed("DISPLAY=:0 Archi --createEmptyModel >&2 &") + machine.wait_for_window("Archi") + + # wait till main UI is open + machine.wait_for_text("Welcome to Archi") + + machine.screenshot("welcome-screen") + ''; +}) diff --git a/nixos/tests/auth-mysql.nix b/nixos/tests/auth-mysql.nix index 0ed4b050a69a..77a69eb1cd58 100644 --- a/nixos/tests/auth-mysql.nix +++ b/nixos/tests/auth-mysql.nix @@ -84,7 +84,7 @@ in getpwuid = '' SELECT name, 'x', uid, gid, name, CONCAT('/home/', name), "/run/current-system/sw/bin/bash" \ FROM users \ - WHERE id=%1$u \ + WHERE uid=%1$u \ LIMIT 1 ''; getspnam = '' @@ -140,6 +140,7 @@ in machine.wait_for_unit("multi-user.target") machine.wait_for_unit("mysql.service") + machine.wait_until_succeeds("cat /etc/security/pam_mysql.conf | grep users.db_passwd") machine.wait_until_succeeds("pgrep -f 'agetty.*tty1'") with subtest("Local login"): diff --git a/nixos/tests/ayatana-indicators.nix b/nixos/tests/ayatana-indicators.nix new file mode 100644 index 000000000000..bc7ff75f390f --- /dev/null +++ b/nixos/tests/ayatana-indicators.nix @@ -0,0 +1,71 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: let + user = "alice"; +in { + name = "ayatana-indicators"; + + meta = { + maintainers = with lib.maintainers; [ OPNA2608 ]; + }; + + nodes.machine = { config, ... }: { + imports = [ + ./common/auto.nix + ./common/user-account.nix + ]; + + test-support.displayManager.auto = { + enable = true; + inherit user; + }; + + services.xserver = { + enable = true; + desktopManager.mate.enable = true; + displayManager.defaultSession = lib.mkForce "mate"; + }; + + services.ayatana-indicators = { + enable = true; + packages = with pkgs; [ + ayatana-indicator-messages + ]; + }; + + # Services needed by some indicators + services.accounts-daemon.enable = true; # messages + }; + + # TODO session indicator starts up in a semi-broken state, but works fine after a restart. maybe being started before graphical session is truly up & ready? + testScript = { nodes, ... }: let + runCommandPerIndicatorService = command: lib.strings.concatMapStringsSep "\n" command nodes.machine.systemd.user.targets."ayatana-indicators".wants; + in '' + start_all() + machine.wait_for_x() + + # Desktop environment should reach graphical-session.target + machine.wait_for_unit("graphical-session.target", "${user}") + + # MATE relies on XDG autostart to bring up the indicators. + # Not sure *when* XDG autostart fires them up, and awaiting pgrep success seems to misbehave? + machine.sleep(10) + + # Now check if all indicators were brought up successfully, and kill them for later + '' + (runCommandPerIndicatorService (service: let serviceExec = builtins.replaceStrings [ "." ] [ "-" ] service; in '' + machine.succeed("pgrep -f ${serviceExec}") + machine.succeed("pkill -f ${serviceExec}") + '')) + '' + + # Ayatana target is the preferred way of starting up indicators on SystemD session, the graphical session is responsible for starting this if it supports them. + # Mate currently doesn't do this, so start it manually for checking (https://github.com/mate-desktop/mate-indicator-applet/issues/63) + machine.systemctl("start ayatana-indicators.target", "${user}") + machine.wait_for_unit("ayatana-indicators.target", "${user}") + + # Let all indicator services do their startups, potential post-launch crash & restart cycles so we can properly check for failures + # Not sure if there's a better way of awaiting this without false-positive potential + machine.sleep(10) + + # Now check if all indicator services were brought up successfully + '' + runCommandPerIndicatorService (service: '' + machine.wait_for_unit("${service}", "${user}") + ''); +}) diff --git a/nixos/tests/centrifugo.nix b/nixos/tests/centrifugo.nix new file mode 100644 index 000000000000..45c2904f5585 --- /dev/null +++ b/nixos/tests/centrifugo.nix @@ -0,0 +1,80 @@ +let + redisPort = 6379; + centrifugoPort = 8080; + nodes = [ + "centrifugo1" + "centrifugo2" + "centrifugo3" + ]; +in +{ lib, ... }: { + name = "centrifugo"; + meta.maintainers = [ lib.maintainers.tie ]; + + nodes = lib.listToAttrs (lib.imap0 + (index: name: { + inherit name; + value = { config, ... }: { + services.centrifugo = { + enable = true; + settings = { + inherit name; + port = centrifugoPort; + # See https://centrifugal.dev/docs/server/engines#redis-sharding + engine = "redis"; + # Connect to local Redis shard via Unix socket. + redis_address = + let + otherNodes = lib.take index nodes ++ lib.drop (index + 1) nodes; + in + map (name: "${name}:${toString redisPort}") otherNodes ++ [ + "unix://${config.services.redis.servers.centrifugo.unixSocket}" + ]; + usage_stats_disable = true; + api_insecure = true; + }; + extraGroups = [ + config.services.redis.servers.centrifugo.user + ]; + }; + services.redis.servers.centrifugo = { + enable = true; + bind = null; # all interfaces + port = redisPort; + openFirewall = true; + settings.protected-mode = false; + }; + }; + }) + nodes); + + testScript = '' + import json + + redisPort = ${toString redisPort} + centrifugoPort = ${toString centrifugoPort} + + start_all() + + for machine in machines: + machine.wait_for_unit("redis-centrifugo.service") + machine.wait_for_open_port(redisPort) + + for machine in machines: + machine.wait_for_unit("centrifugo.service") + machine.wait_for_open_port(centrifugoPort) + + # See https://centrifugal.dev/docs/server/server_api#info + def list_nodes(machine): + curl = "curl --fail-with-body --silent" + body = "{}" + resp = json.loads(machine.succeed(f"{curl} -d '{body}' http://localhost:{centrifugoPort}/api/info")) + return resp["result"]["nodes"] + machineNames = {m.name for m in machines} + for machine in machines: + nodes = list_nodes(machine) + assert len(nodes) == len(machines) + nodeNames = {n['name'] for n in nodes} + assert machineNames == nodeNames + ''; +} diff --git a/nixos/tests/cinnamon-wayland.nix b/nixos/tests/cinnamon-wayland.nix new file mode 100644 index 000000000000..58dddbbb0866 --- /dev/null +++ b/nixos/tests/cinnamon-wayland.nix @@ -0,0 +1,71 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "cinnamon-wayland"; + + meta.maintainers = lib.teams.cinnamon.members; + + nodes.machine = { nodes, ... }: { + imports = [ ./common/user-account.nix ]; + services.xserver.enable = true; + services.xserver.desktopManager.cinnamon.enable = true; + services.xserver.displayManager = { + autoLogin.enable = true; + autoLogin.user = nodes.machine.users.users.alice.name; + defaultSession = "cinnamon-wayland"; + }; + }; + + enableOCR = true; + + testScript = { nodes, ... }: + let + user = nodes.machine.users.users.alice; + env = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${toString user.uid}/bus"; + su = command: "su - ${user.name} -c '${env} ${command}'"; + + # Call javascript in cinnamon (the shell), returns a tuple (success, output), + # where `success` is true if the dbus call was successful and `output` is what + # the javascript evaluates to. + eval = name: su "gdbus call --session -d org.Cinnamon -o /org/Cinnamon -m org.Cinnamon.Eval ${name}"; + in + '' + machine.wait_for_unit("display-manager.service") + + with subtest("Wait for wayland server"): + machine.wait_for_file("/run/user/${toString user.uid}/wayland-0") + + with subtest("Check that logging in has given the user ownership of devices"): + machine.succeed("getfacl -p /dev/snd/timer | grep -q ${user.name}") + + with subtest("Wait for the Cinnamon shell"): + # Correct output should be (true, '2') + # https://github.com/linuxmint/cinnamon/blob/5.4.0/js/ui/main.js#L183-L187 + machine.wait_until_succeeds("${eval "Main.runState"} | grep -q 'true,..2'") + + with subtest("Check if Cinnamon components actually start"): + for i in ["csd-media-keys", "xapp-sn-watcher", "nemo-desktop"]: + machine.wait_until_succeeds(f"pgrep -f {i}") + machine.wait_until_succeeds("journalctl -b --grep 'Loaded applet menu@cinnamon.org'") + machine.wait_until_succeeds("journalctl -b --grep 'calendar@cinnamon.org: Calendar events supported'") + + with subtest("Open Cinnamon Settings"): + machine.succeed("${su "cinnamon-settings themes >&2 &"}") + machine.wait_until_succeeds("${eval "global.display.focus_window.wm_class"} | grep -i 'cinnamon-settings'") + machine.wait_for_text('(Style|Appearance|Color)') + machine.sleep(2) + machine.screenshot("cinnamon_settings") + + with subtest("Check if screensaver works"): + # This is not supported at the moment. + # https://trello.com/b/HHs01Pab/cinnamon-wayland + machine.execute("${su "cinnamon-screensaver-command -l >&2 &"}") + machine.wait_until_succeeds("journalctl -b --grep 'Cinnamon Screensaver is unavailable on Wayland'") + + with subtest("Open GNOME Terminal"): + machine.succeed("${su "dbus-launch gnome-terminal"}") + machine.wait_until_succeeds("${eval "global.display.focus_window.wm_class"} | grep -i 'gnome-terminal'") + machine.sleep(2) + + with subtest("Check if Cinnamon has ever coredumped"): + machine.fail("coredumpctl --json=short | grep -E 'cinnamon|nemo'") + ''; +}) diff --git a/nixos/tests/code-server.nix b/nixos/tests/code-server.nix deleted file mode 100644 index 7d523dfc617e..000000000000 --- a/nixos/tests/code-server.nix +++ /dev/null @@ -1,22 +0,0 @@ -import ./make-test-python.nix ({pkgs, lib, ...}: -{ - name = "code-server"; - - nodes = { - machine = {pkgs, ...}: { - services.code-server = { - enable = true; - auth = "none"; - }; - }; - }; - - testScript = '' - start_all() - machine.wait_for_unit("code-server.service") - machine.wait_for_open_port(4444) - machine.succeed("curl -k --fail http://localhost:4444", timeout=10) - ''; - - meta.maintainers = [ lib.maintainers.drupol ]; -}) diff --git a/nixos/tests/containers-ip.nix b/nixos/tests/containers-ip.nix index ecead5c22f75..ecff99a3f0c2 100644 --- a/nixos/tests/containers-ip.nix +++ b/nixos/tests/containers-ip.nix @@ -19,10 +19,7 @@ in import ./make-test-python.nix ({ pkgs, lib, ... }: { nodes.machine = { pkgs, ... }: { - imports = [ ../modules/installer/cd-dvd/channel.nix ]; - virtualisation = { - writableStore = true; - }; + virtualisation.writableStore = true; containers.webserver4 = webserverFor "10.231.136.1" "10.231.136.2"; containers.webserver6 = webserverFor "fc00::2" "fc00::1"; diff --git a/nixos/tests/convos.nix b/nixos/tests/convos.nix index 8fe5892da9e5..06d8d30fcb14 100644 --- a/nixos/tests/convos.nix +++ b/nixos/tests/convos.nix @@ -22,7 +22,6 @@ in testScript = '' machine.wait_for_unit("convos") machine.wait_for_open_port(${toString port}) - machine.succeed("journalctl -u convos | grep -q 'application available at.*${toString port}'") machine.succeed("curl -f http://localhost:${toString port}/") ''; }) diff --git a/nixos/tests/deepin.nix b/nixos/tests/deepin.nix index 7b2e2430f31c..d3ce79a535c1 100644 --- a/nixos/tests/deepin.nix +++ b/nixos/tests/deepin.nix @@ -36,12 +36,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { with subtest("Check that logging in has given the user ownership of devices"): machine.succeed("getfacl -p /dev/snd/timer | grep -q ${user.name}") - with subtest("Check if DDE wm chooser actually start"): - machine.wait_until_succeeds("pgrep -f dde-wm-chooser") - machine.wait_for_window("dde-wm-chooser") - machine.execute("pkill dde-wm-chooser") - - with subtest("Check if Deepin session components actually start"): machine.wait_until_succeeds("pgrep -f dde-session-daemon") machine.wait_for_window("dde-session-daemon") diff --git a/nixos/tests/dex-oidc.nix b/nixos/tests/dex-oidc.nix index 37275a97ef0f..e54ae18ca937 100644 --- a/nixos/tests/dex-oidc.nix +++ b/nixos/tests/dex-oidc.nix @@ -49,7 +49,7 @@ import ./make-test-python.nix ({ lib, ... }: { ensureUsers = [ { name = "dex"; - ensurePermissions = { "DATABASE dex" = "ALL PRIVILEGES"; }; + ensureDBOwnership = true; } ]; }; diff --git a/nixos/tests/dnscrypt-wrapper/default.nix b/nixos/tests/dnscrypt-wrapper/default.nix index 1c05376e097b..1a794931dc50 100644 --- a/nixos/tests/dnscrypt-wrapper/default.nix +++ b/nixos/tests/dnscrypt-wrapper/default.nix @@ -1,5 +1,15 @@ + { lib, pkgs, ... }: +let + snakeoil = import ../common/acme/server/snakeoil-certs.nix; + + hosts = lib.mkForce + { "fd::a" = [ "server" snakeoil.domain ]; + "fd::b" = [ "client" ]; + }; +in + { name = "dnscrypt-wrapper"; meta = with pkgs.lib.maintainers; { @@ -7,59 +17,122 @@ }; nodes = { - server = { lib, ... }: - { services.dnscrypt-wrapper = with builtins; + server = { + networking.hosts = hosts; + networking.interfaces.eth1.ipv6.addresses = lib.singleton + { address = "fd::a"; prefixLength = 64; }; + + services.dnscrypt-wrapper = { enable = true; - address = "192.168.1.1"; + address = "[::]"; + port = 5353; keys.expiration = 5; # days keys.checkInterval = 2; # min # The keypair was generated by the command: # dnscrypt-wrapper --gen-provider-keypair \ # --provider-name=2.dnscrypt-cert.server \ - # --ext-address=192.168.1.1:5353 - providerKey.public = toFile "public.key" (readFile ./public.key); - providerKey.secret = toFile "secret.key" (readFile ./secret.key); + providerKey.public = "${./public.key}"; + providerKey.secret = "${./secret.key}"; + }; + + # nameserver + services.bind.enable = true; + services.bind.zones = lib.singleton + { name = "."; + master = true; + file = pkgs.writeText "root.zone" '' + $TTL 3600 + . IN SOA example.org. admin.example.org. ( 1 3h 1h 1w 1d ) + . IN NS example.org. + example.org. IN AAAA 2001:db8::1 + ''; + }; + + # webserver + services.nginx.enable = true; + services.nginx.virtualHosts.${snakeoil.domain} = + { onlySSL = true; + listenAddresses = [ "localhost" ]; + sslCertificate = snakeoil.${snakeoil.domain}.cert; + sslCertificateKey = snakeoil.${snakeoil.domain}.key; + locations."/ip".extraConfig = '' + default_type text/plain; + return 200 "Ciao $remote_addr!\n"; + ''; }; - services.tinydns.enable = true; - services.tinydns.data = '' - ..:192.168.1.1:a - +it.works:1.2.3.4 - ''; - networking.firewall.allowedUDPPorts = [ 5353 ]; - networking.firewall.allowedTCPPorts = [ 5353 ]; - networking.interfaces.eth1.ipv4.addresses = lib.mkForce - [ { address = "192.168.1.1"; prefixLength = 24; } ]; + + # demultiplex HTTP and DNS from port 443 + services.sslh = + { enable = true; + method = "ev"; + settings.transparent = true; + settings.listen = lib.mkForce + [ { host = "server"; port = "443"; is_udp = false; } + { host = "server"; port = "443"; is_udp = true; } + ]; + settings.protocols = + [ # Send TLS to webserver (TCP) + { name = "tls"; host= "localhost"; port= "443"; } + # Send DNSCrypt to dnscrypt-wrapper (TCP or UDP) + { name = "anyprot"; host = "localhost"; port = "5353"; } + { name = "anyprot"; host = "localhost"; port = "5353"; is_udp = true;} + ]; + }; + + networking.firewall.allowedTCPPorts = [ 443 ]; + networking.firewall.allowedUDPPorts = [ 443 ]; }; - client = { lib, ... }: - { services.dnscrypt-proxy2.enable = true; - services.dnscrypt-proxy2.upstreamDefaults = false; - services.dnscrypt-proxy2.settings = { - server_names = [ "server" ]; - static.server.stamp = "sdns://AQAAAAAAAAAAEDE5Mi4xNjguMS4xOjUzNTMgFEHYOv0SCKSuqR5CDYa7-58cCBuXO2_5uTSVU9wNQF0WMi5kbnNjcnlwdC1jZXJ0LnNlcnZlcg"; + client = { + networking.hosts = hosts; + networking.interfaces.eth1.ipv6.addresses = lib.singleton + { address = "fd::b"; prefixLength = 64; }; + + services.dnscrypt-proxy2.enable = true; + services.dnscrypt-proxy2.upstreamDefaults = false; + services.dnscrypt-proxy2.settings = + { server_names = [ "server" ]; + listen_addresses = [ "[::1]:53" ]; + cache = false; + # Computed using https://dnscrypt.info/stamps/ + static.server.stamp = + "sdns://AQAAAAAAAAAADzE5Mi4xNjguMS4yOjQ0MyAUQdg6" + +"_RIIpK6pHkINhrv7nxwIG5c7b_m5NJVT3A1AXRYyLmRuc2NyeXB0LWNlcnQuc2VydmVy"; }; - networking.nameservers = [ "127.0.0.1" ]; - networking.interfaces.eth1.ipv4.addresses = lib.mkForce - [ { address = "192.168.1.2"; prefixLength = 24; } ]; - }; + networking.nameservers = [ "::1" ]; + security.pki.certificateFiles = [ snakeoil.ca.cert ]; + }; }; testScript = '' - start_all() - with subtest("The server can generate the ephemeral keypair"): server.wait_for_unit("dnscrypt-wrapper") server.wait_for_file("/var/lib/dnscrypt-wrapper/2.dnscrypt-cert.server.key") server.wait_for_file("/var/lib/dnscrypt-wrapper/2.dnscrypt-cert.server.crt") almost_expiration = server.succeed("date --date '4days 23 hours 56min'").strip() - with subtest("The client can connect to the server"): - server.wait_for_unit("tinydns") - client.wait_for_unit("dnscrypt-proxy2") - assert "1.2.3.4" in client.wait_until_succeeds( - "host it.works" - ), "The IP address of 'it.works' does not match 1.2.3.4" + with subtest("The DNSCrypt client can connect to the server"): + server.wait_for_unit("sslh") + client.wait_until_succeeds("journalctl -u dnscrypt-proxy2 --grep '\[server\] OK'") + + with subtest("HTTP client can connect to the server"): + server.wait_for_unit("nginx") + client.succeed("curl -s --fail https://${snakeoil.domain}/ip | grep -q fd::b") + + with subtest("DNS queries over UDP are working"): + server.wait_for_unit("bind") + client.wait_for_open_port(53) + assert "2001:db8::1" in client.wait_until_succeeds( + "host -U example.org" + ), "The IP address of 'example.org' does not match 2001:db8::1" + + with subtest("DNS queries over TCP are working"): + server.wait_for_unit("bind") + client.wait_for_open_port(53) + assert "2001:db8::1" in client.wait_until_succeeds( + "host -T example.org" + ), "The IP address of 'example.org' does not match 2001:db8::1" with subtest("The server rotates the ephemeral keys"): # advance time by a little less than 5 days @@ -68,7 +141,8 @@ server.wait_for_file("/var/lib/dnscrypt-wrapper/oldkeys") with subtest("The client can still connect to the server"): - server.wait_for_unit("dnscrypt-wrapper") - client.succeed("host it.works") + client.systemctl("restart dnscrypt-proxy2") + client.wait_until_succeeds("host -T example.org") + client.wait_until_succeeds("host -U example.org") ''; } diff --git a/nixos/tests/dublin-traceroute.nix b/nixos/tests/dublin-traceroute.nix new file mode 100644 index 000000000000..b359b7fcdd6f --- /dev/null +++ b/nixos/tests/dublin-traceroute.nix @@ -0,0 +1,63 @@ +# This is a simple distributed test involving a topology with two +# separate virtual networks - the "inside" and the "outside" - with a +# client on the inside network, a server on the outside network, and a +# router connected to both that performs Network Address Translation +# for the client. +import ./make-test-python.nix ({ pkgs, lib, ... }: + let + routerBase = + lib.mkMerge [ + { virtualisation.vlans = [ 2 1 ]; + networking.nftables.enable = true; + networking.nat.internalIPs = [ "192.168.1.0/24" ]; + networking.nat.externalInterface = "eth1"; + } + ]; + in + { + name = "dublin-traceroute"; + meta = with pkgs.lib.maintainers; { + maintainers = [ baloo ]; + }; + + nodes.client = { nodes, ... }: { + imports = [ ./common/user-account.nix ]; + virtualisation.vlans = [ 1 ]; + + networking.defaultGateway = + (builtins.head nodes.router.networking.interfaces.eth2.ipv4.addresses).address; + networking.nftables.enable = true; + + programs.dublin-traceroute.enable = true; + }; + + nodes.router = { ... }: { + virtualisation.vlans = [ 2 1 ]; + networking.nftables.enable = true; + networking.nat.internalIPs = [ "192.168.1.0/24" ]; + networking.nat.externalInterface = "eth1"; + networking.nat.enable = true; + }; + + nodes.server = { ... }: { + virtualisation.vlans = [ 2 ]; + networking.firewall.enable = false; + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + services.vsftpd.enable = true; + services.vsftpd.anonymousUser = true; + }; + + testScript = '' + client.start() + router.start() + server.start() + + server.wait_for_unit("network.target") + router.wait_for_unit("network.target") + client.wait_for_unit("network.target") + + # Make sure we can trace from an unprivileged user + client.succeed("sudo -u alice dublin-traceroute server") + ''; + }) diff --git a/nixos/tests/elk.nix b/nixos/tests/elk.nix index 0122bc440361..900ea6320100 100644 --- a/nixos/tests/elk.nix +++ b/nixos/tests/elk.nix @@ -119,11 +119,6 @@ let package = elk.elasticsearch; }; - kibana = { - enable = true; - package = elk.kibana; - }; - elasticsearch-curator = { enable = true; actionYAML = '' @@ -217,13 +212,6 @@ let one.wait_until_succeeds("cat /tmp/logstash.out | grep flowers") one.wait_until_succeeds("cat /tmp/logstash.out | grep -v dragons") - with subtest("Kibana is healthy"): - one.wait_for_unit("kibana.service") - one.wait_until_succeeds( - "curl --silent --show-error --fail-with-body 'http://localhost:5601/api/status'" - + " | jq -es 'if . == [] then null else .[] | .status.overall.state == \"green\" end'" - ) - with subtest("Metricbeat is running"): one.wait_for_unit("metricbeat.service") @@ -274,7 +262,6 @@ in { # name = "elk-7"; # elasticsearch = pkgs.elasticsearch7-oss; # logstash = pkgs.logstash7-oss; - # kibana = pkgs.kibana7-oss; # filebeat = pkgs.filebeat7; # metricbeat = pkgs.metricbeat7; # }; @@ -282,7 +269,6 @@ in { ELK-7 = mkElkTest "elk-7" { elasticsearch = pkgs.elasticsearch7; logstash = pkgs.logstash7; - kibana = pkgs.kibana7; filebeat = pkgs.filebeat7; metricbeat = pkgs.metricbeat7; }; diff --git a/nixos/tests/eris-server.nix b/nixos/tests/eris-server.nix index a50db3afebf5..b9d2b57401e0 100644 --- a/nixos/tests/eris-server.nix +++ b/nixos/tests/eris-server.nix @@ -3,7 +3,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { meta.maintainers = with lib.maintainers; [ ehmry ]; nodes.server = { - environment.systemPackages = [ pkgs.eris-go pkgs.nim.pkgs.eris ]; + environment.systemPackages = [ pkgs.eris-go pkgs.eriscmd ]; services.eris-server = { enable = true; decode = true; diff --git a/nixos/tests/ferretdb.nix b/nixos/tests/ferretdb.nix index 9ad7397ade80..7251198af77d 100644 --- a/nixos/tests/ferretdb.nix +++ b/nixos/tests/ferretdb.nix @@ -39,7 +39,7 @@ with import ../lib/testing-python.nix { inherit system; }; ensureDatabases = [ "ferretdb" ]; ensureUsers = [{ name = "ferretdb"; - ensurePermissions."DATABASE ferretdb" = "ALL PRIVILEGES"; + ensureDBOwnership = true; }]; }; diff --git a/nixos/tests/freshrss-pgsql.nix b/nixos/tests/freshrss-pgsql.nix index 055bd51ed43d..c685f4a8159b 100644 --- a/nixos/tests/freshrss-pgsql.nix +++ b/nixos/tests/freshrss-pgsql.nix @@ -22,9 +22,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { ensureUsers = [ { name = "freshrss"; - ensurePermissions = { - "DATABASE freshrss" = "ALL PRIVILEGES"; - }; + ensureDBOwnership = true; } ]; initialScript = pkgs.writeText "postgresql-password" '' diff --git a/nixos/tests/geoserver.nix b/nixos/tests/geoserver.nix new file mode 100644 index 000000000000..7e5507a296ea --- /dev/null +++ b/nixos/tests/geoserver.nix @@ -0,0 +1,24 @@ +{ pkgs, lib, ... }: { + + name = "geoserver"; + meta = { + maintainers = with lib; [ teams.geospatial.members ]; + }; + + nodes = { + machine = { pkgs, ... }: { + virtualisation.diskSize = 2 * 1024; + + environment.systemPackages = [ pkgs.geoserver ]; + }; + }; + + testScript = '' + start_all() + + machine.execute("${pkgs.geoserver}/bin/geoserver-startup > /dev/null 2>&1 &") + machine.wait_until_succeeds("curl --fail --connect-timeout 2 http://localhost:8080/geoserver", timeout=60) + + machine.succeed("curl --fail --connect-timeout 2 http://localhost:8080/geoserver/ows?service=WMS&version=1.3.0&request=GetCapabilities") + ''; +} diff --git a/nixos/tests/gnome-extensions.nix b/nixos/tests/gnome-extensions.nix new file mode 100644 index 000000000000..2faff9a4a80d --- /dev/null +++ b/nixos/tests/gnome-extensions.nix @@ -0,0 +1,151 @@ +import ./make-test-python.nix ( +{ pkgs, lib, ...}: +{ + name = "gnome-extensions"; + meta.maintainers = [ lib.maintainers.piegames ]; + + nodes.machine = + { pkgs, ... }: + { + imports = [ ./common/user-account.nix ]; + + # Install all extensions + environment.systemPackages = lib.filter (e: e ? extensionUuid) (lib.attrValues pkgs.gnomeExtensions); + + # Some extensions are broken, but that's kind of the point of a testing VM + nixpkgs.config.allowBroken = true; + # There are some aliases which throw exceptions; ignore them. + # Also prevent duplicate extensions under different names. + nixpkgs.config.allowAliases = false; + + # Configure GDM + services.xserver.enable = true; + services.xserver.displayManager = { + gdm = { + enable = true; + debug = true; + wayland = true; + }; + autoLogin = { + enable = true; + user = "alice"; + }; + }; + + # Configure Gnome + services.xserver.desktopManager.gnome.enable = true; + services.xserver.desktopManager.gnome.debug = true; + + systemd.user.services = { + "org.gnome.Shell@wayland" = { + serviceConfig = { + ExecStart = [ + # Clear the list before overriding it. + "" + # Eval API is now internal so Shell needs to run in unsafe mode. + # TODO: improve test driver so that it supports openqa-like manipulation + # that would allow us to drop this mess. + "${pkgs.gnome.gnome-shell}/bin/gnome-shell --unsafe-mode" + ]; + }; + }; + }; + + }; + + testScript = { nodes, ... }: let + # Keep line widths somewhat manageable + user = nodes.machine.users.users.alice; + uid = toString user.uid; + bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${uid}/bus"; + # Run a command in the appropriate user environment + run = command: "su - ${user.name} -c '${bus} ${command}'"; + + # Call javascript in gnome shell, returns a tuple (success, output), where + # `success` is true if the dbus call was successful and output is what the + # javascript evaluates to. + eval = command: run "gdbus call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval ${command}"; + + # False when startup is done + startingUp = eval "Main.layoutManager._startingUp"; + + # Extensions to keep always enabled together + # Those are extensions that are usually always on for many users, and that we expect to work + # well together with most others without conflicts + alwaysOnExtensions = map (name: pkgs.gnomeExtensions.${name}.extensionUuid) [ + "applications-menu" + "user-themes" + ]; + + # Extensions to enable and disable individually + # Extensions like dash-to-dock and dash-to-panel cannot be enabled at the same time. + testExtensions = map (name: pkgs.gnomeExtensions.${name}.extensionUuid) [ + "appindicator" + "dash-to-dock" + "dash-to-panel" + "ddterm" + "emoji-selector" + "gsconnect" + "system-monitor" + "desktop-icons-ng-ding" + "workspace-indicator" + "vitals" + ]; + in + '' + with subtest("Login to GNOME with GDM"): + # wait for gdm to start + machine.wait_for_unit("display-manager.service") + # wait for the wayland server + machine.wait_for_file("/run/user/${uid}/wayland-0") + # wait for alice to be logged in + machine.wait_for_unit("default.target", "${user.name}") + # check that logging in has given the user ownership of devices + assert "alice" in machine.succeed("getfacl -p /dev/snd/timer") + + with subtest("Wait for GNOME Shell"): + # correct output should be (true, 'false') + machine.wait_until_succeeds( + "${startingUp} | grep -q 'true,..false'" + ) + + # Close the Activities view so that Shell can correctly track the focused window. + machine.send_key("esc") + # # Disable extension version validation (only use for manual testing) + # machine.succeed( + # "${run "gsettings set org.gnome.shell disable-extension-version-validation true"}" + # ) + + # Assert that some extension is in a specific state + def checkState(target, extension): + state = machine.succeed( + f"${run "gnome-extensions info {extension}"} | grep '^ State: .*$'" + ) + assert target in state, f"{state} instead of {target}" + + def checkExtension(extension, disable): + with subtest(f"Enable extension '{extension}'"): + # Check that the extension is properly initialized; skip out of date ones + state = machine.succeed( + f"${run "gnome-extensions info {extension}"} | grep '^ State: .*$'" + ) + if "OUT OF DATE" in state: + machine.log(f"Extension {extension} will be skipped because out of date") + return + + assert "INITIALIZED" in state, f"{state} instead of INITIALIZED" + + # Enable and optionally disable + + machine.succeed(f"${run "gnome-extensions enable {extension}"}") + checkState("ENABLED", extension) + + if disable: + machine.succeed(f"${run "gnome-extensions disable {extension}"}") + checkState("DISABLED", extension) + '' + + lib.concatLines (map (e: ''checkExtension("${e}", False)'') alwaysOnExtensions) + + lib.concatLines (map (e: ''checkExtension("${e}", True)'') testExtensions) + ; +} +) diff --git a/nixos/tests/gnome-xorg.nix b/nixos/tests/gnome-xorg.nix index 7762fff5c3a2..6ca700edcac3 100644 --- a/nixos/tests/gnome-xorg.nix +++ b/nixos/tests/gnome-xorg.nix @@ -5,7 +5,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { }; nodes.machine = { nodes, ... }: let - user = nodes.machine.config.users.users.alice; + user = nodes.machine.users.users.alice; in { imports = [ ./common/user-account.nix ]; @@ -43,28 +43,28 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { }; testScript = { nodes, ... }: let - user = nodes.machine.config.users.users.alice; + user = nodes.machine.users.users.alice; uid = toString user.uid; bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${uid}/bus"; xauthority = "/run/user/${uid}/gdm/Xauthority"; display = "DISPLAY=:0.0"; env = "${bus} XAUTHORITY=${xauthority} ${display}"; - gdbus = "${env} gdbus"; - su = command: "su - ${user.name} -c '${env} ${command}'"; + # Run a command in the appropriate user environment + run = command: "su - ${user.name} -c '${bus} ${command}'"; # Call javascript in gnome shell, returns a tuple (success, output), where # `success` is true if the dbus call was successful and output is what the # javascript evaluates to. - eval = "call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval"; + eval = command: run "gdbus call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval ${command}"; # False when startup is done - startingUp = su "${gdbus} ${eval} Main.layoutManager._startingUp"; + startingUp = eval "Main.layoutManager._startingUp"; # Start Console - launchConsole = su "${bus} gapplication launch org.gnome.Console"; + launchConsole = run "gapplication launch org.gnome.Console"; # Hopefully Console's wm class - wmClass = su "${gdbus} ${eval} global.display.focus_window.wm_class"; + wmClass = eval "global.display.focus_window.wm_class"; in '' with subtest("Login to GNOME Xorg with GDM"): machine.wait_for_x() diff --git a/nixos/tests/gnome.nix b/nixos/tests/gnome.nix index 448a3350240c..91182790cb24 100644 --- a/nixos/tests/gnome.nix +++ b/nixos/tests/gnome.nix @@ -40,25 +40,25 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { testScript = { nodes, ... }: let # Keep line widths somewhat manageable - user = nodes.machine.config.users.users.alice; + user = nodes.machine.users.users.alice; uid = toString user.uid; bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${uid}/bus"; - gdbus = "${bus} gdbus"; - su = command: "su - ${user.name} -c '${command}'"; + # Run a command in the appropriate user environment + run = command: "su - ${user.name} -c '${bus} ${command}'"; # Call javascript in gnome shell, returns a tuple (success, output), where # `success` is true if the dbus call was successful and output is what the # javascript evaluates to. - eval = "call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval"; + eval = command: run "gdbus call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval ${command}"; # False when startup is done - startingUp = su "${gdbus} ${eval} Main.layoutManager._startingUp"; + startingUp = eval "Main.layoutManager._startingUp"; # Start Console - launchConsole = su "${bus} gapplication launch org.gnome.Console"; + launchConsole = run "gapplication launch org.gnome.Console"; # Hopefully Console's wm class - wmClass = su "${gdbus} ${eval} global.display.focus_window.wm_class"; + wmClass = eval "global.display.focus_window.wm_class"; in '' with subtest("Login to GNOME with GDM"): # wait for gdm to start diff --git a/nixos/tests/grafana/basic.nix b/nixos/tests/grafana/basic.nix index 8bf4caad7fbf..dd389bc8a3d1 100644 --- a/nixos/tests/grafana/basic.nix +++ b/nixos/tests/grafana/basic.nix @@ -55,7 +55,7 @@ let ensureDatabases = [ "grafana" ]; ensureUsers = [{ name = "grafana"; - ensurePermissions."DATABASE grafana" = "ALL PRIVILEGES"; + ensureDBOwnership = true; }]; }; systemd.services.grafana.after = [ "postgresql.service" ]; diff --git a/nixos/tests/guix/basic.nix b/nixos/tests/guix/basic.nix new file mode 100644 index 000000000000..7f90bdeeb1e0 --- /dev/null +++ b/nixos/tests/guix/basic.nix @@ -0,0 +1,38 @@ +# Take note the Guix store directory is empty. Also, we're trying to prevent +# Guix from trying to downloading substitutes because of the restricted +# access (assuming it's in a sandboxed environment). +# +# So this test is what it is: a basic test while trying to use Guix as much as +# we possibly can (including the API) without triggering its download alarm. + +import ../make-test-python.nix ({ lib, pkgs, ... }: { + name = "guix-basic"; + meta.maintainers = with lib.maintainers; [ foo-dogsquared ]; + + nodes.machine = { config, ... }: { + environment.etc."guix/scripts".source = ./scripts; + services.guix.enable = true; + }; + + testScript = '' + import pathlib + + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("guix-daemon.service") + + # Can't do much here since the environment has restricted network access. + with subtest("Guix basic package management"): + machine.succeed("guix build --dry-run --verbosity=0 hello") + machine.succeed("guix show hello") + + # This is to see if the Guix API is usable and mostly working. + with subtest("Guix API scripting"): + scripts_dir = pathlib.Path("/etc/guix/scripts") + + text_msg = "Hello there, NixOS!" + text_store_file = machine.succeed(f"guix repl -- {scripts_dir}/create-file-to-store.scm '{text_msg}'") + assert machine.succeed(f"cat {text_store_file}") == text_msg + + machine.succeed(f"guix repl -- {scripts_dir}/add-existing-files-to-store.scm {scripts_dir}") + ''; +}) diff --git a/nixos/tests/guix/default.nix b/nixos/tests/guix/default.nix new file mode 100644 index 000000000000..a017668c05a7 --- /dev/null +++ b/nixos/tests/guix/default.nix @@ -0,0 +1,8 @@ +{ system ? builtins.currentSystem +, pkgs ? import ../../.. { inherit system; } +}: + +{ + basic = import ./basic.nix { inherit system pkgs; }; + publish = import ./publish.nix { inherit system pkgs; }; +} diff --git a/nixos/tests/guix/publish.nix b/nixos/tests/guix/publish.nix new file mode 100644 index 000000000000..6dbe8f99ebd6 --- /dev/null +++ b/nixos/tests/guix/publish.nix @@ -0,0 +1,95 @@ +# Testing out the substitute server with two machines in a local network. As a +# bonus, we'll also test a feature of the substitute server being able to +# advertise its service to the local network with Avahi. + +import ../make-test-python.nix ({ pkgs, lib, ... }: let + publishPort = 8181; + inherit (builtins) toString; +in { + name = "guix-publish"; + + meta.maintainers = with lib.maintainers; [ foo-dogsquared ]; + + nodes = let + commonConfig = { config, ... }: { + # We'll be using '--advertise' flag with the + # substitute server which requires Avahi. + services.avahi = { + enable = true; + nssmdns = true; + publish = { + enable = true; + userServices = true; + }; + }; + }; + in { + server = { config, lib, pkgs, ... }: { + imports = [ commonConfig ]; + + services.guix = { + enable = true; + publish = { + enable = true; + port = publishPort; + + generateKeyPair = true; + extraArgs = [ "--advertise" ]; + }; + }; + + networking.firewall.allowedTCPPorts = [ publishPort ]; + }; + + client = { config, lib, pkgs, ... }: { + imports = [ commonConfig ]; + + services.guix = { + enable = true; + + extraArgs = [ + # Force to only get all substitutes from the local server. We don't + # have anything in the Guix store directory and we cannot get + # anything from the official substitute servers anyways. + "--substitute-urls='http://server.local:${toString publishPort}'" + + # Enable autodiscovery of the substitute servers in the local + # network. This machine shouldn't need to import the signing key from + # the substitute server since it is automatically done anyways. + "--discover=yes" + ]; + }; + }; + }; + + testScript = '' + import pathlib + + start_all() + + scripts_dir = pathlib.Path("/etc/guix/scripts") + + for machine in machines: + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("guix-daemon.service") + machine.wait_for_unit("avahi-daemon.service") + + server.wait_for_unit("guix-publish.service") + server.wait_for_open_port(${toString publishPort}) + server.succeed("curl http://localhost:${toString publishPort}/") + + # Now it's the client turn to make use of it. + substitute_server = "http://server.local:${toString publishPort}" + client.wait_for_unit("network-online.target") + response = client.succeed(f"curl {substitute_server}") + assert "Guix Substitute Server" in response + + # Authorizing the server to be used as a substitute server. + client.succeed(f"curl -O {substitute_server}/signing-key.pub") + client.succeed("guix archive --authorize < ./signing-key.pub") + + # Since we're using the substitute server with the `--advertise` flag, we + # might as well check it. + client.succeed("avahi-browse --resolve --terminate _guix_publish._tcp | grep '_guix_publish._tcp'") + ''; +}) diff --git a/nixos/tests/guix/scripts/add-existing-files-to-store.scm b/nixos/tests/guix/scripts/add-existing-files-to-store.scm new file mode 100644 index 000000000000..fa47320b6a51 --- /dev/null +++ b/nixos/tests/guix/scripts/add-existing-files-to-store.scm @@ -0,0 +1,52 @@ +;; A simple script that adds each file given from the command-line into the +;; store and checks them if it's the same. +(use-modules (guix) + (srfi srfi-1) + (ice-9 ftw) + (rnrs io ports)) + +;; This is based from tests/derivations.scm from Guix source code. +(define* (directory-contents dir #:optional (slurp get-bytevector-all)) + "Return an alist representing the contents of DIR" + (define prefix-len (string-length dir)) + (sort (file-system-fold (const #t) + (lambda (path stat result) + (alist-cons (string-drop path prefix-len) + (call-with-input-file path slurp) + result)) + (lambda (path stat result) result) + (lambda (path stat result) result) + (lambda (path stat result) result) + (lambda (path stat errno result) result) + '() + dir) + (lambda (e1 e2) + (string<? (car e1) (car e2))))) + +(define* (check-if-same store drv path) + "Check if the given path and its store item are the same" + (let* ((filetype (stat:type (stat drv)))) + (case filetype + ((regular) + (and (valid-path? store drv) + (equal? (call-with-input-file path get-bytevector-all) + (call-with-input-file drv get-bytevector-all)))) + ((directory) + (and (valid-path? store drv) + (equal? (directory-contents path) + (directory-contents drv)))) + (else #f)))) + +(define* (add-and-check-item-to-store store path) + "Add PATH to STORE and check if the contents are the same" + (let* ((store-item (add-to-store store + (basename path) + #t "sha256" path)) + (is-same (check-if-same store store-item path))) + (if (not is-same) + (exit 1)))) + +(with-store store + (map (lambda (path) + (add-and-check-item-to-store store (readlink* path))) + (cdr (command-line)))) diff --git a/nixos/tests/guix/scripts/create-file-to-store.scm b/nixos/tests/guix/scripts/create-file-to-store.scm new file mode 100644 index 000000000000..467e4c4fd53f --- /dev/null +++ b/nixos/tests/guix/scripts/create-file-to-store.scm @@ -0,0 +1,8 @@ +;; A script that creates a store item with the given text and prints the +;; resulting store item path. +(use-modules (guix)) + +(with-store store + (display (add-text-to-store store "guix-basic-test-text" + (string-join + (cdr (command-line)))))) diff --git a/nixos/tests/gvisor.nix b/nixos/tests/gvisor.nix index 77ff29341bed..7f130b709fc9 100644 --- a/nixos/tests/gvisor.nix +++ b/nixos/tests/gvisor.nix @@ -1,6 +1,6 @@ # This test runs a container through gvisor and checks if simple container starts -import ./make-test-python.nix ({ pkgs, ...} : { +import ./make-test-python.nix ({ pkgs, ... }: { name = "gvisor"; meta = with pkgs.lib.maintainers; { maintainers = [ andrew-d ]; @@ -9,21 +9,21 @@ import ./make-test-python.nix ({ pkgs, ...} : { nodes = { gvisor = { pkgs, ... }: - { - virtualisation.docker = { - enable = true; - extraOptions = "--add-runtime runsc=${pkgs.gvisor}/bin/runsc"; - }; + { + virtualisation.docker = { + enable = true; + extraOptions = "--add-runtime runsc=${pkgs.gvisor}/bin/runsc"; + }; - networking = { - dhcpcd.enable = false; - defaultGateway = "192.168.1.1"; - interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [ - { address = "192.168.1.2"; prefixLength = 24; } - ]; - }; + networking = { + dhcpcd.enable = false; + defaultGateway = "192.168.1.1"; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [ + { address = "192.168.1.2"; prefixLength = 24; } + ]; }; - }; + }; + }; testScript = '' start_all() @@ -31,13 +31,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { gvisor.wait_for_unit("network.target") gvisor.wait_for_unit("sockets.target") - # Start by verifying that gvisor itself works - output = gvisor.succeed( - "${pkgs.gvisor}/bin/runsc -alsologtostderr do ${pkgs.coreutils}/bin/echo hello world" - ) - assert output.strip() == "hello world" - - # Also test the Docker runtime + # Test the Docker runtime gvisor.succeed("tar cv --files-from /dev/null | docker import - scratchimg") gvisor.succeed( "docker run -d --name=sleeping --runtime=runsc -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10" diff --git a/nixos/tests/hockeypuck.nix b/nixos/tests/hockeypuck.nix index 2b9dba8720ab..675d6b226ad2 100644 --- a/nixos/tests/hockeypuck.nix +++ b/nixos/tests/hockeypuck.nix @@ -35,7 +35,7 @@ in { ensureDatabases = [ "hockeypuck" ]; ensureUsers = [{ name = "hockeypuck"; - ensurePermissions."DATABASE hockeypuck" = "ALL PRIVILEGES"; + ensureDBOwnership = true; }]; }; }; diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix index e97e8a467b18..e1588088ba19 100644 --- a/nixos/tests/home-assistant.nix +++ b/nixos/tests/home-assistant.nix @@ -9,13 +9,11 @@ in { nodes.hass = { pkgs, ... }: { services.postgresql = { enable = true; - - # FIXME: hack for https://github.com/NixOS/nixpkgs/issues/216989 - # Should be replaced with ensureUsers again when a solution for that is found - initialScript = pkgs.writeText "hass-setup-db.sql" '' - CREATE ROLE hass WITH LOGIN; - CREATE DATABASE hass WITH OWNER hass; - ''; + ensureDatabases = [ "hass" ]; + ensureUsers = [{ + name = "hass"; + ensureDBOwnership = true; + }]; }; services.home-assistant = { diff --git a/nixos/tests/incus/container.nix b/nixos/tests/incus/container.nix index 79b9e2fbabdc..49a22c08aad1 100644 --- a/nixos/tests/incus/container.nix +++ b/nixos/tests/incus/container.nix @@ -73,5 +73,33 @@ in 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"): + machine.execute("incus delete --force container") + machine.succeed("incus launch nixos container") + 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") + + 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" + + 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) + # give generator an extra second to run + machine.sleep(1) + + machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf") ''; }) diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix index 1b4c92b584b9..d0c01a779ef1 100644 --- a/nixos/tests/installer-systemd-stage-1.nix +++ b/nixos/tests/installer-systemd-stage-1.nix @@ -32,6 +32,10 @@ stratisRoot swraid zfsroot + clevisLuks + clevisLuksFallback + clevisZfs + clevisZfsFallback ; } diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index 1baa4396424f..f7fc168eba8c 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -12,6 +12,7 @@ let # The configuration to install. makeConfig = { bootLoader, grubDevice, grubIdentifier, grubUseEfi , extraConfig, forceGrubReinstallCount ? 0, flake ? false + , clevisTest }: pkgs.writeText "configuration.nix" '' { config, lib, pkgs, modulesPath, ... }: @@ -52,6 +53,15 @@ let boot.initrd.secrets."/etc/secret" = ./secret; + ${optionalString clevisTest '' + boot.kernelParams = [ "console=tty0" "ip=192.168.1.1:::255.255.255.0::eth1:none" ]; + boot.initrd = { + availableKernelModules = [ "tpm_tis" ]; + clevis = { enable = true; useTang = true; }; + network.enable = true; + }; + ''} + users.users.alice = { isNormalUser = true; home = "/home/alice"; @@ -71,7 +81,7 @@ let # partitions and filesystems. testScriptFun = { bootLoader, createPartitions, grubDevice, grubUseEfi, grubIdentifier , postInstallCommands, preBootCommands, postBootCommands, extraConfig - , testSpecialisationConfig, testFlakeSwitch + , testSpecialisationConfig, testFlakeSwitch, clevisTest, clevisFallbackTest }: let iface = "virtio"; isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi); @@ -79,12 +89,16 @@ let in if !isEfi && !pkgs.stdenv.hostPlatform.isx86 then '' machine.succeed("true") '' else '' + import subprocess + tpm_folder = os.environ['NIX_BUILD_TOP'] def assemble_qemu_flags(): flags = "-cpu max" ${if (system == "x86_64-linux" || system == "i686-linux") then ''flags += " -m 1024"'' else ''flags += " -m 768 -enable-kvm -machine virt,gic-version=host"'' } + ${optionalString clevisTest ''flags += f" -chardev socket,id=chrtpm,path={tpm_folder}/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"''} + ${optionalString clevisTest ''flags += " -device virtio-net-pci,netdev=vlan1,mac=52:54:00:12:11:02 -netdev vde,id=vlan1,sock=\"$QEMU_VDE_SOCKET_1\""''} return flags @@ -110,8 +124,45 @@ let def create_machine_named(name): return create_machine({**default_flags, "name": name}) + class Tpm: + def __init__(self): + self.start() + + def start(self): + self.proc = subprocess.Popen(["${pkgs.swtpm}/bin/swtpm", + "socket", + "--tpmstate", f"dir={tpm_folder}/swtpm", + "--ctrl", f"type=unixio,path={tpm_folder}/swtpm-sock", + "--tpm2" + ]) + + # Check whether starting swtpm failed + try: + exit_code = self.proc.wait(timeout=0.2) + if exit_code is not None and exit_code != 0: + raise Exception("failed to start swtpm") + except subprocess.TimeoutExpired: + pass + + """Check whether the swtpm process exited due to an error""" + def check(self): + exit_code = self.proc.poll() + if exit_code is not None and exit_code != 0: + raise Exception("swtpm process died") + + + os.mkdir(f"{tpm_folder}/swtpm") + tpm = Tpm() + tpm.check() + + start_all() + ${optionalString clevisTest '' + tang.wait_for_unit("sockets.target") + tang.wait_for_unit("network-online.target") + machine.wait_for_unit("network-online.target") + ''} + machine.wait_for_unit("multi-user.target") - machine.start() with subtest("Assert readiness of login prompt"): machine.succeed("echo hello") @@ -127,13 +178,23 @@ let machine.copy_from_host( "${ makeConfig { inherit bootLoader grubDevice grubIdentifier - grubUseEfi extraConfig; + grubUseEfi extraConfig clevisTest; } }", "/mnt/etc/nixos/configuration.nix", ) machine.copy_from_host("${pkgs.writeText "secret" "secret"}", "/mnt/etc/nixos/secret") + ${optionalString clevisTest '' + with subtest("Create the Clevis secret with Tang"): + machine.wait_for_unit("network-online.target") + machine.succeed('echo -n password | clevis encrypt sss \'{"t": 2, "pins": {"tpm2": {}, "tang": {"url": "http://192.168.1.2"}}}\' -y > /mnt/etc/nixos/clevis-secret.jwe')''} + + ${optionalString clevisFallbackTest '' + with subtest("Shutdown Tang to check fallback to interactive prompt"): + tang.shutdown() + ''} + with subtest("Perform the installation"): machine.succeed("nixos-install < /dev/null >&2") @@ -200,7 +261,7 @@ let machine.copy_from_host_via_shell( "${ makeConfig { inherit bootLoader grubDevice grubIdentifier - grubUseEfi extraConfig; + grubUseEfi extraConfig clevisTest; forceGrubReinstallCount = 1; } }", @@ -229,7 +290,7 @@ let machine.copy_from_host_via_shell( "${ makeConfig { inherit bootLoader grubDevice grubIdentifier - grubUseEfi extraConfig; + grubUseEfi extraConfig clevisTest; forceGrubReinstallCount = 2; } }", @@ -303,7 +364,7 @@ let """) machine.copy_from_host_via_shell( "${makeConfig { - inherit bootLoader grubDevice grubIdentifier grubUseEfi extraConfig; + inherit bootLoader grubDevice grubIdentifier grubUseEfi extraConfig clevisTest; forceGrubReinstallCount = 1; flake = true; }}", @@ -379,6 +440,8 @@ let , enableOCR ? false, meta ? {} , testSpecialisationConfig ? false , testFlakeSwitch ? false + , clevisTest ? false + , clevisFallbackTest ? false }: makeTest { inherit enableOCR; @@ -416,13 +479,13 @@ let virtualisation.rootDevice = "/dev/vdb"; virtualisation.bootLoaderDevice = "/dev/vda"; virtualisation.qemu.diskInterface = "virtio"; - - # We don't want to have any networking in the guest whatsoever. - # Also, if any vlans are enabled, the guest will reboot - # (with a different configuration for legacy reasons), - # and spend 5 minutes waiting for the vlan interface to show up - # (which will never happen). - virtualisation.vlans = []; + virtualisation.qemu.options = mkIf (clevisTest) [ + "-chardev socket,id=chrtpm,path=$NIX_BUILD_TOP/swtpm-sock" + "-tpmdev emulator,id=tpm0,chardev=chrtpm" + "-device tpm-tis,tpmdev=tpm0" + ]; + # We don't want to have any networking in the guest apart from the clevis tests. + virtualisation.vlans = mkIf (!clevisTest) []; boot.loader.systemd-boot.enable = mkIf (bootLoader == "systemd-boot") true; @@ -471,7 +534,7 @@ let in [ (pkgs.grub2.override { inherit zfsSupport; }) (pkgs.grub2_efi.override { inherit zfsSupport; }) - ]); + ]) ++ optionals clevisTest [ pkgs.klibc ]; nix.settings = { substituters = mkForce []; @@ -480,12 +543,21 @@ let }; }; + } // optionalAttrs clevisTest { + tang = { + services.tang = { + enable = true; + listenStream = [ "80" ]; + ipAddressAllow = [ "192.168.1.0/24" ]; + }; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; }; testScript = testScriptFun { inherit bootLoader createPartitions postInstallCommands preBootCommands postBootCommands grubDevice grubIdentifier grubUseEfi extraConfig - testSpecialisationConfig testFlakeSwitch; + testSpecialisationConfig testFlakeSwitch clevisTest clevisFallbackTest; }; }; @@ -586,6 +658,145 @@ let zfs = super.zfs.overrideAttrs(_: {meta.platforms = [];});} )]; }; + + mkClevisBcachefsTest = { fallback ? false }: makeInstallerTest "clevis-bcachefs${optionalString fallback "-fallback"}" { + clevisTest = true; + clevisFallbackTest = fallback; + enableOCR = fallback; + extraInstallerConfig = { + imports = [ no-zfs-module ]; + boot.supportedFilesystems = [ "bcachefs" ]; + environment.systemPackages = with pkgs; [ keyutils clevis ]; + }; + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + + " mkpart primary ext2 1M 100MB" + + " mkpart primary linux-swap 100M 1024M" + + " mkpart primary 1024M -1s", + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "keyctl link @u @s", + "echo -n password | mkfs.bcachefs -L root --encrypted /dev/vda3", + "echo -n password | bcachefs unlock /dev/vda3", + "echo -n password | mount -t bcachefs /dev/vda3 /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=boot /mnt/boot", + "udevadm settle") + ''; + extraConfig = '' + boot.initrd.clevis.devices."/dev/vda3".secretFile = "/etc/nixos/clevis-secret.jwe"; + + # We override what nixos-generate-config has generated because we do + # not know the UUID in advance. + fileSystems."/" = lib.mkForce { device = "/dev/vda3"; fsType = "bcachefs"; }; + ''; + preBootCommands = '' + tpm = Tpm() + tpm.check() + '' + optionalString fallback '' + machine.start() + machine.wait_for_text("enter passphrase for") + machine.send_chars("password\n") + ''; + }; + + mkClevisLuksTest = { fallback ? false }: makeInstallerTest "clevis-luks${optionalString fallback "-fallback"}" { + clevisTest = true; + clevisFallbackTest = fallback; + enableOCR = fallback; + extraInstallerConfig = { + environment.systemPackages = with pkgs; [ clevis ]; + }; + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + + " mkpart primary ext2 1M 100MB" + + " mkpart primary linux-swap 100M 1024M" + + " mkpart primary 1024M -1s", + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "modprobe dm_mod dm_crypt", + "echo -n password | cryptsetup luksFormat -q /dev/vda3 -", + "echo -n password | cryptsetup luksOpen --key-file - /dev/vda3 crypt-root", + "mkfs.ext3 -L nixos /dev/mapper/crypt-root", + "mount LABEL=nixos /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=boot /mnt/boot", + "udevadm settle") + ''; + extraConfig = '' + boot.initrd.clevis.devices."crypt-root".secretFile = "/etc/nixos/clevis-secret.jwe"; + ''; + preBootCommands = '' + tpm = Tpm() + tpm.check() + '' + optionalString fallback '' + machine.start() + ${if systemdStage1 then '' + machine.wait_for_text("Please enter") + '' else '' + machine.wait_for_text("Passphrase for") + ''} + machine.send_chars("password\n") + ''; + }; + + mkClevisZfsTest = { fallback ? false }: makeInstallerTest "clevis-zfs${optionalString fallback "-fallback"}" { + clevisTest = true; + clevisFallbackTest = fallback; + enableOCR = fallback; + extraInstallerConfig = { + boot.supportedFilesystems = [ "zfs" ]; + environment.systemPackages = with pkgs; [ clevis ]; + }; + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + + " mkpart primary ext2 1M 100MB" + + " mkpart primary linux-swap 100M 1024M" + + " mkpart primary 1024M -1s", + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "zpool create -O mountpoint=legacy rpool /dev/vda3", + "echo -n password | zfs create" + + " -o encryption=aes-256-gcm -o keyformat=passphrase rpool/root", + "mount -t zfs rpool/root /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=boot /mnt/boot", + "udevadm settle") + ''; + extraConfig = '' + boot.initrd.clevis.devices."rpool/root".secretFile = "/etc/nixos/clevis-secret.jwe"; + boot.zfs.requestEncryptionCredentials = true; + + + # Using by-uuid overrides the default of by-id, and is unique + # to the qemu disks, as they don't produce by-id paths for + # some reason. + boot.zfs.devNodes = "/dev/disk/by-uuid/"; + networking.hostId = "00000000"; + ''; + preBootCommands = '' + tpm = Tpm() + tpm.check() + '' + optionalString fallback '' + machine.start() + ${if systemdStage1 then '' + machine.wait_for_text("Enter key for rpool/root") + '' else '' + machine.wait_for_text("Key load error") + ''} + machine.send_chars("password\n") + ''; + }; + in { # !!! `parted mkpart' seems to silently create overlapping partitions. @@ -991,6 +1202,68 @@ in { ''; }; + bcachefsLinuxTesting = makeInstallerTest "bcachefs-linux-testing" { + extraInstallerConfig = { + imports = [ no-zfs-module ]; + + boot = { + supportedFilesystems = [ "bcachefs" ]; + kernelPackages = pkgs.linuxPackages_testing; + }; + }; + + extraConfig = '' + boot.kernelPackages = pkgs.linuxPackages_testing; + ''; + + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + + " mkpart primary ext2 1M 100MB" # /boot + + " mkpart primary linux-swap 100M 1024M" # swap + + " mkpart primary 1024M -1s", # / + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.bcachefs -L root /dev/vda3", + "mount -t bcachefs /dev/vda3 /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount /dev/vda1 /mnt/boot", + ) + ''; + }; + + bcachefsUpgradeToLinuxTesting = makeInstallerTest "bcachefs-upgrade-to-linux-testing" { + extraInstallerConfig = { + imports = [ no-zfs-module ]; + boot.supportedFilesystems = [ "bcachefs" ]; + # We don't have network access in the VM, we need this for `nixos-install` + system.extraDependencies = [ pkgs.linux_testing ]; + }; + + extraConfig = '' + boot.kernelPackages = pkgs.linuxPackages_testing; + ''; + + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + + " mkpart primary ext2 1M 100MB" # /boot + + " mkpart primary linux-swap 100M 1024M" # swap + + " mkpart primary 1024M -1s", # / + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.bcachefs -L root /dev/vda3", + "mount -t bcachefs /dev/vda3 /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount /dev/vda1 /mnt/boot", + ) + ''; + }; + # Test using labels to identify volumes in grub simpleLabels = makeInstallerTest "simpleLabels" { createPartitions = '' @@ -1113,6 +1386,13 @@ in { ) ''; }; +} // { + clevisBcachefs = mkClevisBcachefsTest { }; + clevisBcachefsFallback = mkClevisBcachefsTest { fallback = true; }; + clevisLuks = mkClevisLuksTest { }; + clevisLuksFallback = mkClevisLuksTest { fallback = true; }; + clevisZfs = mkClevisZfsTest { }; + clevisZfsFallback = mkClevisZfsTest { fallback = true; }; } // optionalAttrs systemdStage1 { stratisRoot = makeInstallerTest "stratisRoot" { createPartitions = '' diff --git a/nixos/tests/invidious.nix b/nixos/tests/invidious.nix index 582d1550fff1..701e8e5e7a3f 100644 --- a/nixos/tests/invidious.nix +++ b/nixos/tests/invidious.nix @@ -44,8 +44,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { enable = true; initialScript = pkgs.writeText "init-postgres-with-password" '' CREATE USER kemal WITH PASSWORD 'correct horse battery staple'; - CREATE DATABASE invidious; - GRANT ALL PRIVILEGES ON DATABASE invidious TO kemal; + CREATE DATABASE invidious OWNER kemal; ''; }; }; diff --git a/nixos/tests/jitsi-meet.nix b/nixos/tests/jitsi-meet.nix index c39cd19e1f0a..fc6654f2c076 100644 --- a/nixos/tests/jitsi-meet.nix +++ b/nixos/tests/jitsi-meet.nix @@ -24,10 +24,23 @@ import ./make-test-python.nix ({ pkgs, ... }: { security.acme.acceptTerms = true; security.acme.defaults.email = "me@example.org"; security.acme.defaults.server = "https://example.com"; # self-signed only + + specialisation.caddy = { + inheritParentConfig = true; + configuration = { + services.jitsi-meet = { + caddy.enable = true; + nginx.enable = false; + }; + services.caddy.virtualHosts.${config.services.jitsi-meet.hostName}.extraConfig = '' + tls internal + ''; + }; + }; }; }; - testScript = '' + testScript = { nodes, ... }: '' server.wait_for_unit("jitsi-videobridge2.service") server.wait_for_unit("jicofo.service") server.wait_for_unit("nginx.service") @@ -41,6 +54,15 @@ import ./make-test-python.nix ({ pkgs, ... }: { ) client.wait_for_unit("network.target") - assert "<title>Jitsi Meet</title>" in client.succeed("curl -sSfkL http://server/") + + def client_curl(): + assert "<title>Jitsi Meet</title>" in client.succeed("curl -sSfkL http://server/") + + client_curl() + + with subtest("Testing backup service"): + server.succeed("${nodes.server.system.build.toplevel}/specialisation/caddy/bin/switch-to-configuration test") + server.wait_for_unit("caddy.service") + client_curl() ''; }) diff --git a/nixos/tests/kafka.nix b/nixos/tests/kafka.nix index 864253fd8c73..f4f9827ab7b5 100644 --- a/nixos/tests/kafka.nix +++ b/nixos/tests/kafka.nix @@ -6,13 +6,62 @@ with pkgs.lib; let - makeKafkaTest = name: kafkaPackage: (import ./make-test-python.nix ({ + makeKafkaTest = name: { kafkaPackage, mode ? "zookeeper" }: (import ./make-test-python.nix ({ inherit name; meta = with pkgs.lib.maintainers; { maintainers = [ nequissimus ]; }; nodes = { + kafka = { ... }: { + services.apache-kafka = mkMerge [ + ({ + enable = true; + package = kafkaPackage; + settings = { + "offsets.topic.replication.factor" = 1; + "log.dirs" = [ + "/var/lib/kafka/logdir1" + "/var/lib/kafka/logdir2" + ]; + }; + }) + (mkIf (mode == "zookeeper") { + settings = { + "zookeeper.session.timeout.ms" = 600000; + "zookeeper.connect" = [ "zookeeper1:2181" ]; + }; + }) + (mkIf (mode == "kraft") { + clusterId = "ak2fIHr4S8WWarOF_ODD0g"; + formatLogDirs = true; + settings = { + "node.id" = 1; + "process.roles" = [ + "broker" + "controller" + ]; + "listeners" = [ + "PLAINTEXT://:9092" + "CONTROLLER://:9093" + ]; + "listener.security.protocol.map" = [ + "PLAINTEXT:PLAINTEXT" + "CONTROLLER:PLAINTEXT" + ]; + "controller.quorum.voters" = [ + "1@kafka:9093" + ]; + "controller.listener.names" = [ "CONTROLLER" ]; + }; + }) + ]; + + networking.firewall.allowedTCPPorts = [ 9092 9093 ]; + # i686 tests: qemu-system-i386 can simulate max 2047MB RAM (not 2048) + virtualisation.memorySize = 2047; + }; + } // optionalAttrs (mode == "zookeeper") { zookeeper1 = { ... }: { services.zookeeper = { enable = true; @@ -20,29 +69,16 @@ let networking.firewall.allowedTCPPorts = [ 2181 ]; }; - kafka = { ... }: { - services.apache-kafka = { - enable = true; - extraProperties = '' - offsets.topic.replication.factor = 1 - zookeeper.session.timeout.ms = 600000 - ''; - package = kafkaPackage; - zookeeper = "zookeeper1:2181"; - }; - - networking.firewall.allowedTCPPorts = [ 9092 ]; - # i686 tests: qemu-system-i386 can simulate max 2047MB RAM (not 2048) - virtualisation.memorySize = 2047; - }; }; testScript = '' start_all() + ${optionalString (mode == "zookeeper") '' zookeeper1.wait_for_unit("default.target") zookeeper1.wait_for_unit("zookeeper.service") zookeeper1.wait_for_open_port(2181) + ''} kafka.wait_for_unit("default.target") kafka.wait_for_unit("apache-kafka.service") @@ -67,12 +103,13 @@ let }) { inherit system; }); in with pkgs; { - kafka_2_8 = makeKafkaTest "kafka_2_8" apacheKafka_2_8; - kafka_3_0 = makeKafkaTest "kafka_3_0" apacheKafka_3_0; - kafka_3_1 = makeKafkaTest "kafka_3_1" apacheKafka_3_1; - kafka_3_2 = makeKafkaTest "kafka_3_2" apacheKafka_3_2; - kafka_3_3 = makeKafkaTest "kafka_3_3" apacheKafka_3_3; - kafka_3_4 = makeKafkaTest "kafka_3_4" apacheKafka_3_4; - kafka_3_5 = makeKafkaTest "kafka_3_5" apacheKafka_3_5; - kafka = makeKafkaTest "kafka" apacheKafka; + kafka_2_8 = makeKafkaTest "kafka_2_8" { kafkaPackage = apacheKafka_2_8; }; + kafka_3_0 = makeKafkaTest "kafka_3_0" { kafkaPackage = apacheKafka_3_0; }; + kafka_3_1 = makeKafkaTest "kafka_3_1" { kafkaPackage = apacheKafka_3_1; }; + kafka_3_2 = makeKafkaTest "kafka_3_2" { kafkaPackage = apacheKafka_3_2; }; + kafka_3_3 = makeKafkaTest "kafka_3_3" { kafkaPackage = apacheKafka_3_3; }; + kafka_3_4 = makeKafkaTest "kafka_3_4" { kafkaPackage = apacheKafka_3_4; }; + kafka_3_5 = makeKafkaTest "kafka_3_5" { kafkaPackage = apacheKafka_3_5; }; + kafka = makeKafkaTest "kafka" { kafkaPackage = apacheKafka; }; + kafka_kraft = makeKafkaTest "kafka_kraft" { kafkaPackage = apacheKafka; mode = "kraft"; }; } diff --git a/nixos/tests/lanraragi.nix b/nixos/tests/lanraragi.nix index f513ac9d252b..7a4a1a489bdf 100644 --- a/nixos/tests/lanraragi.nix +++ b/nixos/tests/lanraragi.nix @@ -10,19 +10,17 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { services.lanraragi = { enable = true; passwordFile = pkgs.writeText "lrr-test-pass" '' - ultra-secure-password + Ultra-secure-p@ssword-"with-spec1al\chars ''; port = 4000; redis = { port = 4001; passwordFile = pkgs.writeText "redis-lrr-test-pass" '' - still-a-very-secure-password + 123-redis-PASS ''; }; }; }; - - }; testScript = '' @@ -34,7 +32,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { machine2.wait_for_unit("lanraragi.service") machine2.wait_until_succeeds("curl -f localhost:4000") - machine2.succeed("[ $(curl -o /dev/null -X post 'http://localhost:4000/login' --data-raw 'password=ultra-secure-password' -w '%{http_code}') -eq 302 ]") + machine2.succeed("[ $(curl -o /dev/null -X post 'http://localhost:4000/login' --data-raw 'password=Ultra-secure-p@ssword-\"with-spec1al\\chars' -w '%{http_code}') -eq 302 ]") ''; }) diff --git a/nixos/tests/libvirtd.nix b/nixos/tests/libvirtd.nix index 41d06cc9643f..df80dcc21a2e 100644 --- a/nixos/tests/libvirtd.nix +++ b/nixos/tests/libvirtd.nix @@ -14,10 +14,10 @@ import ./make-test-python.nix ({ pkgs, ... }: { libvirtd.hooks.qemu.is_working = "${pkgs.writeShellScript "testHook.sh" '' touch /tmp/qemu_hook_is_working ''}"; + libvirtd.nss.enable = true; }; boot.supportedFilesystems = [ "zfs" ]; networking.hostId = "deadbeef"; # needed for zfs - networking.nameservers = [ "192.168.122.1" ]; security.polkit.enable = true; environment.systemPackages = with pkgs; [ virt-manager ]; }; diff --git a/nixos/tests/lxd/ui.nix b/nixos/tests/lxd/ui.nix index 86cb30d8c2b6..ff651725ba70 100644 --- a/nixos/tests/lxd/ui.nix +++ b/nixos/tests/lxd/ui.nix @@ -11,9 +11,37 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: { lxd.ui.enable = true; }; - environment.systemPackages = [ pkgs.curl ]; + environment.systemPackages = + let + seleniumScript = pkgs.writers.writePython3Bin "selenium-script" + { + libraries = with pkgs.python3Packages; [ selenium ]; + } '' + from selenium import webdriver + from selenium.webdriver.common.by import By + from selenium.webdriver.firefox.options import Options + from selenium.webdriver.support.ui import WebDriverWait + + options = Options() + options.add_argument("--headless") + service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501 + + driver = webdriver.Firefox(options=options, service=service) + driver.implicitly_wait(10) + driver.get("https://localhost:8443/ui") + + wait = WebDriverWait(driver, 60) + + assert len(driver.find_elements(By.CLASS_NAME, "l-application")) > 0 + assert len(driver.find_elements(By.CLASS_NAME, "l-navigation__drawer")) > 0 + + driver.close() + ''; + in + with pkgs; [ curl firefox-unwrapped geckodriver seleniumScript ]; }; + testScript = '' machine.wait_for_unit("sockets.target") machine.wait_for_unit("lxd.service") @@ -31,5 +59,8 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: { # Ensure the endpoint returns an HTML page with 'LXD UI' in the title machine.succeed("curl -kLs https://localhost:8443/ui | grep '<title>LXD UI</title>'") + + # Ensure the application is actually rendered by the Javascript + machine.succeed("PYTHONUNBUFFERED=1 selenium-script") ''; }) diff --git a/nixos/tests/matrix/synapse.nix b/nixos/tests/matrix/synapse.nix index 98b077469192..8c10a575ffbd 100644 --- a/nixos/tests/matrix/synapse.nix +++ b/nixos/tests/matrix/synapse.nix @@ -1,31 +1,15 @@ import ../make-test-python.nix ({ pkgs, ... } : let - - runWithOpenSSL = file: cmd: pkgs.runCommand file { - buildInputs = [ pkgs.openssl ]; - } cmd; - - - ca_key = runWithOpenSSL "ca-key.pem" "openssl genrsa -out $out 2048"; - ca_pem = runWithOpenSSL "ca.pem" '' - openssl req \ - -x509 -new -nodes -key ${ca_key} \ - -days 10000 -out $out -subj "/CN=snakeoil-ca" + ca_key = mailerCerts.ca.key; + ca_pem = mailerCerts.ca.cert; + + bundle = pkgs.runCommand "bundle" { + nativeBuildInputs = [ pkgs.minica ]; + } '' + minica -ca-cert ${ca_pem} -ca-key ${ca_key} \ + -domains localhost + install -Dm444 -t $out localhost/{key,cert}.pem ''; - key = runWithOpenSSL "matrix_key.pem" "openssl genrsa -out $out 2048"; - csr = runWithOpenSSL "matrix.csr" '' - openssl req \ - -new -key ${key} \ - -out $out -subj "/CN=localhost" \ - ''; - cert = runWithOpenSSL "matrix_cert.pem" '' - openssl x509 \ - -req -in ${csr} \ - -CA ${ca_pem} -CAkey ${ca_key} \ - -CAcreateserial -out $out \ - -days 365 - ''; - mailerCerts = import ../common/acme/server/snakeoil-certs.nix; mailerDomain = mailerCerts.domain; @@ -82,8 +66,8 @@ in { host = "localhost"; port = config.services.redis.servers.matrix-synapse.port; }; - tls_certificate_path = "${cert}"; - tls_private_key_path = "${key}"; + tls_certificate_path = "${bundle}/cert.pem"; + tls_private_key_path = "${bundle}/key.pem"; registration_shared_secret = registrationSharedSecret; public_baseurl = "https://example.com"; email = { @@ -203,8 +187,8 @@ in { settings = { inherit listeners; database.name = "sqlite3"; - tls_certificate_path = "${cert}"; - tls_private_key_path = "${key}"; + tls_certificate_path = "${bundle}/cert.pem"; + tls_private_key_path = "${bundle}/key.pem"; }; }; }; @@ -222,7 +206,7 @@ in { "journalctl -u matrix-synapse.service | grep -q 'Connected to redis'" ) serverpostgres.require_unit_state("postgresql.service") - serverpostgres.succeed("register_new_matrix_user -u ${testUser} -p ${testPassword} -a -k ${registrationSharedSecret} https://localhost:8448/") + serverpostgres.succeed("REQUESTS_CA_BUNDLE=${ca_pem} register_new_matrix_user -u ${testUser} -p ${testPassword} -a -k ${registrationSharedSecret} https://localhost:8448/") serverpostgres.succeed("obtain-token-and-register-email") serversqlite.wait_for_unit("matrix-synapse.service") serversqlite.wait_until_succeeds( diff --git a/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix b/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix index e638f2e5b861..addc898bd760 100644 --- a/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix +++ b/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix @@ -41,10 +41,13 @@ in { }; secretFile = "/etc/nextcloud-secrets.json"; - extraOptions.redis = { - dbindex = 0; - timeout = 1.5; - # password handled via secretfile below + extraOptions = { + allow_local_remote_servers = true; + redis = { + dbindex = 0; + timeout = 1.5; + # password handled via secretfile below + }; }; configureRedis = true; }; @@ -62,6 +65,7 @@ in { services.postgresql = { enable = true; + package = pkgs.postgresql_14; }; systemd.services.postgresql.postStart = pkgs.lib.mkAfter '' password=$(cat ${passFile}) diff --git a/nixos/tests/nixops/default.nix b/nixos/tests/nixops/default.nix index b8f747b2a19f..f7a26f2461c4 100644 --- a/nixos/tests/nixops/default.nix +++ b/nixos/tests/nixops/default.nix @@ -1,4 +1,6 @@ -{ pkgs, ... }: +{ pkgs +, testers +, ... }: let inherit (pkgs) lib; @@ -19,7 +21,7 @@ let passthru.override = args': testsForPackage (args // args'); }; - testLegacyNetwork = { nixopsPkg, ... }: pkgs.nixosTest ({ + testLegacyNetwork = { nixopsPkg, ... }: testers.nixosTest ({ name = "nixops-legacy-network"; nodes = { deployer = { config, lib, nodes, pkgs, ... }: { diff --git a/nixos/tests/ocsinventory-agent.nix b/nixos/tests/ocsinventory-agent.nix new file mode 100644 index 000000000000..67b0c8c91103 --- /dev/null +++ b/nixos/tests/ocsinventory-agent.nix @@ -0,0 +1,33 @@ +import ./make-test-python.nix ({ pkgs, ...} : { + name = "ocsinventory-agent"; + + nodes.machine = { pkgs, ... }: { + services.ocsinventory-agent = { + enable = true; + settings = { + debug = true; + local = "/var/lib/ocsinventory-agent/reports"; + tag = "MY_INVENTORY_TAG"; + }; + }; + }; + + testScript = '' + path = "/var/lib/ocsinventory-agent/reports" + + # Run the agent to generate the inventory file in offline mode + start_all() + machine.succeed("mkdir -p {}".format(path)) + machine.wait_for_unit("ocsinventory-agent.service") + machine.wait_until_succeeds("journalctl -u ocsinventory-agent.service | grep 'Inventory saved in'") + + # Fetch the path to the generated inventory file + report_file = machine.succeed("find {}/*.ocs -type f | head -n1".format(path)) + + with subtest("Check the tag value"): + tag = machine.succeed( + "${pkgs.libxml2}/bin/xmllint --xpath 'string(/REQUEST/CONTENT/ACCOUNTINFO/KEYVALUE)' {}".format(report_file) + ).rstrip() + assert tag == "MY_INVENTORY_TAG", f"tag is not valid, was '{tag}'" + ''; +}) diff --git a/nixos/tests/paperless.nix b/nixos/tests/paperless.nix index 22409e899236..6a51cc522bdc 100644 --- a/nixos/tests/paperless.nix +++ b/nixos/tests/paperless.nix @@ -17,7 +17,7 @@ import ./make-test-python.nix ({ lib, ... }: { ensureDatabases = [ "paperless" ]; ensureUsers = [ { name = config.services.paperless.user; - ensurePermissions = { "DATABASE \"paperless\"" = "ALL PRIVILEGES"; }; + ensureDBOwnership = true; } ]; }; diff --git a/nixos/tests/pgadmin4.nix b/nixos/tests/pgadmin4.nix index cb8de87c9ee3..3ee7ed19fa1c 100644 --- a/nixos/tests/pgadmin4.nix +++ b/nixos/tests/pgadmin4.nix @@ -19,14 +19,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: authentication = '' host all all localhost trust ''; - ensureUsers = [ - { - name = "postgres"; - ensurePermissions = { - "DATABASE \"postgres\"" = "ALL PRIVILEGES"; - }; - } - ]; }; services.pgadmin = { diff --git a/nixos/tests/pgbouncer.nix b/nixos/tests/pgbouncer.nix index 1e72327d4200..bb5afd35ee28 100644 --- a/nixos/tests/pgbouncer.nix +++ b/nixos/tests/pgbouncer.nix @@ -17,7 +17,8 @@ in systemd.services.postgresql = { postStart = '' - ${pkgs.postgresql}/bin/psql -U postgres -c "ALTER ROLE testuser WITH LOGIN PASSWORD 'testpass'"; + ${pkgs.postgresql}/bin/psql -U postgres -c "ALTER ROLE testuser WITH LOGIN PASSWORD 'testpass'"; + ${pkgs.postgresql}/bin/psql -U postgres -c "ALTER DATABASE testdb OWNER TO testuser;"; ''; }; @@ -28,9 +29,6 @@ in ensureUsers = [ { name = "testuser"; - ensurePermissions = { - "DATABASE testdb" = "ALL PRIVILEGES"; - }; }]; authentication = '' local testdb testuser scram-sha-256 @@ -40,7 +38,7 @@ in pgbouncer = { enable = true; listenAddress = "localhost"; - databases = { testdb = "host=/run/postgresql/ port=5432 auth_user=testuser dbname=testdb"; }; + databases = { test = "host=/run/postgresql/ port=5432 auth_user=testuser dbname=testdb"; }; authType = "scram-sha-256"; authFile = testAuthFile; }; @@ -55,7 +53,7 @@ in # Test if we can make a query through PgBouncer one.wait_until_succeeds( - "psql 'postgres://testuser:testpass@localhost:6432/testdb' -c 'SELECT 1;'" + "psql 'postgres://testuser:testpass@localhost:6432/test' -c 'SELECT 1;'" ) ''; }) diff --git a/nixos/tests/plantuml-server.nix b/nixos/tests/plantuml-server.nix new file mode 100644 index 000000000000..460c30919aec --- /dev/null +++ b/nixos/tests/plantuml-server.nix @@ -0,0 +1,20 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "plantuml-server"; + meta.maintainers = with lib.maintainers; [ anthonyroussel ]; + + nodes.machine = { pkgs, ... }: { + environment.systemPackages = [ pkgs.curl ]; + services.plantuml-server.enable = true; + }; + + testScript = '' + start_all() + + machine.wait_for_unit("plantuml-server.service") + machine.wait_for_open_port(8080) + + with subtest("Generate chart"): + chart_id = machine.succeed("curl -sSf http://localhost:8080/plantuml/coder -d 'Alice -> Bob'") + machine.succeed("curl -sSf http://localhost:8080/plantuml/txt/{}".format(chart_id)) + ''; +}) diff --git a/nixos/tests/plausible.nix b/nixos/tests/plausible.nix index 9afd3db75de8..9c26c509a5ab 100644 --- a/nixos/tests/plausible.nix +++ b/nixos/tests/plausible.nix @@ -8,9 +8,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { virtualisation.memorySize = 4096; services.plausible = { enable = true; - releaseCookiePath = "${pkgs.runCommand "cookie" { } '' - ${pkgs.openssl}/bin/openssl rand -base64 64 >"$out" - ''}"; adminUser = { email = "admin@example.org"; passwordFile = "${pkgs.writeText "pwd" "foobar"}"; @@ -28,6 +25,10 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { machine.wait_for_unit("plausible.service") machine.wait_for_open_port(8000) + # Ensure that the software does not make not make the machine + # listen on any public interfaces by default. + machine.fail("ss -tlpn 'src = 0.0.0.0 or src = [::]' | grep LISTEN") + machine.succeed("curl -f localhost:8000 >&2") machine.succeed("curl -f localhost:8000/js/script.js >&2") diff --git a/nixos/tests/pleroma.nix b/nixos/tests/pleroma.nix index 4f1aef854146..08a01585f877 100644 --- a/nixos/tests/pleroma.nix +++ b/nixos/tests/pleroma.nix @@ -164,9 +164,12 @@ import ./make-test-python.nix ({ pkgs, ... }: ''; tls-cert = pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } '' - openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=pleroma.nixos.test' -days 36500 mkdir -p $out - cp key.pem cert.pem $out + openssl req -x509 \ + -subj '/CN=pleroma.nixos.test/' -days 49710 \ + -addext 'subjectAltName = DNS:pleroma.nixos.test' \ + -keyout "$out/key.pem" -newkey ed25519 \ + -out "$out/cert.pem" -noenc ''; hosts = nodes: '' @@ -180,7 +183,7 @@ import ./make-test-python.nix ({ pkgs, ... }: security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ]; networking.extraHosts = hosts nodes; environment.systemPackages = with pkgs; [ - toot + pkgs.toot send-toot ]; }; diff --git a/nixos/tests/powerdns-admin.nix b/nixos/tests/powerdns-admin.nix index d7bacb24eec5..d326d74a9826 100644 --- a/nixos/tests/powerdns-admin.nix +++ b/nixos/tests/powerdns-admin.nix @@ -87,9 +87,7 @@ let ensureUsers = [ { name = "powerdnsadmin"; - ensurePermissions = { - "DATABASE powerdnsadmin" = "ALL PRIVILEGES"; - }; + ensureDBOwnership = true; } ]; }; diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix index 4bad56991cc6..7840130d4a36 100644 --- a/nixos/tests/prometheus-exporters.nix +++ b/nixos/tests/prometheus-exporters.nix @@ -257,6 +257,21 @@ let ''; }; + exportarr-sonarr = { + nodeName = "exportarr_sonarr"; + exporterConfig = { + enable = true; + url = "http://127.0.0.1:8989"; + # testing for real data is tricky, because the api key can not be preconfigured + apiKeyFile = pkgs.writeText "dummy-api-key" "eccff6a992bc2e4b88e46d064b26bb4e"; + }; + exporterTest = '' + wait_for_unit("prometheus-exportarr-sonarr-exporter.service") + wait_for_open_port(9707) + succeed("curl -sSf 'http://localhost:9707/metrics") + ''; + }; + fastly = { exporterConfig = { enable = true; @@ -1318,12 +1333,12 @@ let wait_for_open_port(9374) wait_until_succeeds( "curl -sSf localhost:9374/metrics | grep '{}' | grep -v ' 0$'".format( - 'smokeping_requests_total{host="127.0.0.1",ip="127.0.0.1"} ' + 'smokeping_requests_total{host="127.0.0.1",ip="127.0.0.1",source=""} ' ) ) wait_until_succeeds( "curl -sSf localhost:9374/metrics | grep '{}'".format( - 'smokeping_response_ttl{host="127.0.0.1",ip="127.0.0.1"}' + 'smokeping_response_ttl{host="127.0.0.1",ip="127.0.0.1",source=""}' ) ) ''; diff --git a/nixos/tests/prometheus.nix b/nixos/tests/prometheus.nix index a075cfc1f1b7..011127389377 100644 --- a/nixos/tests/prometheus.nix +++ b/nixos/tests/prometheus.nix @@ -3,6 +3,7 @@ let queryPort = 9090; minioPort = 9000; pushgwPort = 9091; + frontPort = 9092; s3 = { accessKey = "BKIKJAA5BMMU2RHO6IBB"; @@ -152,10 +153,15 @@ in import ./make-test-python.nix { services.thanos.query = { enable = true; http-address = "0.0.0.0:${toString queryPort}"; - store.addresses = [ + endpoints = [ "prometheus:${toString grpcPort}" ]; }; + services.thanos.query-frontend = { + enable = true; + http-address = "0.0.0.0:${toString frontPort}"; + query-frontend.downstream-url = "http://127.0.0.1:${toString queryPort}"; + }; }; store = { pkgs, ... }: { @@ -178,7 +184,7 @@ in import ./make-test-python.nix { services.thanos.query = { enable = true; http-address = "0.0.0.0:${toString queryPort}"; - store.addresses = [ + endpoints = [ "localhost:${toString grpcPort}" ]; }; @@ -262,6 +268,10 @@ in import ./make-test-python.nix { query.wait_for_unit("thanos-query.service") wait_for_metric(query) + # Test Thanos query frontend service + query.wait_for_unit("thanos-query-frontend.service") + query.succeed("curl -sS http://localhost:${toString frontPort}/-/healthy") + # Test if the Thanos sidecar has correctly uploaded its TSDB to S3, if the # Thanos storage service has correctly downloaded it from S3 and if the Thanos # query service running on $store can correctly retrieve the metric: diff --git a/nixos/tests/seatd.nix b/nixos/tests/seatd.nix new file mode 100644 index 000000000000..138a6cb1cf44 --- /dev/null +++ b/nixos/tests/seatd.nix @@ -0,0 +1,51 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: + +let + seatd-test = pkgs.writeShellApplication { + name = "seatd-client-pid"; + text = '' + journalctl -u seatd --no-pager -b | while read -r line; do + case "$line" in + *"New client connected"*) + line="''${line##*pid: }" + pid="''${line%%,*}" + ;; + *"Opened client"*) + echo "$pid" + exit + esac + done; + ''; + }; +in +{ + name = "seatd"; + meta.maintainers = with lib.maintainers; [ sinanmohd ]; + + nodes.machine = { ... }: { + imports = [ ./common/user-account.nix ]; + services.getty.autologinUser = "alice"; + users.users.alice.extraGroups = [ "seat" "wheel" ]; + + fonts.enableDefaultPackages = true; + environment.systemPackages = with pkgs; [ + dwl + foot + seatd-test + ]; + + programs.bash.loginShellInit = '' + [ "$(tty)" = "/dev/tty1" ] && + dwl -s 'foot touch /tmp/foot_started' + ''; + + hardware.opengl.enable = true; + virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci" ]; + services.seatd.enable = true; + }; + + testScript = '' + machine.wait_for_file("/tmp/foot_started") + machine.succeed("test $(seatd-client-pid) = $(pgrep dwl)") + ''; +}) diff --git a/nixos/tests/sftpgo.nix b/nixos/tests/sftpgo.nix index db0098d2ac48..a5bb1981d2c3 100644 --- a/nixos/tests/sftpgo.nix +++ b/nixos/tests/sftpgo.nix @@ -156,7 +156,7 @@ in ensureDatabases = [ "sftpgo" ]; ensureUsers = [{ name = "sftpgo"; - ensurePermissions."DATABASE sftpgo" = "ALL PRIVILEGES"; + ensureDBOwnership = true; }]; }; diff --git a/nixos/tests/slimserver.nix b/nixos/tests/slimserver.nix new file mode 100644 index 000000000000..c3f7b6fde4de --- /dev/null +++ b/nixos/tests/slimserver.nix @@ -0,0 +1,47 @@ +import ./make-test-python.nix ({ pkgs, ...} : { + name = "slimserver"; + meta.maintainers = with pkgs.lib.maintainers; [ adamcstephens ]; + + nodes.machine = { ... }: { + services.slimserver.enable = true; + services.squeezelite = { + enable = true; + extraArguments = "-s 127.0.0.1 -d slimproto=info"; + }; + sound.enable = true; + boot.initrd.kernelModules = ["snd-dummy"]; + }; + + testScript = + '' + import json + rpc_get_player = { + "id": 1, + "method": "slim.request", + "params":[0,["player", "id", "0", "?"]] + } + + with subtest("slimserver is started"): + machine.wait_for_unit("slimserver.service") + # give slimserver a moment to report errors + machine.sleep(2) + + with subtest('slimserver module errors are not reported'): + machine.fail("journalctl -u slimserver.service | grep 'throw_exception'") + machine.fail("journalctl -u slimserver.service | grep 'not installed'") + machine.fail("journalctl -u slimserver.service | grep 'not found'") + machine.fail("journalctl -u slimserver.service | grep 'The following CPAN modules were found but cannot work with Logitech Media Server'") + machine.fail("journalctl -u slimserver.service | grep 'please use the buildme.sh'") + + with subtest('slimserver is ready'): + machine.wait_for_open_port(9000) + machine.wait_until_succeeds("journalctl -u slimserver.service | grep 'Completed dbOptimize Scan'") + + with subtest("squeezelite player successfully connects to slimserver"): + machine.wait_for_unit("squeezelite.service") + machine.wait_until_succeeds("journalctl -u squeezelite.service | grep 'slimproto:937 connected'") + player_mac = machine.wait_until_succeeds("journalctl -eu squeezelite.service | grep 'sendHELO:148 mac:'").strip().split(" ")[-1] + player_id = machine.succeed(f"curl http://localhost:9000/jsonrpc.js -g -X POST -d '{json.dumps(rpc_get_player)}'") + assert player_mac == json.loads(player_id)["result"]["_id"], "squeezelite player not found" + ''; +}) diff --git a/nixos/tests/sonic-server.nix b/nixos/tests/sonic-server.nix new file mode 100644 index 000000000000..bb98047619b2 --- /dev/null +++ b/nixos/tests/sonic-server.nix @@ -0,0 +1,22 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "sonic-server"; + + meta = { + maintainers = with lib.maintainers; [ anthonyroussel ]; + }; + + nodes.machine = { pkgs, ... }: { + services.sonic-server.enable = true; + }; + + testScript = '' + machine.start() + + machine.wait_for_unit("sonic-server.service") + machine.wait_for_open_port(1491) + + with subtest("Check control mode"): + result = machine.succeed('(echo START control; sleep 1; echo PING; echo QUIT) | nc localhost 1491').splitlines() + assert result[2] == "PONG", f"expected 'PONG', got '{result[2]}'" + ''; +}) diff --git a/nixos/tests/sourcehut.nix b/nixos/tests/sourcehut.nix index 87e6d82bdd8f..0b258acc2af1 100644 --- a/nixos/tests/sourcehut.nix +++ b/nixos/tests/sourcehut.nix @@ -126,6 +126,7 @@ in virtualisation.diskSize = 4 * 1024; virtualisation.memorySize = 2 * 1024; networking.domain = domain; + networking.enableIPv6 = false; networking.extraHosts = '' ${config.networking.primaryIPAddress} builds.${domain} ${config.networking.primaryIPAddress} git.${domain} @@ -134,11 +135,6 @@ in services.sourcehut = { enable = true; - services = [ - "builds" - "git" - "meta" - ]; nginx.enable = true; nginx.virtualHost = { forceSSL = true; diff --git a/nixos/tests/sudo-rs.nix b/nixos/tests/sudo-rs.nix index 6006863217b6..753e00686e95 100644 --- a/nixos/tests/sudo-rs.nix +++ b/nixos/tests/sudo-rs.nix @@ -22,11 +22,8 @@ in test5 = { isNormalUser = true; }; }; - security.sudo.enable = false; - security.sudo-rs = { enable = true; - package = pkgs.sudo-rs; wheelNeedsPassword = false; extraRules = [ @@ -56,10 +53,7 @@ in noadmin = { isNormalUser = true; }; }; - security.sudo.enable = false; - security.sudo-rs = { - package = pkgs.sudo-rs; enable = true; wheelNeedsPassword = false; execWheelOnly = true; diff --git a/nixos/tests/systemd-boot.nix b/nixos/tests/systemd-boot.nix index 13007d0d80d8..256a18532b0a 100644 --- a/nixos/tests/systemd-boot.nix +++ b/nixos/tests/systemd-boot.nix @@ -252,6 +252,35 @@ in ''; }; + garbage-collect-entry = makeTest { + name = "systemd-boot-switch-test"; + meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; + + nodes = { + inherit common; + machine = { pkgs, nodes, ... }: { + imports = [ common ]; + + # These are configs for different nodes, but we'll use them here in `machine` + system.extraDependencies = [ + nodes.common.system.build.toplevel + ]; + }; + }; + + testScript = { nodes, ... }: + let + baseSystem = nodes.common.system.build.toplevel; + in + '' + machine.succeed("nix-env -p /nix/var/nix/profiles/system --set ${baseSystem}") + machine.succeed("nix-env -p /nix/var/nix/profiles/system --delete-generations 1") + machine.succeed("${baseSystem}/bin/switch-to-configuration boot") + machine.fail("test -e /boot/loader/entries/nixos-generation-1.conf") + machine.succeed("test -e /boot/loader/entries/nixos-generation-2.conf") + ''; + }; + # Some UEFI firmwares fail on large reads. Now that systemd-boot loads initrd # itself, systems with such firmware won't boot without this fix uefiLargeFileWorkaround = makeTest { @@ -277,4 +306,20 @@ in machine.wait_for_unit("multi-user.target") ''; }; + + no-bootspec = makeTest + { + name = "systemd-boot-no-bootspec"; + meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; + + nodes.machine = { + imports = [ common ]; + boot.bootspec.enable = false; + }; + + testScript = '' + machine.start() + machine.wait_for_unit("multi-user.target") + ''; + }; } diff --git a/nixos/tests/systemd-initrd-luks-unl0kr.nix b/nixos/tests/systemd-initrd-luks-unl0kr.nix new file mode 100644 index 000000000000..0658a098cfa2 --- /dev/null +++ b/nixos/tests/systemd-initrd-luks-unl0kr.nix @@ -0,0 +1,75 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: let + passphrase = "secret"; +in { + name = "systemd-initrd-luks-unl0kr"; + meta = with pkgs.lib.maintainers; { + maintainers = [ tomfitzhenry ]; + }; + + enableOCR = true; + + nodes.machine = { pkgs, ... }: { + virtualisation = { + emptyDiskImages = [ 512 512 ]; + useBootLoader = true; + mountHostNixStore = true; + useEFIBoot = true; + qemu.options = [ + "-vga virtio" + ]; + }; + boot.loader.systemd-boot.enable = true; + + boot.initrd.availableKernelModules = [ + "evdev" # for entering pw + "bochs" + ]; + + environment.systemPackages = with pkgs; [ cryptsetup ]; + boot.initrd = { + systemd = { + enable = true; + emergencyAccess = true; + }; + unl0kr.enable = true; + }; + + specialisation.boot-luks.configuration = { + boot.initrd.luks.devices = lib.mkVMOverride { + # We have two disks and only type one password - key reuse is in place + cryptroot.device = "/dev/vdb"; + cryptroot2.device = "/dev/vdc"; + }; + virtualisation.rootDevice = "/dev/mapper/cryptroot"; + virtualisation.fileSystems."/".autoFormat = true; + # test mounting device unlocked in initrd after switching root + virtualisation.fileSystems."/cryptroot2".device = "/dev/mapper/cryptroot2"; + }; + }; + + testScript = '' + # Create encrypted volume + machine.wait_for_unit("multi-user.target") + machine.succeed("echo -n ${passphrase} | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -") + machine.succeed("echo -n ${passphrase} | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -") + machine.succeed("echo -n ${passphrase} | cryptsetup luksOpen -q /dev/vdc cryptroot2") + machine.succeed("mkfs.ext4 /dev/mapper/cryptroot2") + + # Boot from the encrypted disk + machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf") + machine.succeed("sync") + machine.crash() + + # Boot and decrypt the disk + machine.start() + machine.wait_for_text("Password required for booting") + machine.screenshot("prompt") + machine.send_chars("${passphrase}") + machine.screenshot("pw") + machine.send_chars("\n") + machine.wait_for_unit("multi-user.target") + + assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount"), "/dev/mapper/cryptroot do not appear in mountpoints list" + assert "/dev/mapper/cryptroot2 on /cryptroot2 type ext4" in machine.succeed("mount") + ''; +}) diff --git a/nixos/tests/systemd-networkd.nix b/nixos/tests/systemd-networkd.nix index 6c423f4140b1..6b241b93d511 100644 --- a/nixos/tests/systemd-networkd.nix +++ b/nixos/tests/systemd-networkd.nix @@ -65,7 +65,7 @@ let generateNodeConf = { lib, pkgs, config, privk, pubk, peerId, nodeId, ...}: { in import ./make-test-python.nix ({pkgs, ... }: { name = "networkd"; meta = with pkgs.lib.maintainers; { - maintainers = [ ninjatrappeur ]; + maintainers = [ picnoir ]; }; nodes = { node1 = { pkgs, ... }@attrs: diff --git a/nixos/tests/systemd-timesyncd.nix b/nixos/tests/systemd-timesyncd.nix index f38d06be1516..02f49f49b8a5 100644 --- a/nixos/tests/systemd-timesyncd.nix +++ b/nixos/tests/systemd-timesyncd.nix @@ -15,13 +15,14 @@ in { # create the path that should be migrated by our activation script when # upgrading to a newer nixos version system.stateVersion = "19.03"; - systemd.tmpfiles.rules = [ - "r /var/lib/systemd/timesync -" - "d /var/lib/systemd -" - "d /var/lib/private/systemd/timesync -" - "L /var/lib/systemd/timesync - - - - /var/lib/private/systemd/timesync" - "d /var/lib/private/systemd/timesync - systemd-timesync systemd-timesync -" - ]; + systemd.tmpfiles.settings.systemd-timesyncd-test = { + "/var/lib/systemd/timesync".R = { }; + "/var/lib/systemd/timesync".L.argument = "/var/lib/private/systemd/timesync"; + "/var/lib/private/systemd/timesync".d = { + user = "systemd-timesync"; + group = "systemd-timesync"; + }; + }; }); }; diff --git a/nixos/tests/tandoor-recipes.nix b/nixos/tests/tandoor-recipes.nix index 54456238fe63..18beaac6f062 100644 --- a/nixos/tests/tandoor-recipes.nix +++ b/nixos/tests/tandoor-recipes.nix @@ -3,10 +3,8 @@ import ./make-test-python.nix ({ lib, ... }: { meta.maintainers = with lib.maintainers; [ ambroisie ]; nodes.machine = { pkgs, ... }: { - # Setup using Postgres services.tandoor-recipes = { enable = true; - extraConfig = { DB_ENGINE = "django.db.backends.postgresql"; POSTGRES_HOST = "/run/postgresql"; @@ -21,7 +19,7 @@ import ./make-test-python.nix ({ lib, ... }: { ensureUsers = [ { name = "tandoor_recipes"; - ensurePermissions."DATABASE tandoor_recipes" = "ALL PRIVILEGES"; + ensureDBOwnership = true; } ]; }; diff --git a/nixos/tests/telegraf.nix b/nixos/tests/telegraf.nix index c3cdb1645213..af9c5c387a5d 100644 --- a/nixos/tests/telegraf.nix +++ b/nixos/tests/telegraf.nix @@ -12,6 +12,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { services.telegraf.extraConfig = { agent.interval = "1s"; agent.flush_interval = "1s"; + inputs.procstat = {}; inputs.exec = { commands = [ "${pkgs.runtimeShell} -c 'echo $SECRET,tag=a i=42i'" diff --git a/nixos/tests/teleport.nix b/nixos/tests/teleport.nix index cdf762b12844..d68917c6c7ac 100644 --- a/nixos/tests/teleport.nix +++ b/nixos/tests/teleport.nix @@ -9,7 +9,8 @@ with import ../lib/testing-python.nix { inherit system pkgs; }; let packages = with pkgs; { "default" = teleport; - "11" = teleport_11; + "12" = teleport_12; + "13" = teleport_13; }; minimal = package: { diff --git a/nixos/tests/terminal-emulators.nix b/nixos/tests/terminal-emulators.nix index 6d76cc8e5741..efaac03e1983 100644 --- a/nixos/tests/terminal-emulators.nix +++ b/nixos/tests/terminal-emulators.nix @@ -23,9 +23,8 @@ with pkgs.lib; let tests = { alacritty.pkg = p: p.alacritty; - # times out after spending many hours - #contour.pkg = p: p.contour; - #contour.cmd = "contour $command"; + contour.pkg = p: p.contour; + contour.cmd = "contour early-exit-threshold 0 execute $command"; cool-retro-term.pkg = p: p.cool-retro-term; cool-retro-term.colourTest = false; # broken by gloss effect @@ -76,6 +75,7 @@ let tests = { rio.pkg = p: p.rio; rio.cmd = "rio -e $command"; + rio.colourTest = false; # the rendering is changing too much so colors change every release. roxterm.pkg = p: p.roxterm; roxterm.cmd = "roxterm -e $command"; diff --git a/nixos/tests/tomcat.nix b/nixos/tests/tomcat.nix index 4cfb3cc5a7d8..ff58ca8ac618 100644 --- a/nixos/tests/tomcat.nix +++ b/nixos/tests/tomcat.nix @@ -1,21 +1,27 @@ -import ./make-test-python.nix ({ pkgs, ... }: - -{ +import ./make-test-python.nix ({ pkgs, ... }: { name = "tomcat"; + meta.maintainers = [ lib.maintainers.anthonyroussel ]; nodes.machine = { pkgs, ... }: { - services.tomcat.enable = true; + services.tomcat = { + enable = true; + axis2.enable = true; + }; }; testScript = '' machine.wait_for_unit("tomcat.service") machine.wait_for_open_port(8080) machine.wait_for_file("/var/tomcat/webapps/examples"); + + machine.succeed( + "curl -sS --fail http://localhost:8080/examples/servlets/servlet/HelloWorldExample | grep 'Hello World!'" + ) machine.succeed( - "curl --fail http://localhost:8080/examples/servlets/servlet/HelloWorldExample | grep 'Hello World!'" + "curl -sS --fail http://localhost:8080/examples/jsp/jsp2/simpletag/hello.jsp | grep 'Hello, world!'" ) machine.succeed( - "curl --fail http://localhost:8080/examples/jsp/jsp2/simpletag/hello.jsp | grep 'Hello, world!'" + "curl -sS --fail http://localhost:8080/axis2/axis2-web/HappyAxis.jsp | grep 'Found Axis2'" ) ''; }) diff --git a/nixos/tests/tsm-client-gui.nix b/nixos/tests/tsm-client-gui.nix index e11501da53d0..c9632546db6e 100644 --- a/nixos/tests/tsm-client-gui.nix +++ b/nixos/tests/tsm-client-gui.nix @@ -18,9 +18,9 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { defaultServername = "testserver"; servers.testserver = { # 192.0.0.8 is a "dummy address" according to RFC 7600 - server = "192.0.0.8"; - node = "SOME-NODE"; - passwdDir = "/tmp"; + tcpserveraddress = "192.0.0.8"; + nodename = "SOME-NODE"; + passworddir = "/tmp"; }; }; }; diff --git a/nixos/tests/udisks2.nix b/nixos/tests/udisks2.nix index 6afb200f8566..8cc148750c7b 100644 --- a/nixos/tests/udisks2.nix +++ b/nixos/tests/udisks2.nix @@ -32,6 +32,9 @@ in '' import lzma + machine.systemctl("start udisks2") + machine.wait_for_unit("udisks2.service") + with lzma.open( "${stick}" ) as data, open(machine.state_dir / "usbstick.img", "wb") as stick: diff --git a/nixos/tests/vikunja.nix b/nixos/tests/vikunja.nix index 2660aa9767ca..60fd5ce13854 100644 --- a/nixos/tests/vikunja.nix +++ b/nixos/tests/vikunja.nix @@ -33,7 +33,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { ensureDatabases = [ "vikunja-api" ]; ensureUsers = [ { name = "vikunja-api"; - ensurePermissions = { "DATABASE \"vikunja-api\"" = "ALL PRIVILEGES"; }; + ensureDBOwnership = true; } ]; }; diff --git a/nixos/tests/web-apps/mastodon/remote-postgresql.nix b/nixos/tests/web-apps/mastodon/remote-postgresql.nix index 715477191bfb..6548883db452 100644 --- a/nixos/tests/web-apps/mastodon/remote-postgresql.nix +++ b/nixos/tests/web-apps/mastodon/remote-postgresql.nix @@ -16,7 +16,7 @@ in meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin ]; nodes = { - database = { + database = { config, ... }: { networking = { interfaces.eth1 = { ipv4.addresses = [ @@ -24,11 +24,13 @@ in ]; }; extraHosts = hosts; - firewall.allowedTCPPorts = [ 5432 ]; + firewall.allowedTCPPorts = [ config.services.postgresql.port ]; }; services.postgresql = { enable = true; + # TODO remove once https://github.com/NixOS/nixpkgs/pull/266270 is resolved. + package = pkgs.postgresql_14; enableTCPIP = true; authentication = '' hostnossl mastodon_local mastodon_test 192.168.2.201/32 md5 @@ -41,7 +43,7 @@ in }; }; - nginx = { + nginx = { nodes, ... }: { networking = { interfaces.eth1 = { ipv4.addresses = [ @@ -69,18 +71,14 @@ in tryFiles = "$uri @proxy"; }; locations."@proxy" = { - proxyPass = "http://192.168.2.201:55001"; - proxyWebsockets = true; - }; - locations."/api/v1/streaming/" = { - proxyPass = "http://192.168.2.201:55002"; + proxyPass = "http://192.168.2.201:${toString nodes.server.services.mastodon.webPort}"; proxyWebsockets = true; }; }; }; }; - server = { pkgs, ... }: { + server = { config, pkgs, ... }: { virtualisation.memorySize = 2048; environment = { @@ -98,7 +96,10 @@ in ]; }; extraHosts = hosts; - firewall.allowedTCPPorts = [ 55001 55002 ]; + firewall.allowedTCPPorts = [ + config.services.mastodon.webPort + config.services.mastodon.sidekiqPort + ]; }; services.mastodon = { @@ -106,6 +107,7 @@ in configureNginx = false; localDomain = "mastodon.local"; enableUnixSocket = false; + streamingProcesses = 2; database = { createLocally = false; host = "192.168.2.102"; diff --git a/nixos/tests/web-apps/mastodon/script.nix b/nixos/tests/web-apps/mastodon/script.nix index a89b4b7480e9..afb7c0e0a0eb 100644 --- a/nixos/tests/web-apps/mastodon/script.nix +++ b/nixos/tests/web-apps/mastodon/script.nix @@ -10,9 +10,8 @@ server.wait_for_unit("redis-mastodon.service") server.wait_for_unit("mastodon-sidekiq-all.service") - server.wait_for_unit("mastodon-streaming.service") + server.wait_for_unit("mastodon-streaming.target") server.wait_for_unit("mastodon-web.service") - server.wait_for_open_port(55000) server.wait_for_open_port(55001) # Check that mastodon-media-auto-remove is scheduled diff --git a/nixos/tests/web-apps/mastodon/standard.nix b/nixos/tests/web-apps/mastodon/standard.nix index 14311afea3f7..e5eb30fef597 100644 --- a/nixos/tests/web-apps/mastodon/standard.nix +++ b/nixos/tests/web-apps/mastodon/standard.nix @@ -40,11 +40,15 @@ in port = 31637; }; + # TODO remove once https://github.com/NixOS/nixpkgs/pull/266270 is resolved. + services.postgresql.package = pkgs.postgresql_14; + services.mastodon = { enable = true; configureNginx = true; localDomain = "mastodon.local"; enableUnixSocket = false; + streamingProcesses = 2; smtp = { createLocally = false; fromAddress = "mastodon@mastodon.local"; diff --git a/nixos/tests/web-servers/stargazer.nix b/nixos/tests/web-servers/stargazer.nix index c522cfee5dbc..6365d6a4fff1 100644 --- a/nixos/tests/web-servers/stargazer.nix +++ b/nixos/tests/web-servers/stargazer.nix @@ -24,7 +24,7 @@ geminiserver.wait_for_open_port(1965) with subtest("check is serving over gemini"): - response = geminiserver.succeed("${pkgs.gmni}/bin/gmni -j once -i -N gemini://localhost:1965") + response = geminiserver.succeed("${pkgs.gemget}/bin/gemget --header -o - gemini://localhost:1965") print(response) assert "Hello NixOS!" in response ''; diff --git a/nixos/tests/wiki-js.nix b/nixos/tests/wiki-js.nix index fd054a9c5909..8b3c51935a6c 100644 --- a/nixos/tests/wiki-js.nix +++ b/nixos/tests/wiki-js.nix @@ -10,14 +10,15 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { enable = true; settings.db.host = "/run/postgresql"; settings.db.user = "wiki-js"; + settings.db.db = "wiki-js"; settings.logLevel = "debug"; }; services.postgresql = { enable = true; - ensureDatabases = [ "wiki" ]; + ensureDatabases = [ "wiki-js" ]; ensureUsers = [ { name = "wiki-js"; - ensurePermissions."DATABASE wiki" = "ALL PRIVILEGES"; + ensureDBOwnership = true; } ]; }; diff --git a/nixos/tests/wordpress.nix b/nixos/tests/wordpress.nix index 937b505af2ac..592af9a094f1 100644 --- a/nixos/tests/wordpress.nix +++ b/nixos/tests/wordpress.nix @@ -67,7 +67,7 @@ rec { networking.hosts."127.0.0.1" = [ "site1.local" "site2.local" ]; }; }) {} [ - "6_3" + "6_3" "6_4" ]; testScript = '' diff --git a/nixos/tests/xmpp/ejabberd.nix b/nixos/tests/xmpp/ejabberd.nix index 7926fe80de2f..1a807b27b6f6 100644 --- a/nixos/tests/xmpp/ejabberd.nix +++ b/nixos/tests/xmpp/ejabberd.nix @@ -1,7 +1,7 @@ import ../make-test-python.nix ({ pkgs, ... }: { name = "ejabberd"; meta = with pkgs.lib.maintainers; { - maintainers = [ ajs124 ]; + maintainers = [ ]; }; nodes = { client = { nodes, pkgs, ... }: { diff --git a/nixos/tests/xscreensaver.nix b/nixos/tests/xscreensaver.nix new file mode 100644 index 000000000000..820ddbb0e962 --- /dev/null +++ b/nixos/tests/xscreensaver.nix @@ -0,0 +1,64 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "pass-secret-service"; + meta.maintainers = with lib.maintainers; [ vancluever AndersonTorres ]; + + nodes = { + ok = { nodes, pkgs, ... }: + { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + test-support.displayManager.auto.user = "alice"; + services.xscreensaver.enable = true; + }; + + empty_wrapperPrefix = { nodes, pkgs, ... }: + { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + test-support.displayManager.auto.user = "alice"; + services.xscreensaver.enable = true; + nixpkgs.overlays = [ + (self: super: { + xscreensaver = super.xscreensaver.override { + wrapperPrefix = ""; + }; + }) + ]; + }; + + bad_wrapperPrefix = { nodes, pkgs, ... }: + { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + test-support.displayManager.auto.user = "alice"; + services.xscreensaver.enable = true; + nixpkgs.overlays = [ + (self: super: { + xscreensaver = super.xscreensaver.override { + wrapperPrefix = "/a/bad/path"; + }; + }) + ]; + }; + }; + + testScript = '' + ok.wait_for_x() + ok.wait_for_unit("xscreensaver", "alice") + _, output_ok = ok.systemctl("status xscreensaver", "alice") + assert 'To prevent the kernel from randomly unlocking' not in output_ok + assert 'your screen via the out-of-memory killer' not in output_ok + assert '"xscreensaver-auth" must be setuid root' not in output_ok + + empty_wrapperPrefix.wait_for_x() + empty_wrapperPrefix.wait_for_unit("xscreensaver", "alice") + _, output_empty_wrapperPrefix = empty_wrapperPrefix.systemctl("status xscreensaver", "alice") + assert 'To prevent the kernel from randomly unlocking' in output_empty_wrapperPrefix + assert 'your screen via the out-of-memory killer' in output_empty_wrapperPrefix + assert '"xscreensaver-auth" must be setuid root' in output_empty_wrapperPrefix + + bad_wrapperPrefix.wait_for_x() + bad_wrapperPrefix.wait_for_unit("xscreensaver", "alice") + _, output_bad_wrapperPrefix = bad_wrapperPrefix.systemctl("status xscreensaver", "alice") + assert 'To prevent the kernel from randomly unlocking' in output_bad_wrapperPrefix + assert 'your screen via the out-of-memory killer' in output_bad_wrapperPrefix + assert '"xscreensaver-auth" must be setuid root' in output_bad_wrapperPrefix + ''; +}) diff --git a/nixos/tests/zfs.nix b/nixos/tests/zfs.nix index 3454fbaf78fe..ad4ea254f34d 100644 --- a/nixos/tests/zfs.nix +++ b/nixos/tests/zfs.nix @@ -13,6 +13,7 @@ let else pkgs.linuxPackages , enableUnstable ? false , enableSystemdStage1 ? false + , zfsPackage ? if enableUnstable then pkgs.zfs else pkgs.zfsUnstable , extraTest ? "" }: makeTest { @@ -21,7 +22,7 @@ let maintainers = [ adisbladis elvishjerricco ]; }; - nodes.machine = { pkgs, lib, ... }: + nodes.machine = { config, pkgs, lib, ... }: let usersharePath = "/var/lib/samba/usershares"; in { @@ -35,8 +36,8 @@ let boot.loader.efi.canTouchEfiVariables = true; networking.hostId = "deadbeef"; boot.kernelPackages = kernelPackage; + boot.zfs.package = zfsPackage; boot.supportedFilesystems = [ "zfs" ]; - boot.zfs.enableUnstable = enableUnstable; boot.initrd.systemd.enable = enableSystemdStage1; environment.systemPackages = [ pkgs.parted ]; @@ -193,6 +194,11 @@ let in { + # maintainer: @raitobezarius + series_2_1 = makeZfsTest "2.1-series" { + zfsPackage = pkgs.zfs_2_1; + }; + stable = makeZfsTest "stable" { }; unstable = makeZfsTest "unstable" { |