diff options
author | Alyssa Ross <hi@alyssa.is> | 2023-11-21 16:12:21 +0100 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2023-11-21 16:12:48 +0100 |
commit | 048a4cd441a59cbf89defb18bb45c9f0b4429b35 (patch) | |
tree | f8f5850ff05521ab82d65745894714a8796cbfb6 /nixos/tests | |
parent | 030c5028b07afcedce7c5956015c629486cc79d9 (diff) | |
parent | 4c2d05dd6435d449a3651a6dd314d9411b5f8146 (diff) | |
download | nixpkgs-rootfs.tar nixpkgs-rootfs.tar.gz nixpkgs-rootfs.tar.bz2 nixpkgs-rootfs.tar.lz nixpkgs-rootfs.tar.xz nixpkgs-rootfs.tar.zst nixpkgs-rootfs.zip |
Signed-off-by: Alyssa Ross <hi@alyssa.is>
Diffstat (limited to 'nixos/tests')
165 files changed, 3675 insertions, 1083 deletions
diff --git a/nixos/tests/activation/nix-channel.nix b/nixos/tests/activation/nix-channel.nix new file mode 100644 index 00000000000..d26ea98e56c --- /dev/null +++ b/nixos/tests/activation/nix-channel.nix @@ -0,0 +1,26 @@ +{ lib, ... }: + +{ + + name = "activation-nix-channel"; + + meta.maintainers = with lib.maintainers; [ nikstur ]; + + nodes.machine = { + nix.channel.enable = true; + }; + + testScript = { nodes, ... }: '' + machine.start(allow_reboot=True) + + assert machine.succeed("cat /root/.nix-channels") == "${nodes.machine.system.defaultChannel} nixos\n" + + nixpkgs_unstable_channel = "https://nixos.org/channels/nixpkgs-unstable nixpkgs" + machine.succeed(f"echo '{nixpkgs_unstable_channel}' > /root/.nix-channels") + + machine.reboot() + + assert machine.succeed("cat /root/.nix-channels") == f"{nixpkgs_unstable_channel}\n" + ''; + +} diff --git a/nixos/tests/activation/var.nix b/nixos/tests/activation/var.nix new file mode 100644 index 00000000000..1a546a7671c --- /dev/null +++ b/nixos/tests/activation/var.nix @@ -0,0 +1,18 @@ +{ lib, ... }: + +{ + + name = "activation-var"; + + meta.maintainers = with lib.maintainers; [ nikstur ]; + + nodes.machine = { }; + + testScript = '' + assert machine.succeed("stat -c '%a' /var/tmp") == "1777\n" + assert machine.succeed("stat -c '%a' /var/empty") == "555\n" + assert machine.succeed("stat -c '%U' /var/empty") == "root\n" + assert machine.succeed("stat -c '%G' /var/empty") == "root\n" + assert "i" in machine.succeed("lsattr -d /var/empty") + ''; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index a3e85c337aa..325e99c9774 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -90,6 +90,14 @@ in { lib-extend = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./nixos-test-driver/lib-extend.nix {}; node-name = runTest ./nixos-test-driver/node-name.nix; busybox = runTest ./nixos-test-driver/busybox.nix; + driver-timeout = pkgs.runCommand "ensure-timeout-induced-failure" { + failed = pkgs.testers.testBuildFailure ((runTest ./nixos-test-driver/timeout.nix).config.rawTestDerivation); + } '' + grep -F "timeout reached; test terminating" $failed/testBuildFailure.log + # The program will always be terminated by SIGTERM (143) if it waits for the deadline thread. + [[ 143 = $(cat $failed/testBuildFailure.exit) ]] + touch $out + ''; }; # NixOS vm tests and non-vm unit tests @@ -116,9 +124,11 @@ in { 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 {}; + audiobookshelf = handleTest ./audiobookshelf.nix {}; auth-mysql = handleTest ./auth-mysql.nix {}; authelia = handleTest ./authelia.nix {}; avahi = handleTest ./avahi.nix {}; @@ -152,12 +162,14 @@ in { budgie = handleTest ./budgie.nix {}; buildbot = handleTest ./buildbot.nix {}; buildkite-agents = handleTest ./buildkite-agents.nix {}; + c2fmzq = handleTest ./c2fmzq.nix {}; caddy = handleTest ./caddy.nix {}; cadvisor = handleTestOn ["x86_64-linux"] ./cadvisor.nix {}; cage = handleTest ./cage.nix {}; cagebreak = handleTest ./cagebreak.nix {}; calibre-web = handleTest ./calibre-web.nix {}; calibre-server = handleTest ./calibre-server.nix {}; + castopod = handleTest ./castopod.nix {}; 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; }; @@ -180,7 +192,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 {}; @@ -214,6 +225,7 @@ in { darling = handleTest ./darling.nix {}; dae = handleTest ./dae.nix {}; dconf = handleTest ./dconf.nix {}; + deconz = handleTest ./deconz.nix {}; deepin = handleTest ./deepin.nix {}; deluge = handleTest ./deluge.nix {}; dendrite = handleTest ./matrix/dendrite.nix {}; @@ -245,6 +257,7 @@ in { ec2-nixops = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-nixops or {}; ecryptfs = handleTest ./ecryptfs.nix {}; fscrypt = handleTest ./fscrypt.nix {}; + fastnetmon-advanced = runTest ./fastnetmon-advanced.nix; ejabberd = handleTest ./xmpp/ejabberd.nix {}; elk = handleTestOn ["x86_64-linux"] ./elk.nix {}; emacs-daemon = handleTest ./emacs-daemon.nix {}; @@ -261,6 +274,8 @@ in { esphome = handleTest ./esphome.nix {}; etc = pkgs.callPackage ../modules/system/etc/test.nix { inherit evalMinimalConfig; }; activation = pkgs.callPackage ../modules/system/activation/test.nix { }; + activation-var = runTest ./activation/var.nix; + activation-nix-channel = runTest ./activation/nix-channel.nix; etcd = handleTestOn ["x86_64-linux"] ./etcd.nix {}; etcd-cluster = handleTestOn ["x86_64-linux"] ./etcd-cluster.nix {}; etebase-server = handleTest ./etebase-server.nix {}; @@ -269,9 +284,11 @@ in { fail2ban = handleTest ./fail2ban.nix { }; fakeroute = handleTest ./fakeroute.nix {}; fancontrol = handleTest ./fancontrol.nix {}; + fanout = handleTest ./fanout.nix {}; fcitx5 = handleTest ./fcitx5 {}; fenics = handleTest ./fenics.nix {}; ferm = handleTest ./ferm.nix {}; + ferretdb = handleTest ./ferretdb.nix {}; firefox = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox; }; firefox-beta = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-beta; }; firefox-devedition = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-devedition; }; @@ -282,12 +299,14 @@ in { firewall-nftables = handleTest ./firewall.nix { nftables = true; }; fish = handleTest ./fish.nix {}; flannel = handleTestOn ["x86_64-linux"] ./flannel.nix {}; + floorp = handleTest ./firefox.nix { firefoxPackage = pkgs.floorp; }; fluentd = handleTest ./fluentd.nix {}; fluidd = handleTest ./fluidd.nix {}; fontconfig-default-fonts = handleTest ./fontconfig-default-fonts.nix {}; forgejo = handleTest ./forgejo.nix { }; freenet = handleTest ./freenet.nix {}; freeswitch = handleTest ./freeswitch.nix {}; + freetube = discoverTests (import ./freetube.nix); freshrss-sqlite = handleTest ./freshrss-sqlite.nix {}; freshrss-pgsql = handleTest ./freshrss-pgsql.nix {}; frigate = handleTest ./frigate.nix {}; @@ -321,6 +340,7 @@ in { gollum = handleTest ./gollum.nix {}; gonic = handleTest ./gonic.nix {}; google-oslogin = handleTest ./google-oslogin {}; + goss = handleTest ./goss.nix {}; gotify-server = handleTest ./gotify-server.nix {}; gotosocial = runTest ./web-apps/gotosocial.nix; grafana = handleTest ./grafana {}; @@ -328,8 +348,8 @@ in { graphite = handleTest ./graphite.nix {}; graylog = handleTest ./graylog.nix {}; grocy = handleTest ./grocy.nix {}; + grow-partition = runTest ./grow-partition.nix; grub = handleTest ./grub.nix {}; - guacamole-client = handleTest ./guacamole-client.nix {}; guacamole-server = handleTest ./guacamole-server.nix {}; gvisor = handleTest ./gvisor.nix {}; hadoop = import ./hadoop { inherit handleTestOn; package=pkgs.hadoop; }; @@ -352,6 +372,7 @@ in { honk = runTest ./honk.nix; installed-tests = pkgs.recurseIntoAttrs (handleTest ./installed-tests {}); invidious = handleTest ./invidious.nix {}; + livebook-service = handleTest ./livebook-service.nix {}; oci-containers = handleTestOn ["aarch64-linux" "x86_64-linux"] ./oci-containers.nix {}; odoo = handleTest ./odoo.nix {}; odoo15 = handleTest ./odoo.nix { package = pkgs.odoo15; }; @@ -373,6 +394,7 @@ in { icingaweb2 = handleTest ./icingaweb2.nix {}; iftop = handleTest ./iftop.nix {}; incron = handleTest ./incron.nix {}; + incus = pkgs.recurseIntoAttrs (handleTest ./incus { inherit handleTestOn; }); influxdb = handleTest ./influxdb.nix {}; influxdb2 = handleTest ./influxdb2.nix {}; initrd-network-openvpn = handleTest ./initrd-network-openvpn {}; @@ -423,14 +445,16 @@ in { ksm = handleTest ./ksm.nix {}; kthxbye = handleTest ./kthxbye.nix {}; kubernetes = handleTestOn ["x86_64-linux"] ./kubernetes {}; - kubo = runTest ./kubo.nix; + kubo = import ./kubo { inherit recurseIntoAttrs runTest; }; ladybird = handleTest ./ladybird.nix {}; languagetool = handleTest ./languagetool.nix {}; + lanraragi = handleTest ./lanraragi.nix {}; latestKernel.login = handleTest ./login.nix { latestKernel = true; }; leaps = handleTest ./leaps.nix {}; lemmy = handleTest ./lemmy.nix {}; libinput = handleTest ./libinput.nix {}; libreddit = handleTest ./libreddit.nix {}; + librenms = handleTest ./librenms.nix {}; libresprite = handleTest ./libresprite.nix {}; libreswan = handleTest ./libreswan.nix {}; librewolf = handleTest ./firefox.nix { firefoxPackage = pkgs.librewolf; }; @@ -530,8 +554,8 @@ in { netdata = handleTest ./netdata.nix {}; networking.networkd = handleTest ./networking.nix { networkd = true; }; networking.scripted = handleTest ./networking.nix { networkd = false; }; - netbox = handleTest ./web-apps/netbox.nix { inherit (pkgs) netbox; }; - netbox_3_3 = handleTest ./web-apps/netbox.nix { netbox = pkgs.netbox_3_3; }; + netbox_3_5 = handleTest ./web-apps/netbox.nix { netbox = pkgs.netbox_3_5; }; + netbox_3_6 = handleTest ./web-apps/netbox.nix { netbox = pkgs.netbox_3_6; }; netbox-upgrade = handleTest ./web-apps/netbox-upgrade.nix {}; # TODO: put in networking.nix after the test becomes more complete networkingProxy = handleTest ./networking-proxy.nix {}; @@ -550,9 +574,10 @@ in { nginx-njs = handleTest ./nginx-njs.nix {}; nginx-proxyprotocol = handleTest ./nginx-proxyprotocol {}; nginx-pubhtml = handleTest ./nginx-pubhtml.nix {}; - nginx-sandbox = handleTestOn ["x86_64-linux"] ./nginx-sandbox.nix {}; nginx-sso = handleTest ./nginx-sso.nix {}; nginx-status-page = handleTest ./nginx-status-page.nix {}; + nginx-tmpdir = handleTest ./nginx-tmpdir.nix {}; + nginx-unix-socket = handleTest ./nginx-unix-socket.nix {}; nginx-variants = handleTest ./nginx-variants.nix {}; nifi = handleTestOn ["x86_64-linux"] ./web-apps/nifi.nix {}; nitter = handleTest ./nitter.nix {}; @@ -561,11 +586,13 @@ in { nix-serve-ssh = handleTest ./nix-serve-ssh.nix {}; nixops = handleTest ./nixops/default.nix {}; nixos-generate-config = handleTest ./nixos-generate-config.nix {}; - nixos-rebuild-specialisations = handleTest ./nixos-rebuild-specialisations.nix {}; + nixos-rebuild-install-bootloader = handleTestOn ["x86_64-linux"] ./nixos-rebuild-install-bootloader.nix {}; + nixos-rebuild-specialisations = handleTestOn ["x86_64-linux"] ./nixos-rebuild-specialisations.nix {}; nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { inherit evalMinimalConfig; }; node-red = handleTest ./node-red.nix {}; nomad = handleTest ./nomad.nix {}; non-default-filesystems = handleTest ./non-default-filesystems.nix {}; + non-switchable-system = runTest ./non-switchable-system.nix; noto-fonts = handleTest ./noto-fonts.nix {}; noto-fonts-cjk-qt-default-weight = handleTest ./noto-fonts-cjk-qt-default-weight.nix {}; novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {}; @@ -629,6 +656,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 {}; @@ -658,7 +686,6 @@ in { predictable-interface-names = handleTest ./predictable-interface-names.nix {}; printing-socket = handleTest ./printing.nix { socket = true; }; printing-service = handleTest ./printing.nix { socket = false; }; - privacyidea = handleTest ./privacyidea.nix {}; privoxy = handleTest ./privoxy.nix {}; prometheus = handleTest ./prometheus.nix {}; prometheus-exporters = handleTest ./prometheus-exporters.nix {}; @@ -674,6 +701,7 @@ in { qboot = handleTestOn ["x86_64-linux" "i686-linux"] ./qboot.nix {}; qemu-vm-restrictnetwork = handleTest ./qemu-vm-restrictnetwork.nix {}; qemu-vm-volatile-root = runTest ./qemu-vm-volatile-root.nix; + qemu-vm-external-disk-image = runTest ./qemu-vm-external-disk-image.nix; qgis = handleTest ./qgis.nix { qgisPackage = pkgs.qgis; }; qgis-ltr = handleTest ./qgis.nix { qgisPackage = pkgs.qgis-ltr; }; qownnotes = handleTest ./qownnotes.nix {}; @@ -690,8 +718,10 @@ in { restartByActivationScript = handleTest ./restart-by-activation-script.nix {}; restic = handleTest ./restic.nix {}; retroarch = handleTest ./retroarch.nix {}; + rkvm = handleTest ./rkvm {}; robustirc-bridge = handleTest ./robustirc-bridge.nix {}; roundcube = handleTest ./roundcube.nix {}; + rosenpass = handleTest ./rosenpass.nix {}; rshim = handleTest ./rshim.nix {}; rspamd = handleTest ./rspamd.nix {}; rss2email = handleTest ./rss2email.nix {}; @@ -711,7 +741,7 @@ in { service-runner = handleTest ./service-runner.nix {}; sftpgo = runTest ./sftpgo.nix; sfxr-qt = handleTest ./sfxr-qt.nix {}; - sgtpuzzles = handleTest ./sgtpuzzles.nix {}; + sgt-puzzles = handleTest ./sgt-puzzles.nix {}; shadow = handleTest ./shadow.nix {}; shadowsocks = handleTest ./shadowsocks {}; shattered-pixel-dungeon = handleTest ./shattered-pixel-dungeon.nix {}; @@ -719,12 +749,14 @@ 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 {}; snapper = handleTest ./snapper.nix {}; snipe-it = runTest ./web-apps/snipe-it.nix; soapui = handleTest ./soapui.nix {}; + soft-serve = handleTest ./soft-serve.nix {}; sogo = handleTest ./sogo.nix {}; solanum = handleTest ./solanum.nix {}; sonarr = handleTest ./sonarr.nix {}; @@ -733,8 +765,9 @@ in { spark = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./spark {}; sqlite3-to-mysql = handleTest ./sqlite3-to-mysql.nix {}; sslh = handleTest ./sslh.nix {}; - sssd = handleTestOn ["x86_64-linux"] ./sssd.nix {}; - sssd-ldap = handleTestOn ["x86_64-linux"] ./sssd-ldap.nix {}; + ssh-audit = handleTest ./ssh-audit.nix {}; + sssd = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./sssd.nix {}; + sssd-ldap = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./sssd-ldap.nix {}; stalwart-mail = handleTest ./stalwart-mail.nix {}; stargazer = runTest ./web-servers/stargazer.nix; starship = handleTest ./starship.nix {}; @@ -754,6 +787,7 @@ in { syncthing = handleTest ./syncthing.nix {}; syncthing-no-settings = handleTest ./syncthing-no-settings.nix {}; syncthing-init = handleTest ./syncthing-init.nix {}; + syncthing-many-devices = handleTest ./syncthing-many-devices.nix {}; syncthing-relay = handleTest ./syncthing-relay.nix {}; systemd = handleTest ./systemd.nix {}; systemd-analyze = handleTest ./systemd-analyze.nix {}; @@ -802,6 +836,7 @@ in { systemd-userdbd = handleTest ./systemd-userdbd.nix {}; systemd-homed = handleTest ./systemd-homed.nix {}; tandoor-recipes = handleTest ./tandoor-recipes.nix {}; + tang = handleTest ./tang.nix {}; taskserver = handleTest ./taskserver.nix {}; tayga = handleTest ./tayga.nix {}; teeworlds = handleTest ./teeworlds.nix {}; @@ -816,18 +851,21 @@ in { timezone = handleTest ./timezone.nix {}; tinc = handleTest ./tinc {}; tinydns = handleTest ./tinydns.nix {}; + tinyproxy = handleTest ./tinyproxy.nix {}; tinywl = handleTest ./tinywl.nix {}; tmate-ssh-server = handleTest ./tmate-ssh-server.nix { }; tomcat = handleTest ./tomcat.nix {}; tor = handleTest ./tor.nix {}; traefik = handleTestOn ["aarch64-linux" "x86_64-linux"] ./traefik.nix {}; trafficserver = handleTest ./trafficserver.nix {}; - transmission = handleTest ./transmission.nix {}; + transmission = handleTest ./transmission.nix { transmission = pkgs.transmission; }; + transmission_4 = handleTest ./transmission.nix { transmission = pkgs.transmission_4; }; # tracee requires bpf tracee = handleTestOn ["x86_64-linux"] ./tracee.nix {}; trezord = handleTest ./trezord.nix {}; trickster = handleTest ./trickster.nix {}; trilium-server = handleTestOn ["x86_64-linux"] ./trilium-server.nix {}; + tsja = handleTest ./tsja.nix {}; tsm-client-gui = handleTest ./tsm-client-gui.nix {}; txredisapi = handleTest ./txredisapi.nix {}; tuptime = handleTest ./tuptime.nix {}; @@ -837,7 +875,7 @@ in { typesense = handleTest ./typesense.nix {}; ucarp = handleTest ./ucarp.nix {}; udisks2 = handleTest ./udisks2.nix {}; - ulogd = handleTest ./ulogd.nix {}; + ulogd = handleTest ./ulogd/ulogd.nix {}; unbound = handleTest ./unbound.nix {}; unifi = handleTest ./unifi.nix {}; unit-php = handleTest ./web-servers/unit-php.nix {}; @@ -851,8 +889,7 @@ in { uwsgi = handleTest ./uwsgi.nix {}; v2ray = handleTest ./v2ray.nix {}; varnish60 = handleTest ./varnish.nix { package = pkgs.varnish60; }; - varnish72 = handleTest ./varnish.nix { package = pkgs.varnish72; }; - varnish73 = handleTest ./varnish.nix { package = pkgs.varnish73; }; + varnish74 = handleTest ./varnish.nix { package = pkgs.varnish74; }; vault = handleTest ./vault.nix {}; vault-agent = handleTest ./vault-agent.nix {}; vault-dev = handleTest ./vault-dev.nix {}; @@ -899,4 +936,5 @@ in { zram-generator = handleTest ./zram-generator.nix {}; zrepl = handleTest ./zrepl.nix {}; zsh-history = handleTest ./zsh-history.nix {}; + zwave-js = handleTest ./zwave-js.nix {}; } diff --git a/nixos/tests/archi.nix b/nixos/tests/archi.nix new file mode 100644 index 00000000000..59f2e940c00 --- /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/audiobookshelf.nix b/nixos/tests/audiobookshelf.nix new file mode 100644 index 00000000000..64bd415160e --- /dev/null +++ b/nixos/tests/audiobookshelf.nix @@ -0,0 +1,23 @@ +import ./make-test-python.nix ({ lib, ... }: + +with lib; + +{ + name = "audiobookshelf"; + meta.maintainers = with maintainers; [ wietsedv ]; + + nodes.machine = + { pkgs, ... }: + { + services.audiobookshelf = { + enable = true; + port = 1234; + }; + }; + + testScript = '' + machine.wait_for_unit("audiobookshelf.service") + machine.wait_for_open_port(1234) + machine.succeed("curl --fail http://localhost:1234/") + ''; +}) diff --git a/nixos/tests/bcachefs.nix b/nixos/tests/bcachefs.nix index 0385e098997..ec3c2427f38 100644 --- a/nixos/tests/bcachefs.nix +++ b/nixos/tests/bcachefs.nix @@ -20,9 +20,8 @@ import ./make-test-python.nix ({ pkgs, ... }: { "parted --script /dev/vdb mklabel msdos", "parted --script /dev/vdb -- mkpart primary 1024M 50% mkpart primary 50% -1s", "udevadm settle", - "keyctl link @u @s", "echo password | bcachefs format --encrypted --metadata_replicas 2 --label vtest /dev/vdb1 /dev/vdb2", - "echo password | bcachefs unlock /dev/vdb1", + "echo password | bcachefs unlock -k session /dev/vdb1", "echo password | mount -t bcachefs /dev/vdb1:/dev/vdb2 /tmp/mnt", "udevadm settle", "bcachefs fs usage /tmp/mnt", diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix index 11420cba9dc..4a73fea6a09 100644 --- a/nixos/tests/bittorrent.nix +++ b/nixos/tests/bittorrent.nix @@ -148,7 +148,7 @@ in ) # Bring down the initial seeder. - # tracker.stop_job("transmission") + tracker.stop_job("transmission") # Now download from the second client. This can only succeed if # the first client created a NAT hole in the router. diff --git a/nixos/tests/buildbot.nix b/nixos/tests/buildbot.nix index 467c8d8baff..dbf68aba946 100644 --- a/nixos/tests/buildbot.nix +++ b/nixos/tests/buildbot.nix @@ -1,11 +1,6 @@ # Test ensures buildbot master comes up correctly and workers can connect -{ system ? builtins.currentSystem, - config ? {}, - pkgs ? import ../.. { inherit system config; } -}: - -import ./make-test-python.nix { +import ./make-test-python.nix ({ pkgs, ... }: { name = "buildbot"; nodes = { @@ -110,4 +105,4 @@ import ./make-test-python.nix { ''; meta.maintainers = with pkgs.lib.maintainers; [ ]; -} {} +}) diff --git a/nixos/tests/c2fmzq.nix b/nixos/tests/c2fmzq.nix new file mode 100644 index 00000000000..d8ec816c7d2 --- /dev/null +++ b/nixos/tests/c2fmzq.nix @@ -0,0 +1,75 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "c2FmZQ"; + meta.maintainers = with lib.maintainers; [ hmenke ]; + + nodes.machine = { + services.c2fmzq-server = { + enable = true; + port = 8080; + passphraseFile = builtins.toFile "pwfile" "hunter2"; # don't do this on real deployments + settings = { + verbose = 3; # debug + }; + }; + environment = { + sessionVariables = { + C2FMZQ_PASSPHRASE = "lol"; + C2FMZQ_API_SERVER = "http://localhost:8080"; + }; + systemPackages = [ + pkgs.c2fmzq + (pkgs.writeScriptBin "c2FmZQ-client-wrapper" '' + #!${pkgs.expect}/bin/expect -f + spawn c2FmZQ-client {*}$argv + expect { + "Enter password:" { send "$env(PASSWORD)\r" } + "Type YES to confirm:" { send "YES\r" } + timeout { exit 1 } + eof { exit 0 } + } + interact + '') + ]; + }; + }; + + testScript = { nodes, ... }: '' + machine.start() + machine.wait_for_unit("c2fmzq-server.service") + machine.wait_for_open_port(8080) + + with subtest("Create accounts for alice and bob"): + machine.succeed("PASSWORD=foobar c2FmZQ-client-wrapper -- -v 3 create-account alice@example.com") + machine.succeed("PASSWORD=fizzbuzz c2FmZQ-client-wrapper -- -v 3 create-account bob@example.com") + + with subtest("Log in as alice"): + machine.succeed("PASSWORD=foobar c2FmZQ-client-wrapper -- -v 3 login alice@example.com") + msg = machine.succeed("c2FmZQ-client -v 3 status") + assert "Logged in as alice@example.com" in msg, f"ERROR: Not logged in as alice:\n{msg}" + + with subtest("Create a new album, upload a file, and delete the uploaded file"): + machine.succeed("c2FmZQ-client -v 3 create-album 'Rarest Memes'") + machine.succeed("echo 'pls do not steal' > meme.txt") + machine.succeed("c2FmZQ-client -v 3 import meme.txt 'Rarest Memes'") + machine.succeed("c2FmZQ-client -v 3 sync") + machine.succeed("rm meme.txt") + + with subtest("Share the album with bob"): + machine.succeed("c2FmZQ-client-wrapper -- -v 3 share 'Rarest Memes' bob@example.com") + + with subtest("Log in as bob"): + machine.succeed("PASSWORD=fizzbuzz c2FmZQ-client-wrapper -- -v 3 login bob@example.com") + msg = machine.succeed("c2FmZQ-client -v 3 status") + assert "Logged in as bob@example.com" in msg, f"ERROR: Not logged in as bob:\n{msg}" + + with subtest("Download the shared file"): + machine.succeed("c2FmZQ-client -v 3 download 'shared/Rarest Memes/meme.txt'") + machine.succeed("c2FmZQ-client -v 3 export 'shared/Rarest Memes/meme.txt' .") + msg = machine.succeed("cat meme.txt") + assert "pls do not steal\n" == msg, f"File content is not the same:\n{msg}" + + with subtest("Test that PWA is served"): + msg = machine.succeed("curl -sSfL http://localhost:8080") + assert "c2FmZQ" in msg, f"Could not find 'c2FmZQ' in the output:\n{msg}" + ''; +}) diff --git a/nixos/tests/castopod.nix b/nixos/tests/castopod.nix new file mode 100644 index 00000000000..4435ec617d4 --- /dev/null +++ b/nixos/tests/castopod.nix @@ -0,0 +1,87 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: +{ + name = "castopod"; + meta = with lib.maintainers; { + maintainers = [ alexoundos misuzu ]; + }; + nodes.castopod = { nodes, ... }: { + networking.firewall.allowedTCPPorts = [ 80 ]; + networking.extraHosts = '' + 127.0.0.1 castopod.example.com + ''; + services.castopod = { + enable = true; + database.createLocally = true; + localDomain = "castopod.example.com"; + }; + environment.systemPackages = + let + username = "admin"; + email = "admin@castood.example.com"; + password = "v82HmEp5"; + testRunner = pkgs.writers.writePython3Bin "test-runner" + { + libraries = [ pkgs.python3Packages.selenium ]; + flakeIgnore = [ + "E501" + ]; + } '' + from selenium.webdriver.common.by import By + from selenium.webdriver import Firefox + from selenium.webdriver.firefox.options import Options + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC + + options = Options() + options.add_argument('--headless') + driver = Firefox(options=options) + try: + driver.implicitly_wait(20) + driver.get('http://castopod.example.com/cp-install') + + wait = WebDriverWait(driver, 10) + + wait.until(EC.title_contains("installer")) + + driver.find_element(By.CSS_SELECTOR, '#username').send_keys( + '${username}' + ) + driver.find_element(By.CSS_SELECTOR, '#email').send_keys( + '${email}' + ) + driver.find_element(By.CSS_SELECTOR, '#password').send_keys( + '${password}' + ) + driver.find_element(By.XPATH, "//button[contains(., 'Finish install')]").click() + + wait.until(EC.title_contains("Auth")) + + driver.find_element(By.CSS_SELECTOR, '#email').send_keys( + '${email}' + ) + driver.find_element(By.CSS_SELECTOR, '#password').send_keys( + '${password}' + ) + driver.find_element(By.XPATH, "//button[contains(., 'Login')]").click() + + wait.until(EC.title_contains("Admin dashboard")) + finally: + driver.close() + driver.quit() + ''; + in + [ pkgs.firefox-unwrapped pkgs.geckodriver testRunner ]; + }; + testScript = '' + start_all() + castopod.wait_for_unit("castopod-setup.service") + castopod.wait_for_file("/run/phpfpm/castopod.sock") + castopod.wait_for_unit("nginx.service") + castopod.wait_for_open_port(80) + castopod.wait_until_succeeds("curl -sS -f http://castopod.example.com") + castopod.succeed("curl -s http://localhost/cp-install | grep 'Create your Super Admin account' > /dev/null") + + with subtest("Create superadmin and log in"): + castopod.succeed("PYTHONUNBUFFERED=1 systemd-cat -t test-runner test-runner") + ''; +}) diff --git a/nixos/tests/cinnamon.nix b/nixos/tests/cinnamon.nix index 2a138923190..7637b55a2b1 100644 --- a/nixos/tests/cinnamon.nix +++ b/nixos/tests/cinnamon.nix @@ -14,27 +14,13 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { testScript = { nodes, ... }: let user = nodes.machine.users.users.alice; - uid = toString user.uid; - bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${uid}/bus"; - display = "DISPLAY=:0.0"; - env = "${bus} ${display}"; - gdbus = "${env} gdbus"; + env = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${toString user.uid}/bus DISPLAY=:0"; 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 = "call --session -d org.Cinnamon -o /org/Cinnamon -m org.Cinnamon.Eval"; - - # Should be 2 (RunState.RUNNING) when startup is done. - # https://github.com/linuxmint/cinnamon/blob/5.4.0/js/ui/main.js#L183-L187 - getRunState = su "${gdbus} ${eval} Main.runState"; - - # Start gnome-terminal. - gnomeTerminalCommand = su "gnome-terminal"; - - # Hopefully gnome-terminal's wm class. - wmClass = su "${gdbus} ${eval} global.display.focus_window.wm_class"; + 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") @@ -54,13 +40,43 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { with subtest("Wait for the Cinnamon shell"): # Correct output should be (true, '2') - machine.wait_until_succeeds("${getRunState} | grep -q '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", "cinnamon-killer-daemon", "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("Lock the screen"): + machine.succeed("${su "cinnamon-screensaver-command -l >&2 &"}") + machine.wait_until_succeeds("${su "cinnamon-screensaver-command -q"} | grep 'The screensaver is active'") + machine.sleep(2) + machine.screenshot("cinnamon_screensaver") + machine.send_chars("${user.password}\n", delay=0.2) + machine.wait_until_succeeds("${su "cinnamon-screensaver-command -q"} | grep 'The screensaver is inactive'") + machine.sleep(2) with subtest("Open GNOME Terminal"): - machine.succeed("${gnomeTerminalCommand}") - # Correct output should be (true, '"Gnome-terminal"') - machine.wait_until_succeeds("${wmClass} | grep -q 'true,...Gnome-terminal'") - machine.sleep(20) - machine.screenshot("screen") + machine.succeed("${su "gnome-terminal"}") + machine.wait_until_succeeds("${eval "global.display.focus_window.wm_class"} | grep -i 'gnome-terminal'") + machine.sleep(2) + + with subtest("Open virtual keyboard"): + machine.succeed("${su "dbus-send --print-reply --dest=org.Cinnamon /org/Cinnamon org.Cinnamon.ToggleKeyboard"}") + machine.wait_for_text('(Ctrl|Alt)') + machine.sleep(2) + machine.screenshot("cinnamon_virtual_keyboard") + + with subtest("Check if Cinnamon has ever coredumped"): + machine.fail("coredumpctl --json=short | grep -E 'cinnamon|nemo'") ''; }) diff --git a/nixos/tests/cockpit.nix b/nixos/tests/cockpit.nix index 6f86d1e2c46..e7165b97901 100644 --- a/nixos/tests/cockpit.nix +++ b/nixos/tests/cockpit.nix @@ -50,7 +50,8 @@ import ./make-test-python.nix ( options = Options() options.add_argument("--headless") - driver = webdriver.Firefox(options=options) + service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501 + driver = webdriver.Firefox(options=options, service=service) driver.implicitly_wait(10) diff --git a/nixos/tests/code-server.nix b/nixos/tests/code-server.nix deleted file mode 100644 index 7d523dfc617..00000000000 --- 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/common/auto-format-root-device.nix b/nixos/tests/common/auto-format-root-device.nix index 56eecef2f41..fef8c700499 100644 --- a/nixos/tests/common/auto-format-root-device.nix +++ b/nixos/tests/common/auto-format-root-device.nix @@ -5,19 +5,19 @@ # `virtualisation.fileSystems."/".autoFormat = true;` # instead. -{ config, pkgs, ... }: +{ lib, config, pkgs, ... }: let rootDevice = config.virtualisation.rootDevice; in { - boot.initrd.extraUtilsCommands = '' + boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) '' # We need mke2fs in the initrd. copy_bin_and_libs ${pkgs.e2fsprogs}/bin/mke2fs ''; - boot.initrd.postDeviceCommands = '' + boot.initrd.postDeviceCommands = lib.mkIf (!config.boot.initrd.systemd.enable) '' # If the disk image appears to be empty, run mke2fs to # initialise. FSTYPE=$(blkid -o value -s TYPE ${rootDevice} || true) diff --git a/nixos/tests/containers-imperative.nix b/nixos/tests/containers-imperative.nix index 22b664a90e1..18bec1db78e 100644 --- a/nixos/tests/containers-imperative.nix +++ b/nixos/tests/containers-imperative.nix @@ -21,9 +21,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { modules = lib.singleton { nixpkgs = { inherit (config.nixpkgs) localSystem; }; - containers.foo.config = { - system.stateVersion = "18.03"; - }; + containers.foo.config = {}; }; # The system is inherited from the host above. diff --git a/nixos/tests/dae.nix b/nixos/tests/dae.nix index b8c8ebce745..42a2eb5fe0b 100644 --- a/nixos/tests/dae.nix +++ b/nixos/tests/dae.nix @@ -14,6 +14,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { }; services.dae = { enable = true; + config = '' + global{} + routing{} + ''; }; }; diff --git a/nixos/tests/dconf.nix b/nixos/tests/dconf.nix index 86f703e3b98..192c075540a 100644 --- a/nixos/tests/dconf.nix +++ b/nixos/tests/dconf.nix @@ -14,8 +14,8 @@ import ./make-test-python.nix profiles.user.databases = [ { settings = { - "test/not/locked" = mkInt32 1; - "test/is/locked" = "locked"; + "test/not".locked = mkInt32 1; + "test/is".locked = "locked"; }; locks = [ "/test/is/locked" diff --git a/nixos/tests/deconz.nix b/nixos/tests/deconz.nix new file mode 100644 index 00000000000..cbe721ba492 --- /dev/null +++ b/nixos/tests/deconz.nix @@ -0,0 +1,28 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: +let + httpPort = 800; +in +{ + name = "deconz"; + + meta.maintainers = with lib.maintainers; [ + bjornfor + ]; + + nodes.machine = { config, pkgs, lib, ... }: { + nixpkgs.config.allowUnfree = true; + services.deconz = { + enable = true; + inherit httpPort; + extraArgs = [ + "--dbg-err=2" + "--dbg-info=2" + ]; + }; + }; + + testScript = '' + machine.wait_for_unit("deconz.service") + machine.succeed("curl -sfL http://localhost:${toString httpPort}") + ''; +}) diff --git a/nixos/tests/dex-oidc.nix b/nixos/tests/dex-oidc.nix index 37275a97ef0..e54ae18ca93 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 1c05376e097..1a794931dc5 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/docker-registry.nix b/nixos/tests/docker-registry.nix index 316b7c9b972..db20cb52c3e 100644 --- a/nixos/tests/docker-registry.nix +++ b/nixos/tests/docker-registry.nix @@ -3,7 +3,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { name = "docker-registry"; meta = with pkgs.lib.maintainers; { - maintainers = [ globin ma27 ironpinguin ]; + maintainers = [ globin ironpinguin ]; }; nodes = { diff --git a/nixos/tests/documize.nix b/nixos/tests/documize.nix index fda79b1a093..3624c0c5676 100644 --- a/nixos/tests/documize.nix +++ b/nixos/tests/documize.nix @@ -1,7 +1,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { name = "documize"; meta = with pkgs.lib.maintainers; { - maintainers = [ ma27 ]; + maintainers = [ ]; }; nodes.machine = { pkgs, ... }: { diff --git a/nixos/tests/elk.nix b/nixos/tests/elk.nix index 0122bc44036..900ea632010 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/fanout.nix b/nixos/tests/fanout.nix new file mode 100644 index 00000000000..c36d34dcce0 --- /dev/null +++ b/nixos/tests/fanout.nix @@ -0,0 +1,30 @@ +{ system ? builtins.currentSystem +, config ? {} +, pkgs ? import ../.. { inherit system config; } +}: +import ./make-test-python.nix ({lib, pkgs, ...}: { + name = "fanout"; + meta.maintainers = [ lib.maintainers.therishidesai ]; + + nodes = let + cfg = { ... }: { + services.fanout = { + enable = true; + fanoutDevices = 2; + bufferSize = 8192; + }; + }; + in { + machine = cfg; + }; + + testScript = '' + start_all() + + # mDNS. + machine.wait_for_unit("multi-user.target") + + machine.succeed("test -c /dev/fanout0") + machine.succeed("test -c /dev/fanout1") + ''; +}) diff --git a/nixos/tests/fastnetmon-advanced.nix b/nixos/tests/fastnetmon-advanced.nix new file mode 100644 index 00000000000..b2d2713a921 --- /dev/null +++ b/nixos/tests/fastnetmon-advanced.nix @@ -0,0 +1,65 @@ +{ pkgs, lib, ... }: + +{ + name = "fastnetmon-advanced"; + meta.maintainers = lib.teams.wdz.members; + + nodes = { + bird = { ... }: { + networking.firewall.allowedTCPPorts = [ 179 ]; + services.bird2 = { + enable = true; + config = '' + router id 192.168.1.1; + + protocol bgp fnm { + local 192.168.1.1 as 64513; + neighbor 192.168.1.2 as 64514; + multihop; + ipv4 { + import all; + export none; + }; + } + ''; + }; + }; + fnm = { ... }: { + networking.firewall.allowedTCPPorts = [ 179 ]; + services.fastnetmon-advanced = { + enable = true; + settings = { + networks_list = [ "172.23.42.0/24" ]; + gobgp = true; + gobgp_flow_spec_announces = true; + }; + bgpPeers = { + bird = { + local_asn = 64514; + remote_asn = 64513; + local_address = "192.168.1.2"; + remote_address = "192.168.1.1"; + + description = "Bird"; + ipv4_unicast = true; + multihop = true; + active = true; + }; + }; + }; + }; + }; + + testScript = { nodes, ... }: '' + start_all() + fnm.wait_for_unit("fastnetmon.service") + bird.wait_for_unit("bird2.service") + + fnm.wait_until_succeeds('journalctl -eu fastnetmon.service | grep "BGP daemon restarted correctly"') + fnm.wait_until_succeeds("journalctl -eu gobgp.service | grep BGP_FSM_OPENCONFIRM") + bird.wait_until_succeeds("birdc show protocol fnm | grep Estab") + fnm.wait_until_succeeds('journalctl -eu fastnetmon.service | grep "API server listening"') + fnm.succeed("fcli set blackhole 172.23.42.123") + bird.succeed("birdc show route | grep 172.23.42.123") + ''; +} diff --git a/nixos/tests/ferretdb.nix b/nixos/tests/ferretdb.nix new file mode 100644 index 00000000000..7251198af77 --- /dev/null +++ b/nixos/tests/ferretdb.nix @@ -0,0 +1,64 @@ +{ system ? builtins.currentSystem +, pkgs ? import ../.. { inherit system; } +, ... +}: +let + lib = pkgs.lib; + testScript = '' + machine.start() + machine.wait_for_unit("ferretdb.service") + machine.wait_for_open_port(27017) + machine.succeed("mongosh --eval 'use myNewDatabase;' --eval 'db.myCollection.insertOne( { x: 1 } );'") + ''; +in +with import ../lib/testing-python.nix { inherit system; }; +{ + + postgresql = makeTest + { + inherit testScript; + name = "ferretdb-postgresql"; + meta.maintainers = with lib.maintainers; [ julienmalka ]; + + nodes.machine = + { pkgs, ... }: + { + services.ferretdb = { + enable = true; + settings.FERRETDB_HANDLER = "pg"; + settings.FERRETDB_POSTGRESQL_URL = "postgres://ferretdb@localhost/ferretdb?host=/run/postgresql"; + }; + + systemd.services.ferretdb.serviceConfig = { + Requires = "postgresql.service"; + After = "postgresql.service"; + }; + + services.postgresql = { + enable = true; + ensureDatabases = [ "ferretdb" ]; + ensureUsers = [{ + name = "ferretdb"; + ensureDBOwnership = true; + }]; + }; + + environment.systemPackages = with pkgs; [ mongosh ]; + }; + }; + + sqlite = makeTest + { + inherit testScript; + name = "ferretdb-sqlite"; + meta.maintainers = with lib.maintainers; [ julienmalka ]; + + nodes.machine = + { pkgs, ... }: + { + services.ferretdb.enable = true; + + environment.systemPackages = with pkgs; [ mongosh ]; + }; + }; +} diff --git a/nixos/tests/firefox.nix b/nixos/tests/firefox.nix index 3f9cea6662f..fbea95dc752 100644 --- a/nixos/tests/firefox.nix +++ b/nixos/tests/firefox.nix @@ -1,14 +1,7 @@ import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }: -let firefoxPackage' = firefoxPackage.override (args: { - extraPrefsFiles = (args.extraPrefsFiles or []) ++ [ - # make sure that autoplay is enabled by default for the audio test - (builtins.toString (builtins.toFile "autoplay-pref.js" ''defaultPref("media.autoplay.default",0);'')) - ]; - }); - -in { - name = firefoxPackage'.unwrapped.pname; + name = firefoxPackage.pname; + meta = with pkgs.lib.maintainers; { maintainers = [ eelco shlevy ]; }; @@ -17,10 +10,13 @@ in { pkgs, ... }: { imports = [ ./common/x11.nix ]; - environment.systemPackages = [ - firefoxPackage' - pkgs.xdotool - ]; + environment.systemPackages = [ pkgs.xdotool ]; + + programs.firefox = { + enable = true; + preferences."media.autoplay.default" = 0; + package = firefoxPackage; + }; # Create a virtual sound device, with mixing # and all, for recording audio. @@ -58,7 +54,9 @@ in }; - testScript = '' + testScript = let + exe = firefoxPackage.unwrapped.binaryName; + in '' from contextlib import contextmanager @@ -97,7 +95,7 @@ in with subtest("Wait until Firefox has finished loading the Valgrind docs page"): machine.execute( - "xterm -e '${firefoxPackage'.unwrapped.binaryName} file://${pkgs.valgrind.doc}/share/doc/valgrind/html/index.html' >&2 &" + "xterm -e '${exe} file://${pkgs.valgrind.doc}/share/doc/valgrind/html/index.html' >&2 &" ) machine.wait_for_window("Valgrind") machine.sleep(40) @@ -105,7 +103,7 @@ in with subtest("Check whether Firefox can play sound"): with record_audio(machine): machine.succeed( - "${firefoxPackage'.unwrapped.binaryName} file://${pkgs.sound-theme-freedesktop}/share/sounds/freedesktop/stereo/phone-incoming-call.oga >&2 &" + "${exe} file://${pkgs.sound-theme-freedesktop}/share/sounds/freedesktop/stereo/phone-incoming-call.oga >&2 &" ) wait_for_sound(machine) machine.copy_from_vm("/tmp/record.wav") diff --git a/nixos/tests/forgejo.nix b/nixos/tests/forgejo.nix index b326819e319..6acd6acb50f 100644 --- a/nixos/tests/forgejo.nix +++ b/nixos/tests/forgejo.nix @@ -37,7 +37,7 @@ let settings."repository.signing".SIGNING_KEY = signingPrivateKeyId; settings.actions.ENABLED = true; }; - environment.systemPackages = [ config.services.forgejo.package pkgs.gnupg pkgs.jq ]; + environment.systemPackages = [ config.services.forgejo.package pkgs.gnupg pkgs.jq pkgs.file ]; services.openssh.enable = true; specialisation.runner = { @@ -53,6 +53,14 @@ let tokenFile = "/var/lib/forgejo/runner_token"; }; }; + specialisation.dump = { + inheritParentConfig = true; + configuration.services.forgejo.dump = { + enable = true; + type = "tar.zst"; + file = "dump.tar.zst"; + }; + }; }; client1 = { config, pkgs, ... }: { environment.systemPackages = [ pkgs.git ]; @@ -66,8 +74,10 @@ let let inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey; serverSystem = nodes.server.system.build.toplevel; + dumpFile = with nodes.server.specialisation.dump.configuration.services.forgejo.dump; "${backupDir}/${file}"; in '' + import json GIT_SSH_COMMAND = "ssh -i $HOME/.ssh/privk -o StrictHostKeyChecking=no" REPO = "forgejo@server:test/repo" PRIVK = "${snakeOilPrivateKey}" @@ -137,6 +147,11 @@ let client2.succeed(f"GIT_SSH_COMMAND='{GIT_SSH_COMMAND}' git clone {REPO}") client2.succeed('test "$(cat repo/testfile | xargs echo -n)" = "hello world"') + with subtest("Testing git protocol version=2 over ssh"): + git_protocol = client2.succeed(f"GIT_SSH_COMMAND='{GIT_SSH_COMMAND}' GIT_TRACE2_EVENT=true git -C repo fetch |& grep negotiated-version") + version = json.loads(git_protocol).get("value") + assert version == "2", f"git did not negotiate protocol version 2, but version {version} instead." + server.wait_until_succeeds( 'test "$(curl http://localhost:3000/api/v1/repos/test/repo/commits ' + '-H "Accept: application/json" | jq length)" = "1"', @@ -150,6 +165,12 @@ let server.succeed("${serverSystem}/specialisation/runner/bin/switch-to-configuration test") server.wait_for_unit("gitea-runner-test.service") server.succeed("journalctl -o cat -u gitea-runner-test.service | grep -q 'Runner registered successfully'") + + with subtest("Testing backup service"): + server.succeed("${serverSystem}/specialisation/dump/bin/switch-to-configuration test") + server.systemctl("start forgejo-dump") + assert "Zstandard compressed data" in server.succeed("file ${dumpFile}") + server.copy_from_vm("${dumpFile}") ''; }); in diff --git a/nixos/tests/freetube.nix b/nixos/tests/freetube.nix new file mode 100644 index 00000000000..f285384b68e --- /dev/null +++ b/nixos/tests/freetube.nix @@ -0,0 +1,41 @@ +let + tests = { + wayland = { pkgs, ... }: { + imports = [ ./common/wayland-cage.nix ]; + services.cage.program = "${pkgs.freetube}/bin/freetube"; + virtualisation.memorySize = 2047; + environment.variables.NIXOS_OZONE_WL = "1"; + environment.variables.DISPLAY = "do not use"; + }; + xorg = { pkgs, ... }: { + imports = [ ./common/user-account.nix ./common/x11.nix ]; + virtualisation.memorySize = 2047; + services.xserver.enable = true; + services.xserver.displayManager.sessionCommands = '' + ${pkgs.freetube}/bin/freetube + ''; + test-support.displayManager.auto.user = "alice"; + }; + }; + + mkTest = name: machine: + import ./make-test-python.nix ({ pkgs, ... }: { + inherit name; + nodes = { "${name}" = machine; }; + meta.maintainers = with pkgs.lib.maintainers; [ kirillrdy ]; + enableOCR = true; + + testScript = '' + start_all() + machine.wait_for_unit('graphical.target') + machine.wait_for_text('Your Subscription list is currently empty') + machine.send_key("ctrl-r") + machine.wait_for_text('Your Subscription list is currently empty') + machine.screenshot("main.png") + machine.send_key("ctrl-comma") + machine.wait_for_text('General Settings', timeout=30) + machine.screenshot("preferences.png") + ''; + }); +in +builtins.mapAttrs (k: v: mkTest k v { }) tests diff --git a/nixos/tests/freshrss-pgsql.nix b/nixos/tests/freshrss-pgsql.nix index 055bd51ed43..c685f4a8159 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/garage/basic.nix b/nixos/tests/garage/basic.nix index b6df1e72af9..88d747ea33b 100644 --- a/nixos/tests/garage/basic.nix +++ b/nixos/tests/garage/basic.nix @@ -1,4 +1,4 @@ -args@{ mkNode, ... }: +args@{ mkNode, ver, ... }: (import ../make-test-python.nix ({ pkgs, ...} : { name = "garage-basic"; meta = { @@ -52,7 +52,7 @@ args@{ mkNode, ... }: machine.succeed(f"garage layout apply --version {version}") def create_api_key(machine: Machine, key_name: str) -> S3Key: - output = machine.succeed(f"garage key new --name {key_name}") + output = machine.succeed(f"garage key ${if ver == "0_8" then "new --name" else "create"} {key_name}") m = key_creation_regex.match(output) if not m or not m.group('key_id') or not m.group('secret_key'): raise ValueError('Cannot parse API key data') @@ -90,7 +90,7 @@ args@{ mkNode, ... }: single_node.wait_for_open_port(3900) # Now Garage is initialized. single_node_id = get_node_id(single_node) - apply_garage_layout(single_node, [f'-z qemutest -c 1 "{single_node_id}"']) + apply_garage_layout(single_node, [f'-z qemutest -c ${if ver == "0_8" then "1" else "1G"} "{single_node_id}"']) # Now Garage is operational. test_bucket_writes(single_node) test_bucket_over_http(single_node) diff --git a/nixos/tests/garage/default.nix b/nixos/tests/garage/default.nix index 0a1ccde056b..a42236e9a5b 100644 --- a/nixos/tests/garage/default.nix +++ b/nixos/tests/garage/default.nix @@ -44,10 +44,11 @@ let in foldl (matrix: ver: matrix // { - "basic${toString ver}" = import ./basic.nix { inherit system pkgs; mkNode = mkNode pkgs."garage_${ver}"; }; - "with-3node-replication${toString ver}" = import ./with-3node-replication.nix { inherit system pkgs; mkNode = mkNode pkgs."garage_${ver}"; }; + "basic${toString ver}" = import ./basic.nix { inherit system pkgs ver; mkNode = mkNode pkgs."garage_${ver}"; }; + "with-3node-replication${toString ver}" = import ./with-3node-replication.nix { inherit system pkgs ver; mkNode = mkNode pkgs."garage_${ver}"; }; }) {} [ "0_8" + "0_9" ] diff --git a/nixos/tests/garage/with-3node-replication.nix b/nixos/tests/garage/with-3node-replication.nix index d372ad1aa00..d4387b198d9 100644 --- a/nixos/tests/garage/with-3node-replication.nix +++ b/nixos/tests/garage/with-3node-replication.nix @@ -1,4 +1,4 @@ -args@{ mkNode, ... }: +args@{ mkNode, ver, ... }: (import ../make-test-python.nix ({ pkgs, ...} : { name = "garage-3node-replication"; @@ -55,7 +55,7 @@ args@{ mkNode, ... }: machine.succeed(f"garage layout apply --version {version}") def create_api_key(machine: Machine, key_name: str) -> S3Key: - output = machine.succeed(f"garage key new --name {key_name}") + output = machine.succeed(f"garage key ${if ver == "0_8" then "new --name" else "create"} {key_name}") m = key_creation_regex.match(output) if not m or not m.group('key_id') or not m.group('secret_key'): raise ValueError('Cannot parse API key data') @@ -110,7 +110,7 @@ args@{ mkNode, ... }: zones = ["nixcon", "nixcon", "paris_meetup", "fosdem"] apply_garage_layout(node1, [ - f'{ndata.node_id} -z {zones[index]} -c 1' + f'{ndata.node_id} -z {zones[index]} -c ${if ver == "0_8" then "1" else "1G"}' for index, ndata in enumerate(node_ids.values()) ]) # Now Garage is operational. diff --git a/nixos/tests/gitea.nix b/nixos/tests/gitea.nix index b747659de82..f62c72bdddd 100644 --- a/nixos/tests/gitea.nix +++ b/nixos/tests/gitea.nix @@ -26,7 +26,7 @@ let supportedDbTypes = [ "mysql" "postgres" "sqlite3" ]; makeGiteaTest = type: nameValuePair type (makeTest { name = "${giteaPackage.pname}-${type}"; - meta.maintainers = with maintainers; [ aanderse emilylange kolaente ma27 ]; + meta.maintainers = with maintainers; [ aanderse kolaente ma27 ]; nodes = { server = { config, pkgs, ... }: { @@ -35,9 +35,11 @@ let enable = true; database = { inherit type; }; package = giteaPackage; + metricsTokenFile = (pkgs.writeText "metrics_secret" "fakesecret").outPath; settings.service.DISABLE_REGISTRATION = true; settings."repository.signing".SIGNING_KEY = signingPrivateKeyId; settings.actions.ENABLED = true; + settings.metrics.ENABLED = true; }; environment.systemPackages = [ giteaPackage pkgs.gnupg pkgs.jq ]; services.openssh.enable = true; @@ -143,6 +145,12 @@ let + '-H "Accept: application/json" | jq length)" = "1"' ) + with subtest("Testing metrics endpoint"): + server.succeed('curl ' + + '-H "Authorization: Bearer fakesecret" ' + + 'http://localhost:3000/metrics ' + + '| grep gitea_accesses') + with subtest("Testing runner registration"): server.succeed( "su -l gitea -c 'GITEA_WORK_DIR=/var/lib/gitea gitea actions generate-runner-token' | sed 's/^/TOKEN=/' | tee /var/lib/gitea/runner_token" diff --git a/nixos/tests/gnome-flashback.nix b/nixos/tests/gnome-flashback.nix index 876d36477c1..f486dabc5c4 100644 --- a/nixos/tests/gnome-flashback.nix +++ b/nixos/tests/gnome-flashback.nix @@ -32,14 +32,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { xauthority = "/run/user/${uid}/gdm/Xauthority"; in '' with subtest("Login to GNOME Flashback with GDM"): - # wait_for_x() checks graphical-session.target, which is expected to be - # inactive on gnome-flashback before #228946 (i.e. systemd managed - # gnome-session) is done. - # https://github.com/NixOS/nixpkgs/pull/208060 - # - # Previously this was unconditionally touched by xsessionWrapper but was - # changed in #233981 (we have GNOME-Flashback:GNOME in XDG_CURRENT_DESKTOP). - # machine.wait_for_x() + machine.wait_for_x() machine.wait_until_succeeds('journalctl -t gnome-session-binary --grep "Entering running state"') # Wait for alice to be logged in" machine.wait_for_unit("default.target", "${user.name}") @@ -49,9 +42,10 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { assert "alice" in machine.succeed("getfacl -p /dev/snd/timer") with subtest("Wait for Metacity"): - machine.wait_until_succeeds( - "pgrep metacity" - ) + machine.wait_until_succeeds("pgrep metacity") + + with subtest("Regression test for #233920"): + machine.wait_until_succeeds("pgrep -fa gnome-flashback-media-keys") machine.sleep(20) machine.screenshot("screen") ''; diff --git a/nixos/tests/goss.nix b/nixos/tests/goss.nix new file mode 100644 index 00000000000..6b772d19215 --- /dev/null +++ b/nixos/tests/goss.nix @@ -0,0 +1,53 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "goss"; + meta.maintainers = [ lib.maintainers.anthonyroussel ]; + + nodes.machine = { + environment.systemPackages = [ pkgs.jq ]; + + services.goss = { + enable = true; + + environment = { + GOSS_FMT = "json"; + }; + + settings = { + addr."tcp://localhost:8080" = { + reachable = true; + local-address = "127.0.0.1"; + }; + command."check-goss-version" = { + exec = "${lib.getExe pkgs.goss} --version"; + exit-status = 0; + }; + dns.localhost.resolvable = true; + file."/nix" = { + filetype = "directory"; + exists = true; + }; + group.root.exists = true; + kernel-param."kernel.ostype".value = "Linux"; + service.goss = { + enabled = true; + running = true; + }; + user.root.exists = true; + }; + }; + }; + + testScript = '' + import json + + machine.wait_for_unit("goss.service") + machine.wait_for_open_port(8080) + + with subtest("returns health status"): + result = json.loads(machine.succeed("curl -sS http://localhost:8080/healthz")) + + assert len(result["results"]) == 10, f".results should be an array of 10 items, was {result['results']!r}" + assert result["summary"]["failed-count"] == 0, f".summary.failed-count should be zero, was {result['summary']['failed-count']}" + assert result["summary"]["test-count"] == 10, f".summary.test-count should be 10, was {result['summary']['test-count']}" + ''; +}) diff --git a/nixos/tests/gotify-server.nix b/nixos/tests/gotify-server.nix index d004f542b39..c8d7fa172a7 100644 --- a/nixos/tests/gotify-server.nix +++ b/nixos/tests/gotify-server.nix @@ -1,7 +1,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { name = "gotify-server"; meta = with pkgs.lib.maintainers; { - maintainers = [ ma27 ]; + maintainers = [ ]; }; nodes.machine = { pkgs, ... }: { diff --git a/nixos/tests/grafana/basic.nix b/nixos/tests/grafana/basic.nix index 8bf4caad7fb..dd389bc8a3d 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/grafana/provision/default.nix b/nixos/tests/grafana/provision/default.nix index 96378452ade..d33d16ce120 100644 --- a/nixos/tests/grafana/provision/default.nix +++ b/nixos/tests/grafana/provision/default.nix @@ -22,15 +22,14 @@ let }; }; - system.activationScripts.setup-grafana = { - deps = [ "users" ]; - text = '' - mkdir -p /var/lib/grafana/dashboards - chown -R grafana:grafana /var/lib/grafana - chmod 0700 -R /var/lib/grafana/dashboards - cp ${pkgs.writeText "test.json" (builtins.readFile ./test_dashboard.json)} /var/lib/grafana/dashboards/ - ''; - }; + systemd.tmpfiles.rules = + let + dashboard = pkgs.writeText "test.json" (builtins.readFile ./test_dashboard.json); + in + [ + "d /var/lib/grafana/dashboards 0700 grafana grafana -" + "C+ /var/lib/grafana/dashboards/test.json - - - - ${dashboard}" + ]; }; extraNodeConfs = { diff --git a/nixos/tests/grow-partition.nix b/nixos/tests/grow-partition.nix new file mode 100644 index 00000000000..344910848dc --- /dev/null +++ b/nixos/tests/grow-partition.nix @@ -0,0 +1,83 @@ +{ lib, ... }: + +let + rootFslabel = "external"; + rootFsDevice = "/dev/disk/by-label/${rootFslabel}"; + + externalModule = partitionTableType: { config, lib, pkgs, ... }: { + virtualisation.directBoot.enable = false; + virtualisation.mountHostNixStore = false; + virtualisation.useEFIBoot = partitionTableType == "efi"; + + # This stops the qemu-vm module from overriding the fileSystems option + # with virtualisation.fileSystems. + virtualisation.fileSystems = lib.mkForce { }; + + + boot.loader.grub.enable = true; + boot.loader.grub.efiSupport = partitionTableType == "efi"; + boot.loader.grub.efiInstallAsRemovable = partitionTableType == "efi"; + boot.loader.grub.device = if partitionTableType == "efi" then "nodev" else "/dev/vda"; + + boot.growPartition = true; + + fileSystems = { + "/".device = rootFsDevice; + }; + + system.build.diskImage = import ../lib/make-disk-image.nix { + inherit config lib pkgs; + label = rootFslabel; + inherit partitionTableType; + format = "raw"; + bootSize = "128M"; + additionalSpace = "0M"; + copyChannel = false; + }; + }; +in +{ + name = "grow-partition"; + + meta.maintainers = with lib.maintainers; [ arianvp ]; + + nodes = { + efi = externalModule "efi"; + legacy = externalModule "legacy"; + legacyGPT = externalModule "legacy+gpt"; + hybrid = externalModule "hybrid"; + }; + + + testScript = { nodes, ... }: + lib.concatLines (lib.mapAttrsToList (name: node: '' + import os + import subprocess + import tempfile + import shutil + + tmp_disk_image = tempfile.NamedTemporaryFile() + + shutil.copyfile("${node.system.build.diskImage}/nixos.img", tmp_disk_image.name) + + subprocess.run([ + "${node.virtualisation.qemu.package}/bin/qemu-img", + "resize", + "-f", + "raw", + tmp_disk_image.name, + "+32M", + ]) + + # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image. + os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name + + ${name}.wait_for_unit("growpart.service") + systemd_growpart_logs = ${name}.succeed("journalctl --boot --unit growpart.service") + assert "CHANGED" in systemd_growpart_logs + ${name}.succeed("systemctl restart growpart.service") + systemd_growpart_logs = ${name}.succeed("journalctl --boot --unit growpart.service") + assert "NOCHANGE" in systemd_growpart_logs + + '') nodes); +} diff --git a/nixos/tests/hadoop/hadoop.nix b/nixos/tests/hadoop/hadoop.nix index b132f4fa58b..6162ccfd33d 100644 --- a/nixos/tests/hadoop/hadoop.nix +++ b/nixos/tests/hadoop/hadoop.nix @@ -176,22 +176,22 @@ import ../make-test-python.nix ({ package, ... }: { nn2.succeed("systemctl stop hdfs-zkfc") # Initialize zookeeper for failover controller - nn1.succeed("sudo -u hdfs hdfs zkfc -formatZK 2>&1 | systemd-cat") + nn1.succeed("sudo -u hdfs systemd-cat hdfs zkfc -formatZK") # Format NN1 and start it - nn1.succeed("sudo -u hdfs hadoop namenode -format 2>&1 | systemd-cat") + nn1.succeed("sudo -u hdfs systemd-cat hadoop namenode -format") nn1.succeed("systemctl start hdfs-namenode") nn1.wait_for_open_port(9870) nn1.wait_for_open_port(8022) nn1.wait_for_open_port(8020) # Bootstrap NN2 from NN1 and start it - nn2.succeed("sudo -u hdfs hdfs namenode -bootstrapStandby 2>&1 | systemd-cat") + nn2.succeed("sudo -u hdfs systemd-cat hdfs namenode -bootstrapStandby") nn2.succeed("systemctl start hdfs-namenode") nn2.wait_for_open_port(9870) nn2.wait_for_open_port(8022) nn2.wait_for_open_port(8020) - nn1.succeed("netstat -tulpne | systemd-cat") + nn1.succeed("systemd-cat netstat -tulpne") # Start failover controllers nn1.succeed("systemctl start hdfs-zkfc") @@ -200,10 +200,10 @@ import ../make-test-python.nix ({ package, ... }: { # DN should have started by now, but confirm anyway dn1.wait_for_unit("hdfs-datanode") # Print states of namenodes - client.succeed("sudo -u hdfs hdfs haadmin -getAllServiceState | systemd-cat") + client.succeed("sudo -u hdfs systemd-cat hdfs haadmin -getAllServiceState") # Wait for cluster to exit safemode client.succeed("sudo -u hdfs hdfs dfsadmin -safemode wait") - client.succeed("sudo -u hdfs hdfs haadmin -getAllServiceState | systemd-cat") + client.succeed("sudo -u hdfs systemd-cat hdfs haadmin -getAllServiceState") # test R/W client.succeed("echo testfilecontents | sudo -u hdfs hdfs dfs -put - /testfile") assert "testfilecontents" in client.succeed("sudo -u hdfs hdfs dfs -cat /testfile") @@ -211,7 +211,7 @@ import ../make-test-python.nix ({ package, ... }: { # Test NN failover nn1.succeed("systemctl stop hdfs-namenode") assert "active" in client.succeed("sudo -u hdfs hdfs haadmin -getAllServiceState") - client.succeed("sudo -u hdfs hdfs haadmin -getAllServiceState | systemd-cat") + client.succeed("sudo -u hdfs systemd-cat hdfs haadmin -getAllServiceState") assert "testfilecontents" in client.succeed("sudo -u hdfs hdfs dfs -cat /testfile") nn1.succeed("systemctl start hdfs-namenode") @@ -219,7 +219,7 @@ import ../make-test-python.nix ({ package, ... }: { nn1.wait_for_open_port(8022) nn1.wait_for_open_port(8020) assert "standby" in client.succeed("sudo -u hdfs hdfs haadmin -getAllServiceState") - client.succeed("sudo -u hdfs hdfs haadmin -getAllServiceState | systemd-cat") + client.succeed("sudo -u hdfs systemd-cat hdfs haadmin -getAllServiceState") #### YARN tests #### @@ -236,20 +236,20 @@ import ../make-test-python.nix ({ package, ... }: { nm1.wait_for_open_port(8042) nm1.wait_for_open_port(8040) client.wait_until_succeeds("yarn node -list | grep Nodes:1") - client.succeed("sudo -u yarn yarn rmadmin -getAllServiceState | systemd-cat") - client.succeed("sudo -u yarn yarn node -list | systemd-cat") + client.succeed("sudo -u yarn systemd-cat yarn rmadmin -getAllServiceState") + client.succeed("sudo -u yarn systemd-cat yarn node -list") # Test RM failover rm1.succeed("systemctl stop yarn-resourcemanager") assert "standby" not in client.succeed("sudo -u yarn yarn rmadmin -getAllServiceState") - client.succeed("sudo -u yarn yarn rmadmin -getAllServiceState | systemd-cat") + client.succeed("sudo -u yarn systemd-cat yarn rmadmin -getAllServiceState") rm1.succeed("systemctl start yarn-resourcemanager") rm1.wait_for_unit("yarn-resourcemanager") rm1.wait_for_open_port(8088) assert "standby" in client.succeed("sudo -u yarn yarn rmadmin -getAllServiceState") - client.succeed("sudo -u yarn yarn rmadmin -getAllServiceState | systemd-cat") + client.succeed("sudo -u yarn systemd-cat yarn rmadmin -getAllServiceState") - assert "Estimated value of Pi is" in client.succeed("HADOOP_USER_NAME=hdfs yarn jar $(readlink $(which yarn) | sed -r 's~bin/yarn~lib/hadoop-*/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar~g') pi 2 10") + assert "Estimated value of Pi is" in client.succeed("HADOOP_USER_NAME=hdfs yarn jar $(readlink $(which yarn) | sed -r 's~bin/yarn~share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar~g') pi 2 10") assert "SUCCEEDED" in client.succeed("yarn application -list -appStates FINISHED") ''; }) diff --git a/nixos/tests/hadoop/hdfs.nix b/nixos/tests/hadoop/hdfs.nix index 429d4bf6b53..65686b37155 100644 --- a/nixos/tests/hadoop/hdfs.nix +++ b/nixos/tests/hadoop/hdfs.nix @@ -50,8 +50,8 @@ import ../make-test-python.nix ({ package, lib, ... }: namenode.wait_for_unit("hdfs-namenode") namenode.wait_for_unit("network.target") namenode.wait_for_open_port(8020) - namenode.succeed("ss -tulpne | systemd-cat") - namenode.succeed("cat /etc/hadoop*/hdfs-site.xml | systemd-cat") + namenode.succeed("systemd-cat ss -tulpne") + namenode.succeed("systemd-cat cat /etc/hadoop*/hdfs-site.xml") namenode.wait_for_open_port(9870) datanode.wait_for_unit("hdfs-datanode") diff --git a/nixos/tests/hardened.nix b/nixos/tests/hardened.nix index f54506224e5..e38834961e1 100644 --- a/nixos/tests/hardened.nix +++ b/nixos/tests/hardened.nix @@ -28,7 +28,7 @@ import ./make-test-python.nix ({ pkgs, ... } : { }; }; boot.extraModulePackages = - optional (versionOlder config.boot.kernelPackages.kernel.version "5.6") + pkgs.lib.optional (pkgs.lib.versionOlder config.boot.kernelPackages.kernel.version "5.6") config.boot.kernelPackages.wireguard; boot.kernelModules = [ "wireguard" ]; }; diff --git a/nixos/tests/hedgedoc.nix b/nixos/tests/hedgedoc.nix index 410350d8362..16e0dc14e94 100644 --- a/nixos/tests/hedgedoc.nix +++ b/nixos/tests/hedgedoc.nix @@ -8,25 +8,54 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: nodes = { hedgedocSqlite = { ... }: { + services.hedgedoc.enable = true; + }; + + hedgedocPostgresWithTCPSocket = { ... }: { + systemd.services.hedgedoc.after = [ "postgresql.service" ]; services = { hedgedoc = { enable = true; - settings.dbURL = "sqlite:///var/lib/hedgedoc/hedgedoc.db"; + settings.db = { + dialect = "postgres"; + user = "hedgedoc"; + password = "$DB_PASSWORD"; + host = "localhost"; + port = 5432; + database = "hedgedocdb"; + }; + + /* + * Do not use pkgs.writeText for secrets as + * they will end up in the world-readable Nix store. + */ + environmentFile = pkgs.writeText "hedgedoc-env" '' + DB_PASSWORD=snakeoilpassword + ''; + }; + postgresql = { + enable = true; + initialScript = pkgs.writeText "pg-init-script.sql" '' + CREATE ROLE hedgedoc LOGIN PASSWORD 'snakeoilpassword'; + CREATE DATABASE hedgedocdb OWNER hedgedoc; + ''; }; }; }; - hedgedocPostgres = { ... }: { + hedgedocPostgresWithUNIXSocket = { ... }: { systemd.services.hedgedoc.after = [ "postgresql.service" ]; services = { hedgedoc = { enable = true; - settings.dbURL = "postgres://hedgedoc:\${DB_PASSWORD}@localhost:5432/hedgedocdb"; + settings.db = { + dialect = "postgres"; + user = "hedgedoc"; + password = "$DB_PASSWORD"; + host = "/run/postgresql"; + database = "hedgedocdb"; + }; - /* - * Do not use pkgs.writeText for secrets as - * they will end up in the world-readable Nix store. - */ environmentFile = pkgs.writeText "hedgedoc-env" '' DB_PASSWORD=snakeoilpassword ''; @@ -50,11 +79,18 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: hedgedocSqlite.wait_for_open_port(3000) hedgedocSqlite.wait_until_succeeds("curl -sSf http://localhost:3000/new") - with subtest("HedgeDoc postgres"): - hedgedocPostgres.wait_for_unit("postgresql.service") - hedgedocPostgres.wait_for_unit("hedgedoc.service") - hedgedocPostgres.wait_for_open_port(5432) - hedgedocPostgres.wait_for_open_port(3000) - hedgedocPostgres.wait_until_succeeds("curl -sSf http://localhost:3000/new") + with subtest("HedgeDoc postgres with TCP socket"): + hedgedocPostgresWithTCPSocket.wait_for_unit("postgresql.service") + hedgedocPostgresWithTCPSocket.wait_for_unit("hedgedoc.service") + hedgedocPostgresWithTCPSocket.wait_for_open_port(5432) + hedgedocPostgresWithTCPSocket.wait_for_open_port(3000) + hedgedocPostgresWithTCPSocket.wait_until_succeeds("curl -sSf http://localhost:3000/new") + + with subtest("HedgeDoc postgres with UNIX socket"): + hedgedocPostgresWithUNIXSocket.wait_for_unit("postgresql.service") + hedgedocPostgresWithUNIXSocket.wait_for_unit("hedgedoc.service") + hedgedocPostgresWithUNIXSocket.wait_for_open_port(5432) + hedgedocPostgresWithUNIXSocket.wait_for_open_port(3000) + hedgedocPostgresWithUNIXSocket.wait_until_succeeds("curl -sSf http://localhost:3000/new") ''; }) diff --git a/nixos/tests/hockeypuck.nix b/nixos/tests/hockeypuck.nix index 2b9dba8720a..675d6b226ad 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 e06c52a5f41..e1588088ba1 100644 --- a/nixos/tests/home-assistant.nix +++ b/nixos/tests/home-assistant.nix @@ -12,9 +12,7 @@ in { ensureDatabases = [ "hass" ]; ensureUsers = [{ name = "hass"; - ensurePermissions = { - "DATABASE hass" = "ALL PRIVILEGES"; - }; + ensureDBOwnership = true; }]; }; @@ -43,6 +41,16 @@ in { psycopg2 ]; + # test loading custom components + customComponents = with pkgs.home-assistant-custom-components; [ + prometheus-sensor + ]; + + # test loading lovelace modules + customLovelaceModules = with pkgs.home-assistant-custom-lovelace-modules; [ + mini-graph-card + ]; + config = { homeassistant = { name = "Home"; @@ -114,6 +122,14 @@ in { inheritParentConfig = true; configuration.services.home-assistant.config.backup = {}; }; + + specialisation.removeCustomThings = { + inheritParentConfig = true; + configuration.services.home-assistant = { + customComponents = lib.mkForce []; + customLovelaceModules = lib.mkForce []; + }; + }; }; testScript = { nodes, ... }: let @@ -161,6 +177,14 @@ in { hass.wait_for_open_port(8123) hass.succeed("curl --fail http://localhost:8123/lovelace") + with subtest("Check that custom components get installed"): + hass.succeed("test -f ${configDir}/custom_components/prometheus_sensor/manifest.json") + hass.wait_until_succeeds("journalctl -u home-assistant.service | grep -q 'We found a custom integration prometheus_sensor which has not been tested by Home Assistant'") + + with subtest("Check that lovelace modules are referenced and fetchable"): + hass.succeed("grep -q 'mini-graph-card-bundle.js' '${configDir}/ui-lovelace.yaml'") + hass.succeed("curl --fail http://localhost:8123/local/nixos-lovelace-modules/mini-graph-card-bundle.js") + with subtest("Check that optional dependencies are in the PYTHONPATH"): env = get_unit_property("Environment") python_path = env.split("PYTHONPATH=")[1].split()[0] @@ -200,6 +224,13 @@ in { for domain in ["backup"]: assert f"Setup of domain {domain} took" in journal, f"{domain} setup missing" + with subtest("Check custom components and custom lovelace modules get removed"): + cursor = get_journal_cursor() + hass.succeed("${system}/specialisation/removeCustomThings/bin/switch-to-configuration test") + hass.fail("grep -q 'mini-graph-card-bundle.js' '${configDir}/ui-lovelace.yaml'") + hass.fail("test -f ${configDir}/custom_components/prometheus_sensor/manifest.json") + wait_for_homeassistant(cursor) + with subtest("Check that no errors were logged"): hass.fail("journalctl -u home-assistant -o cat | grep -q ERROR") diff --git a/nixos/tests/hydra/default.nix b/nixos/tests/hydra/default.nix index baf18afbc56..98c3c6fbae9 100644 --- a/nixos/tests/hydra/default.nix +++ b/nixos/tests/hydra/default.nix @@ -17,7 +17,7 @@ let makeHydraTest = with pkgs.lib; name: package: makeTest { name = "hydra-${name}"; meta = with pkgs.lib.maintainers; { - maintainers = [ lewo ma27 ]; + maintainers = [ lewo ]; }; nodes.machine = { pkgs, lib, ... }: { diff --git a/nixos/tests/incus/container.nix b/nixos/tests/incus/container.nix new file mode 100644 index 00000000000..79b9e2fbabd --- /dev/null +++ b/nixos/tests/incus/container.nix @@ -0,0 +1,77 @@ +import ../make-test-python.nix ({ pkgs, lib, ... } : + +let + releases = import ../../release.nix { + configuration = { + # Building documentation makes the test unnecessarily take a longer time: + documentation.enable = lib.mkForce false; + }; + }; + + container-image-metadata = releases.lxdContainerMeta.${pkgs.stdenv.hostPlatform.system}; + container-image-rootfs = releases.lxdContainerImage.${pkgs.stdenv.hostPlatform.system}; +in +{ + name = "incus-container"; + + meta.maintainers = with lib.maintainers; [ adamcstephens ]; + + nodes.machine = { ... }: { + virtualisation = { + # Ensure test VM has enough resources for creating and managing guests + cores = 2; + memorySize = 1024; + diskSize = 4096; + + incus.enable = true; + }; + }; + + testScript = '' + def instance_is_up(_) -> bool: + status, _ = machine.execute("incus exec container --disable-stdin --force-interactive /run/current-system/sw/bin/true") + return status == 0 + + def set_container(config): + machine.succeed(f"incus config set container {config}") + machine.succeed("incus restart container") + with machine.nested("Waiting for instance to start and be usable"): + retry(instance_is_up) + + machine.wait_for_unit("incus.service") + + # no preseed should mean no service + machine.fail("systemctl status incus-preseed.service") + + machine.succeed("incus admin init --minimal") + + with subtest("Container image can be imported"): + machine.succeed("incus image import ${container-image-metadata}/*/*.tar.xz ${container-image-rootfs}/*/*.tar.xz --alias nixos") + + with subtest("Container can be launched and managed"): + machine.succeed("incus launch nixos container") + with machine.nested("Waiting for instance to start and be usable"): + retry(instance_is_up) + machine.succeed("echo true | incus exec container /run/current-system/sw/bin/bash -") + + with subtest("Container CPU limits can be managed"): + set_container("limits.cpu 1") + cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip() + assert cpuinfo == "1", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 1, got: {cpuinfo}" + + set_container("limits.cpu 2") + cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip() + assert cpuinfo == "2", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 2, got: {cpuinfo}" + + with subtest("Container memory limits can be managed"): + set_container("limits.memory 64MB") + meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip() + meminfo_bytes = " ".join(meminfo.split(' ')[-2:]) + assert meminfo_bytes == "62500 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '62500 kB', got: '{meminfo_bytes}'" + + set_container("limits.memory 128MB") + meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip() + meminfo_bytes = " ".join(meminfo.split(' ')[-2:]) + assert meminfo_bytes == "125000 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '125000 kB', got: '{meminfo_bytes}'" + ''; +}) diff --git a/nixos/tests/incus/default.nix b/nixos/tests/incus/default.nix new file mode 100644 index 00000000000..c88974605e3 --- /dev/null +++ b/nixos/tests/incus/default.nix @@ -0,0 +1,14 @@ +{ + system ? builtins.currentSystem, + config ? { }, + pkgs ? import ../../.. { inherit system config; }, + handleTestOn, +}: +{ + container = import ./container.nix { inherit system pkgs; }; + preseed = import ./preseed.nix { inherit system pkgs; }; + socket-activated = import ./socket-activated.nix { inherit system pkgs; }; + virtual-machine = handleTestOn [ "x86_64-linux" ] ./virtual-machine.nix { + inherit system pkgs; + }; +} diff --git a/nixos/tests/incus/preseed.nix b/nixos/tests/incus/preseed.nix new file mode 100644 index 00000000000..47b2d0cd622 --- /dev/null +++ b/nixos/tests/incus/preseed.nix @@ -0,0 +1,60 @@ +import ../make-test-python.nix ({ pkgs, lib, ... } : + +{ + name = "incus-preseed"; + + meta.maintainers = with lib.maintainers; [ adamcstephens ]; + + nodes.machine = { lib, ... }: { + virtualisation = { + incus.enable = true; + + incus.preseed = { + networks = [ + { + name = "nixostestbr0"; + type = "bridge"; + config = { + "ipv4.address" = "10.0.100.1/24"; + "ipv4.nat" = "true"; + }; + } + ]; + profiles = [ + { + name = "nixostest_default"; + devices = { + eth0 = { + name = "eth0"; + network = "nixostestbr0"; + type = "nic"; + }; + root = { + path = "/"; + pool = "default"; + size = "35GiB"; + type = "disk"; + }; + }; + } + ]; + storage_pools = [ + { + name = "nixostest_pool"; + driver = "dir"; + } + ]; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("incus.service") + machine.wait_for_unit("incus-preseed.service") + + with subtest("Verify preseed resources created"): + machine.succeed("incus profile show nixostest_default") + machine.succeed("incus network info nixostestbr0") + machine.succeed("incus storage show nixostest_pool") + ''; +}) diff --git a/nixos/tests/incus/socket-activated.nix b/nixos/tests/incus/socket-activated.nix new file mode 100644 index 00000000000..4d25b26a15f --- /dev/null +++ b/nixos/tests/incus/socket-activated.nix @@ -0,0 +1,26 @@ +import ../make-test-python.nix ({ pkgs, lib, ... } : + +{ + name = "incus-socket-activated"; + + meta.maintainers = with lib.maintainers; [ adamcstephens ]; + + nodes.machine = { lib, ... }: { + virtualisation = { + incus.enable = true; + incus.socketActivation = true; + }; + }; + + testScript = '' + machine.wait_for_unit("incus.socket") + + # ensure service is not running by default + machine.fail("systemctl is-active incus.service") + machine.fail("systemctl is-active incus-preseed.service") + + # access the socket and ensure the service starts + machine.succeed("incus list") + machine.wait_for_unit("incus.service") + ''; +}) diff --git a/nixos/tests/incus/virtual-machine.nix b/nixos/tests/incus/virtual-machine.nix new file mode 100644 index 00000000000..bfa116679d4 --- /dev/null +++ b/nixos/tests/incus/virtual-machine.nix @@ -0,0 +1,55 @@ +import ../make-test-python.nix ({ pkgs, lib, ... }: + +let + releases = import ../../release.nix { + configuration = { + # Building documentation makes the test unnecessarily take a longer time: + documentation.enable = lib.mkForce false; + + # Our tests require `grep` & friends: + environment.systemPackages = with pkgs; [busybox]; + }; + }; + + vm-image-metadata = releases.lxdVirtualMachineImageMeta.${pkgs.stdenv.hostPlatform.system}; + vm-image-disk = releases.lxdVirtualMachineImage.${pkgs.stdenv.hostPlatform.system}; + + instance-name = "instance1"; +in +{ + name = "incus-virtual-machine"; + + meta.maintainers = with lib.maintainers; [ adamcstephens ]; + + nodes.machine = {...}: { + virtualisation = { + # Ensure test VM has enough resources for creating and managing guests + cores = 2; + memorySize = 1024; + diskSize = 4096; + + incus.enable = true; + }; + }; + + testScript = '' + def instance_is_up(_) -> bool: + status, _ = machine.execute("incus exec ${instance-name} --disable-stdin --force-interactive /run/current-system/sw/bin/true") + return status == 0 + + machine.wait_for_unit("incus.service") + + machine.succeed("incus admin init --minimal") + + with subtest("virtual-machine image can be imported"): + machine.succeed("incus image import ${vm-image-metadata}/*/*.tar.xz ${vm-image-disk}/nixos.qcow2 --alias nixos") + + with subtest("virtual-machine can be launched and become available"): + machine.succeed("incus launch nixos ${instance-name} --vm --config limits.memory=512MB --config security.secureboot=false") + with machine.nested("Waiting for instance to start and be usable"): + retry(instance_is_up) + + with subtest("lxd-agent is started"): + machine.succeed("incus exec ${instance-name} systemctl is-active lxd-agent") + ''; +}) diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix index 85155a6c682..1b4c92b584b 100644 --- a/nixos/tests/installer-systemd-stage-1.nix +++ b/nixos/tests/installer-systemd-stage-1.nix @@ -8,15 +8,17 @@ # them when fixed. inherit (import ./installer.nix { inherit system config pkgs; systemdStage1 = true; }) # bcache + bcachefsSimple + bcachefsEncrypted btrfsSimple btrfsSubvolDefault btrfsSubvolEscape btrfsSubvols - # encryptedFSWithKeyfile + encryptedFSWithKeyfile # grub1 - # luksroot - # luksroot-format1 - # luksroot-format2 + luksroot + luksroot-format1 + luksroot-format2 # lvm separateBoot separateBootFat diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index 56ba85b76e6..e9ec2874985 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -69,8 +69,8 @@ let # disk, and then reboot from the hard disk. It's parameterized with # a test script fragment `createPartitions', which must create # partitions and filesystems. - testScriptFun = { bootLoader, createPartitions, grubDevice, grubUseEfi - , grubIdentifier, preBootCommands, postBootCommands, extraConfig + testScriptFun = { bootLoader, createPartitions, grubDevice, grubUseEfi, grubIdentifier + , postInstallCommands, preBootCommands, postBootCommands, extraConfig , testSpecialisationConfig, testFlakeSwitch }: let iface = "virtio"; @@ -153,6 +153,8 @@ let """ ) + ${postInstallCommands} + with subtest("Shutdown system after installation"): machine.succeed("umount -R /mnt") machine.succeed("sync") @@ -368,7 +370,9 @@ let makeInstallerTest = name: - { createPartitions, preBootCommands ? "", postBootCommands ? "", extraConfig ? "" + { createPartitions + , postInstallCommands ? "", preBootCommands ? "", postBootCommands ? "" + , extraConfig ? "" , extraInstallerConfig ? {} , bootLoader ? "grub" # either "grub" or "systemd-boot" , grubDevice ? "/dev/vda", grubIdentifier ? "uuid", grubUseEfi ? false @@ -479,7 +483,7 @@ let }; testScript = testScriptFun { - inherit bootLoader createPartitions preBootCommands postBootCommands + inherit bootLoader createPartitions postInstallCommands preBootCommands postBootCommands grubDevice grubIdentifier grubUseEfi extraConfig testSpecialisationConfig testFlakeSwitch; }; @@ -511,7 +515,7 @@ let enableOCR = true; preBootCommands = '' machine.start() - machine.wait_for_text("Passphrase for") + machine.wait_for_text("[Pp]assphrase for") machine.send_chars("supersecret\n") ''; }; @@ -682,17 +686,32 @@ in { createPartitions = '' machine.succeed( "flock /dev/vda parted --script /dev/vda -- mklabel msdos" - + " mkpart primary linux-swap 1M 1024M" - + " mkpart primary 1024M -1s", + + " mkpart primary 1M 100MB" # bpool + + " mkpart primary linux-swap 100M 1024M" + + " mkpart primary 1024M -1s", # rpool "udevadm settle", - "mkswap /dev/vda1 -L swap", + "mkswap /dev/vda2 -L swap", "swapon -L swap", - "zpool create rpool /dev/vda2", + "zpool create rpool /dev/vda3", "zfs create -o mountpoint=legacy rpool/root", "mount -t zfs rpool/root /mnt", + "zfs create -o mountpoint=legacy rpool/root/usr", + "mkdir /mnt/usr", + "mount -t zfs rpool/root/usr /mnt/usr", + "zpool create -o compatibility=grub2 bpool /dev/vda1", + "zfs create -o mountpoint=legacy bpool/boot", + "mkdir /mnt/boot", + "mount -t zfs bpool/boot /mnt/boot", "udevadm settle", ) ''; + + # umount & export bpool before shutdown + # this is a fix for "cannot import 'bpool': pool was previously in use from another system." + postInstallCommands = '' + machine.succeed("umount /mnt/boot") + machine.succeed("zpool export bpool") + ''; }; # Create two physical LVM partitions combined into one volume group @@ -762,7 +781,7 @@ in { encrypted.enable = true; encrypted.blkDev = "/dev/vda3"; encrypted.label = "crypt"; - encrypted.keyFile = "/mnt-root/keyfile"; + encrypted.keyFile = "/${if systemdStage1 then "sysroot" else "mnt-root"}/keyfile"; }; ''; }; @@ -918,6 +937,10 @@ in { enableOCR = true; preBootCommands = '' machine.start() + # Enter it wrong once + machine.wait_for_text("enter passphrase for ") + machine.send_chars("wrong\n") + # Then enter it right. machine.wait_for_text("enter passphrase for ") machine.send_chars("password\n") ''; @@ -931,9 +954,8 @@ in { "udevadm settle", "mkswap /dev/vda2 -L swap", "swapon -L swap", - "keyctl link @u @s", "echo password | mkfs.bcachefs -L root --encrypted /dev/vda3", - "echo password | bcachefs unlock /dev/vda3", + "echo password | bcachefs unlock -k session /dev/vda3", "echo password | mount -t bcachefs /dev/vda3 /mnt", "mkfs.ext3 -L boot /dev/vda1", "mkdir -p /mnt/boot", @@ -969,6 +991,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 = '' diff --git a/nixos/tests/invidious.nix b/nixos/tests/invidious.nix index 582d1550fff..701e8e5e7a3 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/iscsi-multipath-root.nix b/nixos/tests/iscsi-multipath-root.nix index 92ae9990c94..494a539b57e 100644 --- a/nixos/tests/iscsi-multipath-root.nix +++ b/nixos/tests/iscsi-multipath-root.nix @@ -202,7 +202,7 @@ import ./make-test-python.nix ( initiatorAuto.succeed("umount /mnt") initiatorAuto.succeed("systemctl restart multipathd") - initiatorAuto.succeed("multipath -ll | systemd-cat") + initiatorAuto.succeed("systemd-cat multipath -ll") # Install our RootDisk machine to 123456, the alias to the device that multipath is now managing initiatorAuto.succeed("mount /dev/mapper/123456 /mnt") @@ -223,7 +223,7 @@ import ./make-test-python.nix ( initiatorRootDisk.fail("iscsiadm -m discovery -o update -t sendtargets -p 192.168.1.3 --login") initiatorRootDisk.fail("iscsiadm -m discovery -o update -t sendtargets -p 192.168.2.3 --login") initiatorRootDisk.succeed("systemctl restart multipathd") - initiatorRootDisk.succeed("multipath -ll | systemd-cat") + initiatorRootDisk.succeed("systemd-cat multipath -ll") # Verify we can write and sync the root disk initiatorRootDisk.succeed("mkdir /scratch") diff --git a/nixos/tests/kernel-generic.nix b/nixos/tests/kernel-generic.nix index 148f66c464d..352deb521a4 100644 --- a/nixos/tests/kernel-generic.nix +++ b/nixos/tests/kernel-generic.nix @@ -25,13 +25,11 @@ let }) args); kernels = pkgs.linuxKernel.vanillaPackages // { inherit (pkgs.linuxKernel.packages) - linux_4_14_hardened linux_4_19_hardened linux_5_4_hardened linux_5_10_hardened linux_5_15_hardened linux_6_1_hardened - linux_6_4_hardened linux_6_5_hardened linux_rt_5_4 linux_rt_5_10 diff --git a/nixos/tests/keyd.nix b/nixos/tests/keyd.nix index 1ee08b4101f..bfc4558b64b 100644 --- a/nixos/tests/keyd.nix +++ b/nixos/tests/keyd.nix @@ -26,13 +26,13 @@ let ''; - mkKeyboardTest = name: { settings, test }: with pkgs.lib; makeTest { + mkKeyboardTest = name: { default, test }: with pkgs.lib; makeTest { inherit name; nodes.machine = { services.keyd = { enable = true; - keyboards.default = { inherit settings; }; + keyboards = { inherit default; }; }; }; @@ -70,13 +70,20 @@ let in pkgs.lib.mapAttrs mkKeyboardTest { swap-ab_and_ctrl-as-shift = { - test.press = [ "a" "ctrl-b" "c" ]; - test.expect = [ "b" "A" "c" ]; + test.press = [ "a" "ctrl-b" "c" "alt_r-h" ]; + test.expect = [ "b" "A" "c" "q" ]; - settings.main = { - "a" = "b"; - "b" = "a"; - "control" = "oneshot(shift)"; + default = { + settings.main = { + "a" = "b"; + "b" = "a"; + "control" = "oneshot(shift)"; + "rightalt" = "layer(rightalt)"; + }; + extraConfig = '' + [rightalt:G] + h = q + ''; }; }; } diff --git a/nixos/tests/keymap.nix b/nixos/tests/keymap.nix index cc45824667e..e8973a50f85 100644 --- a/nixos/tests/keymap.nix +++ b/nixos/tests/keymap.nix @@ -31,7 +31,7 @@ let nodes.machine.console.keyMap = mkOverride 900 layout; nodes.machine.services.xserver.desktopManager.xterm.enable = false; - nodes.machine.services.xserver.layout = mkOverride 900 layout; + nodes.machine.services.xserver.xkb.layout = mkOverride 900 layout; nodes.machine.imports = [ ./common/x11.nix extraConfig ]; testScript = '' @@ -116,7 +116,7 @@ in pkgs.lib.mapAttrs mkKeyboardTest { }; extraConfig.console.keyMap = "fr"; - extraConfig.services.xserver.layout = "fr"; + extraConfig.services.xserver.xkb.layout = "fr"; }; bone = { @@ -130,8 +130,8 @@ in pkgs.lib.mapAttrs mkKeyboardTest { }; extraConfig.console.keyMap = "bone"; - extraConfig.services.xserver.layout = "de"; - extraConfig.services.xserver.xkbVariant = "bone"; + extraConfig.services.xserver.xkb.layout = "de"; + extraConfig.services.xserver.xkb.variant = "bone"; }; colemak = { @@ -141,8 +141,8 @@ in pkgs.lib.mapAttrs mkKeyboardTest { }; extraConfig.console.keyMap = "colemak"; - extraConfig.services.xserver.layout = "us"; - extraConfig.services.xserver.xkbVariant = "colemak"; + extraConfig.services.xserver.xkb.layout = "us"; + extraConfig.services.xserver.xkb.variant = "colemak"; }; dvorak = { @@ -154,8 +154,8 @@ in pkgs.lib.mapAttrs mkKeyboardTest { }; extraConfig.console.keyMap = "dvorak"; - extraConfig.services.xserver.layout = "us"; - extraConfig.services.xserver.xkbVariant = "dvorak"; + extraConfig.services.xserver.xkb.layout = "us"; + extraConfig.services.xserver.xkb.variant = "dvorak"; }; dvorak-programmer = { @@ -170,8 +170,8 @@ in pkgs.lib.mapAttrs mkKeyboardTest { }; extraConfig.console.keyMap = "dvorak-programmer"; - extraConfig.services.xserver.layout = "us"; - extraConfig.services.xserver.xkbVariant = "dvp"; + extraConfig.services.xserver.xkb.layout = "us"; + extraConfig.services.xserver.xkb.variant = "dvp"; }; neo = { @@ -185,8 +185,8 @@ in pkgs.lib.mapAttrs mkKeyboardTest { }; extraConfig.console.keyMap = "neo"; - extraConfig.services.xserver.layout = "de"; - extraConfig.services.xserver.xkbVariant = "neo"; + extraConfig.services.xserver.xkb.layout = "de"; + extraConfig.services.xserver.xkb.variant = "neo"; }; qwertz = { @@ -199,7 +199,7 @@ in pkgs.lib.mapAttrs mkKeyboardTest { }; extraConfig.console.keyMap = "de"; - extraConfig.services.xserver.layout = "de"; + extraConfig.services.xserver.xkb.layout = "de"; }; custom = { @@ -212,8 +212,8 @@ in pkgs.lib.mapAttrs mkKeyboardTest { }; extraConfig.console.useXkbConfig = true; - extraConfig.services.xserver.layout = "us-greek"; - extraConfig.services.xserver.extraLayouts.us-greek = + extraConfig.services.xserver.xkb.layout = "us-greek"; + extraConfig.services.xserver.xkb.extraLayouts.us-greek = { description = "US layout with alt-gr greek"; languages = [ "eng" ]; symbolsFile = pkgs.writeText "us-greek" '' diff --git a/nixos/tests/kubo/default.nix b/nixos/tests/kubo/default.nix new file mode 100644 index 00000000000..629922fc366 --- /dev/null +++ b/nixos/tests/kubo/default.nix @@ -0,0 +1,5 @@ +{ recurseIntoAttrs, runTest }: +recurseIntoAttrs { + kubo = runTest ./kubo.nix; + kubo-fuse = runTest ./kubo-fuse.nix; +} diff --git a/nixos/tests/kubo/kubo-fuse.nix b/nixos/tests/kubo/kubo-fuse.nix new file mode 100644 index 00000000000..71a5bf61649 --- /dev/null +++ b/nixos/tests/kubo/kubo-fuse.nix @@ -0,0 +1,42 @@ +{ lib, ...} : { + name = "kubo-fuse"; + meta = with lib.maintainers; { + maintainers = [ mguentner Luflosi ]; + }; + + nodes.machine = { config, ... }: { + services.kubo = { + enable = true; + autoMount = true; + }; + users.users.alice = { + isNormalUser = true; + extraGroups = [ config.services.kubo.group ]; + }; + users.users.bob = { + isNormalUser = true; + }; + }; + + testScript = '' + start_all() + + with subtest("FUSE mountpoint"): + machine.fail("echo a | su bob -l -c 'ipfs add --quieter'") + # The FUSE mount functionality is broken as of v0.13.0 and v0.17.0. + # See https://github.com/ipfs/kubo/issues/9044. + # Workaround: using CID Version 1 avoids that. + ipfs_hash = machine.succeed( + "echo fnord3 | su alice -l -c 'ipfs add --quieter --cid-version=1'" + ).strip() + + machine.succeed(f"cat /ipfs/{ipfs_hash} | grep fnord3") + + with subtest("Unmounting of /ipns and /ipfs"): + # Force Kubo to crash and wait for it to restart + machine.systemctl("kill --signal=SIGKILL ipfs.service") + machine.wait_for_unit("ipfs.service", timeout = 30) + + machine.succeed(f"cat /ipfs/{ipfs_hash} | grep fnord3") + ''; +} diff --git a/nixos/tests/kubo.nix b/nixos/tests/kubo/kubo.nix index 496f409a40a..7965ad27738 100644 --- a/nixos/tests/kubo.nix +++ b/nixos/tests/kubo/kubo.nix @@ -18,20 +18,6 @@ }; }; - nodes.fuse = { config, ... }: { - services.kubo = { - enable = true; - autoMount = true; - }; - users.users.alice = { - isNormalUser = true; - extraGroups = [ config.services.kubo.group ]; - }; - users.users.bob = { - isNormalUser = true; - }; - }; - testScript = '' start_all() @@ -63,23 +49,5 @@ with subtest("Setting dataDir works properly with the hardened systemd unit"): machine.succeed("test -e /mnt/ipfs/config") machine.succeed("test ! -e /var/lib/ipfs/") - - with subtest("FUSE mountpoint"): - fuse.fail("echo a | su bob -l -c 'ipfs add --quieter'") - # The FUSE mount functionality is broken as of v0.13.0 and v0.17.0. - # See https://github.com/ipfs/kubo/issues/9044. - # Workaround: using CID Version 1 avoids that. - ipfs_hash = fuse.succeed( - "echo fnord3 | su alice -l -c 'ipfs add --quieter --cid-version=1'" - ).strip() - - fuse.succeed(f"cat /ipfs/{ipfs_hash} | grep fnord3") - - with subtest("Unmounting of /ipns and /ipfs"): - # Force Kubo to crash and wait for it to restart - fuse.systemctl("kill --signal=SIGKILL ipfs.service") - fuse.wait_for_unit("ipfs.service", timeout = 30) - - fuse.succeed(f"cat /ipfs/{ipfs_hash} | grep fnord3") ''; } diff --git a/nixos/tests/lanraragi.nix b/nixos/tests/lanraragi.nix new file mode 100644 index 00000000000..f513ac9d252 --- /dev/null +++ b/nixos/tests/lanraragi.nix @@ -0,0 +1,40 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "lanraragi"; + meta.maintainers = with lib.maintainers; [ tomasajt ]; + + nodes = { + machine1 = { pkgs, ... }: { + services.lanraragi.enable = true; + }; + machine2 = { pkgs, ... }: { + services.lanraragi = { + enable = true; + passwordFile = pkgs.writeText "lrr-test-pass" '' + ultra-secure-password + ''; + port = 4000; + redis = { + port = 4001; + passwordFile = pkgs.writeText "redis-lrr-test-pass" '' + still-a-very-secure-password + ''; + }; + }; + }; + + + }; + + testScript = '' + start_all() + + machine1.wait_for_unit("lanraragi.service") + machine1.wait_until_succeeds("curl -f localhost:3000") + machine1.succeed("[ $(curl -o /dev/null -X post 'http://localhost:3000/login' --data-raw 'password=kamimamita' -w '%{http_code}') -eq 302 ]") + + 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 ]") + ''; +}) + diff --git a/nixos/tests/legit.nix b/nixos/tests/legit.nix index 3eb3f503569..a71fb1743c7 100644 --- a/nixos/tests/legit.nix +++ b/nixos/tests/legit.nix @@ -8,7 +8,7 @@ in meta.maintainers = [ lib.maintainers.ratsclub ]; nodes = { - server = { config, pkgs }: { + server = { config, pkgs, ... }: { services.legit = { enable = true; settings = { diff --git a/nixos/tests/librenms.nix b/nixos/tests/librenms.nix new file mode 100644 index 00000000000..c59f56a3231 --- /dev/null +++ b/nixos/tests/librenms.nix @@ -0,0 +1,108 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: + +let + api_token = "f87f42114e44b63ad1b9e3c3d33d6fbe"; # random md5 hash + wrong_api_token = "e68ba041fcf1eab923a7a6de3af5f726"; # another random md5 hash +in { + name = "librenms"; + meta.maintainers = lib.teams.wdz.members; + + nodes.librenms = { + time.timeZone = "Europe/Berlin"; + + environment.systemPackages = with pkgs; [ + curl + jq + ]; + + services.librenms = { + enable = true; + hostname = "librenms"; + database = { + createLocally = true; + host = "localhost"; + database = "librenms"; + username = "librenms"; + passwordFile = pkgs.writeText "librenms-db-pass" "librenmsdbpass"; + }; + nginx = { + default = true; + }; + enableOneMinutePolling = true; + settings = { + enable_billing = true; + }; + }; + + # systemd oneshot to create a dummy admin user and a API token for testing + systemd.services.lnms-api-init = { + description = "LibreNMS API init"; + after = [ "librenms-setup.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + User = "root"; + Group = "root"; + }; + script = '' + API_USER_NAME=api + API_TOKEN=${api_token} # random md5 hash + + # we don't need to know the password, it just has to exist + API_USER_PASS=$(${pkgs.pwgen}/bin/pwgen -s 64 1) + ${pkgs.librenms}/artisan user:add $API_USER_NAME -r admin -p $API_USER_PASS + API_USER_ID=$(${pkgs.mariadb}/bin/mysql -D librenms -N -B -e "SELECT user_id FROM users WHERE username = '$API_USER_NAME';") + + ${pkgs.mariadb}/bin/mysql -D librenms -e "INSERT INTO api_tokens (user_id, token_hash, description) VALUES ($API_USER_ID, '$API_TOKEN', 'API User')" + ''; + }; + }; + + nodes.snmphost = { + networking.firewall.allowedUDPPorts = [ 161 ]; + + systemd.services.snmpd = { + description = "snmpd"; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "forking"; + User = "root"; + Group = "root"; + ExecStart = let + snmpd-config = pkgs.writeText "snmpd-config" '' + com2sec readonly default public + + group MyROGroup v2c readonly + view all included .1 80 + access MyROGroup "" any noauth exact all none none + + syslocation Testcity, Testcountry + syscontact Testi mc Test <test@example.com> + ''; + in "${pkgs.net-snmp}/bin/snmpd -c ${snmpd-config} -C"; + }; + }; + }; + + testScript = '' + start_all() + + snmphost.wait_until_succeeds("pgrep snmpd") + + librenms.wait_for_unit("lnms-api-init.service") + librenms.wait_for_open_port(80) + + # Test that we can authenticate against the API + librenms.succeed("curl --fail -H 'X-Auth-Token: ${api_token}' http://localhost/api/v0") + librenms.fail("curl --fail -H 'X-Auth-Token: ${wrong_api_token}' http://localhost/api/v0") + + # add snmphost as a device + librenms.succeed("curl --fail -X POST -d '{\"hostname\":\"snmphost\",\"version\":\"v2c\",\"community\":\"public\"}' -H 'X-Auth-Token: ${api_token}' http://localhost/api/v0/devices") + + # wait until snmphost gets polled + librenms.wait_until_succeeds("test $(curl -H 'X-Auth-Token: ${api_token}' http://localhost/api/v0/devices/snmphost | jq -Mr .devices[0].last_polled) != 'null'") + ''; +}) diff --git a/nixos/tests/lighttpd.nix b/nixos/tests/lighttpd.nix index 36e2745c55c..daef1584a45 100644 --- a/nixos/tests/lighttpd.nix +++ b/nixos/tests/lighttpd.nix @@ -17,5 +17,6 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { server.wait_for_unit("lighttpd.service") res = server.succeed("curl --fail http://localhost/file.txt") assert "hello nixos test" in res, f"bad server response: '{res}'" + server.succeed("systemctl reload lighttpd") ''; }) diff --git a/nixos/tests/litestream.nix b/nixos/tests/litestream.nix index f9d71c526e9..a281d853869 100644 --- a/nixos/tests/litestream.nix +++ b/nixos/tests/litestream.nix @@ -44,14 +44,22 @@ import ./make-test-python.nix ({ pkgs, ...} : { }; services.grafana = { enable = true; - security = { - adminUser = "admin"; - adminPassword = "admin"; - }; - addr = "localhost"; - port = 3000; - extraOptions = { - DATABASE_URL = "sqlite3:///var/lib/grafana/data/grafana.db?cache=private&mode=rwc&_journal_mode=WAL"; + settings = { + security = { + admin_user = "admin"; + admin_password = "admin"; + }; + + server = { + http_addr = "localhost"; + http_port = 3000; + }; + + database = { + type = "sqlite3"; + path = "/var/lib/grafana/data/grafana.db"; + wal = true; + }; }; }; users.users.foo = { diff --git a/nixos/tests/livebook-service.nix b/nixos/tests/livebook-service.nix new file mode 100644 index 00000000000..9397e3cb75f --- /dev/null +++ b/nixos/tests/livebook-service.nix @@ -0,0 +1,43 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: { + name = "livebook-service"; + + nodes = { + machine = { config, pkgs, ... }: { + imports = [ + ./common/user-account.nix + ]; + + services.livebook = { + enableUserService = true; + port = 20123; + environmentFile = pkgs.writeText "livebook.env" '' + LIVEBOOK_PASSWORD = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + ''; + options = { + cookie = "chocolate chip"; + }; + }; + }; + }; + + testScript = { nodes, ... }: + let + user = nodes.machine.config.users.users.alice; + sudo = lib.concatStringsSep " " [ + "XDG_RUNTIME_DIR=/run/user/${toString user.uid}" + "sudo" + "--preserve-env=XDG_RUNTIME_DIR" + "-u" + "alice" + ]; + in + '' + machine.wait_for_unit("multi-user.target") + + machine.succeed("loginctl enable-linger alice") + machine.wait_until_succeeds("${sudo} systemctl --user is-active livebook.service") + machine.wait_for_open_port(20123) + + machine.succeed("curl -L localhost:20123 | grep 'Type password'") + ''; +}) diff --git a/nixos/tests/lxd-image-server.nix b/nixos/tests/lxd-image-server.nix index d0afa495a5b..619542bdd94 100644 --- a/nixos/tests/lxd-image-server.nix +++ b/nixos/tests/lxd-image-server.nix @@ -8,8 +8,8 @@ let }; }; - lxd-image-metadata = lxd-image.lxdMeta.${pkgs.stdenv.hostPlatform.system}; - lxd-image-rootfs = lxd-image.lxdImage.${pkgs.stdenv.hostPlatform.system}; + lxd-image-metadata = lxd-image.lxdContainerMeta.${pkgs.stdenv.hostPlatform.system}; + lxd-image-rootfs = lxd-image.lxdContainerImage.${pkgs.stdenv.hostPlatform.system}; in { name = "lxd-image-server"; diff --git a/nixos/tests/lxd/container.nix b/nixos/tests/lxd/container.nix index bdaaebfc002..0ebe73d872f 100644 --- a/nixos/tests/lxd/container.nix +++ b/nixos/tests/lxd/container.nix @@ -13,6 +13,7 @@ let lxd-image-metadata = releases.lxdContainerMeta.${pkgs.stdenv.hostPlatform.system}; lxd-image-rootfs = releases.lxdContainerImage.${pkgs.stdenv.hostPlatform.system}; + lxd-image-rootfs-squashfs = releases.lxdContainerImageSquashfs.${pkgs.stdenv.hostPlatform.system}; in { name = "lxd-container"; @@ -23,7 +24,7 @@ in { nodes.machine = { lib, ... }: { virtualisation = { - diskSize = 4096; + diskSize = 6144; # Since we're testing `limits.cpu`, we've gotta have a known number of # cores to lean on @@ -65,6 +66,16 @@ in { machine.succeed("echo true | lxc exec container /run/current-system/sw/bin/bash -") machine.succeed("lxc delete -f container") + with subtest("Squashfs image is functional"): + machine.succeed( + "lxc image import ${lxd-image-metadata}/*/*.tar.xz ${lxd-image-rootfs-squashfs} --alias nixos-squashfs" + ) + machine.succeed("lxc launch nixos-squashfs container") + with machine.nested("Waiting for instance to start and be usable"): + retry(instance_is_up) + machine.succeed("echo true | lxc exec container /run/current-system/sw/bin/bash -") + machine.succeed("lxc delete -f container") + with subtest("Container is mounted with lxcfs inside"): machine.succeed("lxc launch nixos container") with machine.nested("Waiting for instance to start and be usable"): diff --git a/nixos/tests/mailman.nix b/nixos/tests/mailman.nix index 2806e9166d9..f9b43861a12 100644 --- a/nixos/tests/mailman.nix +++ b/nixos/tests/mailman.nix @@ -63,5 +63,11 @@ import ./make-test-python.nix { wait_for_api() machine.succeed("curl --fail-with-body -sLSu restadmin:secretpassword http://localhost:8001/3.1/domains") machine.succeed("curl --fail-with-body -sILS http://localhost/") + + with subtest("service locking"): + machine.fail("su -s /bin/sh -c 'mailman start' mailman") + machine.execute("systemctl kill --signal=SIGKILL mailman") + machine.succeed("systemctl restart mailman") + wait_for_api() ''; } diff --git a/nixos/tests/mediawiki.nix b/nixos/tests/mediawiki.nix index 52122755ad9..e30cc55ff61 100644 --- a/nixos/tests/mediawiki.nix +++ b/nixos/tests/mediawiki.nix @@ -74,4 +74,20 @@ in assert "MediaWiki has been installed" in page, f"no 'MediaWiki has been installed' in:\n{page}" ''; }; + + nginx = testLib.makeTest { + name = "mediawiki-nginx"; + nodes.machine = { + services.mediawiki.webserver = "nginx"; + }; + testScript = '' + start_all() + + machine.wait_for_unit("phpfpm-mediawiki.service") + machine.wait_for_unit("nginx.service") + + page = machine.succeed("curl -fL http://localhost/") + assert "MediaWiki has been installed" in page + ''; + }; } diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix index 442b45948c6..e7842debba7 100644 --- a/nixos/tests/misc.nix +++ b/nixos/tests/misc.nix @@ -13,6 +13,7 @@ in { environment.variables.EDITOR = lib.mkOverride 0 "emacs"; documentation.nixos.enable = lib.mkOverride 0 true; systemd.tmpfiles.rules = [ "d /tmp 1777 root root 10d" ]; + systemd.tmpfiles.settings."10-test"."/tmp/somefile".d = {}; virtualisation.fileSystems = { "/tmp2" = { fsType = "tmpfs"; options = [ "mode=1777" "noauto" ]; @@ -117,6 +118,9 @@ in { ) machine.fail("[ -e /tmp/foo ]") + with subtest("whether systemd-tmpfiles settings works"): + machine.succeed("[ -e /tmp/somefile ]") + with subtest("whether automounting works"): machine.fail("grep '/tmp2 tmpfs' /proc/mounts") machine.succeed("touch /tmp2/x") diff --git a/nixos/tests/mobilizon.nix b/nixos/tests/mobilizon.nix index 2b070ca9d96..398c8530dc5 100644 --- a/nixos/tests/mobilizon.nix +++ b/nixos/tests/mobilizon.nix @@ -10,7 +10,7 @@ import ./make-test-python.nix ({ lib, ... }: meta.maintainers = with lib.maintainers; [ minijackson erictapen ]; nodes.server = - { ... }: + { pkgs, ... }: { services.mobilizon = { enable = true; @@ -25,6 +25,8 @@ import ./make-test-python.nix ({ lib, ... }: }; }; + services.postgresql.package = pkgs.postgresql_14; + security.pki.certificateFiles = [ certs.ca.cert ]; services.nginx.virtualHosts."${mobilizonDomain}" = { diff --git a/nixos/tests/mongodb.nix b/nixos/tests/mongodb.nix index 75b0c4c2ab2..1afc891817a 100644 --- a/nixos/tests/mongodb.nix +++ b/nixos/tests/mongodb.nix @@ -27,7 +27,7 @@ import ./make-test-python.nix ({ pkgs, ... }: in { name = "mongodb"; meta = with pkgs.lib.maintainers; { - maintainers = [ bluescreen303 offline cstrahan rvl phile314 ]; + maintainers = [ bluescreen303 offline rvl phile314 ]; }; nodes = { diff --git a/nixos/tests/mosquitto.nix b/nixos/tests/mosquitto.nix index 8eca4f25922..c0980b23e78 100644 --- a/nixos/tests/mosquitto.nix +++ b/nixos/tests/mosquitto.nix @@ -4,7 +4,6 @@ let port = 1888; tlsPort = 1889; anonPort = 1890; - bindTestPort = 18910; password = "VERY_secret"; hashedPassword = "$7$101$/WJc4Mp+I+uYE9sR$o7z9rD1EYXHPwEP5GqQj6A7k4W1yVbePlb8TqNcuOLV9WNCiDgwHOB0JHC1WCtdkssqTBduBNUnUGd6kmZvDSw=="; topic = "test/foo"; @@ -127,10 +126,6 @@ in { }; }; } - { - settings.bind_interface = "eth0"; - port = bindTestPort; - } ]; }; }; @@ -140,8 +135,6 @@ in { }; testScript = '' - import json - def mosquitto_cmd(binary, user, topic, port): return ( "mosquitto_{} " @@ -174,27 +167,6 @@ in { start_all() server.wait_for_unit("mosquitto.service") - with subtest("bind_interface"): - addrs = dict() - for iface in json.loads(server.succeed("ip -json address show")): - for addr in iface['addr_info']: - # don't want to deal with multihoming here - assert addr['local'] not in addrs - addrs[addr['local']] = (iface['ifname'], addr['family']) - - # mosquitto grabs *one* random address per type for bind_interface - (has4, has6) = (False, False) - for line in server.succeed("ss -HlptnO sport = ${toString bindTestPort}").splitlines(): - items = line.split() - if "mosquitto" not in items[5]: continue - listener = items[3].rsplit(':', maxsplit=1)[0].strip('[]') - assert listener in addrs - assert addrs[listener][0] == "eth0" - has4 |= addrs[listener][1] == 'inet' - has6 |= addrs[listener][1] == 'inet6' - assert has4 - assert has6 - with subtest("check passwords"): client1.succeed(publish("-m test", "password_store")) client1.succeed(publish("-m test", "password_file")) diff --git a/nixos/tests/mysql/common.nix b/nixos/tests/mysql/common.nix index 7fdf0f33d3f..1cf52347f4c 100644 --- a/nixos/tests/mysql/common.nix +++ b/nixos/tests/mysql/common.nix @@ -3,5 +3,8 @@ mysqlPackages = { inherit (pkgs) mysql80; }; + perconaPackages = { + inherit (pkgs) percona-server_8_0; + }; mkTestName = pkg: "mariadb_${builtins.replaceStrings ["."] [""] (lib.versions.majorMinor pkg.version)}"; } diff --git a/nixos/tests/mysql/mysql.nix b/nixos/tests/mysql/mysql.nix index 6ddc49f86f7..3e059cad09e 100644 --- a/nixos/tests/mysql/mysql.nix +++ b/nixos/tests/mysql/mysql.nix @@ -6,7 +6,7 @@ }: let - inherit (import ./common.nix { inherit pkgs lib; }) mkTestName mariadbPackages mysqlPackages; + inherit (import ./common.nix { inherit pkgs lib; }) mkTestName mariadbPackages mysqlPackages perconaPackages; makeTest = import ./../make-test-python.nix; # Setup common users @@ -78,9 +78,6 @@ let }; }; }; - - mariadb = { - }; }; testScript = '' @@ -147,3 +144,8 @@ in // (lib.mapAttrs (_: package: makeMySQLTest { inherit package; }) mariadbPackages) + // (lib.mapAttrs (_: package: makeMySQLTest { + inherit package; + name = "percona_8_0"; + hasMroonga = false; useSocketAuth = false; + }) perconaPackages) diff --git a/nixos/tests/netdata.nix b/nixos/tests/netdata.nix index c5f7294f79a..e3438f63404 100644 --- a/nixos/tests/netdata.nix +++ b/nixos/tests/netdata.nix @@ -30,8 +30,8 @@ import ./make-test-python.nix ({ pkgs, ...} : { # check if netdata can read disk ops for root owned processes. # if > 0, successful. verifies both netdata working and # apps.plugin has elevated capabilities. - url = "http://localhost:19999/api/v1/data\?chart=users.pwrites" - filter = '[.data[range(10)][.labels | indices("root")[0]]] | add | . > 0' + url = "http://localhost:19999/api/v1/data\?chart=user.root_disk_physical_io" + filter = '[.data[range(10)][2]] | add | . < 0' cmd = f"curl -s {url} | jq -e '{filter}'" netdata.wait_until_succeeds(cmd) diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix index 46fc715d089..768d0cfa223 100644 --- a/nixos/tests/networking.nix +++ b/nixos/tests/networking.nix @@ -113,8 +113,8 @@ let networking = { useNetworkd = networkd; useDHCP = false; - defaultGateway = "192.168.1.1"; - defaultGateway6 = "fd00:1234:5678:1::1"; + defaultGateway = { address = "192.168.1.1"; interface = "enp1s0"; }; + defaultGateway6 = { address = "fd00:1234:5678:1::1"; interface = "enp1s0"; }; interfaces.enp1s0.ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } { address = "192.168.1.3"; prefixLength = 32; } @@ -185,7 +185,11 @@ let nodes.router = router; nodes.client = { lib, ... }: { # Disable test driver default config - networking.interfaces = lib.mkForce {}; + networking.interfaces = lib.mkForce { + # Make sure DHCP defaults correctly even when some unrelated config + # is set on the interface (nothing, in this case). + enp1s0 = {}; + }; networking.useNetworkd = networkd; virtualisation.interfaces.enp1s0.vlan = 1; }; diff --git a/nixos/tests/nextcloud/basic.nix b/nixos/tests/nextcloud/basic.nix index b7af6d6d736..ab1d8353dba 100644 --- a/nixos/tests/nextcloud/basic.nix +++ b/nixos/tests/nextcloud/basic.nix @@ -37,8 +37,6 @@ in { "d /var/lib/nextcloud-data 0750 nextcloud nginx - -" ]; - system.stateVersion = "22.11"; # stateVersion >=21.11 to make sure that we use OpenSSL3 - services.nextcloud = { enable = true; datadir = "/var/lib/nextcloud-data"; diff --git a/nixos/tests/nextcloud/default.nix b/nixos/tests/nextcloud/default.nix index b9f35b398cf..19d04b28b4f 100644 --- a/nixos/tests/nextcloud/default.nix +++ b/nixos/tests/nextcloud/default.nix @@ -8,10 +8,6 @@ with pkgs.lib; foldl (matrix: ver: matrix // { "basic${toString ver}" = import ./basic.nix { inherit system pkgs; nextcloudVersion = ver; }; - "openssl-sse${toString ver}" = import ./openssl-sse.nix { - inherit system pkgs; - nextcloudVersion = ver; - }; "with-postgresql-and-redis${toString ver}" = import ./with-postgresql-and-redis.nix { inherit system pkgs; nextcloudVersion = ver; @@ -26,4 +22,4 @@ foldl }; }) { } - [ 25 26 27 ] + [ 26 27 ] diff --git a/nixos/tests/nextcloud/openssl-sse.nix b/nixos/tests/nextcloud/openssl-sse.nix deleted file mode 100644 index d6ea39c6155..00000000000 --- a/nixos/tests/nextcloud/openssl-sse.nix +++ /dev/null @@ -1,109 +0,0 @@ -args@{ pkgs, nextcloudVersion ? 25, ... }: - -(import ../make-test-python.nix ({ pkgs, ...}: let - adminuser = "root"; - adminpass = "notproduction"; - nextcloudBase = { - networking.firewall.allowedTCPPorts = [ 80 ]; - system.stateVersion = "22.05"; # stateVersions <22.11 use openssl 1.1 by default - services.nextcloud = { - enable = true; - config.adminpassFile = "${pkgs.writeText "adminpass" adminpass}"; - database.createLocally = true; - package = pkgs.${"nextcloud" + (toString nextcloudVersion)}; - }; - }; -in { - name = "nextcloud-openssl"; - meta = with pkgs.lib.maintainers; { - maintainers = [ ma27 ]; - }; - nodes.nextcloudwithopenssl1 = { - imports = [ nextcloudBase ]; - services.nextcloud.hostName = "nextcloudwithopenssl1"; - }; - nodes.nextcloudwithopenssl3 = { - imports = [ nextcloudBase ]; - services.nextcloud = { - hostName = "nextcloudwithopenssl3"; - enableBrokenCiphersForSSE = false; - }; - }; - testScript = { nodes, ... }: let - withRcloneEnv = host: pkgs.writeScript "with-rclone-env" '' - #!${pkgs.runtimeShell} - export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav - export RCLONE_CONFIG_NEXTCLOUD_URL="http://${host}/remote.php/dav/files/${adminuser}" - export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud" - export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}" - export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})" - "''${@}" - ''; - withRcloneEnv1 = withRcloneEnv "nextcloudwithopenssl1"; - withRcloneEnv3 = withRcloneEnv "nextcloudwithopenssl3"; - copySharedFile1 = pkgs.writeScript "copy-shared-file" '' - #!${pkgs.runtimeShell} - echo 'hi' | ${withRcloneEnv1} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file - ''; - copySharedFile3 = pkgs.writeScript "copy-shared-file" '' - #!${pkgs.runtimeShell} - echo 'bye' | ${withRcloneEnv3} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file2 - ''; - openssl1-node = nodes.nextcloudwithopenssl1.system.build.toplevel; - openssl3-node = nodes.nextcloudwithopenssl3.system.build.toplevel; - in '' - nextcloudwithopenssl1.start() - nextcloudwithopenssl1.wait_for_unit("multi-user.target") - nextcloudwithopenssl1.succeed("nextcloud-occ status") - nextcloudwithopenssl1.succeed("curl -sSf http://nextcloudwithopenssl1/login") - nextcloud_version = ${toString nextcloudVersion} - - with subtest("With OpenSSL 1 SSE can be enabled and used"): - nextcloudwithopenssl1.succeed("nextcloud-occ app:enable encryption") - nextcloudwithopenssl1.succeed("nextcloud-occ encryption:enable") - - with subtest("Upload file and ensure it's encrypted"): - nextcloudwithopenssl1.succeed("${copySharedFile1}") - nextcloudwithopenssl1.succeed("grep -E '^HBEGIN:oc_encryption_module' /var/lib/nextcloud/data/root/files/test-shared-file") - nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi") - - with subtest("Switch to OpenSSL 3"): - nextcloudwithopenssl1.succeed("${openssl3-node}/bin/switch-to-configuration test") - nextcloudwithopenssl1.wait_for_open_port(80) - nextcloudwithopenssl1.succeed("nextcloud-occ status") - - with subtest("Existing encrypted files cannot be read, but new files can be added"): - # This will succeed starting NC26 because of their custom implementation of openssl_seal - read_existing_file_test = nextcloudwithopenssl1.fail if nextcloud_version < 26 else nextcloudwithopenssl1.succeed - read_existing_file_test("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file >&2") - nextcloudwithopenssl1.succeed("nextcloud-occ encryption:disable") - nextcloudwithopenssl1.succeed("${copySharedFile3}") - nextcloudwithopenssl1.succeed("grep bye /var/lib/nextcloud/data/root/files/test-shared-file2") - nextcloudwithopenssl1.succeed("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye") - - with subtest("Switch back to OpenSSL 1.1 and ensure that encrypted files are readable again"): - nextcloudwithopenssl1.succeed("${openssl1-node}/bin/switch-to-configuration test") - nextcloudwithopenssl1.wait_for_open_port(80) - nextcloudwithopenssl1.succeed("nextcloud-occ status") - nextcloudwithopenssl1.succeed("nextcloud-occ encryption:enable") - nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye") - nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi") - nextcloudwithopenssl1.succeed("grep -E '^HBEGIN:oc_encryption_module' /var/lib/nextcloud/data/root/files/test-shared-file") - nextcloudwithopenssl1.succeed("grep bye /var/lib/nextcloud/data/root/files/test-shared-file2") - - with subtest("Ensure that everything can be decrypted"): - nextcloudwithopenssl1.succeed("echo y | nextcloud-occ encryption:decrypt-all >&2") - nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye") - nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi") - nextcloudwithopenssl1.succeed("grep -vE '^HBEGIN:oc_encryption_module' /var/lib/nextcloud/data/root/files/test-shared-file") - - with subtest("Switch to OpenSSL 3 ensure that all files are usable now"): - nextcloudwithopenssl1.succeed("${openssl3-node}/bin/switch-to-configuration test") - nextcloudwithopenssl1.wait_for_open_port(80) - nextcloudwithopenssl1.succeed("nextcloud-occ status") - nextcloudwithopenssl1.succeed("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye") - nextcloudwithopenssl1.succeed("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi") - - nextcloudwithopenssl1.shutdown() - ''; -})) args diff --git a/nixos/tests/nginx-sandbox.nix b/nixos/tests/nginx-sandbox.nix deleted file mode 100644 index 92ba30a09cf..00000000000 --- a/nixos/tests/nginx-sandbox.nix +++ /dev/null @@ -1,65 +0,0 @@ -import ./make-test-python.nix ({ pkgs, ... }: { - name = "nginx-sandbox"; - meta = with pkgs.lib.maintainers; { - maintainers = [ izorkin ]; - }; - - # This test checks the creation and reading of a file in sandbox mode. Used simple lua script. - - nodes.machine = { pkgs, ... }: { - nixpkgs.overlays = [ - (self: super: { - nginx-lua = super.nginx.override { - modules = [ - pkgs.nginxModules.lua - ]; - }; - }) - ]; - services.nginx.enable = true; - services.nginx.package = pkgs.nginx-lua; - services.nginx.virtualHosts.localhost = { - extraConfig = '' - location /test1-write { - content_by_lua_block { - local create = os.execute('${pkgs.coreutils}/bin/mkdir /tmp/test1-read') - local create = os.execute('${pkgs.coreutils}/bin/touch /tmp/test1-read/foo.txt') - local echo = os.execute('${pkgs.coreutils}/bin/echo worked > /tmp/test1-read/foo.txt') - } - } - location /test1-read { - root /tmp; - } - location /test2-write { - content_by_lua_block { - local create = os.execute('${pkgs.coreutils}/bin/mkdir /var/web/test2-read') - local create = os.execute('${pkgs.coreutils}/bin/touch /var/web/test2-read/bar.txt') - local echo = os.execute('${pkgs.coreutils}/bin/echo error-worked > /var/web/test2-read/bar.txt') - } - } - location /test2-read { - root /var/web; - } - ''; - }; - users.users.foo.isNormalUser = true; - }; - - testScript = '' - machine.wait_for_unit("nginx") - machine.wait_for_open_port(80) - - # Checking write in temporary folder - machine.succeed("$(curl -vvv http://localhost/test1-write)") - machine.succeed('test "$(curl -fvvv http://localhost/test1-read/foo.txt)" = worked') - - # Checking write in protected folder. In sandbox mode for the nginx service, the folder /var/web is mounted - # in read-only mode. - machine.succeed("mkdir -p /var/web") - machine.succeed("chown nginx:nginx /var/web") - machine.succeed("$(curl -vvv http://localhost/test2-write)") - assert "404 Not Found" in machine.succeed( - "curl -vvv -s http://localhost/test2-read/bar.txt" - ) - ''; -}) diff --git a/nixos/tests/nginx-tmpdir.nix b/nixos/tests/nginx-tmpdir.nix new file mode 100644 index 00000000000..f26f992ffe1 --- /dev/null +++ b/nixos/tests/nginx-tmpdir.nix @@ -0,0 +1,60 @@ +let + dst-dir = "/run/nginx-test-tmpdir-uploads"; +in + import ./make-test-python.nix { + name = "nginx-tmpdir"; + + nodes.machine = { pkgs, ... }: { + environment.etc."tmpfiles.d/nginx-uploads.conf".text = "d ${dst-dir} 0755 nginx nginx 1d"; + + # overwrite the tmp.conf with a short age, there will be a duplicate line info from systemd-tmpfiles in the log + systemd.tmpfiles.rules = [ + "q /tmp 1777 root root 1min" + ]; + + services.nginx.enable = true; + # simple upload service using the nginx client body temp path + services.nginx.virtualHosts = { + localhost = { + locations."~ ^/upload/([0-9a-zA-Z-.]*)$" = { + extraConfig = '' + alias ${dst-dir}/$1; + client_body_in_file_only clean; + dav_methods PUT; + create_full_put_path on; + dav_access group:rw all:r; + ''; + }; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("nginx") + machine.wait_for_open_port(80) + + with subtest("Needed prerequisite --http-client-body-temp-path=/tmp/nginx_client_body and private temp"): + machine.succeed("touch /tmp/systemd-private-*-nginx.service-*/tmp/nginx_client_body") + + with subtest("Working upload of test setup"): + machine.succeed("curl -X PUT http://localhost/upload/test1 --fail --data-raw 'Raw data 1'") + machine.succeed('test "$(cat ${dst-dir}/test1)" = "Raw data 1"') + + # let the tmpfiles clean service do its job + machine.succeed("touch /tmp/touched") + machine.wait_until_succeeds( + "sleep 15 && systemctl start systemd-tmpfiles-clean.service && [ ! -f /tmp/touched ]", + timeout=150 + ) + + with subtest("Working upload after cleaning"): + machine.succeed("curl -X PUT http://localhost/upload/test2 --fail --data-raw 'Raw data 2'") + machine.succeed('test "$(cat ${dst-dir}/test2)" = "Raw data 2"') + + # manually remove the nginx temp dir + machine.succeed("rm -r --interactive=never /tmp/systemd-private-*-nginx.service-*/tmp/nginx_client_body") + + with subtest("Broken upload after manual temp dir removal"): + machine.fail("curl -X PUT http://localhost/upload/test3 --fail --data-raw 'Raw data 3'") + ''; + } diff --git a/nixos/tests/nginx-unix-socket.nix b/nixos/tests/nginx-unix-socket.nix new file mode 100644 index 00000000000..4640eaa171b --- /dev/null +++ b/nixos/tests/nginx-unix-socket.nix @@ -0,0 +1,27 @@ +import ./make-test-python.nix ({ pkgs, ... }: +let + nginxSocketPath = "/var/run/nginx/test.sock"; +in +{ + name = "nginx-unix-socket"; + + nodes = { + webserver = { pkgs, lib, ... }: { + services.nginx = { + enable = true; + virtualHosts.localhost = { + serverName = "localhost"; + listen = [{ addr = "unix:${nginxSocketPath}"; }]; + locations."/test".return = "200 'foo'"; + }; + }; + }; + }; + + testScript = '' + webserver.wait_for_unit("nginx") + webserver.wait_for_open_unix_socket("${nginxSocketPath}") + + webserver.succeed("curl --fail --silent --unix-socket '${nginxSocketPath}' http://localhost/test | grep '^foo$'") + ''; +}) diff --git a/nixos/tests/nixos-rebuild-install-bootloader.nix b/nixos/tests/nixos-rebuild-install-bootloader.nix new file mode 100644 index 00000000000..3ade90ea24a --- /dev/null +++ b/nixos/tests/nixos-rebuild-install-bootloader.nix @@ -0,0 +1,73 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "nixos-rebuild-install-bootloader"; + + nodes = { + machine = { lib, pkgs, ... }: { + imports = [ + ../modules/profiles/installation-device.nix + ../modules/profiles/base.nix + ]; + + nix.settings = { + substituters = lib.mkForce [ ]; + hashed-mirrors = null; + connect-timeout = 1; + }; + + system.includeBuildDependencies = true; + + virtualisation = { + cores = 2; + memorySize = 2048; + }; + + virtualisation.useBootLoader = true; + }; + }; + + testScript = + let + configFile = pkgs.writeText "configuration.nix" '' + { lib, pkgs, ... }: { + imports = [ + ./hardware-configuration.nix + <nixpkgs/nixos/modules/testing/test-instrumentation.nix> + ]; + + boot.loader.grub = { + enable = true; + device = "/dev/vda"; + forceInstall = true; + }; + + documentation.enable = false; + } + ''; + + in + '' + machine.start() + machine.succeed("udevadm settle") + machine.wait_for_unit("multi-user.target") + + machine.succeed("nixos-generate-config") + machine.copy_from_host( + "${configFile}", + "/etc/nixos/configuration.nix", + ) + machine.succeed("nixos-rebuild switch") + + # Need to run `nixos-rebuild` twice because the first run will install + # GRUB anyway + with subtest("Switch system again and install bootloader"): + result = machine.succeed("nixos-rebuild switch --install-bootloader") + # install-grub2.pl messages + assert "updating GRUB 2 menu..." in result + assert "installing the GRUB 2 boot loader on /dev/vda..." in result + # GRUB message + assert "Installation finished. No error reported." in result + # at this point we've tested regression #262724, but haven't tested the bootloader itself + # TODO: figure out how to how to tell the test driver to start the bootloader instead of + # booting into the kernel directly. + ''; +}) diff --git a/nixos/tests/nixos-test-driver/timeout.nix b/nixos/tests/nixos-test-driver/timeout.nix new file mode 100644 index 00000000000..29bd85d2498 --- /dev/null +++ b/nixos/tests/nixos-test-driver/timeout.nix @@ -0,0 +1,15 @@ +{ + name = "Test that sleep of 6 seconds fails a timeout of 5 seconds"; + globalTimeout = 5; + + nodes = { + machine = ({ pkgs, ... }: { + }); + }; + + testScript = '' + start_all() + machine.wait_for_unit("multi-user.target") + machine.succeed("sleep 6") + ''; +} diff --git a/nixos/tests/non-switchable-system.nix b/nixos/tests/non-switchable-system.nix new file mode 100644 index 00000000000..54bede75453 --- /dev/null +++ b/nixos/tests/non-switchable-system.nix @@ -0,0 +1,15 @@ +{ lib, ... }: + +{ + name = "non-switchable-system"; + + meta.maintainers = with lib.maintainers; [ nikstur ]; + + nodes.machine = { + system.switch.enable = false; + }; + + testScript = '' + machine.succeed("test ! -e /run/current-system/bin/switch-to-configuration") + ''; +} diff --git a/nixos/tests/openresty-lua.nix b/nixos/tests/openresty-lua.nix index b177b3c194d..9e987398f51 100644 --- a/nixos/tests/openresty-lua.nix +++ b/nixos/tests/openresty-lua.nix @@ -16,6 +16,12 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: nodes = { webserver = { pkgs, lib, ... }: { + networking = { + extraHosts = '' + 127.0.0.1 default.test + 127.0.0.1 sandbox.test + ''; + }; services.nginx = { enable = true; package = pkgs.openresty; @@ -24,7 +30,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: lua_package_path '${luaPath};;'; ''; - virtualHosts."default" = { + virtualHosts."default.test" = { default = true; locations."/" = { extraConfig = '' @@ -36,6 +42,33 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: ''; }; }; + + virtualHosts."sandbox.test" = { + locations."/test1-write" = { + extraConfig = '' + content_by_lua_block { + local create = os.execute('${pkgs.coreutils}/bin/mkdir /tmp/test1-read') + local create = os.execute('${pkgs.coreutils}/bin/touch /tmp/test1-read/foo.txt') + local echo = os.execute('${pkgs.coreutils}/bin/echo worked > /tmp/test1-read/foo.txt') + } + ''; + }; + locations."/test1-read" = { + root = "/tmp"; + }; + locations."/test2-write" = { + extraConfig = '' + content_by_lua_block { + local create = os.execute('${pkgs.coreutils}/bin/mkdir /var/web/test2-read') + local create = os.execute('${pkgs.coreutils}/bin/touch /var/web/test2-read/bar.txt') + local echo = os.execute('${pkgs.coreutils}/bin/echo error-worked > /var/web/test2-read/bar.txt') + } + ''; + }; + locations."/test2-read" = { + root = "/var/web"; + }; + }; }; }; }; @@ -51,5 +84,18 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: f"curl -w '%{{http_code}}' --head --fail {url}" ) assert http_code.split("\n")[-1] == "200" + + # This test checks the creation and reading of a file in sandbox mode. + # Checking write in temporary folder + webserver.succeed("$(curl -vvv http://sandbox.test/test1-write)") + webserver.succeed('test "$(curl -fvvv http://sandbox.test/test1-read/foo.txt)" = worked') + # Checking write in protected folder. In sandbox mode for the nginx service, the folder /var/web is mounted + # in read-only mode. + webserver.succeed("mkdir -p /var/web") + webserver.succeed("chown nginx:nginx /var/web") + webserver.succeed("$(curl -vvv http://sandbox.test/test2-write)") + assert "404 Not Found" in machine.succeed( + "curl -vvv -s http://sandbox.test/test2-read/bar.txt" + ) ''; }) diff --git a/nixos/tests/opensearch.nix b/nixos/tests/opensearch.nix index c0caf950cb9..2887ac96776 100644 --- a/nixos/tests/opensearch.nix +++ b/nixos/tests/opensearch.nix @@ -31,14 +31,9 @@ in services.opensearch.dataDir = "/var/opensearch_test"; services.opensearch.user = "open_search"; services.opensearch.group = "open_search"; - system.activationScripts.createDirectory = { - text = '' - mkdir -p "/var/opensearch_test" - chown open_search:open_search /var/opensearch_test - chmod 0700 /var/opensearch_test - ''; - deps = [ "users" "groups" ]; - }; + systemd.tmpfiles.rules = [ + "d /var/opensearch_test 0700 open_search open_search -" + ]; users = { groups.open_search = {}; users.open_search = { diff --git a/nixos/tests/openssh.nix b/nixos/tests/openssh.nix index d771ffd3e0f..79949747799 100644 --- a/nixos/tests/openssh.nix +++ b/nixos/tests/openssh.nix @@ -22,7 +22,7 @@ in { ]; }; - server_lazy = + server-lazy = { ... }: { @@ -34,7 +34,7 @@ in { ]; }; - server_localhost_only = + server-localhost-only = { ... }: { @@ -43,7 +43,7 @@ in { }; }; - server_localhost_only_lazy = + server-localhost-only-lazy = { ... }: { @@ -52,12 +52,12 @@ in { }; }; - server_match_rule = + server-match-rule = { ... }: { services.openssh = { - enable = true; listenAddresses = [ { addr = "127.0.0.1"; port = 22; } ]; + enable = true; listenAddresses = [ { addr = "127.0.0.1"; port = 22; } { addr = "[::]"; port = 22; } ]; extraConfig = '' # Combined test for two (predictable) Match criterias Match LocalAddress 127.0.0.1 LocalPort 22 @@ -82,6 +82,19 @@ in { }; }; + server_allowedusers = + { ... }: + + { + services.openssh = { enable = true; settings.AllowUsers = [ "alice" "bob" ]; }; + users.groups = { alice = { }; bob = { }; carol = { }; }; + users.users = { + alice = { isNormalUser = true; group = "alice"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; }; + bob = { isNormalUser = true; group = "bob"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; }; + carol = { isNormalUser = true; group = "carol"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; }; + }; + }; + client = { ... }: { }; @@ -90,7 +103,12 @@ in { testScript = '' start_all() - server.wait_for_unit("sshd") + server.wait_for_unit("sshd", timeout=30) + server_localhost_only.wait_for_unit("sshd", timeout=30) + server_match_rule.wait_for_unit("sshd", timeout=30) + + server_lazy.wait_for_unit("sshd.socket", timeout=30) + server_localhost_only_lazy.wait_for_unit("sshd.socket", timeout=30) with subtest("manual-authkey"): client.succeed("mkdir -m 700 /root/.ssh") @@ -119,11 +137,11 @@ in { ) client.succeed( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'echo hello world' >&2", + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server-lazy 'echo hello world' >&2", timeout=30 ) client.succeed( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'ulimit -l' | grep 1024", + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server-lazy 'ulimit -l' | grep 1024", timeout=30 ) @@ -137,7 +155,7 @@ in { timeout=30 ) client.succeed( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server_lazy true", + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server-lazy true", timeout=30 ) @@ -147,5 +165,23 @@ in { with subtest("match-rules"): server_match_rule.succeed("ss -nlt | grep '127.0.0.1:22'") + + with subtest("allowed-users"): + client.succeed( + "cat ${snakeOilPrivateKey} > privkey.snakeoil" + ) + client.succeed("chmod 600 privkey.snakeoil") + client.succeed( + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil alice@server_allowedusers true", + timeout=30 + ) + client.succeed( + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil bob@server_allowedusers true", + timeout=30 + ) + client.fail( + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil carol@server_allowedusers true", + timeout=30 + ) ''; }) diff --git a/nixos/tests/osquery.nix b/nixos/tests/osquery.nix index 9aa9820e50c..e98e7c1baf0 100644 --- a/nixos/tests/osquery.nix +++ b/nixos/tests/osquery.nix @@ -36,7 +36,7 @@ in machine.succeed("echo 'SELECT address FROM etc_hosts LIMIT 1;' | osqueryi | tee /dev/console | grep -q '127.0.0.1'") # osquery binaries respect configuration from the Nix config option. - machine.succeed("echo 'SELECT value FROM osquery_flags WHERE name = \"utc\";' | osqueryi | tee /dev/console | grep -q ${boolToString utc}") + machine.succeed("echo 'SELECT value FROM osquery_flags WHERE name = \"utc\";' | osqueryi | tee /dev/console | grep -q ${lib.boolToString utc}") # osquery binaries respect configuration from the Nix flags option. machine.succeed("echo 'SELECT value FROM osquery_flags WHERE name = \"config_refresh\";' | osqueryi | tee /dev/console | grep -q ${config_refresh}") diff --git a/nixos/tests/pam/pam-u2f.nix b/nixos/tests/pam/pam-u2f.nix index 07408dea797..46e307a3f12 100644 --- a/nixos/tests/pam/pam-u2f.nix +++ b/nixos/tests/pam/pam-u2f.nix @@ -20,7 +20,7 @@ import ../make-test-python.nix ({ ... }: '' machine.wait_for_unit("multi-user.target") machine.succeed( - 'egrep "auth required .*/lib/security/pam_u2f.so.*debug.*interactive.*cue.*origin=nixos-test" /etc/pam.d/ -R' + 'egrep "auth required .*/lib/security/pam_u2f.so.*cue.*debug.*interactive.*origin=nixos-test" /etc/pam.d/ -R' ) ''; }) diff --git a/nixos/tests/pam/test_chfn.py b/nixos/tests/pam/test_chfn.py index a48438b8d30..3cfbb3908e9 100644 --- a/nixos/tests/pam/test_chfn.py +++ b/nixos/tests/pam/test_chfn.py @@ -6,7 +6,7 @@ expected_lines = { "auth required pam_deny.so", "auth sufficient @@pam_ccreds@@/lib/security/pam_ccreds.so action=store use_first_pass", "auth sufficient pam_rootok.so", - "auth sufficient pam_unix.so likeauth try_first_pass", + "auth sufficient pam_unix.so likeauth try_first_pass", "password sufficient @@pam_krb5@@/lib/security/pam_krb5.so use_first_pass", "password sufficient pam_unix.so nullok yescrypt", "session optional @@pam_krb5@@/lib/security/pam_krb5.so", @@ -15,9 +15,10 @@ expected_lines = { } actual_lines = set(machine.succeed("cat /etc/pam.d/chfn").splitlines()) -missing_lines = expected_lines - actual_lines -extra_lines = actual_lines - expected_lines -non_functional_lines = set([line for line in extra_lines if (line == "" or line.startswith("#"))]) +stripped_lines = set([line.split("#")[0].rstrip() for line in actual_lines]) +missing_lines = expected_lines - stripped_lines +extra_lines = stripped_lines - expected_lines +non_functional_lines = set([line for line in extra_lines if line == ""]) unexpected_functional_lines = extra_lines - non_functional_lines with subtest("All expected lines are in the file"): diff --git a/nixos/tests/pantheon.nix b/nixos/tests/pantheon.nix index dee6964644c..be1351283d9 100644 --- a/nixos/tests/pantheon.nix +++ b/nixos/tests/pantheon.nix @@ -50,6 +50,20 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : machine.wait_for_window("io.elementary.wingpanel") machine.wait_until_succeeds("pgrep plank") machine.wait_for_window("plank") + machine.wait_until_succeeds("pgrep -f gsd-media-keys") + machine.wait_for_unit("bamfdaemon.service", "${user.name}") + machine.wait_for_unit("io.elementary.files.xdg-desktop-portal.service", "${user.name}") + + with subtest("Open elementary videos"): + machine.execute("su - ${user.name} -c 'DISPLAY=:0 io.elementary.videos >&2 &'") + machine.sleep(2) + machine.wait_for_window("io.elementary.videos") + machine.wait_for_text("No Videos Open") + + with subtest("Open elementary calendar"): + machine.execute("su - ${user.name} -c 'DISPLAY=:0 io.elementary.calendar >&2 &'") + machine.sleep(2) + machine.wait_for_window("io.elementary.calendar") with subtest("Open system settings"): machine.execute("su - ${user.name} -c 'DISPLAY=:0 io.elementary.switchboard >&2 &'") @@ -63,7 +77,9 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : with subtest("Check if gala has ever coredumped"): machine.fail("coredumpctl --json=short | grep gala") - machine.sleep(20) + # So you can see the dock in the below screenshot. + machine.succeed("su - ${user.name} -c 'DISPLAY=:0 xdotool mousemove 450 1000 >&2 &'") + machine.sleep(10) machine.screenshot("screen") ''; }) diff --git a/nixos/tests/paperless.nix b/nixos/tests/paperless.nix index ce6a4d8128d..6a51cc522bd 100644 --- a/nixos/tests/paperless.nix +++ b/nixos/tests/paperless.nix @@ -2,65 +2,88 @@ import ./make-test-python.nix ({ lib, ... }: { name = "paperless"; meta.maintainers = with lib.maintainers; [ erikarvstedt Flakebi ]; - nodes.machine = { pkgs, ... }: { - environment.systemPackages = with pkgs; [ imagemagick jq ]; - services.paperless = { - enable = true; - passwordFile = builtins.toFile "password" "admin"; + nodes = let self = { + simple = { pkgs, ... }: { + environment.systemPackages = with pkgs; [ imagemagick jq ]; + services.paperless = { + enable = true; + passwordFile = builtins.toFile "password" "admin"; + }; }; - }; + postgres = { config, pkgs, ... }: { + imports = [ self.simple ]; + services.postgresql = { + enable = true; + ensureDatabases = [ "paperless" ]; + ensureUsers = [ + { name = config.services.paperless.user; + ensureDBOwnership = true; + } + ]; + }; + services.paperless.extraConfig = { + PAPERLESS_DBHOST = "/run/postgresql"; + }; + }; + }; in self; testScript = '' import json - machine.wait_for_unit("paperless-consumer.service") + def test_paperless(node): + node.wait_for_unit("paperless-consumer.service") - with subtest("Add a document via the file system"): - machine.succeed( - "convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black " - "-annotate +5+20 'hello world 16-10-2005' /var/lib/paperless/consume/doc.png" + with subtest("Add a document via the file system"): + node.succeed( + "convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black " + "-annotate +5+20 'hello world 16-10-2005' /var/lib/paperless/consume/doc.png" ) - with subtest("Web interface gets ready"): - machine.wait_for_unit("paperless-web.service") + with subtest("Web interface gets ready"): + node.wait_for_unit("paperless-web.service") # Wait until server accepts connections - machine.wait_until_succeeds("curl -fs localhost:28981") + node.wait_until_succeeds("curl -fs localhost:28981") - # Required for consuming documents via the web interface - with subtest("Task-queue gets ready"): - machine.wait_for_unit("paperless-task-queue.service") + # Required for consuming documents via the web interface + with subtest("Task-queue gets ready"): + node.wait_for_unit("paperless-task-queue.service") - with subtest("Add a png document via the web interface"): - machine.succeed( - "convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black " - "-annotate +5+20 'hello web 16-10-2005' /tmp/webdoc.png" + with subtest("Add a png document via the web interface"): + node.succeed( + "convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black " + "-annotate +5+20 'hello web 16-10-2005' /tmp/webdoc.png" ) - machine.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.png -fs localhost:28981/api/documents/post_document/") + node.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.png -fs localhost:28981/api/documents/post_document/") - with subtest("Add a txt document via the web interface"): - machine.succeed( - "echo 'hello web 16-10-2005' > /tmp/webdoc.txt" + with subtest("Add a txt document via the web interface"): + node.succeed( + "echo 'hello web 16-10-2005' > /tmp/webdoc.txt" ) - machine.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.txt -fs localhost:28981/api/documents/post_document/") + node.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.txt -fs localhost:28981/api/documents/post_document/") - with subtest("Documents are consumed"): - machine.wait_until_succeeds( - "(($(curl -u admin:admin -fs localhost:28981/api/documents/ | jq .count) == 3))" + with subtest("Documents are consumed"): + node.wait_until_succeeds( + "(($(curl -u admin:admin -fs localhost:28981/api/documents/ | jq .count) == 3))" ) - docs = json.loads(machine.succeed("curl -u admin:admin -fs localhost:28981/api/documents/"))['results'] + docs = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/"))['results'] assert "2005-10-16" in docs[0]['created'] assert "2005-10-16" in docs[1]['created'] assert "2005-10-16" in docs[2]['created'] - # Detects gunicorn issues, see PR #190888 - with subtest("Document metadata can be accessed"): - metadata = json.loads(machine.succeed("curl -u admin:admin -fs localhost:28981/api/documents/1/metadata/")) + # Detects gunicorn issues, see PR #190888 + with subtest("Document metadata can be accessed"): + metadata = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/1/metadata/")) assert "original_checksum" in metadata - metadata = json.loads(machine.succeed("curl -u admin:admin -fs localhost:28981/api/documents/2/metadata/")) + metadata = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/2/metadata/")) assert "original_checksum" in metadata - metadata = json.loads(machine.succeed("curl -u admin:admin -fs localhost:28981/api/documents/3/metadata/")) + metadata = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/3/metadata/")) assert "original_checksum" in metadata + + test_paperless(simple) + simple.send_monitor_command("quit") + simple.wait_for_shutdown() + test_paperless(postgres) ''; }) diff --git a/nixos/tests/pgadmin4.nix b/nixos/tests/pgadmin4.nix index cb8de87c9ee..3ee7ed19fa1 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 1e72327d420..bb5afd35ee2 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 00000000000..460c30919ae --- /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 ef32bb3a805..9c26c509a5a 100644 --- a/nixos/tests/plausible.nix +++ b/nixos/tests/plausible.nix @@ -1,16 +1,13 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { name = "plausible"; meta = with lib.maintainers; { - maintainers = [ ma27 ]; + maintainers = [ ]; }; nodes.machine = { pkgs, ... }: { 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 4f1aef85414..08a01585f87 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/postgresql.nix b/nixos/tests/postgresql.nix index b44849e0a14..c0dd24cf6ad 100644 --- a/nixos/tests/postgresql.nix +++ b/nixos/tests/postgresql.nix @@ -219,8 +219,6 @@ let in concatMapAttrs (name: package: { ${name} = make-postgresql-test name package false; + ${name + "-backup-all"} = make-postgresql-test "${name + "-backup-all"}" package true; ${name + "-clauses"} = mk-ensure-clauses-test name package; }) postgresql-versions - // { - postgresql_11-backup-all = make-postgresql-test "postgresql_11-backup-all" postgresql-versions.postgresql_11 true; - } diff --git a/nixos/tests/powerdns-admin.nix b/nixos/tests/powerdns-admin.nix index d7bacb24eec..d326d74a982 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/predictable-interface-names.nix b/nixos/tests/predictable-interface-names.nix index 42183625c7c..51d5e8ae59b 100644 --- a/nixos/tests/predictable-interface-names.nix +++ b/nixos/tests/predictable-interface-names.nix @@ -36,7 +36,7 @@ in pkgs.lib.listToAttrs (builtins.map ({ predictable, withNetworkd, systemdStage networking.useDHCP = !withNetworkd; # Check if predictable interface names are working in stage-1 - boot.initrd.postDeviceCommands = script; + boot.initrd.postDeviceCommands = lib.mkIf (!systemdStage1) script; boot.initrd.systemd = lib.mkIf systemdStage1 { enable = true; diff --git a/nixos/tests/printing.nix b/nixos/tests/printing.nix index 7df042e72e9..29c5d810f21 100644 --- a/nixos/tests/printing.nix +++ b/nixos/tests/printing.nix @@ -19,6 +19,7 @@ import ./make-test-python.nix ( startWhenNeeded = socket; listenAddresses = [ "*:631" ]; defaultShared = true; + openFirewall = true; extraConf = '' <Location /> Order allow,deny @@ -26,7 +27,6 @@ import ./make-test-python.nix ( </Location> ''; }; - networking.firewall.allowedTCPPorts = [ 631 ]; # Add a HP Deskjet printer connected via USB to the server. hardware.printers.ensurePrinters = [{ name = "DeskjetLocal"; diff --git a/nixos/tests/privacyidea.nix b/nixos/tests/privacyidea.nix deleted file mode 100644 index 401ad72c37b..00000000000 --- a/nixos/tests/privacyidea.nix +++ /dev/null @@ -1,43 +0,0 @@ -# Miscellaneous small tests that don't warrant their own VM run. - -import ./make-test-python.nix ({ pkgs, ...} : rec { - name = "privacyidea"; - meta = with pkgs.lib.maintainers; { - maintainers = [ ]; - }; - - nodes.machine = { ... }: { - virtualisation.cores = 2; - - services.privacyidea = { - enable = true; - secretKey = "$SECRET_KEY"; - pepper = "$PEPPER"; - adminPasswordFile = pkgs.writeText "admin-password" "testing"; - adminEmail = "root@localhost"; - - # Don't try this at home! - environmentFile = pkgs.writeText "pi-secrets.env" '' - SECRET_KEY=testing - PEPPER=testing - ''; - }; - services.nginx = { - enable = true; - virtualHosts."_".locations."/".extraConfig = '' - uwsgi_pass unix:/run/privacyidea/socket; - ''; - }; - }; - - testScript = '' - machine.start() - machine.wait_for_unit("multi-user.target") - machine.succeed("curl --fail http://localhost | grep privacyIDEA") - machine.succeed("grep \"SECRET_KEY = 'testing'\" /var/lib/privacyidea/privacyidea.cfg") - machine.succeed("grep \"PI_PEPPER = 'testing'\" /var/lib/privacyidea/privacyidea.cfg") - machine.succeed( - "curl --fail http://localhost/auth -F username=admin -F password=testing | grep token" - ) - ''; -}) diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix index 7db7fdf13eb..7840130d4a3 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; @@ -416,8 +431,8 @@ let }; kea = let - controlSocketPathV4 = "/run/kea/dhcp4.sock"; - controlSocketPathV6 = "/run/kea/dhcp6.sock"; + controlSocketPathV4 = "/run/kea-dhcp4/dhcp4.sock"; + controlSocketPathV6 = "/run/kea-dhcp6/dhcp6.sock"; in { exporterConfig = { @@ -471,7 +486,7 @@ let services.knot = { enable = true; extraArgs = [ "-v" ]; - extraConfig = '' + settingsFile = pkgs.writeText "knot.conf" '' server: listen: 127.0.0.1@53 @@ -512,7 +527,7 @@ let wait_for_unit("knot.service") wait_for_unit("prometheus-knot-exporter.service") wait_for_open_port(9433) - succeed("curl -sSf 'localhost:9433' | grep 'knot_server_zone_count 1.0'") + succeed("curl -sSf 'localhost:9433' | grep '2\.019031301'") ''; }; @@ -966,6 +981,36 @@ let ''; }; + pgbouncer = { + exporterConfig = { + enable = true; + connectionStringFile = pkgs.writeText "connection.conf" "postgres://admin:@localhost:6432/pgbouncer?sslmode=disable"; + }; + + metricProvider = { + services.postgresql.enable = true; + services.pgbouncer = { + # https://github.com/prometheus-community/pgbouncer_exporter#pgbouncer-configuration + ignoreStartupParameters = "extra_float_digits"; + enable = true; + listenAddress = "*"; + databases = { postgres = "host=/run/postgresql/ port=5432 auth_user=postgres dbname=postgres"; }; + authType = "any"; + maxClientConn = 99; + }; + }; + exporterTest = '' + wait_for_unit("postgresql.service") + wait_for_unit("pgbouncer.service") + wait_for_unit("prometheus-pgbouncer-exporter.service") + wait_for_open_port(9127) + succeed("curl -sSf http://localhost:9127/metrics | grep 'pgbouncer_up 1'") + succeed( + "curl -sSf http://localhost:9127/metrics | grep 'pgbouncer_config_max_client_connections 99'" + ) + ''; + }; + php-fpm = { nodeName = "php_fpm"; exporterConfig = { @@ -1288,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/qemu-vm-external-disk-image.nix b/nixos/tests/qemu-vm-external-disk-image.nix new file mode 100644 index 00000000000..a229fc5e396 --- /dev/null +++ b/nixos/tests/qemu-vm-external-disk-image.nix @@ -0,0 +1,73 @@ +# Tests that you can boot from an external disk image with the qemu-vm module. +# "External" here means that the image was not produced within the qemu-vm +# module and relies on the fileSystems option also set outside the qemu-vm +# module. Most notably, this tests that you can stop the qemu-vm module from +# overriding fileSystems with virtualisation.fileSystems so you don't have to +# replicate the previously set fileSystems in virtualisation.fileSystems. + +{ lib, ... }: + +let + rootFslabel = "external"; + rootFsDevice = "/dev/disk/by-label/${rootFslabel}"; + + externalModule = { config, lib, pkgs, ... }: { + boot.loader.systemd-boot.enable = true; + + fileSystems = { + "/".device = rootFsDevice; + }; + + system.build.diskImage = import ../lib/make-disk-image.nix { + inherit config lib pkgs; + label = rootFslabel; + partitionTableType = "efi"; + format = "qcow2"; + bootSize = "32M"; + additionalSpace = "0M"; + copyChannel = false; + }; + }; +in +{ + name = "qemu-vm-external-disk-image"; + + meta.maintainers = with lib.maintainers; [ nikstur ]; + + nodes.machine = { config, lib, pkgs, ... }: { + virtualisation.directBoot.enable = false; + virtualisation.mountHostNixStore = false; + virtualisation.useEFIBoot = true; + + # This stops the qemu-vm module from overriding the fileSystems option + # with virtualisation.fileSystems. + virtualisation.fileSystems = lib.mkForce { }; + + imports = [ externalModule ]; + }; + + testScript = { nodes, ... }: '' + import os + import subprocess + import tempfile + + tmp_disk_image = tempfile.NamedTemporaryFile() + + subprocess.run([ + "${nodes.machine.virtualisation.qemu.package}/bin/qemu-img", + "create", + "-f", + "qcow2", + "-b", + "${nodes.machine.system.build.diskImage}/nixos.qcow2", + "-F", + "qcow2", + tmp_disk_image.name, + ]) + + # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image. + os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name + + machine.succeed("findmnt --kernel --source ${rootFsDevice} --target /") + ''; +} diff --git a/nixos/tests/restic.nix b/nixos/tests/restic.nix index 3b9ea2f85b1..4111720cf6b 100644 --- a/nixos/tests/restic.nix +++ b/nixos/tests/restic.nix @@ -4,6 +4,7 @@ import ./make-test-python.nix ( let remoteRepository = "/root/restic-backup"; remoteFromFileRepository = "/root/restic-backup-from-file"; + remoteNoInitRepository = "/root/restic-backup-no-init"; rcloneRepository = "rclone:local:/root/restic-rclone-backup"; backupPrepareCommand = '' @@ -21,7 +22,10 @@ import ./make-test-python.nix ( unpackPhase = "true"; installPhase = '' mkdir $out - touch $out/some_file + echo some_file > $out/some_file + echo some_other_file > $out/some_other_file + mkdir $out/a_dir + echo a_file > $out/a_dir/a_file ''; }; @@ -51,11 +55,21 @@ import ./make-test-python.nix ( inherit passwordFile paths exclude pruneOpts backupPrepareCommand backupCleanupCommand; repository = remoteRepository; initialize = true; + timerConfig = null; # has no effect here, just checking that it doesn't break the service }; remote-from-file-backup = { - inherit passwordFile paths exclude pruneOpts; + inherit passwordFile exclude pruneOpts; initialize = true; repositoryFile = pkgs.writeText "repositoryFile" remoteFromFileRepository; + paths = [ "/opt/a_dir" ]; + dynamicFilesFrom = '' + find /opt -mindepth 1 -maxdepth 1 ! -name a_dir # all files in /opt except for a_dir + ''; + }; + remote-noinit-backup = { + inherit passwordFile exclude pruneOpts paths; + initialize = false; + repository = remoteNoInitRepository; }; rclonebackup = { inherit passwordFile paths exclude pruneOpts; @@ -107,6 +121,7 @@ import ./make-test-python.nix ( "cp -rT ${testDir} /opt", "touch /opt/excluded_file_1 /opt/excluded_file_2", "mkdir -p /root/restic-rclone-backup", + "restic-remote-noinit-backup init", # test that remotebackup runs custom commands and produces a snapshot "timedatectl set-time '2016-12-13 13:45'", @@ -123,13 +138,22 @@ import ./make-test-python.nix ( "systemctl start restic-backups-remote-from-file-backup.service", 'restic-remote-from-file-backup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"', + # test that remote-noinit-backup produces a snapshot + "systemctl start restic-backups-remote-noinit-backup.service", + 'restic-remote-noinit-backup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"', + + # test that restoring that snapshot produces the same directory + "mkdir /tmp/restore-2", + "${pkgs.restic}/bin/restic -r ${remoteRepository} -p ${passwordFile} restore latest -t /tmp/restore-2", + "diff -ru ${testDir} /tmp/restore-2/opt", + # test that rclonebackup produces a snapshot "systemctl start restic-backups-rclonebackup.service", 'restic-rclonebackup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"', # test that custompackage runs both `restic backup` and `restic check` with reasonable commandlines "systemctl start restic-backups-custompackage.service", - "grep 'backup.* /opt' /root/fake-restic.log", + "grep 'backup' /root/fake-restic.log", "grep 'check.* --some-check-option' /root/fake-restic.log", # test that we can create four snapshots in remotebackup and rclonebackup diff --git a/nixos/tests/rkvm/cert.pem b/nixos/tests/rkvm/cert.pem new file mode 100644 index 00000000000..933efe52057 --- /dev/null +++ b/nixos/tests/rkvm/cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC3jCCAcagAwIBAgIUWW1hb9xdRtxAhA42jkS89goW9LUwDQYJKoZIhvcNAQEL +BQAwDzENMAsGA1UEAwwEcmt2bTAeFw0yMzA4MjIxOTI1NDlaFw0zMzA4MTkxOTI1 +NDlaMA8xDTALBgNVBAMMBHJrdm0wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCuBsh0+LDXN4b2o/PJjzuiZ9Yv9Pz1Oho9WRiXtNIuHTRdBCcht/iu3PGF +ICIX+H3dqQOziGSCTAQGJD2p+1ik8d+boJbpa0oxXuHuomsMAT3mib3GpipQoBLP +KaEbWEsvQbr3RMx8WOtG4dmRQFzSVVtmAXyM0pNyisd4eUCplyIl9gsRJIvsO/0M +OkgOZW9XLfKiAWlZoyXEkBmPAshg3EkwQtmwxPA/NgWbAOW3zJKSChxnnGYiuIIu +R/wJ8OQXHP6boQLQGUhCWBKa1uK1gEBmV3Pj6uK8RzTkQq6/47F5sPa6VfqQYdyl +TCs9bSqHXZjqMBoiSp22uH6+Lh9RAgMBAAGjMjAwMA8GA1UdEQQIMAaHBAoAAAEw +HQYDVR0OBBYEFEh9HEsnY3dfNKVyPWDbwfR0qHopMA0GCSqGSIb3DQEBCwUAA4IB +AQB/r+K20JqegUZ/kepPxIU95YY81aUUoxvLbu4EAgh8o46Fgm75qrTZPg4TaIZa +wtVejekrF+p3QVf0ErUblh/iCjTZPSzCmKHZt8cc9OwTH7bt3bx7heknzLDyIa5z +szAL+6241UggQ5n5NUGn5+xZHA7TMe47xAZPaRMlCQ/tp5pWFjH6WSSQSP5t4Ag9 +ObhY+uudFjmWi3QIBTr3iIscbWx7tD8cjus7PzM7+kszSDRV04xb6Ox8JzW9MKIN +GwgwVgs3zCuyqBmTGnR1og3aMk6VtlyZUYE78uuc+fMBxqoBZ0mykeOp0Tbzgtf7 +gPkYcQ6vonoQhuTXYj/NrY+b +-----END CERTIFICATE----- diff --git a/nixos/tests/rkvm/default.nix b/nixos/tests/rkvm/default.nix new file mode 100644 index 00000000000..22425948d8b --- /dev/null +++ b/nixos/tests/rkvm/default.nix @@ -0,0 +1,104 @@ +import ../make-test-python.nix ({ pkgs, ... }: +let + # Generated with + # + # nix shell .#rkvm --command "rkvm-certificate-gen --ip-addresses 10.0.0.1 cert.pem key.pem" + # + snakeoil-cert = ./cert.pem; + snakeoil-key = ./key.pem; +in +{ + name = "rkvm"; + + nodes = { + server = { pkgs, ... }: { + imports = [ ../common/user-account.nix ]; + + virtualisation.vlans = [ 1 ]; + + networking = { + useNetworkd = true; + useDHCP = false; + firewall.enable = false; + }; + + systemd.network.networks."01-eth1" = { + name = "eth1"; + networkConfig.Address = "10.0.0.1/24"; + }; + + services.getty.autologinUser = "alice"; + + services.rkvm.server = { + enable = true; + settings = { + certificate = snakeoil-cert; + key = snakeoil-key; + password = "snakeoil"; + switch-keys = [ "left-alt" "right-alt" ]; + }; + }; + }; + + client = { pkgs, ... }: { + imports = [ ../common/user-account.nix ]; + + virtualisation.vlans = [ 1 ]; + + networking = { + useNetworkd = true; + useDHCP = false; + firewall.enable = false; + }; + + systemd.network.networks."01-eth1" = { + name = "eth1"; + networkConfig.Address = "10.0.0.2/24"; + }; + + services.getty.autologinUser = "alice"; + + services.rkvm.client = { + enable = true; + settings = { + server = "10.0.0.1:5258"; + certificate = snakeoil-cert; + key = snakeoil-key; + password = "snakeoil"; + }; + }; + }; + }; + + testScript = '' + server.wait_for_unit("getty@tty1.service") + server.wait_until_succeeds("pgrep -f 'agetty.*tty1'") + server.wait_for_unit("rkvm-server") + server.wait_for_open_port(5258) + + client.wait_for_unit("getty@tty1.service") + client.wait_until_succeeds("pgrep -f 'agetty.*tty1'") + client.wait_for_unit("rkvm-client") + + server.sleep(1) + + # Switch to client + server.send_key("alt-alt_r", delay=0.2) + server.send_chars("echo 'hello client' > /tmp/test.txt\n") + + # Switch to server + server.send_key("alt-alt_r", delay=0.2) + server.send_chars("echo 'hello server' > /tmp/test.txt\n") + + server.sleep(1) + + client.systemctl("stop rkvm-client.service") + server.systemctl("stop rkvm-server.service") + + server_file = server.succeed("cat /tmp/test.txt") + assert server_file.strip() == "hello server" + + client_file = client.succeed("cat /tmp/test.txt") + assert client_file.strip() == "hello client" + ''; +}) diff --git a/nixos/tests/rkvm/key.pem b/nixos/tests/rkvm/key.pem new file mode 100644 index 00000000000..7197decff8d --- /dev/null +++ b/nixos/tests/rkvm/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCuBsh0+LDXN4b2 +o/PJjzuiZ9Yv9Pz1Oho9WRiXtNIuHTRdBCcht/iu3PGFICIX+H3dqQOziGSCTAQG +JD2p+1ik8d+boJbpa0oxXuHuomsMAT3mib3GpipQoBLPKaEbWEsvQbr3RMx8WOtG +4dmRQFzSVVtmAXyM0pNyisd4eUCplyIl9gsRJIvsO/0MOkgOZW9XLfKiAWlZoyXE +kBmPAshg3EkwQtmwxPA/NgWbAOW3zJKSChxnnGYiuIIuR/wJ8OQXHP6boQLQGUhC +WBKa1uK1gEBmV3Pj6uK8RzTkQq6/47F5sPa6VfqQYdylTCs9bSqHXZjqMBoiSp22 +uH6+Lh9RAgMBAAECggEABo2V1dBu5E51zsAiFCMdypdLZEyUNphvWC5h3oXowONz +pH8ICYfXyEnkma/kk2+ALy0dSRDn6/94dVIUX7Fpx0hJCcoJyhSysK+TJWfIonqX +ffYOMeFG8vicIgs+GFKs/hoPtB5LREbFkUqRj/EoWE6Y3aX3roaCwTZC8vaUk0OK +54gExcNXRwQtFmfM9BiPT76F2J641NVsddgKumrryMi605CgZ57OFfSYEena6T3t +JbQ1TKB3SH1LvSQIspyp56E3bjh8bcwSh72g88YxWZI9yarOesmyU+fXnmVqcBc+ +CiJDX3Te1C2GIkBiH3HZJo4P88aXrkJ7J8nub/812QKBgQDfCHjBy5uWzzbDnqZc +cllIyUqMHq1iY2/btdZQbz83maZhQhH2UL4Zvoa7qgMX7Ou5jn1xpDaMeXNaajGK +Fz66nmqQEUFX1i+2md2J8TeKD37yUJRdlrMiAc+RNp5wiOH9EI18g2m6h/nj3s/P +MdNyxsz+wqOiJT0sZatarKiFhQKBgQDHv+lPy4OPH1MeSv5vmv3Pa41O/CeiPy+T +gi6nEZayVRVog3zF9T6gNIHrZ1fdIppWPiPXv9fmC3s/IVEftLG6YC+MAfigYhiz +Iceoal0iJJ8DglzOhlKgHEnxEwENCz8aJxjpvbxHHcpvgXdBSEVfHvVqDkAFTsvF +JA5YTmqGXQKBgQCL6uqm2S7gq1o12p+PO4VbrjwAL3aiVLNl6Gtsxn2oSdIhDavr +FLhNukMYFA4gwlcXb5au5k/6TG7bd+dgNDj8Jkm/27NcgVgpe9mJojQvfo0rQvXw +yIvUd8JZ3SQEgTsU4X+Bb4eyp39TPwKrfxyh0qnj4QN6w1XfNmELX2nRaQKBgEq6 +a0ik9JTovSnKGKIcM/QTYow4HYO/a8cdnuJ13BDfb+DnwBg3BbTdr/UndmGOfnrh +SHuAk/7GMNePWVApQ4xcS61vV1p5GJB7hLxm/my1kp+3d4z0B5lKvAbqeywsFvFr +yxA3IWbhqEhLARh1Ny684EdLCXxy3Bzmvk8fFw8pAoGAGkt9pJC2wkk9fnJIHq+f +h/WnEO0YrGzYnVA+RyCNKrimRd+GylGHJ/Ev6PRZvMwyGE7RCB+fHVrrEcEJAcxL +SaOg5NA8cwrG+UpTQqi4gt6tCW87afVCyL6dC/E8giJlzI0LY9DnFGoVqYL0qJvm +Sj4SU0fyLsW/csOLd5T+Bf8= +-----END PRIVATE KEY----- diff --git a/nixos/tests/rosenpass.nix b/nixos/tests/rosenpass.nix new file mode 100644 index 00000000000..ec4046c8c03 --- /dev/null +++ b/nixos/tests/rosenpass.nix @@ -0,0 +1,217 @@ +import ./make-test-python.nix ({ pkgs, ... }: +let + deviceName = "rp0"; + + server = { + ip = "fe80::1"; + wg = { + public = "mQufmDFeQQuU/fIaB2hHgluhjjm1ypK4hJr1cW3WqAw="; + secret = "4N5Y1dldqrpsbaEiY8O0XBUGUFf8vkvtBtm8AoOX7Eo="; + listen = 10000; + }; + }; + client = { + ip = "fe80::2"; + wg = { + public = "Mb3GOlT7oS+F3JntVKiaD7SpHxLxNdtEmWz/9FMnRFU="; + secret = "uC5dfGMv7Oxf5UDfdPkj6rZiRZT2dRWp5x8IQxrNcUE="; + }; + }; +in +{ + name = "rosenpass"; + + nodes = + let + shared = peer: { config, modulesPath, ... }: { + imports = [ "${modulesPath}/services/networking/rosenpass.nix" ]; + + boot.kernelModules = [ "wireguard" ]; + + services.rosenpass = { + enable = true; + defaultDevice = deviceName; + settings = { + verbosity = "Verbose"; + public_key = "/etc/rosenpass/pqpk"; + secret_key = "/etc/rosenpass/pqsk"; + }; + }; + + networking.firewall.allowedUDPPorts = [ 9999 ]; + + systemd.network = { + enable = true; + networks."rosenpass" = { + matchConfig.Name = deviceName; + networkConfig.IPForward = true; + address = [ "${peer.ip}/64" ]; + }; + + netdevs."10-rp0" = { + netdevConfig = { + Kind = "wireguard"; + Name = deviceName; + }; + wireguardConfig.PrivateKeyFile = "/etc/wireguard/wgsk"; + }; + }; + + environment.etc."wireguard/wgsk" = { + text = peer.wg.secret; + user = "systemd-network"; + group = "systemd-network"; + }; + }; + in + { + server = { + imports = [ (shared server) ]; + + networking.firewall.allowedUDPPorts = [ server.wg.listen ]; + + systemd.network.netdevs."10-${deviceName}" = { + wireguardConfig.ListenPort = server.wg.listen; + wireguardPeers = [ + { + wireguardPeerConfig = { + AllowedIPs = [ "::/0" ]; + PublicKey = client.wg.public; + }; + } + ]; + }; + + services.rosenpass.settings = { + listen = [ "0.0.0.0:9999" ]; + peers = [ + { + public_key = "/etc/rosenpass/peers/client/pqpk"; + peer = client.wg.public; + } + ]; + }; + }; + client = { + imports = [ (shared client) ]; + + systemd.network.netdevs."10-${deviceName}".wireguardPeers = [ + { + wireguardPeerConfig = { + AllowedIPs = [ "::/0" ]; + PublicKey = server.wg.public; + Endpoint = "server:${builtins.toString server.wg.listen}"; + }; + } + ]; + + services.rosenpass.settings.peers = [ + { + public_key = "/etc/rosenpass/peers/server/pqpk"; + endpoint = "server:9999"; + peer = server.wg.public; + } + ]; + }; + }; + + testScript = { ... }: '' + from os import system + + # Full path to rosenpass in the store, to avoid fiddling with `$PATH`. + rosenpass = "${pkgs.rosenpass}/bin/rosenpass" + + # Path in `/etc` where keys will be placed. + etc = "/etc/rosenpass" + + start_all() + + for machine in [server, client]: + machine.wait_for_unit("multi-user.target") + + # Gently stop Rosenpass to avoid crashes during key generation/distribution. + for machine in [server, client]: + machine.execute("systemctl stop rosenpass.service") + + for (name, machine, remote) in [("server", server, client), ("client", client, server)]: + pk, sk = f"{name}.pqpk", f"{name}.pqsk" + system(f"{rosenpass} gen-keys --force --secret-key {sk} --public-key {pk}") + machine.copy_from_host(sk, f"{etc}/pqsk") + machine.copy_from_host(pk, f"{etc}/pqpk") + remote.copy_from_host(pk, f"{etc}/peers/{name}/pqpk") + + for machine in [server, client]: + machine.execute("systemctl start rosenpass.service") + + for machine in [server, client]: + machine.wait_for_unit("rosenpass.service") + + with subtest("ping"): + client.succeed("ping -c 2 -i 0.5 ${server.ip}%${deviceName}") + + with subtest("preshared-keys"): + # Rosenpass works by setting the WireGuard preshared key at regular intervals. + # Thus, if it is not active, then no key will be set, and the output of `wg show` will contain "none". + # Otherwise, if it is active, then the key will be set and "none" will not be found in the output of `wg show`. + for machine in [server, client]: + machine.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5) + ''; + + # NOTE: Below configuration is for "interactive" (=developing/debugging) only. + interactive.nodes = + let + inherit (import ./ssh-keys.nix pkgs) snakeOilPublicKey snakeOilPrivateKey; + + sshAndKeyGeneration = { + services.openssh.enable = true; + users.users.root.openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; + environment.systemPackages = [ + (pkgs.writeShellApplication { + name = "gen-keys"; + runtimeInputs = [ pkgs.rosenpass ]; + text = '' + HOST="$(hostname)" + if [ "$HOST" == "server" ] + then + PEER="client" + else + PEER="server" + fi + + # Generate keypair. + mkdir -vp /etc/rosenpass/peers/$PEER + rosenpass gen-keys --force --secret-key /etc/rosenpass/pqsk --public-key /etc/rosenpass/pqpk + + # Set up SSH key. + mkdir -p /root/.ssh + cp ${snakeOilPrivateKey} /root/.ssh/id_ecdsa + chmod 0400 /root/.ssh/id_ecdsa + + # Copy public key to other peer. + # shellcheck disable=SC2029 + ssh -o StrictHostKeyChecking=no $PEER "mkdir -pv /etc/rosenpass/peers/$HOST" + scp /etc/rosenpass/pqpk "$PEER:/etc/rosenpass/peers/$HOST/pqpk" + ''; + }) + ]; + }; + + # Use kmscon <https://www.freedesktop.org/wiki/Software/kmscon/> + # to provide a slightly nicer console, and while we're at it, + # also use a nice font. + # With kmscon, we can for example zoom in/out using [Ctrl] + [+] + # and [Ctrl] + [-] + niceConsoleAndAutologin.services.kmscon = { + enable = true; + autologinUser = "root"; + fonts = [{ + name = "Fira Code"; + package = pkgs.fira-code; + }]; + }; + in + { + server = sshAndKeyGeneration // niceConsoleAndAutologin; + client = sshAndKeyGeneration // niceConsoleAndAutologin; + }; +}) diff --git a/nixos/tests/sabnzbd.nix b/nixos/tests/sabnzbd.nix index 075bd0b1fe0..64cb655b431 100644 --- a/nixos/tests/sabnzbd.nix +++ b/nixos/tests/sabnzbd.nix @@ -18,5 +18,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { machine.wait_until_succeeds( "curl --fail -L http://localhost:8080/" ) + _, out = machine.execute("grep SABCTools /var/lib/sabnzbd/logs/sabnzbd.log") + machine.log(out) + machine.fail("grep 'SABCTools disabled: no correct version found!' /var/lib/sabnzbd/logs/sabnzbd.log") ''; }) diff --git a/nixos/tests/sftpgo.nix b/nixos/tests/sftpgo.nix index 8cd5675c1d4..a5bb1981d2c 100644 --- a/nixos/tests/sftpgo.nix +++ b/nixos/tests/sftpgo.nix @@ -17,7 +17,7 @@ let # Returns an attributeset of users who are not system users. normalUsers = config: - filterAttrs (name: user: user.isNormalUser) config.users.users; + lib.filterAttrs (name: user: user.isNormalUser) config.users.users; # Returns true if a user is a member of the given group isMemberOf = @@ -26,7 +26,7 @@ let groupName: # users.users attrset user: - any (x: x == user.name) config.users.groups.${groupName}.members; + lib.any (x: x == user.name) config.users.groups.${groupName}.members; # Generates a valid SFTPGo user configuration for a given user # Will be converted to JSON and loaded on application startup. @@ -52,7 +52,7 @@ let # inside the dataprovider they will be automatically created. # You have to create the folder on the filesystem yourself virtual_folders = - lib.optional (lib.isMemberOf config sharedFolderName user) { + lib.optional (isMemberOf config sharedFolderName user) { name = sharedFolderName; mapped_path = "${config.services.sftpgo.dataDir}/${sharedFolderName}"; virtual_path = "/${sharedFolderName}"; @@ -63,7 +63,7 @@ let lib.recursiveUpdate { "/" = [ "list" ]; # read-only top level directory "/private" = [ "*" ]; # private subdirectory, not shared with others - } (lib.optionalAttrs (lib.isMemberOf config "shared" user) { + } (lib.optionalAttrs (isMemberOf config "shared" user) { "/shared" = [ "*" ]; }); @@ -89,7 +89,7 @@ let # of users and folders to import to SFTPGo. loadDataJson = config: pkgs.writeText "users-and-folders.json" (builtins.toJSON { users = - lib.mapAttrsToList (name: user: lib.generateUserAttrSet config user) (normalUsers config); + lib.mapAttrsToList (name: user: generateUserAttrSet config user) (normalUsers config); folders = [ { @@ -144,7 +144,7 @@ in { name = "sftpgo"; - meta.maintainers = with maintainers; [ yayayayaka ]; + meta.maintainers = with lib.maintainers; [ yayayayaka ]; nodes = { server = { nodes, ... }: { @@ -156,7 +156,7 @@ in ensureDatabases = [ "sftpgo" ]; ensureUsers = [{ name = "sftpgo"; - ensurePermissions."DATABASE sftpgo" = "ALL PRIVILEGES"; + ensureDBOwnership = true; }]; }; @@ -228,7 +228,7 @@ in # Created shared folder directories "d ${statePath}/${sharedFolderName} 2770 ${sftpgoUser} ${sharedFolderName} -" ] - ++ mapAttrsToList (name: user: + ++ lib.mapAttrsToList (name: user: # Create private user directories '' d ${statePath}/users/${user.name} 0700 ${sftpgoUser} ${sftpgoGroup} - @@ -273,12 +273,12 @@ in networking.firewall.allowedTCPPorts = [ 22 80 ]; services.sftpgo = { settings = { - sftpd.bindings = mkForce [{ + sftpd.bindings = lib.mkForce [{ address = ""; port = 22; }]; - httpd.bindings = mkForce [{ + httpd.bindings = lib.mkForce [{ address = ""; port = 80; }]; diff --git a/nixos/tests/sgtpuzzles.nix b/nixos/tests/sgt-puzzles.nix index b8d25d42d31..4c5210bfce7 100644 --- a/nixos/tests/sgtpuzzles.nix +++ b/nixos/tests/sgt-puzzles.nix @@ -1,6 +1,6 @@ import ./make-test-python.nix ({ pkgs, ...} : { - name = "sgtpuzzles"; + name = "sgt-puzzles"; meta = with pkgs.lib.maintainers; { maintainers = [ tomfitzhenry ]; }; @@ -14,7 +14,7 @@ import ./make-test-python.nix ({ pkgs, ...} : services.xserver.enable = true; environment.systemPackages = with pkgs; [ - sgtpuzzles + sgt-puzzles ]; }; diff --git a/nixos/tests/shattered-pixel-dungeon.nix b/nixos/tests/shattered-pixel-dungeon.nix index a256bbdfd73..b4ac1670b5c 100644 --- a/nixos/tests/shattered-pixel-dungeon.nix +++ b/nixos/tests/shattered-pixel-dungeon.nix @@ -21,9 +21,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { machine.wait_for_x() machine.execute("shattered-pixel-dungeon >&2 &") machine.wait_for_window(r"Shattered Pixel Dungeon") - machine.sleep(5) - if "Enter" not in machine.get_screen_text(): - raise Exception("Program did not start successfully") + machine.wait_for_text("Enter") machine.screenshot("screen") ''; }) diff --git a/nixos/tests/slimserver.nix b/nixos/tests/slimserver.nix new file mode 100644 index 00000000000..c3f7b6fde4d --- /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/soft-serve.nix b/nixos/tests/soft-serve.nix new file mode 100644 index 00000000000..1c4cb4c9581 --- /dev/null +++ b/nixos/tests/soft-serve.nix @@ -0,0 +1,102 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: +let + inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey; + sshPort = 8231; + httpPort = 8232; + statsPort = 8233; + gitPort = 8418; +in +{ + name = "soft-serve"; + meta.maintainers = with lib.maintainers; [ dadada ]; + nodes = { + client = { pkgs, ... }: { + environment.systemPackages = with pkgs; [ + curl + git + openssh + ]; + environment.etc.sshKey = { + source = snakeOilPrivateKey; + mode = "0600"; + }; + }; + + server = + { config, ... }: + { + services.soft-serve = { + enable = true; + settings = { + name = "TestServer"; + ssh.listen_addr = ":${toString sshPort}"; + git.listen_addr = ":${toString gitPort}"; + http.listen_addr = ":${toString httpPort}"; + stats.listen_addr = ":${toString statsPort}"; + initial_admin_keys = [ snakeOilPublicKey ]; + }; + }; + networking.firewall.allowedTCPPorts = [ sshPort httpPort statsPort ]; + }; + }; + + testScript = + { ... }: + '' + SSH_PORT = ${toString sshPort} + HTTP_PORT = ${toString httpPort} + STATS_PORT = ${toString statsPort} + KEY = "${snakeOilPublicKey}" + SSH_KEY = "/etc/sshKey" + SSH_COMMAND = f"ssh -p {SSH_PORT} -i {SSH_KEY} -o StrictHostKeyChecking=no" + TEST_DIR = "/tmp/test" + GIT = f"git -C {TEST_DIR}" + + for machine in client, server: + machine.wait_for_unit("network.target") + + server.wait_for_unit("soft-serve.service") + server.wait_for_open_port(SSH_PORT) + + with subtest("Get info"): + status, test = client.execute(f"{SSH_COMMAND} server info") + if status != 0: + raise Exception("Failed to get SSH info") + key = " ".join(KEY.split(" ")[0:2]) + if not key in test: + raise Exception("Admin key must be configured correctly") + + with subtest("Create user"): + client.succeed(f"{SSH_COMMAND} server user create beatrice") + client.succeed(f"{SSH_COMMAND} server user info beatrice") + + with subtest("Create repo"): + client.succeed(f"git init {TEST_DIR}") + client.succeed(f"{GIT} config --global user.email you@example.com") + client.succeed(f"touch {TEST_DIR}/foo") + client.succeed(f"{GIT} add foo") + client.succeed(f"{GIT} commit --allow-empty -m test") + client.succeed(f"{GIT} remote add origin git@server:test") + client.succeed(f"GIT_SSH_COMMAND='{SSH_COMMAND}' {GIT} push -u origin master") + client.execute("rm -r /tmp/test") + + server.wait_for_open_port(HTTP_PORT) + + with subtest("Clone over HTTP"): + client.succeed(f"curl --connect-timeout 10 http://server:{HTTP_PORT}/") + client.succeed(f"git clone http://server:{HTTP_PORT}/test /tmp/test") + client.execute("rm -r /tmp/test") + + with subtest("Clone over SSH"): + client.succeed(f"GIT_SSH_COMMAND='{SSH_COMMAND}' git clone git@server:test /tmp/test") + client.execute("rm -r /tmp/test") + + with subtest("Get stats over HTTP"): + server.wait_for_open_port(STATS_PORT) + status, test = client.execute(f"curl --connect-timeout 10 http://server:{STATS_PORT}/metrics") + if status != 0: + raise Exception("Failed to get metrics from status port") + if not "go_gc_duration_seconds_count" in test: + raise Exception("Metrics did not contain key 'go_gc_duration_seconds_count'") + ''; +}) diff --git a/nixos/tests/sourcehut.nix b/nixos/tests/sourcehut.nix index 87e6d82bdd8..0b258acc2af 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/sqlite3-to-mysql.nix b/nixos/tests/sqlite3-to-mysql.nix index 029058187df..f18a442157e 100644 --- a/nixos/tests/sqlite3-to-mysql.nix +++ b/nixos/tests/sqlite3-to-mysql.nix @@ -19,7 +19,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: python3Packages.pytest python3Packages.pytest-mock python3Packages.pytest-timeout - python3Packages.factory_boy + python3Packages.factory-boy python3Packages.docker # only needed so import does not fail sqlite3-to-mysql ]) diff --git a/nixos/tests/ssh-audit.nix b/nixos/tests/ssh-audit.nix new file mode 100644 index 00000000000..bd6255b8044 --- /dev/null +++ b/nixos/tests/ssh-audit.nix @@ -0,0 +1,103 @@ +import ./make-test-python.nix ( + {pkgs, ...}: let + sshKeys = import (pkgs.path + "/nixos/tests/ssh-keys.nix") pkgs; + sshUsername = "any-user"; + serverName = "server"; + clientName = "client"; + sshAuditPort = 2222; + in { + name = "ssh"; + + nodes = { + "${serverName}" = { + networking.firewall.allowedTCPPorts = [ + sshAuditPort + ]; + services.openssh.enable = true; + users.users."${sshUsername}" = { + isNormalUser = true; + openssh.authorizedKeys.keys = [ + sshKeys.snakeOilPublicKey + ]; + }; + }; + "${clientName}" = { + programs.ssh = { + ciphers = [ + "aes128-ctr" + "aes128-gcm@openssh.com" + "aes192-ctr" + "aes256-ctr" + "aes256-gcm@openssh.com" + "chacha20-poly1305@openssh.com" + ]; + extraConfig = '' + IdentitiesOnly yes + ''; + hostKeyAlgorithms = [ + "rsa-sha2-256" + "rsa-sha2-256-cert-v01@openssh.com" + "rsa-sha2-512" + "rsa-sha2-512-cert-v01@openssh.com" + "sk-ssh-ed25519-cert-v01@openssh.com" + "sk-ssh-ed25519@openssh.com" + "ssh-ed25519" + "ssh-ed25519-cert-v01@openssh.com" + ]; + kexAlgorithms = [ + "curve25519-sha256" + "curve25519-sha256@libssh.org" + "diffie-hellman-group-exchange-sha256" + "diffie-hellman-group16-sha512" + "diffie-hellman-group18-sha512" + "sntrup761x25519-sha512@openssh.com" + ]; + macs = [ + "hmac-sha2-256-etm@openssh.com" + "hmac-sha2-512-etm@openssh.com" + "umac-128-etm@openssh.com" + ]; + }; + }; + }; + + testScript = '' + start_all() + + ${serverName}.wait_for_open_port(22) + + # Should pass SSH server audit + ${serverName}.succeed("${pkgs.ssh-audit}/bin/ssh-audit 127.0.0.1") + + # Wait for client to be able to connect to the server + ${clientName}.wait_for_unit("network-online.target") + + # Set up trusted private key + ${clientName}.succeed("cat ${sshKeys.snakeOilPrivateKey} > privkey.snakeoil") + ${clientName}.succeed("chmod 600 privkey.snakeoil") + + # Fail fast and disable interactivity + ssh_options = "-o BatchMode=yes -o ConnectTimeout=1 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" + + # Should deny root user + ${clientName}.fail(f"ssh {ssh_options} root@${serverName} true") + + # Should deny non-root user password login + ${clientName}.fail(f"ssh {ssh_options} -o PasswordAuthentication=yes ${sshUsername}@${serverName} true") + + # Should allow non-root user certificate login + ${clientName}.succeed(f"ssh {ssh_options} -i privkey.snakeoil ${sshUsername}@${serverName} true") + + # Should pass SSH client audit + service_name = "ssh-audit.service" + ${serverName}.succeed(f"systemd-run --unit={service_name} ${pkgs.ssh-audit}/bin/ssh-audit --client-audit --port=${toString sshAuditPort}") + ${clientName}.sleep(5) # We can't use wait_for_open_port because ssh-audit exits as soon as anything talks to it + ${clientName}.execute( + f"ssh {ssh_options} -i privkey.snakeoil -p ${toString sshAuditPort} ${sshUsername}@${serverName} true", + check_return=False, + timeout=10 + ) + ${serverName}.succeed(f"exit $(systemctl show --property=ExecMainStatus --value {service_name})") + ''; + } +) diff --git a/nixos/tests/sslh.nix b/nixos/tests/sslh.nix index 17094606e8e..30ffd389d44 100644 --- a/nixos/tests/sslh.nix +++ b/nixos/tests/sslh.nix @@ -10,21 +10,13 @@ import ./make-test-python.nix { prefixLength = 64; } ]; - # sslh is really slow when reverse dns does not work - networking.hosts = { - "fe00:aa:bb:cc::2" = [ "server" ]; - "fe00:aa:bb:cc::1" = [ "client" ]; - }; services.sslh = { enable = true; - transparent = true; - appendConfig = '' - protocols: - ( - { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, - { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, - ); - ''; + settings.transparent = true; + settings.protocols = [ + { name = "ssh"; service = "ssh"; host = "localhost"; port = "22"; probe = "builtin"; } + { name = "http"; host = "localhost"; port = "80"; probe = "builtin"; } + ]; }; services.openssh.enable = true; users.users.root.openssh.authorizedKeys.keyFiles = [ ./initrd-network-ssh/id_ed25519.pub ]; diff --git a/nixos/tests/stratis/encryption.nix b/nixos/tests/stratis/encryption.nix index a555ff8a8e8..81b5f92b4ac 100644 --- a/nixos/tests/stratis/encryption.nix +++ b/nixos/tests/stratis/encryption.nix @@ -26,7 +26,7 @@ import ../make-test-python.nix ({ pkgs, ... }: # test rebinding encrypted pool machine.succeed("stratis pool rebind keyring testpool testkey2") # test restarting encrypted pool - machine.succeed("stratis pool stop testpool") - machine.succeed("stratis pool start --name testpool --unlock-method keyring") + machine.succeed("stratis pool stop --name testpool") + machine.succeed("stratis pool start --name testpool --unlock-method keyring") ''; }) diff --git a/nixos/tests/stunnel.nix b/nixos/tests/stunnel.nix index 22c087290fc..07fba435d4d 100644 --- a/nixos/tests/stunnel.nix +++ b/nixos/tests/stunnel.nix @@ -17,11 +17,16 @@ let }; }; makeCert = { config, pkgs, ... }: { - system.activationScripts.create-test-cert = stringAfter [ "users" ] '' - ${pkgs.openssl}/bin/openssl req -batch -x509 -newkey rsa -nodes -out /test-cert.pem -keyout /test-key.pem -subj /CN=${config.networking.hostName} - ( umask 077; cat /test-key.pem /test-cert.pem > /test-key-and-cert.pem ) - chown stunnel /test-key.pem /test-key-and-cert.pem + systemd.services.create-test-cert = { + wantedBy = [ "sysinit.target" ]; + before = [ "sysinit.target" ]; + unitConfig.DefaultDependencies = false; + script = '' + ${pkgs.openssl}/bin/openssl req -batch -x509 -newkey rsa -nodes -out /test-cert.pem -keyout /test-key.pem -subj /CN=${config.networking.hostName} + ( umask 077; cat /test-key.pem /test-cert.pem > /test-key-and-cert.pem ) + chown stunnel /test-key.pem /test-key-and-cert.pem ''; + }; }; serverCommon = { pkgs, ... }: { networking.firewall.allowedTCPPorts = [ 443 ]; diff --git a/nixos/tests/sudo.nix b/nixos/tests/sudo.nix index 1b177391488..1fe478f0bff 100644 --- a/nixos/tests/sudo.nix +++ b/nixos/tests/sudo.nix @@ -21,7 +21,8 @@ in }; security.sudo = { - enable = true; + # Explicitly _not_ defining 'enable = true;' here, to check that sudo is enabled by default + wheelNeedsPassword = false; extraConfig = '' diff --git a/nixos/tests/syncthing-init.nix b/nixos/tests/syncthing-init.nix index 195c157ffb6..97fcf2ad28d 100644 --- a/nixos/tests/syncthing-init.nix +++ b/nixos/tests/syncthing-init.nix @@ -1,7 +1,6 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: let testId = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU"; - testName = "testDevice foo'bar"; in { name = "syncthing-init"; diff --git a/nixos/tests/syncthing-many-devices.nix b/nixos/tests/syncthing-many-devices.nix new file mode 100644 index 00000000000..2251bf07745 --- /dev/null +++ b/nixos/tests/syncthing-many-devices.nix @@ -0,0 +1,203 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: + +# This nixosTest is supposed to check the following: +# +# - Whether syncthing's API handles multiple requests for many devices, see +# https://github.com/NixOS/nixpkgs/issues/260262 +# +# - Whether syncthing-init.service generated bash script removes devices and +# folders that are not present in the user's configuration, which is partly +# injected into the script. See also: +# https://github.com/NixOS/nixpkgs/issues/259256 +# + +let + # Just a long path not to copy paste + configPath = "/var/lib/syncthing/.config/syncthing/config.xml"; + + # We will iterate this and more attribute sets defined here, later in the + # testScript. Start with this, and distinguish these settings from other + # settings, as we check these differently with xmllint, due to the ID. + settingsWithId = { + devices = { + # All of the device IDs used here were generated by the following command: + # + # (${pkgs.syncthing}/bin/syncthing generate --home /tmp/foo\ + # | grep ID: | sed 's/.*ID: *//') && rm -rf /tmp/foo + # + # See also discussion at: + # https://forum.syncthing.net/t/how-to-generate-dummy-device-ids/20927/8 + test_device1.id = "IVTZ5XF-EF3GKFT-GS4AZLG-IT6H2ZP-6WK75SF-AFXQXJJ-BNRZ4N6-XPDKVAU"; + test_device2.id = "5C35H56-Z2GFF4F-F3IVD4B-GJYVWIE-SMDBJZN-GI66KWP-52JIQGN-4AVLYAM"; + test_device3.id = "XKLSKHE-BZOHV7B-WQZACEF-GTH36NP-6JSBB6L-RXS3M7C-EEVWO2L-C5B4OAJ"; + test_device4.id = "APN5Q7J-35GZETO-5KCLF35-ZA7KBWK-HGWPBNG-FERF24R-UTLGMEX-4VJ6PQX"; + test_device5.id = "D4YXQEE-5MK6LIK-BRU5QWM-ZRXJCK2-N3RQBJE-23JKTQQ-LYGDPHF-RFPZIQX"; + test_device6.id = "TKMCH64-T44VSLI-6FN2YLF-URBZOBR-ATO4DYX-GEDRIII-CSMRQAI-UAQMDQG"; + test_device7.id = "472EEBG-Q4PZCD4-4CX6PGF-XS3FSQ2-UFXBZVB-PGNXWLX-7FKBLER-NJ3EMAR"; + test_device8.id = "HW6KUMK-WTBG24L-2HZQXLO-TGJSG2M-2JG3FHX-5OGYRUJ-T6L5NN7-L364QAZ"; + test_device9.id = "YAE24AP-7LSVY4T-J74ZSEM-A2IK6RB-FGA35TP-AG4CSLU-ED4UYYY-2J2TDQU"; + test_device10.id = "277XFSB-OFMQOBI-3XGNGUE-Y7FWRV3-QQDADIY-QIIPQ26-EOGTYKW-JP2EXAI"; + test_device11.id = "2WWXVTN-Q3QWAAY-XFORMRM-2FDI5XZ-OGN33BD-XOLL42R-DHLT2ML-QYXDQAU"; + }; + # Generates a few folders with IDs and paths as written... + folders = lib.pipe 6 [ + (builtins.genList (x: { + name = "/var/lib/syncthing/test_folder${builtins.toString x}"; + value = { + id = "DontDeleteMe${builtins.toString x}"; + }; + })) + builtins.listToAttrs + ]; + }; + # Non default options that we check later if were applied + settingsWithoutId = { + options = { + autoUpgradeIntervalH = 0; + urAccepted = -1; + }; + gui = { + theme = "dark"; + }; + }; + # Used later when checking whether settings were set in config.xml: + checkSettingWithId = { t # t for type + , id + , not ? false + }: '' + print("Searching for a ${t} with id ${id}") + configVal_${t} = machine.succeed( + "${pkgs.libxml2}/bin/xmllint " + "--xpath 'string(//${t}[@id=\"${id}\"]/@id)' ${configPath}" + ) + print("${t}.id = {}".format(configVal_${t})) + assert "${id}" ${if not then "not" else ""} in configVal_${t} + ''; + # Same as checkSettingWithId, but for 'options' and 'gui' + checkSettingWithoutId = { t # t for type + , n # n for name + , v # v for value + , not ? false + }: '' + print("checking whether setting ${t}.${n} is set to ${v}") + configVal_${t}_${n} = machine.succeed( + "${pkgs.libxml2}/bin/xmllint " + "--xpath 'string(/configuration/${t}/${n})' ${configPath}" + ) + print("${t}.${n} = {}".format(configVal_${t}_${n})) + assert "${v}" ${if not then "not" else ""} in configVal_${t}_${n} + ''; + # Removes duplication a bit to define this function for the IDs to delete - + # we check whether they were added after our script ran, and before the + # systemd unit's bash script ran, and afterwards - whether the systemd unit + # worked. + checkSettingsToDelete = { + not + }: lib.pipe IDsToDelete [ + (lib.mapAttrsToList (t: id: + checkSettingWithId { + inherit t id; + inherit not; + } + )) + lib.concatStrings + ]; + # These IDs are added to syncthing using the API, similarly to how the + # generated systemd unit's bash script does it. Only we add it and expect the + # systemd unit bash script to remove them when executed. + IDsToDelete = { + # Also created using the syncthing generate command above + device = "LZ2CTHT-3W2M7BC-CMKDFZL-DLUQJFS-WJR73PA-NZGODWG-DZBHCHI-OXTQXAK"; + # Intentionally this is a substring of the IDs of the 'test_folder's, as + # explained in: https://github.com/NixOS/nixpkgs/issues/259256 + folder = "DeleteMe"; + }; + addDeviceToDeleteScript = pkgs.writers.writeBash "syncthing-add-device-to-delete.sh" '' + set -euo pipefail + + export RUNTIME_DIRECTORY=/tmp + + curl() { + # get the api key by parsing the config.xml + while + ! ${pkgs.libxml2}/bin/xmllint \ + --xpath 'string(configuration/gui/apikey)' \ + ${configPath} \ + >"$RUNTIME_DIRECTORY/api_key" + do sleep 1; done + + (printf "X-API-Key: "; cat "$RUNTIME_DIRECTORY/api_key") >"$RUNTIME_DIRECTORY/headers" + + ${pkgs.curl}/bin/curl -sSLk -H "@$RUNTIME_DIRECTORY/headers" \ + --retry 1000 --retry-delay 1 --retry-all-errors \ + "$@" + } + curl -d ${lib.escapeShellArg (builtins.toJSON { deviceID = IDsToDelete.device;})} \ + -X POST 127.0.0.1:8384/rest/config/devices + curl -d ${lib.escapeShellArg (builtins.toJSON { id = IDsToDelete.folder;})} \ + -X POST 127.0.0.1:8384/rest/config/folders + ''; +in { + name = "syncthing-init"; + meta.maintainers = with lib.maintainers; [ doronbehar ]; + + nodes.machine = { + services.syncthing = { + enable = true; + overrideDevices = true; + overrideFolders = true; + settings = settingsWithoutId // settingsWithId; + }; + }; + testScript = '' + machine.wait_for_unit("syncthing-init.service") + '' + (lib.pipe settingsWithId [ + # Check that folders and devices were added properly and that all IDs exist + (lib.mapAttrsRecursive (path: id: + checkSettingWithId { + # plural -> solitary + t = (lib.removeSuffix "s" (builtins.elemAt path 0)); + inherit id; + } + )) + # Get all the values we applied the above function upon + (lib.collect builtins.isString) + lib.concatStrings + ]) + (lib.pipe settingsWithoutId [ + # Check that all other syncthing.settings were added properly with correct + # values + (lib.mapAttrsRecursive (path: value: + checkSettingWithoutId { + t = (builtins.elemAt path 0); + n = (builtins.elemAt path 1); + v = (builtins.toString value); + } + )) + # Get all the values we applied the above function upon + (lib.collect builtins.isString) + lib.concatStrings + ]) + '' + # Run the script on the machine + machine.succeed("${addDeviceToDeleteScript}") + '' + (checkSettingsToDelete { + not = false; + }) + '' + # Useful for debugging later + machine.copy_from_vm("${configPath}", "before") + + machine.systemctl("restart syncthing-init.service") + machine.wait_for_unit("syncthing-init.service") + '' + (checkSettingsToDelete { + not = true; + }) + '' + # Useful for debugging later + machine.copy_from_vm("${configPath}", "after") + + # Copy the systemd unit's bash script, to inspect it for debugging. + mergeScript = machine.succeed( + "systemctl cat syncthing-init.service | " + "${pkgs.initool}/bin/initool g - Service ExecStart --value-only" + ).strip() # strip from new lines + machine.copy_from_vm(mergeScript, "") + ''; +}) diff --git a/nixos/tests/systemd-boot.nix b/nixos/tests/systemd-boot.nix index c1f8637989e..256a18532b0 100644 --- a/nixos/tests/systemd-boot.nix +++ b/nixos/tests/systemd-boot.nix @@ -18,7 +18,7 @@ in { basic = makeTest { name = "systemd-boot"; - meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer ]; + meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; nodes.machine = common; @@ -42,7 +42,7 @@ in # Check that specialisations create corresponding boot entries. specialisation = makeTest { name = "systemd-boot-specialisation"; - meta.maintainers = with pkgs.lib.maintainers; [ lukegb ]; + meta.maintainers = with pkgs.lib.maintainers; [ lukegb julienmalka ]; nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; @@ -65,7 +65,7 @@ in # Boot without having created an EFI entry--instead using default "/EFI/BOOT/BOOTX64.EFI" fallback = makeTest { name = "systemd-boot-fallback"; - meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer ]; + meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; @@ -91,7 +91,7 @@ in update = makeTest { name = "systemd-boot-update"; - meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer ]; + meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; nodes.machine = common; @@ -107,13 +107,13 @@ in ) output = machine.succeed("/run/current-system/bin/switch-to-configuration boot") - assert "updating systemd-boot from 000.0-1-notnixos to " in output + assert "updating systemd-boot from 000.0-1-notnixos to " in output, "Couldn't find systemd-boot update message" ''; }; memtest86 = makeTest { name = "systemd-boot-memtest86"; - meta.maintainers = with pkgs.lib.maintainers; [ Enzime ]; + meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ]; nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; @@ -128,7 +128,7 @@ in netbootxyz = makeTest { name = "systemd-boot-netbootxyz"; - meta.maintainers = with pkgs.lib.maintainers; [ Enzime ]; + meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ]; nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; @@ -143,7 +143,7 @@ in entryFilename = makeTest { name = "systemd-boot-entry-filename"; - meta.maintainers = with pkgs.lib.maintainers; [ Enzime ]; + meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ]; nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; @@ -160,7 +160,7 @@ in extraEntries = makeTest { name = "systemd-boot-extra-entries"; - meta.maintainers = with pkgs.lib.maintainers; [ Enzime ]; + meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ]; nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; @@ -179,7 +179,7 @@ in extraFiles = makeTest { name = "systemd-boot-extra-files"; - meta.maintainers = with pkgs.lib.maintainers; [ Enzime ]; + meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ]; nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; @@ -196,7 +196,7 @@ in switch-test = makeTest { name = "systemd-boot-switch-test"; - meta.maintainers = with pkgs.lib.maintainers; [ Enzime ]; + meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ]; nodes = { inherit common; @@ -252,11 +252,40 @@ 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 { name = "uefi-large-file-workaround"; - + meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; nodes.machine = { pkgs, ... }: { imports = [common]; virtualisation.efi.OVMF = pkgs.OVMF.overrideAttrs (old: { @@ -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-credentials-tpm2.nix b/nixos/tests/systemd-credentials-tpm2.nix index d2dc1fd7b61..bf741831223 100644 --- a/nixos/tests/systemd-credentials-tpm2.nix +++ b/nixos/tests/systemd-credentials-tpm2.nix @@ -1,13 +1,4 @@ -import ./make-test-python.nix ({ lib, pkgs, system, ... }: - -let - tpmSocketPath = "/tmp/swtpm-sock"; - tpmDeviceModels = { - x86_64-linux = "tpm-tis"; - aarch64-linux = "tpm-tis-device"; - }; -in - +import ./make-test-python.nix ({ lib, pkgs, ... }: { name = "systemd-credentials-tpm2"; @@ -16,51 +7,11 @@ in }; nodes.machine = { pkgs, ... }: { - virtualisation = { - qemu.options = [ - "-chardev socket,id=chrtpm,path=${tpmSocketPath}" - "-tpmdev emulator,id=tpm_dev_0,chardev=chrtpm" - "-device ${tpmDeviceModels.${system}},tpmdev=tpm_dev_0" - ]; - }; - - boot.initrd.availableKernelModules = [ "tpm_tis" ]; - + virtualisation.tpm.enable = true; environment.systemPackages = with pkgs; [ diffutils ]; }; testScript = '' - import subprocess - from tempfile import TemporaryDirectory - - # From systemd-initrd-luks-tpm2.nix - class Tpm: - def __init__(self): - self.state_dir = TemporaryDirectory() - self.start() - - def start(self): - self.proc = subprocess.Popen(["${pkgs.swtpm}/bin/swtpm", - "socket", - "--tpmstate", f"dir={self.state_dir.name}", - "--ctrl", "type=unixio,path=${tpmSocketPath}", - "--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") - CRED_NAME = "testkey" CRED_RAW_FILE = f"/root/{CRED_NAME}" CRED_FILE = f"/root/{CRED_NAME}.cred" @@ -85,12 +36,6 @@ in machine.log("systemd-run finished successfully") - tpm = Tpm() - - @polling_condition - def swtpm_running(): - tpm.check() - machine.wait_for_unit("multi-user.target") with subtest("Check whether TPM device exists"): diff --git a/nixos/tests/systemd-cryptenroll.nix b/nixos/tests/systemd-cryptenroll.nix index 055ae7d1681..034aae1d5e9 100644 --- a/nixos/tests/systemd-cryptenroll.nix +++ b/nixos/tests/systemd-cryptenroll.nix @@ -8,47 +8,34 @@ import ./make-test-python.nix ({ pkgs, ... }: { environment.systemPackages = [ pkgs.cryptsetup ]; virtualisation = { emptyDiskImages = [ 512 ]; - qemu.options = [ - "-chardev socket,id=chrtpm,path=/tmp/swtpm-sock" - "-tpmdev emulator,id=tpm0,chardev=chrtpm" - "-device tpm-tis,tpmdev=tpm0" - ]; + tpm.enable = true; }; }; testScript = '' - import subprocess - import tempfile - - def start_swtpm(tpmstate): - subprocess.Popen(["${pkgs.swtpm}/bin/swtpm", "socket", "--tpmstate", "dir="+tpmstate, "--ctrl", "type=unixio,path=/tmp/swtpm-sock", "--log", "level=0", "--tpm2"]) - - with tempfile.TemporaryDirectory() as tpmstate: - start_swtpm(tpmstate) - machine.start() - - # Verify the TPM device is available and accessible by systemd-cryptenroll - machine.succeed("test -e /dev/tpm0") - machine.succeed("test -e /dev/tpmrm0") - machine.succeed("systemd-cryptenroll --tpm2-device=list") - - # Create LUKS partition - machine.succeed("echo -n lukspass | cryptsetup luksFormat -q /dev/vdb -") - # Enroll new LUKS key and bind it to Secure Boot state - # For more details on PASSWORD variable, check the following issue: - # https://github.com/systemd/systemd/issues/20955 - machine.succeed("PASSWORD=lukspass systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 /dev/vdb") - # Add LUKS partition to /etc/crypttab to test auto unlock - machine.succeed("echo 'luks /dev/vdb - tpm2-device=auto' >> /etc/crypttab") - machine.shutdown() - - start_swtpm(tpmstate) - machine.start() - - # Test LUKS partition automatic unlock on boot - machine.wait_for_unit("systemd-cryptsetup@luks.service") - # Wipe TPM2 slot - machine.succeed("systemd-cryptenroll --wipe-slot=tpm2 /dev/vdb") + machine.start() + + # Verify the TPM device is available and accessible by systemd-cryptenroll + machine.succeed("test -e /dev/tpm0") + machine.succeed("test -e /dev/tpmrm0") + machine.succeed("systemd-cryptenroll --tpm2-device=list") + + # Create LUKS partition + machine.succeed("echo -n lukspass | cryptsetup luksFormat -q /dev/vdb -") + # Enroll new LUKS key and bind it to Secure Boot state + # For more details on PASSWORD variable, check the following issue: + # https://github.com/systemd/systemd/issues/20955 + machine.succeed("PASSWORD=lukspass systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 /dev/vdb") + # Add LUKS partition to /etc/crypttab to test auto unlock + machine.succeed("echo 'luks /dev/vdb - tpm2-device=auto' >> /etc/crypttab") + + machine.shutdown() + machine.start() + + # Test LUKS partition automatic unlock on boot + machine.wait_for_unit("systemd-cryptsetup@luks.service") + # Wipe TPM2 slot + machine.succeed("systemd-cryptenroll --wipe-slot=tpm2 /dev/vdb") ''; }) diff --git a/nixos/tests/systemd-initrd-luks-tpm2.nix b/nixos/tests/systemd-initrd-luks-tpm2.nix index d9dd9118a3a..e292acfd1c5 100644 --- a/nixos/tests/systemd-initrd-luks-tpm2.nix +++ b/nixos/tests/systemd-initrd-luks-tpm2.nix @@ -9,7 +9,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { # Booting off the TPM2-encrypted device requires an available init script mountHostNixStore = true; useEFIBoot = true; - qemu.options = ["-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"]; + tpm.enable = true; }; boot.loader.systemd-boot.enable = true; @@ -33,29 +33,6 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { }; testScript = '' - import subprocess - import os - import time - - - class Tpm: - def __init__(self): - os.mkdir("/tmp/mytpm1") - self.start() - - def start(self): - self.proc = subprocess.Popen(["${pkgs.swtpm}/bin/swtpm", "socket", "--tpmstate", "dir=/tmp/mytpm1", "--ctrl", "type=unixio,path=/tmp/mytpm1/swtpm-sock", "--log", "level=20", "--tpm2"]) - - def wait_for_death_then_restart(self): - while self.proc.poll() is None: - print("waiting for tpm to die") - time.sleep(1) - assert self.proc.returncode == 0 - self.start() - - tpm = Tpm() - - # Create encrypted volume machine.wait_for_unit("multi-user.target") machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -") @@ -66,8 +43,6 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { machine.succeed("sync") machine.crash() - tpm.wait_for_death_then_restart() - # Boot and decrypt the disk machine.wait_for_unit("multi-user.target") assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount") diff --git a/nixos/tests/systemd-initrd-modprobe.nix b/nixos/tests/systemd-initrd-modprobe.nix index bf635a10d0e..0f93492176b 100644 --- a/nixos/tests/systemd-initrd-modprobe.nix +++ b/nixos/tests/systemd-initrd-modprobe.nix @@ -2,6 +2,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { name = "systemd-initrd-modprobe"; nodes.machine = { pkgs, ... }: { + testing.initrdBackdoor = true; boot.initrd.systemd.enable = true; boot.initrd.kernelModules = [ "loop" ]; # Load module in initrd. boot.extraModprobeConfig = '' @@ -10,6 +11,12 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { }; testScript = '' + machine.wait_for_unit("initrd.target") + max_loop = machine.succeed("cat /sys/module/loop/parameters/max_loop") + assert int(max_loop) == 42, "Parameter should be respected for initrd kernel modules" + + # Make sure it sticks in stage 2 + machine.switch_root() machine.wait_for_unit("multi-user.target") max_loop = machine.succeed("cat /sys/module/loop/parameters/max_loop") assert int(max_loop) == 42, "Parameter should be respected for initrd kernel modules" diff --git a/nixos/tests/systemd-initrd-networkd-ssh.nix b/nixos/tests/systemd-initrd-networkd-ssh.nix index 6aaa6c828f7..d4c168f40e2 100644 --- a/nixos/tests/systemd-initrd-networkd-ssh.nix +++ b/nixos/tests/systemd-initrd-networkd-ssh.nix @@ -4,34 +4,16 @@ import ./make-test-python.nix ({ lib, ... }: { nodes = { server = { config, pkgs, ... }: { - environment.systemPackages = [ pkgs.cryptsetup ]; - boot.loader.systemd-boot.enable = true; - boot.loader.timeout = 0; - virtualisation = { - emptyDiskImages = [ 4096 ]; - useBootLoader = true; - # Booting off the encrypted disk requires an available init script from - # the Nix store - mountHostNixStore = true; - useEFIBoot = true; - }; - - specialisation.encrypted-root.configuration = { - virtualisation.rootDevice = "/dev/mapper/root"; - virtualisation.fileSystems."/".autoFormat = true; - boot.initrd.luks.devices = lib.mkVMOverride { - root.device = "/dev/vdb"; - }; - boot.initrd.systemd.enable = true; - boot.initrd.network = { + testing.initrdBackdoor = true; + boot.initrd.systemd.enable = true; + boot.initrd.systemd.contents."/etc/msg".text = "foo"; + boot.initrd.network = { + enable = true; + ssh = { enable = true; - ssh = { - enable = true; - authorizedKeys = [ (lib.readFile ./initrd-network-ssh/id_ed25519.pub) ]; - port = 22; - # Terrible hack so it works with useBootLoader - hostKeys = [ { outPath = "${./initrd-network-ssh/ssh_host_ed25519_key}"; } ]; - }; + authorizedKeys = [ (lib.readFile ./initrd-network-ssh/id_ed25519.pub) ]; + port = 22; + hostKeys = [ ./initrd-network-ssh/ssh_host_ed25519_key ]; }; }; }; @@ -63,24 +45,16 @@ import ./make-test-python.nix ({ lib, ... }: { status, _ = client.execute("nc -z server 22") return status == 0 - server.wait_for_unit("multi-user.target") - server.succeed( - "echo somepass | cryptsetup luksFormat --type=luks2 /dev/vdb", - "bootctl set-default nixos-generation-1-specialisation-encrypted-root.conf", - "sync", - ) - server.shutdown() - server.start() - client.wait_for_unit("network.target") with client.nested("waiting for SSH server to come up"): retry(ssh_is_up) - client.succeed( - "echo somepass | ssh -i /etc/sshKey -o UserKnownHostsFile=/etc/knownHosts server 'systemd-tty-ask-password-agent' & exit" + msg = client.succeed( + "ssh -i /etc/sshKey -o UserKnownHostsFile=/etc/knownHosts server 'cat /etc/msg'" ) + assert "foo" in msg + server.switch_root() server.wait_for_unit("multi-user.target") - server.succeed("mount | grep '/dev/mapper/root on /'") ''; }) diff --git a/nixos/tests/systemd-initrd-networkd.nix b/nixos/tests/systemd-initrd-networkd.nix index 8376276d8f6..9c4ddb6e4b3 100644 --- a/nixos/tests/systemd-initrd-networkd.nix +++ b/nixos/tests/systemd-initrd-networkd.nix @@ -1,14 +1,36 @@ -import ./make-test-python.nix ({ pkgs, lib, ... }: { - name = "systemd-initrd-network"; - meta.maintainers = [ lib.maintainers.elvishjerricco ]; +{ system ? builtins.currentSystem +, config ? {} +, pkgs ? import ../.. { inherit system config; } +, lib ? pkgs.lib +}: - nodes = let - mkFlushTest = flush: script: { ... }: { - boot.initrd.systemd.enable = true; - boot.initrd.network = { - enable = true; - flushBeforeStage2 = flush; - }; +with import ../lib/testing-python.nix { inherit system pkgs; }; + +let + inherit (lib.maintainers) elvishjerricco; + + common = { + boot.initrd.systemd = { + enable = true; + network.wait-online.timeout = 10; + network.wait-online.anyInterface = true; + targets.network-online.requiredBy = [ "initrd.target" ]; + services.systemd-networkd-wait-online.requiredBy = + [ "network-online.target" ]; + initrdBin = [ pkgs.iproute2 pkgs.iputils pkgs.gnugrep ]; + }; + testing.initrdBackdoor = true; + boot.initrd.network.enable = true; + }; + + mkFlushTest = flush: script: makeTest { + name = "systemd-initrd-network-${lib.optionalString (!flush) "no-"}flush"; + meta.maintainers = [ elvishjerricco ]; + + nodes.machine = { + imports = [ common ]; + + boot.initrd.network.flushBeforeStage2 = flush; systemd.services.check-flush = { requiredBy = ["multi-user.target"]; before = ["network-pre.target" "multi-user.target"]; @@ -19,57 +41,53 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { inherit script; }; }; - in { - basic = { ... }: { - boot.initrd.network.enable = true; - boot.initrd.systemd = { - enable = true; - # Enable network-online to fail the test in case of timeout - network.wait-online.timeout = 10; - network.wait-online.anyInterface = true; - targets.network-online.requiredBy = [ "initrd.target" ]; - services.systemd-networkd-wait-online.requiredBy = - [ "network-online.target" ]; + testScript = '' + machine.wait_for_unit("network-online.target") + machine.succeed( + "ip addr | grep 10.0.2.15", + "ping -c1 10.0.2.2", + ) + machine.switch_root() - initrdBin = [ pkgs.iproute2 pkgs.iputils pkgs.gnugrep ]; - services.check = { - requiredBy = [ "initrd.target" ]; - before = [ "initrd.target" ]; - after = [ "network-online.target" ]; - serviceConfig.Type = "oneshot"; - path = [ pkgs.iproute2 pkgs.iputils pkgs.gnugrep ]; - script = '' - ip addr | grep 10.0.2.15 || exit 1 - ping -c1 10.0.2.2 || exit 1 - ''; - }; - }; - }; - - doFlush = mkFlushTest true '' - if ip addr | grep 10.0.2.15; then - echo "Network configuration survived switch-root; flushBeforeStage2 failed" - exit 1 - fi + machine.wait_for_unit("multi-user.target") ''; + }; + +in { + basic = makeTest { + name = "systemd-initrd-network"; + meta.maintainers = [ elvishjerricco ]; - dontFlush = mkFlushTest false '' - if ! (ip addr | grep 10.0.2.15); then - echo "Network configuration didn't survive switch-root" - exit 1 - fi + nodes.machine = common; + + testScript = '' + machine.wait_for_unit("network-online.target") + machine.succeed( + "ip addr | grep 10.0.2.15", + "ping -c1 10.0.2.2", + ) + machine.switch_root() + + # Make sure the systemd-network user was set correctly in initrd + machine.wait_for_unit("multi-user.target") + machine.succeed("[ $(stat -c '%U,%G' /run/systemd/netif/links) = systemd-network,systemd-network ]") + machine.succeed("ip addr show >&2") + machine.succeed("ip route show >&2") ''; }; - testScript = '' - start_all() - basic.wait_for_unit("multi-user.target") - doFlush.wait_for_unit("multi-user.target") - dontFlush.wait_for_unit("multi-user.target") - # Make sure the systemd-network user was set correctly in initrd - basic.succeed("[ $(stat -c '%U,%G' /run/systemd/netif/links) = systemd-network,systemd-network ]") - basic.succeed("ip addr show >&2") - basic.succeed("ip route show >&2") + doFlush = mkFlushTest true '' + if ip addr | grep 10.0.2.15; then + echo "Network configuration survived switch-root; flushBeforeStage2 failed" + exit 1 + fi + ''; + + dontFlush = mkFlushTest false '' + if ! (ip addr | grep 10.0.2.15); then + echo "Network configuration didn't survive switch-root" + exit 1 + fi ''; -}) +} diff --git a/nixos/tests/systemd-initrd-simple.nix b/nixos/tests/systemd-initrd-simple.nix index a6a22e9d48e..2b7283a8219 100644 --- a/nixos/tests/systemd-initrd-simple.nix +++ b/nixos/tests/systemd-initrd-simple.nix @@ -2,16 +2,19 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { name = "systemd-initrd-simple"; nodes.machine = { pkgs, ... }: { - boot.initrd.systemd = { - enable = true; - emergencyAccess = true; - }; + testing.initrdBackdoor = true; + boot.initrd.systemd.enable = true; virtualisation.fileSystems."/".autoResize = true; }; testScript = '' import subprocess + with subtest("testing initrd backdoor"): + machine.wait_for_unit("initrd.target") + machine.succeed("systemctl status initrd-fs.target") + machine.switch_root() + with subtest("handover to stage-2 systemd works"): machine.wait_for_unit("multi-user.target") machine.succeed("systemd-analyze | grep -q '(initrd)'") # direct handover @@ -37,6 +40,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { subprocess.check_call(["qemu-img", "resize", "vm-state-machine/machine.qcow2", "+1G"]) machine.start() + machine.switch_root() newAvail = machine.succeed("df --output=avail / | sed 1d") assert int(oldAvail) < int(newAvail), "File system did not grow" diff --git a/nixos/tests/systemd-networkd.nix b/nixos/tests/systemd-networkd.nix index 6c423f4140b..6b241b93d51 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-repart.nix b/nixos/tests/systemd-repart.nix index 22ea8fbd227..3914d5b3239 100644 --- a/nixos/tests/systemd-repart.nix +++ b/nixos/tests/systemd-repart.nix @@ -29,16 +29,6 @@ let "+32M", ]) - # Fix the GPT table by moving the backup table to the end of the enlarged - # disk image. This is necessary because we increased the size of the disk - # before. The disk needs to be a raw disk because sgdisk can only run on - # raw images. - subprocess.run([ - "${pkgs.gptfdisk}/bin/sgdisk", - "--move-second-header", - tmp_disk_image.name, - ]) - # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image. os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name ''; diff --git a/nixos/tests/systemd-timesyncd.nix b/nixos/tests/systemd-timesyncd.nix index 43abd36c47d..f38d06be151 100644 --- a/nixos/tests/systemd-timesyncd.nix +++ b/nixos/tests/systemd-timesyncd.nix @@ -15,12 +15,13 @@ in { # create the path that should be migrated by our activation script when # upgrading to a newer nixos version system.stateVersion = "19.03"; - system.activationScripts.simulate-old-timesync-state-dir = lib.mkBefore '' - rm -f /var/lib/systemd/timesync - mkdir -p /var/lib/systemd /var/lib/private/systemd/timesync - ln -s /var/lib/private/systemd/timesync /var/lib/systemd/timesync - chown systemd-timesync: /var/lib/private/systemd/timesync - ''; + 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 -" + ]; }); }; diff --git a/nixos/tests/systemd.nix b/nixos/tests/systemd.nix index 3c36291b733..1a39cc73c88 100644 --- a/nixos/tests/systemd.nix +++ b/nixos/tests/systemd.nix @@ -76,6 +76,17 @@ import ./make-test-python.nix ({ pkgs, ... }: { # wait for user services machine.wait_for_unit("default.target", "alice") + with subtest("systemctl edit suggests --runtime"): + # --runtime is suggested when using `systemctl edit` + ret, out = machine.execute("systemctl edit testservice1.service 2>&1") + assert ret == 1 + assert out.rstrip("\n") == "The unit-directory '/etc/systemd/system' is read-only on NixOS, so it's not possible to edit system-units directly. Use 'systemctl edit --runtime' instead." + # editing w/o `--runtime` is possible for user-services, however + # it's not possible because we're not in a tty when grepping + # (i.e. hacky way to ensure that the error from above doesn't appear here). + _, out = machine.execute("systemctl --user edit testservice2.service 2>&1") + assert out.rstrip("\n") == "Cannot edit units if not on a tty." + # Regression test for https://github.com/NixOS/nixpkgs/issues/105049 with subtest("systemd reads timezone database in /etc/zoneinfo"): timer = machine.succeed("TZ=UTC systemctl show --property=TimersCalendar oncalendar-test.timer") @@ -169,7 +180,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { # Do some IP traffic output_ping = machine.succeed( - "systemd-run --wait -- /run/wrappers/bin/ping -c 1 127.0.0.1 2>&1" + "systemd-run --wait -- ping -c 1 127.0.0.1 2>&1" ) with subtest("systemd reports accounting data on system.slice"): diff --git a/nixos/tests/tandoor-recipes.nix b/nixos/tests/tandoor-recipes.nix index 54456238fe6..18beaac6f06 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/tang.nix b/nixos/tests/tang.nix new file mode 100644 index 00000000000..10486a9feb8 --- /dev/null +++ b/nixos/tests/tang.nix @@ -0,0 +1,81 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "tang"; + meta = with pkgs.lib.maintainers; { + maintainers = [ jfroche ]; + }; + + nodes.server = + { config + , pkgs + , modulesPath + , ... + }: { + imports = [ + "${modulesPath}/../tests/common/auto-format-root-device.nix" + ]; + virtualisation = { + emptyDiskImages = [ 512 ]; + useBootLoader = true; + useEFIBoot = true; + # This requires to have access + # to a host Nix store as + # the new root device is /dev/vdb + # an empty 512MiB drive, containing no Nix store. + mountHostNixStore = true; + }; + + boot.loader.systemd-boot.enable = true; + + networking.interfaces.eth1.ipv4.addresses = [ + { address = "192.168.0.1"; prefixLength = 24; } + ]; + + environment.systemPackages = with pkgs; [ clevis tang cryptsetup ]; + services.tang = { + enable = true; + ipAddressAllow = [ "127.0.0.1/32" ]; + }; + }; + testScript = '' + start_all() + machine.wait_for_unit("sockets.target") + + with subtest("Check keys are generated"): + machine.wait_until_succeeds("curl -v http://127.0.0.1:7654/adv") + key = machine.wait_until_succeeds("tang-show-keys 7654") + + with subtest("Check systemd access list"): + machine.succeed("ping -c 3 192.168.0.1") + machine.fail("curl -v --connect-timeout 3 http://192.168.0.1:7654/adv") + + with subtest("Check basic encrypt and decrypt message"): + machine.wait_until_succeeds(f"""echo 'Hello World' | clevis encrypt tang '{{ "url": "http://127.0.0.1:7654", "thp":"{key}"}}' > /tmp/encrypted""") + decrypted = machine.wait_until_succeeds("clevis decrypt < /tmp/encrypted") + assert decrypted.strip() == "Hello World" + machine.wait_until_succeeds("tang-show-keys 7654") + + with subtest("Check encrypt and decrypt disk"): + machine.succeed("cryptsetup luksFormat --force-password --batch-mode /dev/vdb <<<'password'") + machine.succeed(f"""clevis luks bind -s1 -y -f -d /dev/vdb tang '{{ "url": "http://127.0.0.1:7654", "thp":"{key}" }}' <<< 'password' """) + clevis_luks = machine.succeed("clevis luks list -d /dev/vdb") + assert clevis_luks.strip() == """1: tang '{"url":"http://127.0.0.1:7654"}'""" + machine.succeed("clevis luks unlock -d /dev/vdb") + machine.succeed("find /dev/mapper -name 'luks*' -exec cryptsetup close {} +") + machine.succeed("clevis luks unlock -d /dev/vdb") + machine.succeed("find /dev/mapper -name 'luks*' -exec cryptsetup close {} +") + # without tang available, unlock should fail + machine.succeed("systemctl stop tangd.socket") + machine.fail("clevis luks unlock -d /dev/vdb") + machine.succeed("systemctl start tangd.socket") + + with subtest("Rotate server keys"): + machine.succeed("${pkgs.tang}/libexec/tangd-rotate-keys -d /var/lib/tang") + machine.succeed("clevis luks unlock -d /dev/vdb") + machine.succeed("find /dev/mapper -name 'luks*' -exec cryptsetup close {} +") + + with subtest("Test systemd service security"): + output = machine.succeed("systemd-analyze security tangd@.service") + machine.log(output) + assert output[-9:-1] == "SAFE :-}" + ''; +}) diff --git a/nixos/tests/tinyproxy.nix b/nixos/tests/tinyproxy.nix new file mode 100644 index 00000000000..b8448d4c23b --- /dev/null +++ b/nixos/tests/tinyproxy.nix @@ -0,0 +1,20 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "tinyproxy"; + + nodes.machine = { config, pkgs, ... }: { + services.tinyproxy = { + enable = true; + settings = { + Listen = "127.0.0.1"; + Port = 8080; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("tinyproxy.service") + machine.wait_for_open_port(8080) + + machine.succeed('curl -s http://localhost:8080 |grep -i tinyproxy') + ''; +}) diff --git a/nixos/tests/tracee.nix b/nixos/tests/tracee.nix index 8ec86ef091e..3dadc0f9fdb 100644 --- a/nixos/tests/tracee.nix +++ b/nixos/tests/tracee.nix @@ -43,6 +43,10 @@ import ./make-test-python.nix ({ pkgs, ... }: { mv $GOPATH/tracee-integration $out/bin/ ''; doInstallCheck = false; + + meta = oa.meta // { + outputsToInstall = []; + }; })) ]; }; diff --git a/nixos/tests/transmission.nix b/nixos/tests/transmission.nix index b69ddd84d00..03fc9a42151 100644 --- a/nixos/tests/transmission.nix +++ b/nixos/tests/transmission.nix @@ -1,4 +1,4 @@ -import ./make-test-python.nix ({ pkgs, ...} : { +import ./make-test-python.nix ({ pkgs, transmission, ... }: { name = "transmission"; meta = with pkgs.lib.maintainers; { maintainers = [ coconnor ]; @@ -12,6 +12,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { security.apparmor.enable = true; services.transmission.enable = true; + services.transmission.package = transmission; }; testScript = diff --git a/nixos/tests/tsja.nix b/nixos/tests/tsja.nix new file mode 100644 index 00000000000..176783088d8 --- /dev/null +++ b/nixos/tests/tsja.nix @@ -0,0 +1,32 @@ +import ./make-test-python.nix ({ pkgs, lib, ...} : { + name = "tsja"; + meta = { + maintainers = with lib.maintainers; [ chayleaf ]; + }; + + nodes = { + master = + { config, ... }: + + { + services.postgresql = { + enable = true; + extraPlugins = with config.services.postgresql.package.pkgs; [ + tsja + ]; + }; + }; + }; + + testScript = '' + start_all() + master.wait_for_unit("postgresql") + master.succeed("sudo -u postgres psql -f /run/current-system/sw/share/postgresql/extension/libtsja_dbinit.sql") + # make sure "日本語" is parsed as a separate lexeme + master.succeed(""" + sudo -u postgres \\ + psql -c "SELECT * FROM ts_debug('japanese', 'PostgreSQLで日本語のテキスト検索ができます。')" \\ + | grep "{日本語}" + """) + ''; +}) diff --git a/nixos/tests/udisks2.nix b/nixos/tests/udisks2.nix index 6afb200f856..8cc148750c7 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/ulogd.nix b/nixos/tests/ulogd.nix deleted file mode 100644 index d351fdae798..00000000000 --- a/nixos/tests/ulogd.nix +++ /dev/null @@ -1,82 +0,0 @@ -import ./make-test-python.nix ({ pkgs, lib, ... }: { - name = "ulogd"; - - meta.maintainers = with lib.maintainers; [ p-h ]; - - nodes.machine = { ... }: { - networking.firewall.enable = false; - networking.nftables.enable = true; - networking.nftables.ruleset = '' - table inet filter { - chain input { - type filter hook input priority 0; - log group 2 accept - } - - chain output { - type filter hook output priority 0; policy accept; - log group 2 accept - } - - chain forward { - type filter hook forward priority 0; policy drop; - log group 2 accept - } - - } - ''; - services.ulogd = { - enable = true; - settings = { - global = { - logfile = "/var/log/ulogd.log"; - stack = "log1:NFLOG,base1:BASE,pcap1:PCAP"; - }; - - log1.group = 2; - - pcap1 = { - file = "/var/log/ulogd.pcap"; - sync = 1; - }; - }; - }; - - environment.systemPackages = with pkgs; [ - tcpdump - ]; - }; - - testScript = '' - start_all() - machine.wait_for_unit("ulogd.service") - machine.wait_for_unit("network-online.target") - - with subtest("Ulogd is running"): - machine.succeed("pgrep ulogd >&2") - - # All packets show up twice in the logs - with subtest("Logs are collected"): - machine.succeed("ping -f 127.0.0.1 -c 5 >&2") - machine.succeed("sleep 2") - machine.wait_until_succeeds("du /var/log/ulogd.pcap >&2") - _, echo_request_packets = machine.execute("tcpdump -r /var/log/ulogd.pcap icmp[0] == 8 and host 127.0.0.1") - expected, actual = 5*2, len(echo_request_packets.splitlines()) - assert expected == actual, f"Expected {expected} packets, got: {actual}" - _, echo_reply_packets = machine.execute("tcpdump -r /var/log/ulogd.pcap icmp[0] == 0 and host 127.0.0.1") - expected, actual = 5*2, len(echo_reply_packets.splitlines()) - assert expected == actual, f"Expected {expected} packets, got: {actual}" - - with subtest("Reloading service reopens log file"): - machine.succeed("mv /var/log/ulogd.pcap /var/log/old_ulogd.pcap") - machine.succeed("systemctl reload ulogd.service") - machine.succeed("ping -f 127.0.0.1 -c 5 >&2") - machine.succeed("sleep 2") - _, echo_request_packets = machine.execute("tcpdump -r /var/log/ulogd.pcap icmp[0] == 8 and host 127.0.0.1") - expected, actual = 5*2, len(echo_request_packets.splitlines()) - assert expected == actual, f"Expected {expected} packets, got: {actual}" - _, echo_reply_packets = machine.execute("tcpdump -r /var/log/ulogd.pcap icmp[0] == 0 and host 127.0.0.1") - expected, actual = 5*2, len(echo_reply_packets.splitlines()) - assert expected == actual, f"Expected {expected} packets, got: {actual}" - ''; -}) diff --git a/nixos/tests/ulogd/ulogd.nix b/nixos/tests/ulogd/ulogd.nix new file mode 100644 index 00000000000..0fa92229a10 --- /dev/null +++ b/nixos/tests/ulogd/ulogd.nix @@ -0,0 +1,56 @@ +import ../make-test-python.nix ({ pkgs, lib, ... }: { + name = "ulogd"; + + meta.maintainers = with lib.maintainers; [ p-h ]; + + nodes.machine = { ... }: { + networking.firewall.enable = false; + networking.nftables.enable = true; + networking.nftables.ruleset = '' + table inet filter { + chain input { + type filter hook input priority 0; + icmp type { echo-request, echo-reply } log group 2 accept + } + + chain output { + type filter hook output priority 0; policy accept; + icmp type { echo-request, echo-reply } log group 2 accept + } + + chain forward { + type filter hook forward priority 0; policy drop; + } + + } + ''; + services.ulogd = { + enable = true; + settings = { + global = { + logfile = "/var/log/ulogd.log"; + stack = [ + "log1:NFLOG,base1:BASE,ifi1:IFINDEX,ip2str1:IP2STR,print1:PRINTPKT,emu1:LOGEMU" + "log1:NFLOG,base1:BASE,pcap1:PCAP" + ]; + }; + + log1.group = 2; + + pcap1 = { + sync = 1; + file = "/var/log/ulogd.pcap"; + }; + + emu1 = { + sync = 1; + file = "/var/log/ulogd_pkts.log"; + }; + }; + }; + + environment.systemPackages = with pkgs; [ tcpdump ]; + }; + + testScript = lib.readFile ./ulogd.py; +}) diff --git a/nixos/tests/ulogd/ulogd.py b/nixos/tests/ulogd/ulogd.py new file mode 100644 index 00000000000..d20daa4d733 --- /dev/null +++ b/nixos/tests/ulogd/ulogd.py @@ -0,0 +1,48 @@ +start_all() +machine.wait_for_unit("ulogd.service") +machine.wait_for_unit("network-online.target") + +with subtest("Ulogd is running"): + machine.succeed("pgrep ulogd >&2") + +# All packets show up twice in the logs +with subtest("Logs are collected"): + machine.succeed("ping -f 127.0.0.1 -c 5 >&2") + machine.succeed("sleep 2") + machine.wait_until_succeeds("du /var/log/ulogd.pcap") + _, echo_request_packets = machine.execute("tcpdump -r /var/log/ulogd.pcap icmp[0] == 8 and host 127.0.0.1") + expected, actual = 5 * 2, len(echo_request_packets.splitlines()) + assert expected == actual, f"Expected {expected} ICMP request packets from pcap, got: {actual}" + _, echo_reply_packets = machine.execute("tcpdump -r /var/log/ulogd.pcap icmp[0] == 0 and host 127.0.0.1") + expected, actual = 5 * 2, len(echo_reply_packets.splitlines()) + assert expected == actual, f"Expected {expected} ICMP reply packets from pcap, got: {actual}" + + machine.wait_until_succeeds("du /var/log/ulogd_pkts.log") + _, echo_request_packets = machine.execute("grep TYPE=8 /var/log/ulogd_pkts.log") + expected, actual = 5 * 2, len(echo_request_packets.splitlines()) + assert expected == actual, f"Expected {expected} ICMP request packets from logfile, got: {actual}" + _, echo_reply_packets = machine.execute("grep TYPE=0 /var/log/ulogd_pkts.log") + expected, actual = 5 * 2, len(echo_reply_packets.splitlines()) + assert expected == actual, f"Expected {expected} ICMP reply packets from logfile, got: {actual}" + +with subtest("Reloading service reopens log file"): + machine.succeed("mv /var/log/ulogd.pcap /var/log/old_ulogd.pcap") + machine.succeed("mv /var/log/ulogd_pkts.log /var/log/old_ulogd_pkts.log") + machine.succeed("systemctl reload ulogd.service") + machine.succeed("ping -f 127.0.0.1 -c 5 >&2") + machine.succeed("sleep 2") + machine.wait_until_succeeds("du /var/log/ulogd.pcap") + _, echo_request_packets = machine.execute("tcpdump -r /var/log/ulogd.pcap icmp[0] == 8 and host 127.0.0.1") + expected, actual = 5 * 2, len(echo_request_packets.splitlines()) + assert expected == actual, f"Expected {expected} packets, got: {actual}" + _, echo_reply_packets = machine.execute("tcpdump -r /var/log/ulogd.pcap icmp[0] == 0 and host 127.0.0.1") + expected, actual = 5 * 2, len(echo_reply_packets.splitlines()) + assert expected == actual, f"Expected {expected} packets, got: {actual}" + + machine.wait_until_succeeds("du /var/log/ulogd_pkts.log") + _, echo_request_packets = machine.execute("grep TYPE=8 /var/log/ulogd_pkts.log") + expected, actual = 5 * 2, len(echo_request_packets.splitlines()) + assert expected == actual, f"Expected {expected} ICMP request packets from logfile, got: {actual}" + _, echo_reply_packets = machine.execute("grep TYPE=0 /var/log/ulogd_pkts.log") + expected, actual = 5 * 2, len(echo_reply_packets.splitlines()) + assert expected == actual, f"Expected {expected} ICMP reply packets from logfile, got: {actual}" diff --git a/nixos/tests/vaultwarden.nix b/nixos/tests/vaultwarden.nix index 95d00c1d8ec..9d2f0e6ab06 100644 --- a/nixos/tests/vaultwarden.nix +++ b/nixos/tests/vaultwarden.nix @@ -54,9 +54,8 @@ let services.postgresql = { enable = true; initialScript = pkgs.writeText "postgresql-init.sql" '' - CREATE DATABASE bitwarden; CREATE USER bitwardenuser WITH PASSWORD '${dbPassword}'; - GRANT ALL PRIVILEGES ON DATABASE bitwarden TO bitwardenuser; + CREATE DATABASE bitwarden WITH OWNER bitwardenuser; ''; }; @@ -174,7 +173,7 @@ let ) with subtest("use the web interface to sign up, log in, and save a password"): - server.succeed("PYTHONUNBUFFERED=1 test-runner | systemd-cat -t test-runner") + server.succeed("PYTHONUNBUFFERED=1 systemd-cat -t test-runner test-runner") with subtest("log in with the cli"): key = client.succeed( diff --git a/nixos/tests/vikunja.nix b/nixos/tests/vikunja.nix index 2660aa9767c..60fd5ce1385 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 715477191bf..6548883db45 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 a89b4b7480e..afb7c0e0a0e 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 14311afea3f..e5eb30fef59 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-apps/netbox-upgrade.nix b/nixos/tests/web-apps/netbox-upgrade.nix index 602cf8d889d..b5403eb678b 100644 --- a/nixos/tests/web-apps/netbox-upgrade.nix +++ b/nixos/tests/web-apps/netbox-upgrade.nix @@ -1,13 +1,15 @@ import ../make-test-python.nix ({ lib, pkgs, ... }: let - oldNetbox = pkgs.netbox_3_3; + oldNetbox = pkgs.netbox_3_5; + newNetbox = pkgs.netbox_3_6; in { name = "netbox-upgrade"; meta = with lib.maintainers; { - maintainers = [ minijackson ]; + maintainers = [ minijackson raitobezarius ]; }; nodes.machine = { config, ... }: { + virtualisation.memorySize = 2048; services.netbox = { enable = true; package = oldNetbox; @@ -32,7 +34,7 @@ in { networking.firewall.allowedTCPPorts = [ 80 ]; - specialisation.upgrade.configuration.services.netbox.package = lib.mkForce pkgs.netbox; + specialisation.upgrade.configuration.services.netbox.package = lib.mkForce newNetbox; }; testScript = { nodes, ... }: @@ -43,7 +45,7 @@ in { (lib.concatStringsSep ".") ]; oldApiVersion = apiVersion oldNetbox.version; - newApiVersion = apiVersion pkgs.netbox.version; + newApiVersion = apiVersion newNetbox.version; in '' start_all() diff --git a/nixos/tests/web-apps/netbox.nix b/nixos/tests/web-apps/netbox.nix index 30de74f1886..233f16a8fe0 100644 --- a/nixos/tests/web-apps/netbox.nix +++ b/nixos/tests/web-apps/netbox.nix @@ -16,6 +16,7 @@ in import ../make-test-python.nix ({ lib, pkgs, netbox, ... }: { }; nodes.machine = { config, ... }: { + virtualisation.memorySize = 2048; services.netbox = { enable = true; package = netbox; diff --git a/nixos/tests/wiki-js.nix b/nixos/tests/wiki-js.nix index fd054a9c590..8b3c51935a6 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 106bbff46c5..592af9a094f 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_1" "6_2" "6_3" + "6_3" "6_4" ]; testScript = '' diff --git a/nixos/tests/xfce.nix b/nixos/tests/xfce.nix index 3758ccbccf4..9620e9188cb 100644 --- a/nixos/tests/xfce.nix +++ b/nixos/tests/xfce.nix @@ -20,26 +20,56 @@ import ./make-test-python.nix ({ pkgs, ...} : { }; services.xserver.desktopManager.xfce.enable = true; + environment.systemPackages = [ pkgs.xfce.xfce4-whiskermenu-plugin ]; hardware.pulseaudio.enable = true; # needed for the factl test, /dev/snd/* exists without them but udev doesn't care then }; + enableOCR = true; + testScript = { nodes, ... }: let user = nodes.machine.users.users.alice; + bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${toString user.uid}/bus"; in '' - machine.wait_for_x() - machine.wait_for_file("${user.home}/.Xauthority") - machine.succeed("xauth merge ${user.home}/.Xauthority") - machine.wait_for_window("xfce4-panel") - machine.sleep(10) - - # Check that logging in has given the user ownership of devices. - machine.succeed("getfacl -p /dev/snd/timer | grep -q ${user.name}") - - machine.succeed("su - ${user.name} -c 'DISPLAY=:0.0 xfce4-terminal >&2 &'") - machine.wait_for_window("Terminal") - machine.sleep(10) - machine.screenshot("screen") + with subtest("Wait for login"): + machine.wait_for_x() + machine.wait_for_file("${user.home}/.Xauthority") + machine.succeed("xauth merge ${user.home}/.Xauthority") + + 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 Xfce components actually start"): + machine.wait_for_window("xfce4-panel") + machine.wait_for_window("Desktop") + for i in ["xfwm4", "xfsettingsd", "xfdesktop", "xfce4-screensaver", "xfce4-notifyd", "xfconfd"]: + machine.wait_until_succeeds(f"pgrep -f {i}") + + with subtest("Open whiskermenu"): + machine.succeed("su - ${user.name} -c 'DISPLAY=:0 ${bus} xfconf-query -c xfce4-panel -p /plugins/plugin-1 -t string -s whiskermenu -n >&2 &'") + machine.succeed("su - ${user.name} -c 'DISPLAY=:0 ${bus} xfconf-query -c xfce4-panel -p /plugins/plugin-1/stay-on-focus-out -t bool -s true -n >&2 &'") + machine.succeed("su - ${user.name} -c 'DISPLAY=:0 ${bus} xfce4-panel -r >&2 &'") + machine.wait_until_succeeds("journalctl -b --grep 'xfce4-panel: Restarting' -t xsession") + machine.sleep(5) + machine.wait_until_succeeds("pgrep -f libwhiskermenu") + machine.succeed("su - ${user.name} -c 'DISPLAY=:0 ${bus} xfce4-popup-whiskermenu >&2 &'") + machine.wait_for_text('Mail Reader') + # Close the menu. + machine.succeed("su - ${user.name} -c 'DISPLAY=:0 ${bus} xfce4-popup-whiskermenu >&2 &'") + + with subtest("Open Xfce terminal"): + machine.succeed("su - ${user.name} -c 'DISPLAY=:0 xfce4-terminal >&2 &'") + machine.wait_for_window("Terminal") + + with subtest("Open Thunar"): + machine.succeed("su - ${user.name} -c 'DISPLAY=:0 thunar >&2 &'") + machine.wait_for_window("Thunar") + machine.wait_for_text('(Pictures|Public|Templates|Videos)') + + with subtest("Check if any coredumps are found"): + machine.succeed("(coredumpctl --json=short 2>&1 || true) | grep 'No coredumps found'") + machine.sleep(10) + machine.screenshot("screen") ''; }) diff --git a/nixos/tests/xmpp/ejabberd.nix b/nixos/tests/xmpp/ejabberd.nix index 7926fe80de2..1a807b27b6f 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/yggdrasil.nix b/nixos/tests/yggdrasil.nix index eaf14e29acb..70d148380bf 100644 --- a/nixos/tests/yggdrasil.nix +++ b/nixos/tests/yggdrasil.nix @@ -116,6 +116,7 @@ in import ./make-test-python.nix ({ pkgs, ...} : { networking.firewall.allowedTCPPorts = [ 43210 ]; services.yggdrasil = { enable = true; + extraArgs = [ "-loglevel" "error" ]; denyDhcpcdInterfaces = [ "ygg0" ]; settings = { IfTAPMode = true; diff --git a/nixos/tests/zfs.nix b/nixos/tests/zfs.nix index 800f5e43cd1..3454fbaf78f 100644 --- a/nixos/tests/zfs.nix +++ b/nixos/tests/zfs.nix @@ -113,8 +113,6 @@ let }; testScript = '' - # TODO: Remove this when upgrading stable to zfs 2.2.0 - unstable = ${if enableUnstable then "True" else "False"}; machine.wait_for_unit("multi-user.target") machine.succeed( "zpool status", @@ -136,8 +134,6 @@ let machine.crash() machine.wait_for_unit("multi-user.target") machine.succeed("zfs set sharesmb=on rpool/shared_smb") - if not unstable: - machine.succeed("zfs share rpool/shared_smb") machine.succeed( "smbclient -gNL localhost | grep rpool_shared_smb", "umount /tmp/mnt", diff --git a/nixos/tests/zwave-js.nix b/nixos/tests/zwave-js.nix new file mode 100644 index 00000000000..9239e6964fd --- /dev/null +++ b/nixos/tests/zwave-js.nix @@ -0,0 +1,31 @@ +import ./make-test-python.nix ({ pkgs, lib, ...} : + +let + secretsConfigFile = pkgs.writeText "secrets.json" (builtins.toJSON { + securityKeys = { + "S0_Legacy" = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + }; + }); +in { + name = "zwave-js"; + meta.maintainers = with lib.maintainers; [ graham33 ]; + + nodes = { + machine = { config, ... }: { + services.zwave-js = { + enable = true; + serialPort = "/dev/null"; + extraFlags = ["--mock-driver"]; + inherit secretsConfigFile; + }; + }; + }; + + testScript = '' + start_all() + + machine.wait_for_unit("zwave-js.service") + machine.wait_for_open_port(3000) + machine.wait_until_succeeds("journalctl --since -1m --unit zwave-js --grep 'ZwaveJS server listening'") + ''; +}) |