diff options
Diffstat (limited to 'nixos/tests')
120 files changed, 3547 insertions, 913 deletions
diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index fc41dc1eb5f..a8188473721 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -48,10 +48,9 @@ in import ./make-test-python.nix ({ lib, ... }: { security.acme.certs."standalone.test" = { webroot = "/var/lib/acme/acme-challenges"; }; - systemd.targets."acme-finished-standalone.test" = {}; - systemd.services."acme-standalone.test" = { - wants = [ "acme-finished-standalone.test.target" ]; - before = [ "acme-finished-standalone.test.target" ]; + systemd.targets."acme-finished-standalone.test" = { + after = [ "acme-standalone.test.service" ]; + wantedBy = [ "acme-standalone.test.service" ]; }; services.nginx.enable = true; services.nginx.virtualHosts."standalone.test" = { @@ -68,11 +67,9 @@ in import ./make-test-python.nix ({ lib, ... }: { # A target remains active. Use this to probe the fact that # a service fired eventhough it is not RemainAfterExit - systemd.targets."acme-finished-a.example.test" = {}; - systemd.services."acme-a.example.test" = { - wants = [ "acme-finished-a.example.test.target" ]; - before = [ "acme-finished-a.example.test.target" ]; - after = [ "nginx.service" ]; + systemd.targets."acme-finished-a.example.test" = { + after = [ "acme-a.example.test.service" ]; + wantedBy = [ "acme-a.example.test.service" ]; }; services.nginx.enable = true; @@ -89,11 +86,9 @@ in import ./make-test-python.nix ({ lib, ... }: { security.acme.server = "https://acme.test/dir"; specialisation.second-cert.configuration = {pkgs, ...}: { - systemd.targets."acme-finished-b.example.test" = {}; - systemd.services."acme-b.example.test" = { - wants = [ "acme-finished-b.example.test.target" ]; - before = [ "acme-finished-b.example.test.target" ]; - after = [ "nginx.service" ]; + systemd.targets."acme-finished-b.example.test" = { + after = [ "acme-b.example.test.service" ]; + wantedBy = [ "acme-b.example.test.service" ]; }; services.nginx.virtualHosts."b.example.test" = { enableACME = true; @@ -104,6 +99,7 @@ in import ./make-test-python.nix ({ lib, ... }: { ''; }; }; + specialisation.dns-01.configuration = {pkgs, config, nodes, lib, ...}: { security.acme.certs."example.test" = { domain = "*.example.test"; @@ -115,10 +111,12 @@ in import ./make-test-python.nix ({ lib, ... }: { user = config.services.nginx.user; group = config.services.nginx.group; }; - systemd.targets."acme-finished-example.test" = {}; + systemd.targets."acme-finished-example.test" = { + after = [ "acme-example.test.service" ]; + wantedBy = [ "acme-example.test.service" ]; + }; systemd.services."acme-example.test" = { - wants = [ "acme-finished-example.test.target" ]; - before = [ "acme-finished-example.test.target" "nginx.service" ]; + before = [ "nginx.service" ]; wantedBy = [ "nginx.service" ]; }; services.nginx.virtualHosts."c.example.test" = { @@ -132,6 +130,26 @@ in import ./make-test-python.nix ({ lib, ... }: { ''; }; }; + + # When nginx depends on a service that is slow to start up, requesting used to fail + # certificates fail. Reproducer for https://github.com/NixOS/nixpkgs/issues/81842 + specialisation.slow-startup.configuration = { pkgs, config, nodes, lib, ...}: { + systemd.services.my-slow-service = { + wantedBy = [ "multi-user.target" "nginx.service" ]; + before = [ "nginx.service" ]; + preStart = "sleep 5"; + script = "${pkgs.python3}/bin/python -m http.server"; + }; + systemd.targets."acme-finished-d.example.com" = { + after = [ "acme-d.example.com.service" ]; + wantedBy = [ "acme-d.example.com.service" ]; + }; + services.nginx.virtualHosts."d.example.com" = { + forceSSL = true; + enableACME = true; + locations."/".proxyPass = "http://localhost:8000"; + }; + }; }; client = {nodes, lib, ...}: { @@ -207,5 +225,15 @@ in import ./make-test-python.nix ({ lib, ... }: { client.succeed( "curl --cacert /tmp/ca.crt https://c.example.test/ | grep -qF 'hello world'" ) + + with subtest("Can request certificate of nginx when startup is delayed"): + webserver.succeed( + "${switchToNewServer}" + ) + webserver.succeed( + "/run/current-system/specialisation/slow-startup/bin/switch-to-configuration test" + ) + webserver.wait_for_unit("acme-finished-d.example.com.target") + client.succeed("curl --cacert /tmp/ca.crt https://d.example.com/") ''; }) diff --git a/nixos/tests/agda.nix b/nixos/tests/agda.nix new file mode 100644 index 00000000000..e158999e57d --- /dev/null +++ b/nixos/tests/agda.nix @@ -0,0 +1,41 @@ +import ./make-test-python.nix ({ pkgs, ... }: + +let + hello-world = pkgs.writeText "hello-world" '' + open import IO + + main = run(putStrLn "Hello World!") + ''; +in +{ + name = "agda"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ alexarice turion ]; + }; + + machine = { pkgs, ... }: { + environment.systemPackages = [ + (pkgs.agda.withPackages { + pkgs = p: [ p.standard-library ]; + }) + ]; + virtualisation.memorySize = 2000; # Agda uses a lot of memory + }; + + testScript = '' + # Minimal script that typechecks + machine.succeed("touch TestEmpty.agda") + machine.succeed("agda TestEmpty.agda") + + # Minimal script that actually uses the standard library + machine.succeed('echo "import IO" > TestIO.agda') + machine.succeed("agda -l standard-library -i . TestIO.agda") + + # # Hello world + machine.succeed( + "cp ${hello-world} HelloWorld.agda" + ) + machine.succeed("agda -l standard-library -i . -c HelloWorld.agda") + ''; +} +) diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 5a0c9d1afae..a4a62d85a59 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -23,14 +23,18 @@ in { _3proxy = handleTest ./3proxy.nix {}; acme = handleTest ./acme.nix {}; + agda = handleTest ./agda.nix {}; atd = handleTest ./atd.nix {}; avahi = handleTest ./avahi.nix {}; babeld = handleTest ./babeld.nix {}; + bazarr = handleTest ./bazarr.nix {}; bcachefs = handleTestOn ["x86_64-linux"] ./bcachefs.nix {}; # linux-4.18.2018.10.12 is unsupported on aarch64 beanstalkd = handleTest ./beanstalkd.nix {}; bees = handleTest ./bees.nix {}; bind = handleTest ./bind.nix {}; + bitcoind = handleTest ./bitcoind.nix {}; bittorrent = handleTest ./bittorrent.nix {}; + blockbook-frontend = handleTest ./blockbook-frontend.nix {}; buildkite-agents = handleTest ./buildkite-agents.nix {}; boot = handleTestOn ["x86_64-linux"] ./boot.nix {}; # syslinux is unsupported on aarch64 boot-stage1 = handleTest ./boot-stage1.nix {}; @@ -63,11 +67,13 @@ in containers-portforward = handleTest ./containers-portforward.nix {}; containers-restart_networking = handleTest ./containers-restart_networking.nix {}; containers-tmpfs = handleTest ./containers-tmpfs.nix {}; + convos = handleTest ./convos.nix {}; corerad = handleTest ./corerad.nix {}; couchdb = handleTest ./couchdb.nix {}; deluge = handleTest ./deluge.nix {}; dhparams = handleTest ./dhparams.nix {}; dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {}; + dnscrypt-wrapper = handleTestOn ["x86_64-linux"] ./dnscrypt-wrapper {}; doas = handleTest ./doas.nix {}; docker = handleTestOn ["x86_64-linux"] ./docker.nix {}; oci-containers = handleTestOn ["x86_64-linux"] ./oci-containers.nix {}; @@ -85,14 +91,17 @@ in ecryptfs = handleTest ./ecryptfs.nix {}; ejabberd = handleTest ./xmpp/ejabberd.nix {}; elk = handleTestOn ["x86_64-linux"] ./elk.nix {}; + engelsystem = handleTest ./engelsystem.nix {}; enlightenment = handleTest ./enlightenment.nix {}; env = handleTest ./env.nix {}; + ergo = handleTest ./ergo.nix {}; etcd = handleTestOn ["x86_64-linux"] ./etcd.nix {}; etcd-cluster = handleTestOn ["x86_64-linux"] ./etcd-cluster.nix {}; fancontrol = handleTest ./fancontrol.nix {}; ferm = handleTest ./ferm.nix {}; firefox = handleTest ./firefox.nix {}; firefox-esr = handleTest ./firefox.nix { esr = true; }; + firejail = handleTest ./firejail.nix {}; firewall = handleTest ./firewall.nix {}; fish = handleTest ./fish.nix {}; flannel = handleTestOn ["x86_64-linux"] ./flannel.nix {}; @@ -114,10 +123,12 @@ in installed-tests = pkgs.recurseIntoAttrs (handleTest ./installed-tests {}); gocd-agent = handleTest ./gocd-agent.nix {}; gocd-server = handleTest ./gocd-server.nix {}; + go-neb = handleTest ./go-neb.nix {}; google-oslogin = handleTest ./google-oslogin {}; grafana = handleTest ./grafana.nix {}; graphite = handleTest ./graphite.nix {}; graylog = handleTest ./graylog.nix {}; + grub = handleTest ./grub.nix {}; gvisor = handleTest ./gvisor.nix {}; hadoop.hdfs = handleTestOn [ "x86_64-linux" ] ./hadoop/hdfs.nix {}; hadoop.yarn = handleTestOn [ "x86_64-linux" ] ./hadoop/yarn.nix {}; @@ -131,6 +142,7 @@ in hitch = handleTest ./hitch {}; hocker-fetchdocker = handleTest ./hocker-fetchdocker {}; home-assistant = handleTest ./home-assistant.nix {}; + hostname = handleTest ./hostname.nix {}; hound = handleTest ./hound.nix {}; hydra = handleTest ./hydra {}; hydra-db-migration = handleTest ./hydra/db-migration.nix {}; @@ -141,6 +153,7 @@ in incron = handleTest ./incron.nix {}; influxdb = handleTest ./influxdb.nix {}; initrd-network-ssh = handleTest ./initrd-network-ssh {}; + initrd-network-openvpn = handleTest ./initrd-network-openvpn {}; initrdNetwork = handleTest ./initrd-network.nix {}; installer = handleTest ./installer.nix {}; iodine = handleTest ./iodine.nix {}; @@ -150,6 +163,7 @@ in jellyfin = handleTest ./jellyfin.nix {}; jenkins = handleTest ./jenkins.nix {}; jirafeau = handleTest ./jirafeau.nix {}; + jitsi-meet = handleTest ./jitsi-meet.nix {}; k3s = handleTest ./k3s.nix {}; kafka = handleTest ./kafka.nix {}; keepalived = handleTest ./keepalived.nix {}; @@ -172,6 +186,8 @@ in limesurvey = handleTest ./limesurvey.nix {}; login = handleTest ./login.nix {}; loki = handleTest ./loki.nix {}; + lxd = handleTest ./lxd.nix {}; + lxd-nftables = handleTest ./lxd-nftables.nix {}; #logstash = handleTest ./logstash.nix {}; lorri = handleTest ./lorri/default.nix {}; magnetico = handleTest ./magnetico.nix {}; @@ -179,12 +195,10 @@ in mailcatcher = handleTest ./mailcatcher.nix {}; mariadb-galera-mariabackup = handleTest ./mysql/mariadb-galera-mariabackup.nix {}; mariadb-galera-rsync = handleTest ./mysql/mariadb-galera-rsync.nix {}; - mathics = handleTest ./mathics.nix {}; matomo = handleTest ./matomo.nix {}; matrix-synapse = handleTest ./matrix-synapse.nix {}; mediawiki = handleTest ./mediawiki.nix {}; memcached = handleTest ./memcached.nix {}; - mesos = handleTest ./mesos.nix {}; metabase = handleTest ./metabase.nix {}; miniflux = handleTest ./miniflux.nix {}; minio = handleTest ./minio.nix {}; @@ -208,6 +222,7 @@ in nat.firewall = handleTest ./nat.nix { withFirewall = true; }; nat.firewall-conntrack = handleTest ./nat.nix { withFirewall = true; withConntrackHelpers = true; }; nat.standalone = handleTest ./nat.nix { withFirewall = false; }; + ncdns = handleTest ./ncdns.nix {}; ndppd = handleTest ./ndppd.nix {}; neo4j = handleTest ./neo4j.nix {}; specialisation = handleTest ./specialisation.nix {}; @@ -225,7 +240,9 @@ in nginx = handleTest ./nginx.nix {}; nginx-etag = handleTest ./nginx-etag.nix {}; nginx-pubhtml = handleTest ./nginx-pubhtml.nix {}; + nginx-sandbox = handleTestOn ["x86_64-linux"] ./nginx-sandbox.nix {}; nginx-sso = handleTest ./nginx-sso.nix {}; + nginx-variants = handleTest ./nginx-variants.nix {}; nix-ssh-serve = handleTest ./nix-ssh-serve.nix {}; nixos-generate-config = handleTest ./nixos-generate-config.nix {}; novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {}; @@ -250,9 +267,12 @@ in pgjwt = handleTest ./pgjwt.nix {}; pgmanage = handleTest ./pgmanage.nix {}; php = handleTest ./php {}; + pinnwand = handleTest ./pinnwand.nix {}; plasma5 = handleTest ./plasma5.nix {}; plotinus = handleTest ./plotinus.nix {}; - podman = handleTest ./podman.nix {}; + podman = handleTestOn ["x86_64-linux"] ./podman.nix {}; + postfix = handleTest ./postfix.nix {}; + postfix-raise-smtpd-tls-security-level = handleTest ./postfix-raise-smtpd-tls-security-level.nix {}; postgis = handleTest ./postgis.nix {}; postgresql = handleTest ./postgresql.nix {}; postgresql-wal-receiver = handleTest ./postgresql-wal-receiver.nix {}; @@ -260,11 +280,14 @@ in pppd = handleTest ./pppd.nix {}; predictable-interface-names = handleTest ./predictable-interface-names.nix {}; printing = handleTest ./printing.nix {}; + privacyidea = handleTest ./privacyidea.nix {}; prometheus = handleTest ./prometheus.nix {}; prometheus-exporters = handleTest ./prometheus-exporters.nix {}; prosody = handleTest ./xmpp/prosody.nix {}; prosodyMysql = handleTest ./xmpp/prosody-mysql.nix {}; proxy = handleTest ./proxy.nix {}; + pt2-clone = handleTest ./pt2-clone.nix {}; + qboot = handleTestOn ["x86_64-linux" "i686-linux"] ./qboot.nix {}; quagga = handleTest ./quagga.nix {}; quorum = handleTest ./quorum.nix {}; rabbitmq = handleTest ./rabbitmq.nix {}; @@ -283,25 +306,31 @@ in sanoid = handleTest ./sanoid.nix {}; sddm = handleTest ./sddm.nix {}; service-runner = handleTest ./service-runner.nix {}; + shattered-pixel-dungeon = handleTest ./shattered-pixel-dungeon.nix {}; shiori = handleTest ./shiori.nix {}; signal-desktop = handleTest ./signal-desktop.nix {}; simple = handleTest ./simple.nix {}; slurm = handleTest ./slurm.nix {}; smokeping = handleTest ./smokeping.nix {}; + snapcast = handleTest ./snapcast.nix {}; snapper = handleTest ./snapper.nix {}; + sogo = handleTest ./sogo.nix {}; solr = handleTest ./solr.nix {}; spacecookie = handleTest ./spacecookie.nix {}; spike = handleTest ./spike.nix {}; sonarr = handleTest ./sonarr.nix {}; + sslh = handleTest ./sslh.nix {}; strongswan-swanctl = handleTest ./strongswan-swanctl.nix {}; sudo = handleTest ./sudo.nix {}; switchTest = handleTest ./switch-test.nix {}; sympa = handleTest ./sympa.nix {}; + syncthing = handleTest ./syncthing.nix {}; syncthing-init = handleTest ./syncthing-init.nix {}; syncthing-relay = handleTest ./syncthing-relay.nix {}; systemd = handleTest ./systemd.nix {}; systemd-analyze = handleTest ./systemd-analyze.nix {}; - systemd-boot = handleTestOn ["x86_64-linux"] ./systemd-boot.nix {}; + systemd-binfmt = handleTestOn ["x86_64-linux"] ./systemd-binfmt.nix {}; + systemd-boot = handleTest ./systemd-boot.nix {}; systemd-confinement = handleTest ./systemd-confinement.nix {}; systemd-timesyncd = handleTest ./systemd-timesyncd.nix {}; systemd-networkd-vrf = handleTest ./systemd-networkd-vrf.nix {}; @@ -331,10 +360,8 @@ in vault = handleTest ./vault.nix {}; victoriametrics = handleTest ./victoriametrics.nix {}; virtualbox = handleTestOn ["x86_64-linux"] ./virtualbox.nix {}; - wg-quick = handleTest ./wireguard/wg-quick.nix {}; + wasabibackend = handleTest ./wasabibackend.nix {}; wireguard = handleTest ./wireguard {}; - wireguard-generated = handleTest ./wireguard/generated.nix {}; - wireguard-namespaces = handleTest ./wireguard/namespaces.nix {}; wordpress = handleTest ./wordpress.nix {}; xandikos = handleTest ./xandikos.nix {}; xautolock = handleTest ./xautolock.nix {}; @@ -345,6 +372,8 @@ in yabar = handleTest ./yabar.nix {}; yggdrasil = handleTest ./yggdrasil.nix {}; zfs = handleTest ./zfs.nix {}; - zsh-history = handleTest ./zsh-history.nix {}; + zigbee2mqtt = handleTest ./zigbee2mqtt.nix {}; + zoneminder = handleTest ./zoneminder.nix {}; zookeeper = handleTest ./zookeeper.nix {}; + zsh-history = handleTest ./zsh-history.nix {}; } diff --git a/nixos/tests/bazarr.nix b/nixos/tests/bazarr.nix new file mode 100644 index 00000000000..b8cd8ef38b4 --- /dev/null +++ b/nixos/tests/bazarr.nix @@ -0,0 +1,26 @@ +import ./make-test-python.nix ({ lib, ... }: + +with lib; + +let + port = 42069; +in +{ + name = "bazarr"; + meta.maintainers = with maintainers; [ xwvvvvwx ]; + + nodes.machine = + { pkgs, ... }: + { + services.bazarr = { + enable = true; + listenPort = port; + }; + }; + + testScript = '' + machine.wait_for_unit("bazarr.service") + machine.wait_for_open_port("${toString port}") + machine.succeed("curl --fail http://localhost:${toString port}/") + ''; +}) diff --git a/nixos/tests/bcachefs.nix b/nixos/tests/bcachefs.nix index 0541e580322..3f116d7df92 100644 --- a/nixos/tests/bcachefs.nix +++ b/nixos/tests/bcachefs.nix @@ -13,7 +13,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { machine.succeed("modprobe bcachefs") machine.succeed("bcachefs version") machine.succeed("ls /dev") - + machine.succeed( "mkdir /tmp/mnt", "udevadm settle", diff --git a/nixos/tests/bitcoind.nix b/nixos/tests/bitcoind.nix new file mode 100644 index 00000000000..09f3e4a6ec0 --- /dev/null +++ b/nixos/tests/bitcoind.nix @@ -0,0 +1,46 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "bitcoind"; + meta = with pkgs.stdenv.lib; { + maintainers = with maintainers; [ _1000101 ]; + }; + + machine = { ... }: { + services.bitcoind."mainnet" = { + enable = true; + rpc = { + port = 8332; + users.rpc.passwordHMAC = "acc2374e5f9ba9e62a5204d3686616cf$53abdba5e67a9005be6a27ca03a93ce09e58854bc2b871523a0d239a72968033"; + users.rpc2.passwordHMAC = "1495e4a3ad108187576c68f7f9b5ddc5$accce0881c74aa01bb8960ff3bdbd39f607fd33178147679e055a4ac35f53225"; + }; + }; + services.bitcoind."testnet" = { + enable = true; + configFile = "/test.blank"; + testnet = true; + rpc = { + port = 18332; + }; + extraCmdlineOptions = [ "-rpcuser=rpc" "-rpcpassword=rpc" "-rpcauth=rpc2:1495e4a3ad108187576c68f7f9b5ddc5$accce0881c74aa01bb8960ff3bdbd39f607fd33178147679e055a4ac35f53225" ]; + }; + }; + + testScript = '' + start_all() + + machine.wait_for_unit("bitcoind-mainnet.service") + machine.wait_for_unit("bitcoind-testnet.service") + + machine.wait_until_succeeds( + 'curl --user rpc:rpc --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:8332 | grep \'"chain":"main"\' ' + ) + machine.wait_until_succeeds( + 'curl --user rpc2:rpc2 --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:8332 | grep \'"chain":"main"\' ' + ) + machine.wait_until_succeeds( + 'curl --user rpc:rpc --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:18332 | grep \'"chain":"test"\' ' + ) + machine.wait_until_succeeds( + 'curl --user rpc2:rpc2 --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:18332 | grep \'"chain":"test"\' ' + ) + ''; +}) diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix index 0a97d5556a2..c195b60cd56 100644 --- a/nixos/tests/bittorrent.nix +++ b/nixos/tests/bittorrent.nix @@ -19,6 +19,7 @@ let externalClient2Address = "80.100.100.2"; externalTrackerAddress = "80.100.100.3"; + download-dir = "/var/lib/transmission/Downloads"; transmissionConfig = { ... }: { environment.systemPackages = [ pkgs.transmission ]; services.transmission = { @@ -26,6 +27,7 @@ let settings = { dht-enabled = false; message-level = 3; + inherit download-dir; }; }; }; @@ -117,12 +119,12 @@ in router.wait_for_unit("miniupnpd") # Create the torrent. - tracker.succeed("mkdir /tmp/data") + tracker.succeed("mkdir ${download-dir}/data") tracker.succeed( - "cp ${file} /tmp/data/test.tar.bz2" + "cp ${file} ${download-dir}/data/test.tar.bz2" ) tracker.succeed( - "transmission-create /tmp/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent" + "transmission-create ${download-dir}/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent" ) tracker.succeed("chmod 644 /tmp/test.torrent") @@ -133,18 +135,16 @@ in # Start the initial seeder. tracker.succeed( - "transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir /tmp/data" + "transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir ${download-dir}/data" ) # Now we should be able to download from the client behind the NAT. tracker.wait_for_unit("httpd") client1.wait_for_unit("network-online.target") + client1.succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent >&2 &") + client1.wait_for_file("${download-dir}/test.tar.bz2") client1.succeed( - "transmission-remote --add http://${externalTrackerAddress}/test.torrent --download-dir /tmp >&2 &" - ) - client1.wait_for_file("/tmp/test.tar.bz2") - client1.succeed( - "cmp /tmp/test.tar.bz2 ${file}" + "cmp ${download-dir}/test.tar.bz2 ${file}" ) # Bring down the initial seeder. @@ -154,11 +154,11 @@ in # the first client created a NAT hole in the router. client2.wait_for_unit("network-online.target") client2.succeed( - "transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht --download-dir /tmp >&2 &" + "transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht >&2 &" ) - client2.wait_for_file("/tmp/test.tar.bz2") + client2.wait_for_file("${download-dir}/test.tar.bz2") client2.succeed( - "cmp /tmp/test.tar.bz2 ${file}" + "cmp ${download-dir}/test.tar.bz2 ${file}" ) ''; }) diff --git a/nixos/tests/blockbook-frontend.nix b/nixos/tests/blockbook-frontend.nix new file mode 100644 index 00000000000..742a02999e7 --- /dev/null +++ b/nixos/tests/blockbook-frontend.nix @@ -0,0 +1,28 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "blockbook-frontend"; + meta = with pkgs.stdenv.lib; { + maintainers = with maintainers; [ _1000101 ]; + }; + + machine = { ... }: { + services.blockbook-frontend."test" = { + enable = true; + }; + services.bitcoind.mainnet = { + enable = true; + rpc = { + port = 8030; + users.rpc.passwordHMAC = "acc2374e5f9ba9e62a5204d3686616cf$53abdba5e67a9005be6a27ca03a93ce09e58854bc2b871523a0d239a72968033"; + }; + }; + }; + + testScript = '' + start_all() + machine.wait_for_unit("blockbook-frontend-test.service") + + machine.wait_for_open_port(9030) + + machine.succeed("curl -sSfL http://localhost:9030 | grep 'Blockbook'") + ''; +}) diff --git a/nixos/tests/borgbackup.nix b/nixos/tests/borgbackup.nix index d97471e293e..bf37eb8607b 100644 --- a/nixos/tests/borgbackup.nix +++ b/nixos/tests/borgbackup.nix @@ -43,7 +43,7 @@ in { nodes = { client = { ... }: { services.borgbackup.jobs = { - + local = { paths = dataDir; repo = localRepo; diff --git a/nixos/tests/ceph-multi-node.nix b/nixos/tests/ceph-multi-node.nix index 22fe5cada48..e26c6d5d670 100644 --- a/nixos/tests/ceph-multi-node.nix +++ b/nixos/tests/ceph-multi-node.nix @@ -183,15 +183,15 @@ let monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'") monA.succeed( - "ceph osd pool create multi-node-test 128 128", + "ceph osd pool create multi-node-test 32 32", "ceph osd pool ls | grep 'multi-node-test'", "ceph osd pool rename multi-node-test multi-node-other-test", "ceph osd pool ls | grep 'multi-node-other-test'", ) - monA.wait_until_succeeds("ceph -s | grep '1 pools, 128 pgs'") + monA.wait_until_succeeds("ceph -s | grep '2 pools, 33 pgs'") monA.succeed("ceph osd pool set multi-node-other-test size 2") monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'") - monA.wait_until_succeeds("ceph -s | grep '128 active+clean'") + monA.wait_until_succeeds("ceph -s | grep '33 active+clean'") monA.fail( "ceph osd pool ls | grep 'multi-node-test'", "ceph osd pool delete multi-node-other-test multi-node-other-test --yes-i-really-really-mean-it", diff --git a/nixos/tests/ceph-single-node.nix b/nixos/tests/ceph-single-node.nix index 01c4b413845..98528f6317b 100644 --- a/nixos/tests/ceph-single-node.nix +++ b/nixos/tests/ceph-single-node.nix @@ -143,12 +143,12 @@ let monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'") monA.succeed( - "ceph osd pool create single-node-test 128 128", + "ceph osd pool create single-node-test 32 32", "ceph osd pool ls | grep 'single-node-test'", "ceph osd pool rename single-node-test single-node-other-test", "ceph osd pool ls | grep 'single-node-other-test'", ) - monA.wait_until_succeeds("ceph -s | grep '1 pools, 128 pgs'") + monA.wait_until_succeeds("ceph -s | grep '2 pools, 33 pgs'") monA.succeed( "ceph osd getcrushmap -o crush", "crushtool -d crush -o decrushed", @@ -158,7 +158,7 @@ let "ceph osd pool set single-node-other-test size 2", ) monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'") - monA.wait_until_succeeds("ceph -s | grep '128 active+clean'") + monA.wait_until_succeeds("ceph -s | grep '33 active+clean'") monA.fail( "ceph osd pool ls | grep 'multi-node-test'", "ceph osd pool delete single-node-other-test single-node-other-test --yes-i-really-really-mean-it", diff --git a/nixos/tests/common/auto.nix b/nixos/tests/common/auto.nix index 2c21a8d5167..da6b14e9f16 100644 --- a/nixos/tests/common/auto.nix +++ b/nixos/tests/common/auto.nix @@ -41,8 +41,8 @@ in config = mkIf cfg.enable { - services.xserver.displayManager.lightdm = { - enable = true; + services.xserver.displayManager = { + lightdm.enable = true; autoLogin = { enable = true; user = cfg.user; diff --git a/nixos/tests/consul.nix b/nixos/tests/consul.nix index 6600dae4770..ee85f1d0b91 100644 --- a/nixos/tests/consul.nix +++ b/nixos/tests/consul.nix @@ -55,30 +55,33 @@ let server = index: { pkgs, ... }: let - ip = builtins.elemAt allConsensusServerHosts index; + numConsensusServers = builtins.length allConsensusServerHosts; + thisConsensusServerHost = builtins.elemAt allConsensusServerHosts index; + ip = thisConsensusServerHost; # since we already use IPs to identify servers in { networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [ - { address = builtins.elemAt allConsensusServerHosts index; prefixLength = 16; } + { address = ip; prefixLength = 16; } ]; networking.firewall = firewallSettings; services.consul = - let - thisConsensusServerHost = builtins.elemAt allConsensusServerHosts index; - in assert builtins.elem thisConsensusServerHost allConsensusServerHosts; { enable = true; inherit webUi; extraConfig = defaultExtraConfig // { server = true; - bootstrap_expect = builtins.length allConsensusServerHosts; + bootstrap_expect = numConsensusServers; + # Tell Consul that we never intend to drop below this many servers. + # Ensures to not permanently lose consensus after temporary loss. + # See https://github.com/hashicorp/consul/issues/8118#issuecomment-645330040 + autopilot.min_quorum = numConsensusServers; retry_join = # If there's only 1 node in the network, we allow self-join; # otherwise, the node must not try to join itself, and join only the other servers. # See https://github.com/hashicorp/consul/issues/2868 - if builtins.length allConsensusServerHosts == 1 + if numConsensusServers == 1 then allConsensusServerHosts else builtins.filter (h: h != thisConsensusServerHost) allConsensusServerHosts; bind_addr = ip; @@ -104,40 +107,123 @@ in { for m in machines: m.wait_for_unit("consul.service") - for m in machines: - m.wait_until_succeeds("[ $(consul members | grep -o alive | wc -l) == 5 ]") + + def wait_for_healthy_servers(): + # See https://github.com/hashicorp/consul/issues/8118#issuecomment-645330040 + # for why the `Voter` column of `list-peers` has that info. + # TODO: The `grep true` relies on the fact that currently in + # the output like + # # consul operator raft list-peers + # Node ID Address State Voter RaftProtocol + # server3 ... 192.168.1.3:8300 leader true 3 + # server2 ... 192.168.1.2:8300 follower true 3 + # server1 ... 192.168.1.1:8300 follower false 3 + # `Voter`is the only boolean column. + # Change this to the more reliable way to be defined by + # https://github.com/hashicorp/consul/issues/8118 + # once that ticket is closed. + for m in machines: + m.wait_until_succeeds( + "[ $(consul operator raft list-peers | grep true | wc -l) == 3 ]" + ) + + + def wait_for_all_machines_alive(): + """ + Note that Serf-"alive" does not mean "Raft"-healthy; + see `wait_for_healthy_servers()` for that instead. + """ + for m in machines: + m.wait_until_succeeds("[ $(consul members | grep -o alive | wc -l) == 5 ]") + + + wait_for_healthy_servers() + # Also wait for clients to be alive. + wait_for_all_machines_alive() client1.succeed("consul kv put testkey 42") client2.succeed("[ $(consul kv get testkey) == 42 ]") - # Test that the cluster can tolearate failures of any single server: - for server in servers: - server.crash() - # For each client, wait until they have connection again - # using `kv get -recurse` before issuing commands. - client1.wait_until_succeeds("consul kv get -recurse") - client2.wait_until_succeeds("consul kv get -recurse") + def rolling_reboot_test(proper_rolling_procedure=True): + """ + Tests that the cluster can tolearate failures of any single server, + following the recommended rolling upgrade procedure from + https://www.consul.io/docs/upgrading#standard-upgrades. - # Do some consul actions while one server is down. - client1.succeed("consul kv put testkey 43") - client2.succeed("[ $(consul kv get testkey) == 43 ]") - client2.succeed("consul kv delete testkey") + Optionally, `proper_rolling_procedure=False` can be given + to wait only for each server to be back `Healthy`, not `Stable` + in the Raft consensus, see Consul setting `ServerStabilizationTime` and + https://github.com/hashicorp/consul/issues/8118#issuecomment-645330040. + """ + + for server in servers: + server.crash() + + # For each client, wait until they have connection again + # using `kv get -recurse` before issuing commands. + client1.wait_until_succeeds("consul kv get -recurse") + client2.wait_until_succeeds("consul kv get -recurse") - # Restart crashed machine. - server.start() + # Do some consul actions while one server is down. + client1.succeed("consul kv put testkey 43") + client2.succeed("[ $(consul kv get testkey) == 43 ]") + client2.succeed("consul kv delete testkey") + + # Restart crashed machine. + server.start() + + if proper_rolling_procedure: + # Wait for recovery. + wait_for_healthy_servers() + else: + # NOT proper rolling upgrade procedure, see above. + wait_for_all_machines_alive() + + # Wait for client connections. + client1.wait_until_succeeds("consul kv get -recurse") + client2.wait_until_succeeds("consul kv get -recurse") + + # Do some consul actions with server back up. + client1.succeed("consul kv put testkey 44") + client2.succeed("[ $(consul kv get testkey) == 44 ]") + client2.succeed("consul kv delete testkey") + + + def all_servers_crash_simultaneously_test(): + """ + Tests that the cluster will eventually come back after all + servers crash simultaneously. + """ + + for server in servers: + server.crash() + + for server in servers: + server.start() # Wait for recovery. - for m in machines: - m.wait_until_succeeds("[ $(consul members | grep -o alive | wc -l) == 5 ]") + wait_for_healthy_servers() # Wait for client connections. client1.wait_until_succeeds("consul kv get -recurse") client2.wait_until_succeeds("consul kv get -recurse") - # Do some consul actions with server back up. + # Do some consul actions with servers back up. client1.succeed("consul kv put testkey 44") client2.succeed("[ $(consul kv get testkey) == 44 ]") client2.succeed("consul kv delete testkey") + + + # Run the tests. + + print("rolling_reboot_test()") + rolling_reboot_test() + + print("all_servers_crash_simultaneously_test()") + all_servers_crash_simultaneously_test() + + print("rolling_reboot_test(proper_rolling_procedure=False)") + rolling_reboot_test(proper_rolling_procedure=False) ''; }) diff --git a/nixos/tests/containers-portforward.nix b/nixos/tests/containers-portforward.nix index fc90e151bd9..1e2c2c6c374 100644 --- a/nixos/tests/containers-portforward.nix +++ b/nixos/tests/containers-portforward.nix @@ -5,7 +5,7 @@ let hostPort = 10080; containerIp = "192.168.0.100"; containerPort = 80; -in +in import ./make-test-python.nix ({ pkgs, ...} : { name = "containers-portforward"; diff --git a/nixos/tests/convos.nix b/nixos/tests/convos.nix new file mode 100644 index 00000000000..b4ff1188fd8 --- /dev/null +++ b/nixos/tests/convos.nix @@ -0,0 +1,30 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: + +with lib; +let + port = 3333; +in +{ + name = "convos"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ sgo ]; + }; + + nodes = { + machine = + { pkgs, ... }: + { + services.convos = { + enable = true; + listenPort = port; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("convos") + machine.wait_for_open_port("${toString port}") + machine.succeed("journalctl -u convos | grep -q 'Listening at.*${toString port}'") + machine.succeed("curl http://localhost:${toString port}/") + ''; +}) diff --git a/nixos/tests/corerad.nix b/nixos/tests/corerad.nix index 741fa448f68..37a1e90477a 100644 --- a/nixos/tests/corerad.nix +++ b/nixos/tests/corerad.nix @@ -1,9 +1,9 @@ import ./make-test-python.nix ( { nodes = { - router = {config, pkgs, ...}: { + router = {config, pkgs, ...}: { config = { - # This machines simulates a router with IPv6 forwarding and a static IPv6 address. + # This machine simulates a router with IPv6 forwarding and a static IPv6 address. boot.kernel.sysctl = { "net.ipv6.conf.all.forwarding" = true; }; @@ -14,13 +14,25 @@ import ./make-test-python.nix ( enable = true; # Serve router advertisements to the client machine with prefix information matching # any IPv6 /64 prefixes configured on this interface. - configFile = pkgs.writeText "corerad.toml" '' - [[interfaces]] - name = "eth1" - advertise = true - [[interfaces.prefix]] - prefix = "::/64" - ''; + # + # This configuration is identical to the example in the CoreRAD NixOS module. + settings = { + interfaces = [ + { + name = "eth0"; + monitor = true; + } + { + name = "eth1"; + advertise = true; + prefix = [{ prefix = "::/64"; }]; + } + ]; + debug = { + address = "localhost:9430"; + prometheus = true; + }; + }; }; }; }; @@ -66,5 +78,12 @@ import ./make-test-python.nix ( assert ( "/64 scope global temporary" in addrs ), "SLAAC temporary address was not configured on client after router advertisement" + + with subtest("Verify HTTP debug server is configured"): + out = router.succeed("curl localhost:9430/metrics") + + assert ( + "corerad_build_info" in out + ), "Build info metric was not found in Prometheus output" ''; }) diff --git a/nixos/tests/dnscrypt-wrapper/default.nix b/nixos/tests/dnscrypt-wrapper/default.nix new file mode 100644 index 00000000000..1dc925f4de7 --- /dev/null +++ b/nixos/tests/dnscrypt-wrapper/default.nix @@ -0,0 +1,71 @@ +import ../make-test-python.nix ({ pkgs, ... }: { + name = "dnscrypt-wrapper"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ rnhmjoj ]; + }; + + nodes = { + server = { lib, ... }: + { services.dnscrypt-wrapper = with builtins; + { enable = true; + address = "192.168.1.1"; + 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); + }; + 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; } ]; + }; + + client = { lib, ... }: + { services.dnscrypt-proxy2.enable = true; + services.dnscrypt-proxy2.settings = { + server_names = [ "server" ]; + static.server.stamp = "sdns://AQAAAAAAAAAAEDE5Mi4xNjguMS4xOjUzNTMgFEHYOv0SCKSuqR5CDYa7-58cCBuXO2_5uTSVU9wNQF0WMi5kbnNjcnlwdC1jZXJ0LnNlcnZlcg"; + }; + networking.nameservers = [ "127.0.0.1" ]; + networking.interfaces.eth1.ipv4.addresses = lib.mkForce + [ { address = "192.168.1.2"; prefixLength = 24; } ]; + }; + + }; + + 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") + + 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.succeed( + "host it.works" + ), "The IP address of 'it.works' does not match 1.2.3.4" + + with subtest("The server rotates the ephemeral keys"): + # advance time by a little less than 5 days + server.succeed("date -s \"$(date --date '4 days 6 hours')\"") + client.succeed("date -s \"$(date --date '4 days 6 hours')\"") + 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") + ''; +}) + diff --git a/nixos/tests/dnscrypt-wrapper/public.key b/nixos/tests/dnscrypt-wrapper/public.key new file mode 100644 index 00000000000..80232b97f52 --- /dev/null +++ b/nixos/tests/dnscrypt-wrapper/public.key @@ -0,0 +1 @@ +AØ:ý¤®©B †»ûŸ—;où¹4•SÜ @] \ No newline at end of file diff --git a/nixos/tests/dnscrypt-wrapper/secret.key b/nixos/tests/dnscrypt-wrapper/secret.key new file mode 100644 index 00000000000..01fbf8e08b7 --- /dev/null +++ b/nixos/tests/dnscrypt-wrapper/secret.key @@ -0,0 +1 @@ +G½>Æ©» ì>Ðà¥(Ò²‡¼J•«º=Ÿ„ÝÁlìAØ:ý¤®©B †»ûŸ—;où¹4•SÜ @] \ No newline at end of file diff --git a/nixos/tests/docker-preloader.nix b/nixos/tests/docker-preloader.nix index eeedec9a392..c3e8aced351 100644 --- a/nixos/tests/docker-preloader.nix +++ b/nixos/tests/docker-preloader.nix @@ -16,10 +16,10 @@ import ./make-test.nix ({ pkgs, ...} : { services.openssh.extraConfig = "PermitEmptyPasswords yes"; users.extraUsers.root.password = ""; }; - }; + }; testScript = '' startAll; - + $docker->waitForUnit("sockets.target"); $docker->succeed("docker run nix nix-store --version"); $docker->succeed("docker run bash bash --version"); diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix index 51b472fcf9c..2543801ae8b 100644 --- a/nixos/tests/docker-tools.nix +++ b/nixos/tests/docker-tools.nix @@ -30,8 +30,45 @@ import ./make-test-python.nix ({ pkgs, ... }: { ) docker.succeed("docker run --rm ${examples.bash.imageName} bash --version") + # Check imageTag attribute matches image + docker.succeed("docker images --format '{{.Tag}}' | grep -F '${examples.bash.imageTag}'") docker.succeed("docker rmi ${examples.bash.imageName}") + # The remaining combinations + with subtest("Ensure imageTag attribute matches image"): + docker.succeed( + "docker load --input='${examples.bashNoTag}'" + ) + docker.succeed( + "docker images --format '{{.Tag}}' | grep -F '${examples.bashNoTag.imageTag}'" + ) + docker.succeed("docker rmi ${examples.bashNoTag.imageName}:${examples.bashNoTag.imageTag}") + + docker.succeed( + "docker load --input='${examples.bashNoTagLayered}'" + ) + docker.succeed( + "docker images --format '{{.Tag}}' | grep -F '${examples.bashNoTagLayered.imageTag}'" + ) + docker.succeed("docker rmi ${examples.bashNoTagLayered.imageName}:${examples.bashNoTagLayered.imageTag}") + + docker.succeed( + "${examples.bashNoTagStreamLayered} | docker load" + ) + docker.succeed( + "docker images --format '{{.Tag}}' | grep -F '${examples.bashNoTagStreamLayered.imageTag}'" + ) + docker.succeed( + "docker rmi ${examples.bashNoTagStreamLayered.imageName}:${examples.bashNoTagStreamLayered.imageTag}" + ) + + docker.succeed( + "docker load --input='${examples.nixLayered}'" + ) + docker.succeed("docker images --format '{{.Tag}}' | grep -F '${examples.nixLayered.imageTag}'") + docker.succeed("docker rmi ${examples.nixLayered.imageName}") + + with subtest( "Check if the nix store is correctly initialized by listing " "dependencies of the installed Nix binary" @@ -42,6 +79,30 @@ import ./make-test-python.nix ({ pkgs, ... }: { "docker rmi ${examples.nix.imageName}", ) + with subtest( + "Ensure (layered) nix store has correct permissions " + "and that the container starts when its process does not have uid 0" + ): + docker.succeed( + "docker load --input='${examples.bashLayeredWithUser}'", + "docker run -u somebody --rm ${examples.bashLayeredWithUser.imageName} ${pkgs.bash}/bin/bash -c 'test 555 == $(stat --format=%a /nix) && test 555 == $(stat --format=%a /nix/store)'", + "docker rmi ${examples.bashLayeredWithUser.imageName}", + ) + + with subtest("The nix binary symlinks are intact"): + docker.succeed( + "docker load --input='${examples.nix}'", + "docker run --rm ${examples.nix.imageName} ${pkgs.bash}/bin/bash -c 'test nix == $(readlink ${pkgs.nix}/bin/nix-daemon)'", + "docker rmi ${examples.nix.imageName}", + ) + + with subtest("The nix binary symlinks are intact when the image is layered"): + docker.succeed( + "docker load --input='${examples.nixLayered}'", + "docker run --rm ${examples.nixLayered.imageName} ${pkgs.bash}/bin/bash -c 'test nix == $(readlink ${pkgs.nix}/bin/nix-daemon)'", + "docker rmi ${examples.nixLayered.imageName}", + ) + with subtest("The pullImage tool works"): docker.succeed( "docker load --input='${examples.nixFromDockerHub}'", @@ -76,13 +137,22 @@ import ./make-test-python.nix ({ pkgs, ... }: { with subtest("Ensure Docker images can use an unstable date"): docker.succeed( - "docker load --input='${examples.bash}'" + "docker load --input='${examples.unstableDate}'" ) assert unix_time_second1 not in docker.succeed( "docker inspect ${examples.unstableDate.imageName} " + "| ${pkgs.jq}/bin/jq -r .[].Created" ) + with subtest("Ensure Layered Docker images can use an unstable date"): + docker.succeed( + "docker load --input='${examples.unstableDateLayered}'" + ) + assert unix_time_second1 not in docker.succeed( + "docker inspect ${examples.unstableDateLayered.imageName} " + + "| ${pkgs.jq}/bin/jq -r .[].Created" + ) + with subtest("Ensure Layered Docker images work"): docker.succeed( "docker load --input='${examples.layered-image}'", @@ -124,6 +194,16 @@ import ./make-test-python.nix ({ pkgs, ... }: { f"docker run --rm ${examples.layersOrder.imageName} cat /tmp/layer{index}" ) + with subtest("Ensure environment variables are correctly inherited"): + docker.succeed( + "docker load --input='${examples.environmentVariables}'" + ) + out = docker.succeed("docker run --rm ${examples.environmentVariables.imageName} env") + env = out.splitlines() + assert "FROM_PARENT=true" in env, "envvars from the parent should be preserved" + assert "FROM_CHILD=true" in env, "envvars from the child should be preserved" + assert "LAST_LAYER=child" in env, "envvars from the child should take priority" + with subtest("Ensure image with only 2 layers can be loaded"): docker.succeed( "docker load --input='${examples.two-layered-image}'" @@ -154,5 +234,12 @@ import ./make-test-python.nix ({ pkgs, ... }: { # This check may be loosened to allow an *empty* store rather than *no* store. docker.succeed("docker run --rm no-store-paths ls /") docker.fail("docker run --rm no-store-paths ls /nix/store") + + with subtest("Ensure buildLayeredImage does not change store path contents."): + docker.succeed( + "docker load --input='${pkgs.dockerTools.examples.filesInStore}'", + "docker run --rm file-in-store nix-store --verify --check-contents", + "docker run --rm file-in-store |& grep 'some data'", + ) ''; }) diff --git a/nixos/tests/docker.nix b/nixos/tests/docker.nix index 8fda7c1395e..a4a61468f33 100644 --- a/nixos/tests/docker.nix +++ b/nixos/tests/docker.nix @@ -43,7 +43,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { docker.fail("sudo -u noprivs docker ps") docker.succeed("docker stop sleeping") - # Must match version twice to ensure client and server versions are correct - docker.succeed('[ $(docker version | grep ${pkgs.docker.version} | wc -l) = "2" ]') + # Must match version 4 times to ensure client and server git commits and versions are correct + docker.succeed('[ $(docker version | grep ${pkgs.docker.version} | wc -l) = "4" ]') ''; }) diff --git a/nixos/tests/dokuwiki.nix b/nixos/tests/dokuwiki.nix index 05271919eff..58069366ca3 100644 --- a/nixos/tests/dokuwiki.nix +++ b/nixos/tests/dokuwiki.nix @@ -32,24 +32,17 @@ let in { name = "dokuwiki"; - meta.maintainers = with pkgs.lib.maintainers; [ "1000101" ]; - + meta = with pkgs.stdenv.lib; { + maintainers = with maintainers; [ _1000101 ]; + }; machine = { ... }: { services.dokuwiki."site1.local" = { aclUse = false; superUser = "admin"; - nginx = { - forceSSL = false; - enableACME = false; - }; }; services.dokuwiki."site2.local" = { - aclUse = true; + usersFile = "/var/lib/dokuwiki/site2.local/users.auth.php"; superUser = "admin"; - nginx = { - forceSSL = false; - enableACME = false; - }; templates = [ template-bootstrap3 ]; plugins = [ plugin-icalevents ]; }; @@ -69,6 +62,15 @@ in { machine.wait_for_open_port(80) machine.succeed("curl -sSfL http://site1.local/ | grep 'DokuWiki'") + machine.fail("curl -sSfL 'http://site1.local/doku.php?do=login' | grep 'Login'") + machine.succeed("curl -sSfL http://site2.local/ | grep 'DokuWiki'") + machine.succeed("curl -sSfL 'http://site2.local/doku.php?do=login' | grep 'Login'") + + machine.succeed( + "echo 'admin:$2y$10$ijdBQMzSVV20SrKtCna8gue36vnsbVm2wItAXvdm876sshI4uwy6S:Admin:admin@example.test:user' >> /var/lib/dokuwiki/site2.local/users.auth.php", + "curl -sSfL -d 'u=admin&p=password' --cookie-jar cjar 'http://site2.local/doku.php?do=login'", + "curl -sSfL --cookie cjar --cookie-jar cjar 'http://site2.local/doku.php?do=login' | grep 'Logged in as: <bdi>Admin</bdi>'", + ) ''; }) diff --git a/nixos/tests/engelsystem.nix b/nixos/tests/engelsystem.nix new file mode 100644 index 00000000000..39c10718093 --- /dev/null +++ b/nixos/tests/engelsystem.nix @@ -0,0 +1,41 @@ +import ./make-test-python.nix ( + { pkgs, lib, ... }: + { + name = "engelsystem"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ talyz ]; + }; + + nodes.engelsystem = + { ... }: + { + services.engelsystem = { + enable = true; + domain = "engelsystem"; + createDatabase = true; + }; + networking.firewall.allowedTCPPorts = [ 80 443 ]; + environment.systemPackages = with pkgs; [ + xmlstarlet + libxml2 + ]; + }; + + testScript = '' + engelsystem.start() + engelsystem.wait_for_unit("phpfpm-engelsystem.service") + engelsystem.wait_until_succeeds("curl engelsystem/login -sS -f") + engelsystem.succeed( + "curl engelsystem/login -sS -f -c cookie | xmllint -html -xmlout - >login" + ) + engelsystem.succeed( + "xml sel -T -t -m \"html/head/meta[@name='csrf-token']\" -v @content login >token" + ) + engelsystem.succeed( + "curl engelsystem/login -sS -f -b cookie -F 'login=admin' -F 'password=asdfasdf' -F '_token=<token' -L | xmllint -html -xmlout - >news" + ) + engelsystem.succeed( + "test 'News - Engelsystem' = \"$(xml sel -T -t -c html/head/title news)\"" + ) + ''; + }) diff --git a/nixos/tests/enlightenment.nix b/nixos/tests/enlightenment.nix index 5fa8d765dd1..0132b98b1cb 100644 --- a/nixos/tests/enlightenment.nix +++ b/nixos/tests/enlightenment.nix @@ -41,28 +41,24 @@ import ./make-test-python.nix ({ pkgs, ...} : with subtest("First time wizard"): machine.wait_for_text("Default") # Language - machine.succeed("xdotool mousemove 512 185 click 1") # Default Language machine.screenshot("wizard1") machine.succeed("xdotool mousemove 512 740 click 1") # Next - - machine.wait_for_text("English") # Keyboard (default) machine.screenshot("wizard2") - machine.succeed("xdotool mousemove 512 740 click 1") # Next - machine.wait_for_text("Standard") # Profile (default) + machine.wait_for_text("English") # Keyboard (default) machine.screenshot("wizard3") machine.succeed("xdotool mousemove 512 740 click 1") # Next - machine.wait_for_text("Title") # Sizing (default) + machine.wait_for_text("Standard") # Profile (default) machine.screenshot("wizard4") machine.succeed("xdotool mousemove 512 740 click 1") # Next - machine.wait_for_text("clicked") # Windows Phocus - machine.succeed("xdotool mousemove 512 370 click 1") # Click + machine.wait_for_text("Title") # Sizing (default) machine.screenshot("wizard5") machine.succeed("xdotool mousemove 512 740 click 1") # Next - machine.wait_for_text("bindings") # Mouse Modifiers (default) + machine.wait_for_text("clicked") # Windows Focus + machine.succeed("xdotool mousemove 512 370 click 1") # Click machine.screenshot("wizard6") machine.succeed("xdotool mousemove 512 740 click 1") # Next @@ -74,7 +70,7 @@ import ./make-test-python.nix ({ pkgs, ...} : machine.screenshot("wizard8") machine.succeed("xdotool mousemove 512 740 click 1") # Next - machine.wait_for_text("Compositing") # Compositing (default) + machine.wait_for_text("OpenGL") # Compositing (default) machine.screenshot("wizard9") machine.succeed("xdotool mousemove 512 740 click 1") # Next diff --git a/nixos/tests/ergo.nix b/nixos/tests/ergo.nix new file mode 100644 index 00000000000..8cdbbf62a95 --- /dev/null +++ b/nixos/tests/ergo.nix @@ -0,0 +1,18 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "ergo"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ mmahut ]; + }; + + nodes = { + machine = { ... }: { + services.ergo.enable = true; + services.ergo.api.keyHash = "324dcf027dd4a30a932c441f365a25e86b173defa4b8e58948253471b81b72cf"; + }; + }; + + testScript = '' + start_all() + machine.wait_for_unit("ergo.service") + ''; +}) diff --git a/nixos/tests/firejail.nix b/nixos/tests/firejail.nix new file mode 100644 index 00000000000..a723cb01664 --- /dev/null +++ b/nixos/tests/firejail.nix @@ -0,0 +1,82 @@ +import ./make-test-python.nix ({ pkgs, ...} : { + name = "firejail"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ sgo ]; + }; + + nodes.machine = { ... }: { + imports = [ ./common/user-account.nix ]; + + programs.firejail = { + enable = true; + wrappedBinaries = { + bash-jailed = "${pkgs.bash}/bin/bash"; + }; + }; + + systemd.services.setupFirejailTest = { + wantedBy = [ "multi-user.target" ]; + before = [ "multi-user.target" ]; + + environment = { + HOME = "/home/alice"; + }; + + unitConfig = { + type = "oneshot"; + RemainAfterExit = true; + user = "alice"; + }; + + script = '' + cd $HOME + + mkdir .password-store && echo s3cret > .password-store/secret + mkdir my-secrets && echo s3cret > my-secrets/secret + + echo publ1c > public + + mkdir -p .config/firejail + echo 'blacklist ''${HOME}/my-secrets' > .config/firejail/globals.local + ''; + }; + }; + + testScript = '' + start_all() + machine.wait_for_unit("multi-user.target") + + # Test path acl with wrapper + machine.succeed("sudo -u alice bash-jailed -c 'cat ~/public' | grep -q publ1c") + machine.fail( + "sudo -u alice bash-jailed -c 'cat ~/.password-store/secret' | grep -q s3cret" + ) + machine.fail("sudo -u alice bash-jailed -c 'cat ~/my-secrets/secret' | grep -q s3cret") + + + # Test path acl with firejail executable + machine.succeed("sudo -u alice firejail -- bash -c 'cat ~/public' | grep -q publ1c") + machine.fail( + "sudo -u alice firejail -- bash -c 'cat ~/.password-store/secret' | grep -q s3cret" + ) + machine.fail( + "sudo -u alice firejail -- bash -c 'cat ~/my-secrets/secret' | grep -q s3cret" + ) + + # Disabling profiles + machine.succeed( + "sudo -u alice bash -c 'firejail --noprofile -- cat ~/.password-store/secret' | grep -q s3cret" + ) + + # CVE-2020-17367 + machine.fail( + "sudo -u alice firejail --private-tmp id --output=/tmp/vuln1 && cat /tmp/vuln1" + ) + + # CVE-2020-17368 + machine.fail( + "sudo -u alice firejail --private-tmp --output=/tmp/foo 'bash -c $(id>/tmp/vuln2;echo id)' && cat /tmp/vuln2" + ) + ''; +}) + diff --git a/nixos/tests/gnome3-xorg.nix b/nixos/tests/gnome3-xorg.nix index b59badcd5de..0d05c12384f 100644 --- a/nixos/tests/gnome3-xorg.nix +++ b/nixos/tests/gnome3-xorg.nix @@ -12,8 +12,9 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { services.xserver.enable = true; - services.xserver.displayManager.gdm = { - enable = true; + services.xserver.displayManager = { + gdm.enable = true; + gdm.debug = true; autoLogin = { enable = true; user = user.name; @@ -21,6 +22,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { }; services.xserver.desktopManager.gnome3.enable = true; + services.xserver.desktopManager.gnome3.debug = true; services.xserver.displayManager.defaultSession = "gnome-xorg"; virtualisation.memorySize = 1024; diff --git a/nixos/tests/gnome3.nix b/nixos/tests/gnome3.nix index 17e72c5f651..b3d7aff8bd7 100644 --- a/nixos/tests/gnome3.nix +++ b/nixos/tests/gnome3.nix @@ -11,8 +11,9 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { services.xserver.enable = true; - services.xserver.displayManager.gdm = { - enable = true; + services.xserver.displayManager = { + gdm.enable = true; + gdm.debug = true; autoLogin = { enable = true; user = "alice"; @@ -20,6 +21,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : { }; services.xserver.desktopManager.gnome3.enable = true; + services.xserver.desktopManager.gnome3.debug = true; virtualisation.memorySize = 1024; }; diff --git a/nixos/tests/go-neb.nix b/nixos/tests/go-neb.nix new file mode 100644 index 00000000000..d9e5db0b4a5 --- /dev/null +++ b/nixos/tests/go-neb.nix @@ -0,0 +1,44 @@ +import ./make-test-python.nix ({ pkgs, ... }: +{ + name = "go-neb"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ hexa maralorn ]; + }; + + nodes = { + server = { + services.go-neb = { + enable = true; + baseUrl = "http://localhost"; + config = { + clients = [ { + UserId = "@test:localhost"; + AccessToken = "changeme"; + HomeServerUrl = "http://localhost"; + Sync = false; + AutoJoinRooms = false; + DisplayName = "neverbeseen"; + } ]; + services = [ { + ID = "wikipedia_service"; + Type = "wikipedia"; + UserID = "@test:localhost"; + Config = { }; + } ]; + }; + }; + }; + }; + + testScript = '' + start_all() + server.wait_for_unit("go-neb.service") + server.wait_until_succeeds( + "curl -L http://localhost:4050/services/hooks/d2lraXBlZGlhX3NlcnZpY2U" + ) + server.wait_until_succeeds( + "journalctl -eu go-neb -o cat | grep -q service_id=wikipedia_service" + ) + ''; + +}) diff --git a/nixos/tests/graphite.nix b/nixos/tests/graphite.nix index 71776a94cbd..137be2d89c8 100644 --- a/nixos/tests/graphite.nix +++ b/nixos/tests/graphite.nix @@ -1,11 +1,6 @@ import ./make-test-python.nix ({ pkgs, ... } : { name = "graphite"; - meta = { - # Fails on dependency `python-2.7-Twisted`'s test suite - # complaining `ImportError: No module named zope.interface`. - broken = true; - }; nodes = { one = { ... }: { @@ -21,7 +16,7 @@ import ./make-test-python.nix ({ pkgs, ... } : api = { enable = true; port = 8082; - finders = [ pkgs.python3Packages.influxgraph ]; + finders = [ ]; }; carbon.enableCache = true; seyren.enable = false; # Implicitely requires openssl-1.0.2u which is marked insecure @@ -41,10 +36,14 @@ import ./make-test-python.nix ({ pkgs, ... } : # even if they're still in preStart (which takes quite long for graphiteWeb). # Wait for ports to open so we're sure the services are up and listening. one.wait_for_open_port(8080) + one.wait_for_open_port(8082) one.wait_for_open_port(2003) one.succeed('echo "foo 1 `date +%s`" | nc -N localhost 2003') one.wait_until_succeeds( "curl 'http://localhost:8080/metrics/find/?query=foo&format=treejson' --silent | grep foo >&2" ) + one.wait_until_succeeds( + "curl 'http://localhost:8082/metrics/find/?query=foo&format=treejson' --silent | grep foo >&2" + ) ''; }) diff --git a/nixos/tests/grub.nix b/nixos/tests/grub.nix new file mode 100644 index 00000000000..84bfc90955b --- /dev/null +++ b/nixos/tests/grub.nix @@ -0,0 +1,60 @@ +import ./make-test-python.nix ({ lib, ... }: { + name = "grub"; + + meta = with lib.maintainers; { + maintainers = [ rnhmjoj ]; + }; + + machine = { ... }: { + virtualisation.useBootLoader = true; + + boot.loader.timeout = null; + boot.loader.grub = { + enable = true; + users.alice.password = "supersecret"; + + # OCR is not accurate enough + extraConfig = "serial; terminal_output serial"; + }; + }; + + testScript = '' + def grub_login_as(user, password): + """ + Enters user and password to log into GRUB + """ + machine.wait_for_console_text("Enter username:") + machine.send_chars(user + "\n") + machine.wait_for_console_text("Enter password:") + machine.send_chars(password + "\n") + + + def grub_select_all_configurations(): + """ + Selects "All configurations" from the GRUB menu + to trigger a login request. + """ + machine.send_monitor_command("sendkey down") + machine.send_monitor_command("sendkey ret") + + + machine.start() + + # wait for grub screen + machine.wait_for_console_text("GNU GRUB") + + grub_select_all_configurations() + with subtest("Invalid credentials are rejected"): + grub_login_as("wronguser", "wrongsecret") + machine.wait_for_console_text("error: access denied.") + + grub_select_all_configurations() + with subtest("Valid credentials are accepted"): + grub_login_as("alice", "supersecret") + machine.send_chars("\n") # press enter to boot + machine.wait_for_console_text("Linux version") + + with subtest("Machine boots correctly"): + machine.wait_for_unit("multi-user.target") + ''; +}) diff --git a/nixos/tests/haproxy.nix b/nixos/tests/haproxy.nix index 79f34b07faf..ffb77c052a2 100644 --- a/nixos/tests/haproxy.nix +++ b/nixos/tests/haproxy.nix @@ -43,5 +43,13 @@ import ./make-test-python.nix ({ pkgs, ...}: { assert "haproxy_process_pool_allocated_bytes" in machine.succeed( "curl -k http://localhost:80/metrics" ) + + with subtest("reload"): + machine.succeed("systemctl reload haproxy") + # wait some time to ensure the following request hits the reloaded haproxy + machine.sleep(5) + assert "We are all good!" in machine.succeed( + "curl -k http://localhost:80/index.txt" + ) ''; }) diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix index 80dca43f1f3..a93a28d877a 100644 --- a/nixos/tests/home-assistant.nix +++ b/nixos/tests/home-assistant.nix @@ -2,69 +2,66 @@ import ./make-test-python.nix ({ pkgs, ... }: let configDir = "/var/lib/foobar"; - apiPassword = "some_secret"; - mqttPassword = "another_secret"; - hassCli = "hass-cli --server http://hass:8123 --password '${apiPassword}'"; + mqttUsername = "homeassistant"; + mqttPassword = "secret"; in { name = "home-assistant"; meta = with pkgs.stdenv.lib; { maintainers = with maintainers; [ dotlambda ]; }; - nodes = { - hass = - { pkgs, ... }: - { - environment.systemPackages = with pkgs; [ - mosquitto home-assistant-cli - ]; - services.home-assistant = { - inherit configDir; - enable = true; - package = pkgs.home-assistant.override { - extraPackages = ps: with ps; [ hbmqtt ]; - }; - config = { - homeassistant = { - name = "Home"; - time_zone = "UTC"; - latitude = "0.0"; - longitude = "0.0"; - elevation = 0; - auth_providers = [ - { - type = "legacy_api_password"; - api_password = apiPassword; - } - ]; - }; - frontend = { }; - mqtt = { # Use hbmqtt as broker - password = mqttPassword; - }; - binary_sensor = [ - { - platform = "mqtt"; - state_topic = "home-assistant/test"; - payload_on = "let_there_be_light"; - payload_off = "off"; - } - ]; - }; - lovelaceConfig = { - title = "My Awesome Home"; - views = [ { - title = "Example"; - cards = [ { - type = "markdown"; - title = "Lovelace"; - content = "Welcome to your **Lovelace UI**."; - } ]; - } ]; - }; - lovelaceConfigWritable = true; + nodes.hass = { pkgs, ... }: { + environment.systemPackages = with pkgs; [ mosquitto ]; + services.mosquitto = { + enable = true; + users = { + "${mqttUsername}" = { + acl = [ "pattern readwrite #" ]; + password = mqttPassword; }; }; + }; + services.home-assistant = { + inherit configDir; + enable = true; + config = { + homeassistant = { + name = "Home"; + time_zone = "UTC"; + latitude = "0.0"; + longitude = "0.0"; + elevation = 0; + }; + frontend = {}; + mqtt = { + broker = "127.0.0.1"; + username = mqttUsername; + password = mqttPassword; + }; + binary_sensor = [{ + platform = "mqtt"; + state_topic = "home-assistant/test"; + payload_on = "let_there_be_light"; + payload_off = "off"; + }]; + logger = { + default = "info"; + logs."homeassistant.components.mqtt" = "debug"; + }; + }; + lovelaceConfig = { + title = "My Awesome Home"; + views = [{ + title = "Example"; + cards = [{ + type = "markdown"; + title = "Lovelace"; + content = "Welcome to your **Lovelace UI**."; + }]; + }]; + }; + lovelaceConfigWritable = true; + }; }; testScript = '' @@ -76,29 +73,14 @@ in { hass.succeed("test -f ${configDir}/ui-lovelace.yaml") with subtest("Check that Home Assistant's web interface and API can be reached"): hass.wait_for_open_port(8123) - hass.succeed("curl --fail http://localhost:8123/states") - assert "API running" in hass.succeed( - "curl --fail -H 'x-ha-access: ${apiPassword}' http://localhost:8123/api/" - ) + hass.succeed("curl --fail http://localhost:8123/lovelace") with subtest("Toggle a binary sensor using MQTT"): - assert '"state": "off"' in hass.succeed( - "curl http://localhost:8123/api/states/binary_sensor.mqtt_binary_sensor -H 'x-ha-access: ${apiPassword}'" - ) + # wait for broker to become available hass.wait_until_succeeds( - "mosquitto_pub -V mqttv311 -t home-assistant/test -u homeassistant -P '${mqttPassword}' -m let_there_be_light" - ) - assert '"state": "on"' in hass.succeed( - "curl http://localhost:8123/api/states/binary_sensor.mqtt_binary_sensor -H 'x-ha-access: ${apiPassword}'" - ) - with subtest("Toggle a binary sensor using hass-cli"): - assert '"state": "on"' in hass.succeed( - "${hassCli} --output json state get binary_sensor.mqtt_binary_sensor" + "mosquitto_sub -V mqttv311 -t home-assistant/test -u ${mqttUsername} -P '${mqttPassword}' -W 1 -t '*'" ) hass.succeed( - "${hassCli} state edit binary_sensor.mqtt_binary_sensor --json='{\"state\": \"off\"}'" - ) - assert '"state": "off"' in hass.succeed( - "curl http://localhost:8123/api/states/binary_sensor.mqtt_binary_sensor -H 'x-ha-access: ${apiPassword}'" + "mosquitto_pub -V mqttv311 -t home-assistant/test -u ${mqttUsername} -P '${mqttPassword}' -m let_there_be_light" ) with subtest("Print log to ease debugging"): output_log = hass.succeed("cat ${configDir}/home-assistant.log") @@ -107,5 +89,9 @@ in { with subtest("Check that no errors were logged"): assert "ERROR" not in output_log + + # example line: 2020-06-20 10:01:32 DEBUG (MainThread) [homeassistant.components.mqtt] Received message on home-assistant/test: b'let_there_be_light' + with subtest("Check we received the mosquitto message"): + assert "let_there_be_light" in output_log ''; }) diff --git a/nixos/tests/hostname.nix b/nixos/tests/hostname.nix new file mode 100644 index 00000000000..3b87303d73e --- /dev/null +++ b/nixos/tests/hostname.nix @@ -0,0 +1,66 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing-python.nix { inherit system pkgs; }; +with pkgs.lib; + +let + makeHostNameTest = hostName: domain: + let + fqdn = hostName + (optionalString (domain != null) ".${domain}"); + in + makeTest { + name = "hostname-${fqdn}"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ primeos blitz ]; + }; + + machine = { lib, ... }: { + networking.hostName = hostName; + networking.domain = domain; + + environment.systemPackages = with pkgs; [ + inetutils + ]; + }; + + testScript = '' + start_all() + + machine = ${hostName} + + machine.wait_for_unit("network-online.target") + + # The FQDN, domain name, and hostname detection should work as expected: + assert "${fqdn}" == machine.succeed("hostname --fqdn").strip() + assert "${optionalString (domain != null) domain}" == machine.succeed("dnsdomainname").strip() + assert ( + "${hostName}" + == machine.succeed( + 'hostnamectl status | grep "Static hostname" | cut -d: -f2' + ).strip() + ) + + # 127.0.0.1 and ::1 should resolve back to "localhost": + assert ( + "localhost" == machine.succeed("getent hosts 127.0.0.1 | awk '{print $2}'").strip() + ) + assert "localhost" == machine.succeed("getent hosts ::1 | awk '{print $2}'").strip() + + # 127.0.0.2 should resolve back to the FQDN and hostname: + fqdn_and_host_name = "${optionalString (domain != null) "${hostName}.${domain} "}${hostName}" + assert ( + fqdn_and_host_name + == machine.succeed("getent hosts 127.0.0.2 | awk '{print $2,$3}'").strip() + ) + ''; + }; + +in +{ + noExplicitDomain = makeHostNameTest "ahost" null; + + explicitDomain = makeHostNameTest "ahost" "adomain"; +} diff --git a/nixos/tests/hydra/common.nix b/nixos/tests/hydra/common.nix index f612717dc96..312c52e889a 100644 --- a/nixos/tests/hydra/common.nix +++ b/nixos/tests/hydra/common.nix @@ -37,6 +37,7 @@ }; services.postfix.enable = true; nix = { + distributedBuilds = true; buildMachines = [{ hostName = "localhost"; systems = [ system ]; diff --git a/nixos/tests/hydra/db-migration.nix b/nixos/tests/hydra/db-migration.nix index cf74acfd67a..ca65e2e66aa 100644 --- a/nixos/tests/hydra/db-migration.nix +++ b/nixos/tests/hydra/db-migration.nix @@ -61,7 +61,7 @@ with pkgs.lib; 'curl -L -s http://localhost:3000/build/1 -H "Accept: application/json" | jq .buildstatus | xargs test 0 -eq' ) - out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ jobs\" -A'") + out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ builds\" -A'") assert "jobset_id" not in out original.succeed( @@ -69,7 +69,7 @@ with pkgs.lib; ) original.wait_for_unit("hydra-init.service") - out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ jobs\" -A'") + out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ builds\" -A'") assert "jobset_id|integer|||" in out original.succeed("hydra-backfill-ids") @@ -79,7 +79,7 @@ with pkgs.lib; ) original.wait_for_unit("hydra-init.service") - out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ jobs\" -A'") + out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ builds\" -A'") assert "jobset_id|integer||not null|" in out original.wait_until_succeeds( diff --git a/nixos/tests/ihatemoney.nix b/nixos/tests/ihatemoney.nix index 7df0ea0b691..0451a450580 100644 --- a/nixos/tests/ihatemoney.nix +++ b/nixos/tests/ihatemoney.nix @@ -1,5 +1,11 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + let - f = backend: import ./make-test-python.nix ({ pkgs, ... }: { + inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest; + f = backend: makeTest { name = "ihatemoney-${backend}"; machine = { lib, ... }: { services.ihatemoney = { @@ -24,9 +30,10 @@ let testScript = '' machine.wait_for_open_port(8000) machine.wait_for_unit("uwsgi.service") + machine.wait_until_succeeds("curl http://localhost:8000") assert '"yay"' in machine.succeed( - "curl -X POST http://localhost:8000/api/projects -d 'name=yay&id=yay&password=yay&contact_email=yay\@example.com'" + "curl -X POST http://localhost:8000/api/projects -d 'name=yay&id=yay&password=yay&contact_email=yay@example.com'" ) owner, timestamp = machine.succeed( "stat --printf %U:%G___%Y /var/lib/ihatemoney/secret_key" @@ -48,7 +55,7 @@ let assert "ihatemoney" in machine.succeed("curl http://localhost:8000") ''; - }); + }; in { ihatemoney-sqlite = f "sqlite"; ihatemoney-postgresql = f "postgresql"; diff --git a/nixos/tests/initrd-network-openvpn/default.nix b/nixos/tests/initrd-network-openvpn/default.nix new file mode 100644 index 00000000000..bb4c41e6d70 --- /dev/null +++ b/nixos/tests/initrd-network-openvpn/default.nix @@ -0,0 +1,145 @@ +import ../make-test-python.nix ({ lib, ...}: + +{ + name = "initrd-network-openvpn"; + + nodes = + let + + # Inlining of the shared secret for the + # OpenVPN server and client + secretblock = '' + secret [inline] + <secret> + ${lib.readFile ./shared.key} + </secret> + ''; + + in + { + + # Minimal test case to check a successful boot, even with invalid config + minimalboot = + { ... }: + { + boot.initrd.network = { + enable = true; + openvpn = { + enable = true; + configuration = "/dev/null"; + }; + }; + }; + + # initrd VPN client + ovpnclient = + { ... }: + { + virtualisation.useBootLoader = true; + virtualisation.vlans = [ 1 ]; + + boot.initrd = { + # This command does not fork to keep the VM in the state where + # only the initramfs is loaded + preLVMCommands = + '' + /bin/nc -p 1234 -lke /bin/echo TESTVALUE + ''; + + network = { + enable = true; + + # Work around udhcpc only getting a lease on eth0 + postCommands = '' + /bin/ip addr add 192.168.1.2/24 dev eth1 + ''; + + # Example configuration for OpenVPN + # This is the main reason for this test + openvpn = { + enable = true; + configuration = "${./initrd.ovpn}"; + }; + }; + }; + }; + + # VPN server and gateway for ovpnclient between vlan 1 and 2 + ovpnserver = + { ... }: + { + virtualisation.vlans = [ 1 2 ]; + + # Enable NAT and forward port 12345 to port 1234 + networking.nat = { + enable = true; + internalInterfaces = [ "tun0" ]; + externalInterface = "eth2"; + forwardPorts = [ { destination = "10.8.0.2:1234"; + sourcePort = 12345; } ]; + }; + + # Trust tun0 and allow the VPN Server to be reached + networking.firewall = { + trustedInterfaces = [ "tun0" ]; + allowedUDPPorts = [ 1194 ]; + }; + + # Minimal OpenVPN server configuration + services.openvpn.servers.testserver = + { + config = '' + dev tun0 + ifconfig 10.8.0.1 10.8.0.2 + ${secretblock} + ''; + }; + }; + + # Client that resides in the "external" VLAN + testclient = + { ... }: + { + virtualisation.vlans = [ 2 ]; + }; + }; + + + testScript = + '' + # Minimal test case, checks whether enabling (with invalid config) harms + # the boot process + with subtest("Check for successful boot with broken openvpn config"): + minimalboot.start() + # If we get to multi-user.target, we booted successfully + minimalboot.wait_for_unit("multi-user.target") + minimalboot.shutdown() + + # Elaborated test case where the ovpnclient (where this module is used) + # can be reached by testclient only over ovpnserver. + # This is an indirect test for success. + with subtest("Check for connection from initrd VPN client, config as file"): + ovpnserver.start() + testclient.start() + ovpnclient.start() + + # Wait until the OpenVPN Server is available + ovpnserver.wait_for_unit("openvpn-testserver.service") + ovpnserver.succeed("ping -c 1 10.8.0.1") + + # Wait for the client to connect + ovpnserver.wait_until_succeeds("ping -c 1 10.8.0.2") + + # Wait until the testclient has network + testclient.wait_for_unit("network.target") + + # Check that ovpnclient is reachable over vlan 1 + ovpnserver.succeed("nc -w 2 192.168.1.2 1234 | grep -q TESTVALUE") + + # Check that ovpnclient is reachable over tun0 + ovpnserver.succeed("nc -w 2 10.8.0.2 1234 | grep -q TESTVALUE") + + # Check that ovpnclient is reachable from testclient over the gateway + testclient.succeed("nc -w 2 192.168.2.3 12345 | grep -q TESTVALUE") + ''; +}) diff --git a/nixos/tests/initrd-network-openvpn/initrd.ovpn b/nixos/tests/initrd-network-openvpn/initrd.ovpn new file mode 100644 index 00000000000..5926a48af00 --- /dev/null +++ b/nixos/tests/initrd-network-openvpn/initrd.ovpn @@ -0,0 +1,29 @@ +remote 192.168.1.3 +dev tun +ifconfig 10.8.0.2 10.8.0.1 +# Only force VLAN 2 through the VPN +route 192.168.2.0 255.255.255.0 10.8.0.1 +secret [inline] +<secret> +# +# 2048 bit OpenVPN static key +# +-----BEGIN OpenVPN Static key V1----- +553aabe853acdfe51cd6fcfea93dcbb0 +c8797deadd1187606b1ea8f2315eb5e6 +67c0d7e830f50df45686063b189d6c6b +aab8bb3430cc78f7bb1f78628d5c3742 +0cef4f53a5acab2894905f4499f95d8e +e69b7b6748b17016f89e19e91481a9fd +bf8c10651f41a1d4fdf5f438925a6733 +13cec8f04701eb47b8f7ffc48bc3d7af +65f07bce766015b87c3db4d668c655ff +be5a69522a8e60ccb217f8521681b45d +27c0b70bdfbfbb426c7646d80adf7482 +3ddac58b25cb1c1bb100de974478b4c6 +8b45a94261a2405e99810cb2b3abd49f +21b3198ada87ff3c4e656a008e540a8d +e7811584363597599cce2040a68ac00e +f2125540e0f7f4adc37cb3f0d922eeb7 +-----END OpenVPN Static key V1----- +</secret> \ No newline at end of file diff --git a/nixos/tests/initrd-network-openvpn/shared.key b/nixos/tests/initrd-network-openvpn/shared.key new file mode 100644 index 00000000000..248a91a3e3d --- /dev/null +++ b/nixos/tests/initrd-network-openvpn/shared.key @@ -0,0 +1,21 @@ +# +# 2048 bit OpenVPN static key +# +-----BEGIN OpenVPN Static key V1----- +553aabe853acdfe51cd6fcfea93dcbb0 +c8797deadd1187606b1ea8f2315eb5e6 +67c0d7e830f50df45686063b189d6c6b +aab8bb3430cc78f7bb1f78628d5c3742 +0cef4f53a5acab2894905f4499f95d8e +e69b7b6748b17016f89e19e91481a9fd +bf8c10651f41a1d4fdf5f438925a6733 +13cec8f04701eb47b8f7ffc48bc3d7af +65f07bce766015b87c3db4d668c655ff +be5a69522a8e60ccb217f8521681b45d +27c0b70bdfbfbb426c7646d80adf7482 +3ddac58b25cb1c1bb100de974478b4c6 +8b45a94261a2405e99810cb2b3abd49f +21b3198ada87ff3c4e656a008e540a8d +e7811584363597599cce2040a68ac00e +f2125540e0f7f4adc37cb3f0d922eeb7 +-----END OpenVPN Static key V1----- diff --git a/nixos/tests/installed-tests/default.nix b/nixos/tests/installed-tests/default.nix index b6bdfea2277..889a00d4b56 100644 --- a/nixos/tests/installed-tests/default.nix +++ b/nixos/tests/installed-tests/default.nix @@ -50,6 +50,12 @@ let environment.systemPackages = with pkgs; [ gnome-desktop-testing ]; + # The installed tests need to be added to the test VM’s closure. + # Otherwise, their dependencies might not actually be registered + # as valid paths in the VM’s Nix store database, + # and `nix-store --query` commands run as part of the tests + # (for example when building Flatpak runtimes) will fail. + environment.variables.TESTED_PACKAGE_INSTALLED_TESTS = "${tested.installedTests}/share"; }; testScript = diff --git a/nixos/tests/installed-tests/flatpak.nix b/nixos/tests/installed-tests/flatpak.nix index 091c9932662..8aeeaca90f6 100644 --- a/nixos/tests/installed-tests/flatpak.nix +++ b/nixos/tests/installed-tests/flatpak.nix @@ -5,14 +5,11 @@ makeInstalledTest { withX11 = true; testConfig = { - services.xserver.desktopManager.gnome3.enable = true; # TODO: figure out minimal environment where the tests work - # common/x11.nix enables the auto display manager (lightdm) - services.xserver.displayManager.gdm.enable = false; - services.gnome3.core-utilities.enable = false; + xdg.portal.enable = true; services.flatpak.enable = true; - environment.systemPackages = with pkgs; [ gnupg ostree python2 ]; + environment.systemPackages = with pkgs; [ gnupg ostree python3 ]; virtualisation.memorySize = 2047; - virtualisation.diskSize = 1024; + virtualisation.diskSize = 3072; }; testRunnerFlags = "--timeout 3600"; diff --git a/nixos/tests/installed-tests/ibus.nix b/nixos/tests/installed-tests/ibus.nix index af54b612b50..a4bc2a7d7de 100644 --- a/nixos/tests/installed-tests/ibus.nix +++ b/nixos/tests/installed-tests/ibus.nix @@ -5,16 +5,12 @@ makeInstalledTest { testConfig = { i18n.inputMethod.enabled = "ibus"; + systemd.user.services.ibus-daemon = { + serviceConfig.ExecStart = "${pkgs.ibus}/bin/ibus-daemon --xim --verbose"; + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + }; }; - preTestScript = '' - # ibus has ibus-desktop-testing-runner but it tries to manage desktop session so we just spawn ibus-daemon ourselves - machine.succeed("ibus-daemon --daemonize --verbose") - ''; - withX11 = true; - - # TODO: ibus-daemon is currently crashing or something - # maybe make ibus systemd service that auto-restarts? - meta.broken = true; } diff --git a/nixos/tests/installed-tests/ostree.nix b/nixos/tests/installed-tests/ostree.nix index eef7cace54c..90e09ad4ddf 100644 --- a/nixos/tests/installed-tests/ostree.nix +++ b/nixos/tests/installed-tests/ostree.nix @@ -3,21 +3,10 @@ makeInstalledTest { tested = pkgs.ostree; - # TODO: Wrap/patch the tests directly in the package testConfig = { environment.systemPackages = with pkgs; [ - (python3.withPackages (p: with p; [ pyyaml ])) gnupg ostree ]; - - # for GJS tests - environment.variables.GI_TYPELIB_PATH = lib.makeSearchPath "lib/girepository-1.0" (with pkgs; [ - gtk3 - pango.out - ostree - gdk-pixbuf - atk - ]); }; } diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index 8443b69359b..02b839fee3f 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -64,7 +64,7 @@ let # a test script fragment `createPartitions', which must create # partitions and filesystems. testScriptFun = { bootLoader, createPartitions, grubVersion, grubDevice, grubUseEfi - , grubIdentifier, preBootCommands, extraConfig + , grubIdentifier, preBootCommands, postBootCommands, extraConfig , testSpecialisationConfig }: let iface = if grubVersion == 1 then "ide" else "virtio"; @@ -216,6 +216,7 @@ let machine = create_machine_named("boot-after-rebuild-switch") ${preBootCommands} machine.wait_for_unit("network.target") + ${postBootCommands} machine.shutdown() # Tests for validating clone configuration entries in grub menu @@ -238,6 +239,7 @@ let with subtest("Set grub to boot the second configuration"): machine.succeed("grub-reboot 1") + ${postBootCommands} machine.shutdown() # Reboot Machine @@ -252,12 +254,13 @@ let with subtest("We should find a file named /etc/gitconfig"): machine.succeed("test -e /etc/gitconfig") + ${postBootCommands} machine.shutdown() ''; makeInstallerTest = name: - { createPartitions, preBootCommands ? "", extraConfig ? "" + { createPartitions, preBootCommands ? "", postBootCommands ? "", extraConfig ? "" , extraInstallerConfig ? {} , bootLoader ? "grub" # either "grub" or "systemd-boot" , grubVersion ? 2, grubDevice ? "/dev/vda", grubIdentifier ? "uuid", grubUseEfi ? false @@ -335,7 +338,7 @@ let }; testScript = testScriptFun { - inherit bootLoader createPartitions preBootCommands + inherit bootLoader createPartitions preBootCommands postBootCommands grubVersion grubDevice grubIdentifier grubUseEfi extraConfig testSpecialisationConfig; }; @@ -552,16 +555,26 @@ in { + " mkpart primary 2048M -1s" # PV2 + " set 2 lvm on", "udevadm settle", + "sleep 1", "pvcreate /dev/vda1 /dev/vda2", + "sleep 1", "vgcreate MyVolGroup /dev/vda1 /dev/vda2", + "sleep 1", "lvcreate --size 1G --name swap MyVolGroup", - "lvcreate --size 2G --name nixos MyVolGroup", + "sleep 1", + "lvcreate --size 3G --name nixos MyVolGroup", + "sleep 1", "mkswap -f /dev/MyVolGroup/swap -L swap", "swapon -L swap", "mkfs.xfs -L nixos /dev/MyVolGroup/nixos", "mount LABEL=nixos /mnt", ) ''; + postBootCommands = '' + assert "loaded active" in machine.succeed( + "systemctl list-units 'lvm2-pvscan@*' -ql --no-legend | tee /dev/stderr" + ) + ''; }; # Boot off an encrypted root partition with the default LUKS header format @@ -650,6 +663,32 @@ in { ''; }; + bcache = makeInstallerTest "bcache" { + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda --" + + " mklabel msdos" + + " mkpart primary ext2 1M 50MB" # /boot + + " mkpart primary 50MB 512MB " # swap + + " mkpart primary 512MB 1024MB" # Cache (typically SSD) + + " mkpart primary 1024MB -1s ", # Backing device (typically HDD) + "modprobe bcache", + "udevadm settle", + "make-bcache -B /dev/vda4 -C /dev/vda3", + "echo /dev/vda3 > /sys/fs/bcache/register", + "echo /dev/vda4 > /sys/fs/bcache/register", + "udevadm settle", + "mkfs.ext3 -L nixos /dev/bcache0", + "mount LABEL=nixos /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir /mnt/boot", + "mount LABEL=boot /mnt/boot", + "mkswap -f /dev/vda2 -L swap", + "swapon -L swap", + ) + ''; + }; + # Test a basic install using GRUB 1. grub1 = makeInstallerTest "grub1" { createPartitions = '' diff --git a/nixos/tests/ipfs.nix b/nixos/tests/ipfs.nix index 4d721aec0c7..9c0ff5306e0 100644 --- a/nixos/tests/ipfs.nix +++ b/nixos/tests/ipfs.nix @@ -7,19 +7,33 @@ import ./make-test-python.nix ({ pkgs, ...} : { nodes.machine = { ... }: { services.ipfs = { enable = true; + # Also will add a unix domain socket socket API address, see module. + startWhenNeeded = true; apiAddress = "/ip4/127.0.0.1/tcp/2324"; }; }; testScript = '' start_all() - machine.wait_for_unit("ipfs") - machine.wait_until_succeeds("ipfs --api /ip4/127.0.0.1/tcp/2324 id") + # IPv4 activation + + machine.succeed("ipfs --api /ip4/127.0.0.1/tcp/2324 id") ipfs_hash = machine.succeed( "echo fnord | ipfs --api /ip4/127.0.0.1/tcp/2324 add | awk '{ print $2 }'" ) machine.succeed(f"ipfs cat /ipfs/{ipfs_hash.strip()} | grep fnord") + + # Unix domain socket activation + + machine.stop_job("ipfs") + + ipfs_hash = machine.succeed( + "echo fnord2 | ipfs --api /unix/run/ipfs.sock add | awk '{ print $2 }'" + ) + machine.succeed( + f"ipfs --api /unix/run/ipfs.sock cat /ipfs/{ipfs_hash.strip()} | grep fnord2" + ) ''; }) diff --git a/nixos/tests/jitsi-meet.nix b/nixos/tests/jitsi-meet.nix new file mode 100644 index 00000000000..42762dfdad8 --- /dev/null +++ b/nixos/tests/jitsi-meet.nix @@ -0,0 +1,55 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "jitsi-meet"; + meta = with pkgs.stdenv.lib; { + maintainers = teams.jitsi.members; + }; + + nodes = { + client = { nodes, pkgs, ... }: { + }; + server = { config, pkgs, ... }: { + services.jitsi-meet = { + enable = true; + hostName = "server"; + }; + services.jitsi-videobridge.openFirewall = true; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + services.nginx.virtualHosts.server = { + enableACME = true; + forceSSL = true; + }; + + security.acme.email = "me@example.org"; + security.acme.acceptTerms = true; + security.acme.server = "https://example.com"; # self-signed only + }; + }; + + testScript = '' + server.wait_for_unit("jitsi-videobridge2.service") + server.wait_for_unit("jicofo.service") + server.wait_for_unit("nginx.service") + server.wait_for_unit("prosody.service") + + server.wait_until_succeeds( + "journalctl -b -u jitsi-videobridge2 -o cat | grep -q 'Performed a successful health check'" + ) + server.wait_until_succeeds( + "journalctl -b -u jicofo -o cat | grep -q 'connected .JID: focus@auth.server'" + ) + server.wait_until_succeeds( + "journalctl -b -u prosody -o cat | grep -q 'Authenticated as focus@auth.server'" + ) + server.wait_until_succeeds( + "journalctl -b -u prosody -o cat | grep -q 'focus.server:component: External component successfully authenticated'" + ) + server.wait_until_succeeds( + "journalctl -b -u prosody -o cat | grep -q 'Authenticated as jvb@auth.server'" + ) + + client.wait_for_unit("network.target") + assert "<title>Jitsi Meet</title>" in client.succeed("curl -sSfkL http://server/") + ''; +}) diff --git a/nixos/tests/kubernetes/base.nix b/nixos/tests/kubernetes/base.nix index adb73650689..8cfac10b6dc 100644 --- a/nixos/tests/kubernetes/base.nix +++ b/nixos/tests/kubernetes/base.nix @@ -3,13 +3,12 @@ pkgs ? import ../../.. { inherit system config; } }: -with import ../../lib/testing.nix { inherit system pkgs; }; +with import ../../lib/testing-python.nix { inherit system pkgs; }; with pkgs.lib; let mkKubernetesBaseTest = { name, domain ? "my.zyx", test, machines - , pkgs ? import <nixpkgs> { inherit system; } , extraConfiguration ? null }: let masterName = head (filter (machineName: any (role: role == "master") machines.${machineName}.roles) (attrNames machines)); @@ -75,10 +74,8 @@ let ) machines; testScript = '' - startAll; - - ${test} - ''; + start_all() + '' + test; }; mkKubernetesMultiNodeTest = attrs: mkKubernetesBaseTest ({ diff --git a/nixos/tests/kubernetes/dns.nix b/nixos/tests/kubernetes/dns.nix index 638942e1540..890499a0fb8 100644 --- a/nixos/tests/kubernetes/dns.nix +++ b/nixos/tests/kubernetes/dns.nix @@ -75,51 +75,75 @@ let singleNodeTest = { test = '' # prepare machine1 for test - $machine1->waitUntilSucceeds("kubectl get node machine1.${domain} | grep -w Ready"); - $machine1->waitUntilSucceeds("docker load < ${redisImage}"); - $machine1->waitUntilSucceeds("kubectl create -f ${redisPod}"); - $machine1->waitUntilSucceeds("kubectl create -f ${redisService}"); - $machine1->waitUntilSucceeds("docker load < ${probeImage}"); - $machine1->waitUntilSucceeds("kubectl create -f ${probePod}"); + machine1.wait_until_succeeds("kubectl get node machine1.${domain} | grep -w Ready") + machine1.wait_until_succeeds( + "docker load < ${redisImage}" + ) + machine1.wait_until_succeeds( + "kubectl create -f ${redisPod}" + ) + machine1.wait_until_succeeds( + "kubectl create -f ${redisService}" + ) + machine1.wait_until_succeeds( + "docker load < ${probeImage}" + ) + machine1.wait_until_succeeds( + "kubectl create -f ${probePod}" + ) # check if pods are running - $machine1->waitUntilSucceeds("kubectl get pod redis | grep Running"); - $machine1->waitUntilSucceeds("kubectl get pod probe | grep Running"); - $machine1->waitUntilSucceeds("kubectl get pods -n kube-system | grep 'coredns.*1/1'"); + machine1.wait_until_succeeds("kubectl get pod redis | grep Running") + machine1.wait_until_succeeds("kubectl get pod probe | grep Running") + machine1.wait_until_succeeds("kubectl get pods -n kube-system | grep 'coredns.*1/1'") # check dns on host (dnsmasq) - $machine1->succeed("host redis.default.svc.cluster.local"); + machine1.succeed("host redis.default.svc.cluster.local") # check dns inside the container - $machine1->succeed("kubectl exec -ti probe -- /bin/host redis.default.svc.cluster.local"); + machine1.succeed("kubectl exec -ti probe -- /bin/host redis.default.svc.cluster.local") ''; }; multiNodeTest = { test = '' # Node token exchange - $machine1->waitUntilSucceeds("cp -f /var/lib/cfssl/apitoken.secret /tmp/shared/apitoken.secret"); - $machine2->waitUntilSucceeds("cat /tmp/shared/apitoken.secret | nixos-kubernetes-node-join"); + machine1.wait_until_succeeds( + "cp -f /var/lib/cfssl/apitoken.secret /tmp/shared/apitoken.secret" + ) + machine2.wait_until_succeeds( + "cat /tmp/shared/apitoken.secret | nixos-kubernetes-node-join" + ) # prepare machines for test - $machine1->waitUntilSucceeds("kubectl get node machine2.${domain} | grep -w Ready"); - $machine2->waitUntilSucceeds("docker load < ${redisImage}"); - $machine1->waitUntilSucceeds("kubectl create -f ${redisPod}"); - $machine1->waitUntilSucceeds("kubectl create -f ${redisService}"); - $machine2->waitUntilSucceeds("docker load < ${probeImage}"); - $machine1->waitUntilSucceeds("kubectl create -f ${probePod}"); + machine1.wait_until_succeeds("kubectl get node machine2.${domain} | grep -w Ready") + machine2.wait_until_succeeds( + "docker load < ${redisImage}" + ) + machine1.wait_until_succeeds( + "kubectl create -f ${redisPod}" + ) + machine1.wait_until_succeeds( + "kubectl create -f ${redisService}" + ) + machine2.wait_until_succeeds( + "docker load < ${probeImage}" + ) + machine1.wait_until_succeeds( + "kubectl create -f ${probePod}" + ) # check if pods are running - $machine1->waitUntilSucceeds("kubectl get pod redis | grep Running"); - $machine1->waitUntilSucceeds("kubectl get pod probe | grep Running"); - $machine1->waitUntilSucceeds("kubectl get pods -n kube-system | grep 'coredns.*1/1'"); + machine1.wait_until_succeeds("kubectl get pod redis | grep Running") + machine1.wait_until_succeeds("kubectl get pod probe | grep Running") + machine1.wait_until_succeeds("kubectl get pods -n kube-system | grep 'coredns.*1/1'") # check dns on hosts (dnsmasq) - $machine1->succeed("host redis.default.svc.cluster.local"); - $machine2->succeed("host redis.default.svc.cluster.local"); + machine1.succeed("host redis.default.svc.cluster.local") + machine2.succeed("host redis.default.svc.cluster.local") # check dns inside the container - $machine1->succeed("kubectl exec -ti probe -- /bin/host redis.default.svc.cluster.local"); + machine1.succeed("kubectl exec -ti probe -- /bin/host redis.default.svc.cluster.local") ''; }; in { diff --git a/nixos/tests/kubernetes/rbac.nix b/nixos/tests/kubernetes/rbac.nix index 3ce7adcd0d7..c922da515d9 100644 --- a/nixos/tests/kubernetes/rbac.nix +++ b/nixos/tests/kubernetes/rbac.nix @@ -94,43 +94,67 @@ let singlenode = base // { test = '' - $machine1->waitUntilSucceeds("kubectl get node machine1.my.zyx | grep -w Ready"); - - $machine1->waitUntilSucceeds("docker load < ${kubectlImage}"); - - $machine1->waitUntilSucceeds("kubectl apply -f ${roServiceAccount}"); - $machine1->waitUntilSucceeds("kubectl apply -f ${roRole}"); - $machine1->waitUntilSucceeds("kubectl apply -f ${roRoleBinding}"); - $machine1->waitUntilSucceeds("kubectl create -f ${kubectlPod}"); - - $machine1->waitUntilSucceeds("kubectl get pod kubectl | grep Running"); - - $machine1->waitUntilSucceeds("kubectl exec -ti kubectl -- kubectl get pods"); - $machine1->fail("kubectl exec -ti kubectl -- kubectl create -f /kubectl-pod-2.json"); - $machine1->fail("kubectl exec -ti kubectl -- kubectl delete pods -l name=kubectl"); + machine1.wait_until_succeeds("kubectl get node machine1.my.zyx | grep -w Ready") + + machine1.wait_until_succeeds( + "docker load < ${kubectlImage}" + ) + + machine1.wait_until_succeeds( + "kubectl apply -f ${roServiceAccount}" + ) + machine1.wait_until_succeeds( + "kubectl apply -f ${roRole}" + ) + machine1.wait_until_succeeds( + "kubectl apply -f ${roRoleBinding}" + ) + machine1.wait_until_succeeds( + "kubectl create -f ${kubectlPod}" + ) + + machine1.wait_until_succeeds("kubectl get pod kubectl | grep Running") + + machine1.wait_until_succeeds("kubectl exec -ti kubectl -- kubectl get pods") + machine1.fail("kubectl exec -ti kubectl -- kubectl create -f /kubectl-pod-2.json") + machine1.fail("kubectl exec -ti kubectl -- kubectl delete pods -l name=kubectl") ''; }; multinode = base // { test = '' # Node token exchange - $machine1->waitUntilSucceeds("cp -f /var/lib/cfssl/apitoken.secret /tmp/shared/apitoken.secret"); - $machine2->waitUntilSucceeds("cat /tmp/shared/apitoken.secret | nixos-kubernetes-node-join"); - - $machine1->waitUntilSucceeds("kubectl get node machine2.my.zyx | grep -w Ready"); - - $machine2->waitUntilSucceeds("docker load < ${kubectlImage}"); - - $machine1->waitUntilSucceeds("kubectl apply -f ${roServiceAccount}"); - $machine1->waitUntilSucceeds("kubectl apply -f ${roRole}"); - $machine1->waitUntilSucceeds("kubectl apply -f ${roRoleBinding}"); - $machine1->waitUntilSucceeds("kubectl create -f ${kubectlPod}"); - - $machine1->waitUntilSucceeds("kubectl get pod kubectl | grep Running"); - - $machine1->waitUntilSucceeds("kubectl exec -ti kubectl -- kubectl get pods"); - $machine1->fail("kubectl exec -ti kubectl -- kubectl create -f /kubectl-pod-2.json"); - $machine1->fail("kubectl exec -ti kubectl -- kubectl delete pods -l name=kubectl"); + machine1.wait_until_succeeds( + "cp -f /var/lib/cfssl/apitoken.secret /tmp/shared/apitoken.secret" + ) + machine2.wait_until_succeeds( + "cat /tmp/shared/apitoken.secret | nixos-kubernetes-node-join" + ) + + machine1.wait_until_succeeds("kubectl get node machine2.my.zyx | grep -w Ready") + + machine2.wait_until_succeeds( + "docker load < ${kubectlImage}" + ) + + machine1.wait_until_succeeds( + "kubectl apply -f ${roServiceAccount}" + ) + machine1.wait_until_succeeds( + "kubectl apply -f ${roRole}" + ) + machine1.wait_until_succeeds( + "kubectl apply -f ${roRoleBinding}" + ) + machine1.wait_until_succeeds( + "kubectl create -f ${kubectlPod}" + ) + + machine1.wait_until_succeeds("kubectl get pod kubectl | grep Running") + + machine1.wait_until_succeeds("kubectl exec -ti kubectl -- kubectl get pods") + machine1.fail("kubectl exec -ti kubectl -- kubectl create -f /kubectl-pod-2.json") + machine1.fail("kubectl exec -ti kubectl -- kubectl delete pods -l name=kubectl") ''; }; diff --git a/nixos/tests/leaps.nix b/nixos/tests/leaps.nix index 65b475d734e..ac0c602d445 100644 --- a/nixos/tests/leaps.nix +++ b/nixos/tests/leaps.nix @@ -7,7 +7,7 @@ import ./make-test-python.nix ({ pkgs, ... }: }; nodes = - { + { client = { }; server = diff --git a/nixos/tests/lorri/default.nix b/nixos/tests/lorri/default.nix index 198171082d8..c33c7503993 100644 --- a/nixos/tests/lorri/default.nix +++ b/nixos/tests/lorri/default.nix @@ -18,7 +18,7 @@ import ../make-test-python.nix { machine.wait_until_succeeds("grep --fixed-strings 'ready' lorri.stdout") # Ping the daemon - machine.succeed("lorri internal__ping shell.nix") + machine.succeed("lorri internal ping shell.nix") # Wait for the daemon to finish the build machine.wait_until_succeeds("grep --fixed-strings 'Completed' lorri.stdout") diff --git a/nixos/tests/lxd-nftables.nix b/nixos/tests/lxd-nftables.nix new file mode 100644 index 00000000000..25517914db8 --- /dev/null +++ b/nixos/tests/lxd-nftables.nix @@ -0,0 +1,50 @@ +# This test makes sure that lxd stops implicitly depending on iptables when +# user enabled nftables. +# +# It has been extracted from `lxd.nix` for clarity, and because switching from +# iptables to nftables requires a full reboot, which is a bit hard inside NixOS +# tests. + +import ./make-test-python.nix ({ pkgs, ...} : { + name = "lxd-nftables"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ patryk27 ]; + }; + + machine = { lib, ... }: { + virtualisation = { + lxd.enable = true; + }; + + networking = { + firewall.enable = false; + nftables.enable = true; + nftables.ruleset = '' + table inet filter { + chain incoming { + type filter hook input priority 0; + policy accept; + } + + chain forward { + type filter hook forward priority 0; + policy accept; + } + + chain output { + type filter hook output priority 0; + policy accept; + } + } + ''; + }; + }; + + testScript = '' + machine.wait_for_unit("network.target") + + with subtest("When nftables are enabled, lxd doesn't depend on iptables anymore"): + machine.succeed("lsmod | grep nf_tables") + machine.fail("lsmod | grep ip_tables") + ''; +}) diff --git a/nixos/tests/lxd.nix b/nixos/tests/lxd.nix new file mode 100644 index 00000000000..db2d44dff55 --- /dev/null +++ b/nixos/tests/lxd.nix @@ -0,0 +1,135 @@ +import ./make-test-python.nix ({ pkgs, ...} : + +let + # Since we don't have access to the internet during the tests, we have to + # pre-fetch lxd containers beforehand. + # + # I've chosen to import Alpine Linux, because its image is turbo-tiny and, + # generally, sufficient for our tests. + + alpine-meta = pkgs.fetchurl { + url = "https://uk.images.linuxcontainers.org/images/alpine/3.11/i386/default/20200608_13:00/lxd.tar.xz"; + sha256 = "1hkvaj3rr333zmx1759njy435lps33gl4ks8zfm7m4nqvipm26a0"; + }; + + alpine-rootfs = pkgs.fetchurl { + url = "https://uk.images.linuxcontainers.org/images/alpine/3.11/i386/default/20200608_13:00/rootfs.tar.xz"; + sha256 = "1v82zdra4j5xwsff09qlp7h5vbsg54s0j7rdg4rynichfid3r347"; + }; + + lxd-config = pkgs.writeText "config.yaml" '' + storage_pools: + - name: default + driver: dir + config: + source: /var/lxd-pool + + networks: + - name: lxdbr0 + type: bridge + config: + ipv4.address: auto + ipv6.address: none + + profiles: + - name: default + devices: + eth0: + name: eth0 + network: lxdbr0 + type: nic + root: + path: / + pool: default + type: disk + ''; + +in { + name = "lxd"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ patryk27 ]; + }; + + machine = { lib, ... }: { + virtualisation = { + # Since we're testing `limits.cpu`, we've gotta have a known number of + # cores to lay on + cores = 2; + + # Ditto, for `limits.memory` + memorySize = 512; + + lxc.lxcfs.enable = true; + lxd.enable = true; + }; + }; + + testScript = '' + machine.wait_for_unit("sockets.target") + machine.wait_for_unit("lxd.service") + + # It takes additional second for lxd to settle + machine.sleep(1) + + # lxd expects the pool's directory to already exist + machine.succeed("mkdir /var/lxd-pool") + + machine.succeed( + "cat ${lxd-config} | lxd init --preseed" + ) + + machine.succeed( + "lxc image import ${alpine-meta} ${alpine-rootfs} --alias alpine" + ) + + with subtest("Containers can be launched and destroyed"): + machine.succeed("lxc launch alpine test") + machine.succeed("lxc exec test true") + machine.succeed("lxc delete -f test") + + with subtest("Containers are being mounted with lxcfs inside"): + machine.succeed("lxc launch alpine test") + + ## ---------- ## + ## limits.cpu ## + + machine.succeed("lxc config set test limits.cpu 1") + + # Since Alpine doesn't have `nproc` pre-installed, we've gotta resort + # to the primal methods + assert ( + "1" + == machine.succeed("lxc exec test grep -- -c ^processor /proc/cpuinfo").strip() + ) + + machine.succeed("lxc config set test limits.cpu 2") + + assert ( + "2" + == machine.succeed("lxc exec test grep -- -c ^processor /proc/cpuinfo").strip() + ) + + ## ------------- ## + ## limits.memory ## + + machine.succeed("lxc config set test limits.memory 64MB") + + assert ( + "MemTotal: 62500 kB" + == machine.succeed("lxc exec test grep -- MemTotal /proc/meminfo").strip() + ) + + machine.succeed("lxc config set test limits.memory 128MB") + + assert ( + "MemTotal: 125000 kB" + == machine.succeed("lxc exec test grep -- MemTotal /proc/meminfo").strip() + ) + + machine.succeed("lxc delete -f test") + + with subtest("Unless explicitly changed, lxd leans on iptables"): + machine.succeed("lsmod | grep ip_tables") + machine.fail("lsmod | grep nf_tables") + ''; +}) diff --git a/nixos/tests/mathics.nix b/nixos/tests/mathics.nix deleted file mode 100644 index fcbeeb18a72..00000000000 --- a/nixos/tests/mathics.nix +++ /dev/null @@ -1,20 +0,0 @@ -import ./make-test.nix ({ pkgs, ... }: { - name = "mathics"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ benley ]; - }; - - nodes = { - machine = { ... }: { - services.mathics.enable = true; - services.mathics.port = 8888; - }; - }; - - testScript = '' - startAll; - $machine->waitForUnit("mathics.service"); - $machine->waitForOpenPort(8888); - $machine->succeed("curl http://localhost:8888/"); - ''; -}) diff --git a/nixos/tests/matrix-synapse.nix b/nixos/tests/matrix-synapse.nix index f3623aa3c09..9ca80872176 100644 --- a/nixos/tests/matrix-synapse.nix +++ b/nixos/tests/matrix-synapse.nix @@ -29,8 +29,8 @@ import ./make-test-python.nix ({ pkgs, ... } : let in { name = "matrix-synapse"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ corngood ]; + meta = with pkgs.stdenv.lib; { + maintainers = teams.matrix.members; }; nodes = { diff --git a/nixos/tests/mesos.nix b/nixos/tests/mesos.nix deleted file mode 100644 index 2e6dc0eda06..00000000000 --- a/nixos/tests/mesos.nix +++ /dev/null @@ -1,92 +0,0 @@ -import ./make-test.nix ({ pkgs, ...} : rec { - name = "mesos"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ offline kamilchm cstrahan ]; - }; - - nodes = { - master = { ... }: { - networking.firewall.enable = false; - services.zookeeper.enable = true; - services.mesos.master = { - enable = true; - zk = "zk://master:2181/mesos"; - }; - }; - - slave = { ... }: { - networking.firewall.enable = false; - networking.nat.enable = true; - virtualisation.docker.enable = true; - services.mesos = { - slave = { - enable = true; - master = "master:5050"; - dockerRegistry = registry; - executorEnvironmentVariables = { - PATH = "/run/current-system/sw/bin"; - }; - }; - }; - }; - }; - - simpleDocker = pkgs.dockerTools.buildImage { - name = "echo"; - tag = "latest"; - contents = [ pkgs.stdenv.shellPackage pkgs.coreutils ]; - config = { - Env = [ - # When shell=true, mesos invokes "sh -c '<cmd>'", so make sure "sh" is - # on the PATH. - "PATH=${pkgs.stdenv.shellPackage}/bin:${pkgs.coreutils}/bin" - ]; - Entrypoint = [ "echo" ]; - }; - }; - - registry = pkgs.runCommand "registry" { } '' - mkdir -p $out - cp ${simpleDocker} $out/echo:latest.tar - ''; - - testFramework = pkgs.pythonPackages.buildPythonPackage { - name = "mesos-tests"; - propagatedBuildInputs = [ pkgs.mesos ]; - catchConflicts = false; - src = ./mesos_test.py; - phases = [ "installPhase" "fixupPhase" ]; - installPhase = '' - install -Dvm 0755 $src $out/bin/mesos_test.py - - echo "done" > test.result - tar czf $out/test.tar.gz test.result - ''; - }; - - testScript = - '' - startAll; - $master->waitForUnit("zookeeper.service"); - $master->waitForUnit("mesos-master.service"); - $slave->waitForUnit("docker.service"); - $slave->waitForUnit("mesos-slave.service"); - $master->waitForOpenPort(2181); - $master->waitForOpenPort(5050); - $slave->waitForOpenPort(5051); - - # is slave registered? - $master->waitUntilSucceeds("curl -s --fail http://master:5050/master/slaves". - " | grep -q \"\\\"hostname\\\":\\\"slave\\\"\""); - - # try to run docker image - $master->succeed("${pkgs.mesos}/bin/mesos-execute --master=master:5050". - " --resources=\"cpus:0.1;mem:32\" --name=simple-docker". - " --containerizer=mesos --docker_image=echo:latest". - " --shell=true --command=\"echo done\" | grep -q TASK_FINISHED"); - - # simple command with .tar.gz uri - $master->succeed("${testFramework}/bin/mesos_test.py master ". - "${testFramework}/test.tar.gz"); - ''; -}) diff --git a/nixos/tests/mesos_test.py b/nixos/tests/mesos_test.py deleted file mode 100644 index be8bb32e49a..00000000000 --- a/nixos/tests/mesos_test.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -import uuid -import time -import subprocess -import os - -import sys - -from mesos.interface import Scheduler -from mesos.native import MesosSchedulerDriver -from mesos.interface import mesos_pb2 - -def log(msg): - process = subprocess.Popen("systemd-cat", stdin=subprocess.PIPE) - (out,err) = process.communicate(msg) - -class NixosTestScheduler(Scheduler): - def __init__(self): - self.master_ip = sys.argv[1] - self.download_uri = sys.argv[2] - - def resourceOffers(self, driver, offers): - log("XXX got resource offer") - - offer = offers[0] - task = self.new_task(offer) - uri = task.command.uris.add() - uri.value = self.download_uri - task.command.value = "cat test.result" - driver.launchTasks(offer.id, [task]) - - def statusUpdate(self, driver, update): - log("XXX status update") - if update.state == mesos_pb2.TASK_FAILED: - log("XXX test task failed with message: " + update.message) - driver.stop() - sys.exit(1) - elif update.state == mesos_pb2.TASK_FINISHED: - driver.stop() - sys.exit(0) - - def new_task(self, offer): - task = mesos_pb2.TaskInfo() - id = uuid.uuid4() - task.task_id.value = str(id) - task.slave_id.value = offer.slave_id.value - task.name = "task {}".format(str(id)) - - cpus = task.resources.add() - cpus.name = "cpus" - cpus.type = mesos_pb2.Value.SCALAR - cpus.scalar.value = 0.1 - - mem = task.resources.add() - mem.name = "mem" - mem.type = mesos_pb2.Value.SCALAR - mem.scalar.value = 32 - - return task - -if __name__ == '__main__': - log("XXX framework started") - - framework = mesos_pb2.FrameworkInfo() - framework.user = "root" - framework.name = "nixos-test-framework" - driver = MesosSchedulerDriver( - NixosTestScheduler(), - framework, - sys.argv[1] + ":5050" - ) - driver.run() diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix index 17260ce6406..ae150553273 100644 --- a/nixos/tests/misc.nix +++ b/nixos/tests/misc.nix @@ -20,12 +20,24 @@ import ./make-test-python.nix ({ pkgs, ...} : rec { { fsType = "tmpfs"; options = [ "mode=1777" "noauto" ]; }; + # Tests https://discourse.nixos.org/t/how-to-make-a-derivations-executables-have-the-s-permission/8555 + "/user-mount/point" = { + device = "/user-mount/source"; + fsType = "none"; + options = [ "bind" "rw" "user" "noauto" ]; + }; + "/user-mount/denied-point" = { + device = "/user-mount/denied-source"; + fsType = "none"; + options = [ "bind" "rw" "noauto" ]; + }; }; systemd.automounts = singleton { wantedBy = [ "multi-user.target" ]; where = "/tmp2"; }; users.users.sybil = { isNormalUser = true; group = "wheel"; }; + users.users.alice = { isNormalUser = true; }; security.sudo = { enable = true; wheelNeedsPassword = false; }; boot.kernel.sysctl."vm.swappiness" = 1; boot.kernelParams = [ "vsyscall=emulate" ]; @@ -112,6 +124,26 @@ import ./make-test-python.nix ({ pkgs, ...} : rec { machine.succeed("touch /tmp2/x") machine.succeed("grep '/tmp2 tmpfs' /proc/mounts") + with subtest( + "Whether mounting by a user is possible with the `user` option in fstab (#95444)" + ): + machine.succeed("mkdir -p /user-mount/source") + machine.succeed("touch /user-mount/source/file") + machine.succeed("chmod -R a+Xr /user-mount/source") + machine.succeed("mkdir /user-mount/point") + machine.succeed("chown alice:users /user-mount/point") + machine.succeed("su - alice -c 'mount /user-mount/point'") + machine.succeed("su - alice -c 'ls /user-mount/point/file'") + with subtest( + "Whether mounting by a user is denied without the `user` option in fstab" + ): + machine.succeed("mkdir -p /user-mount/denied-source") + machine.succeed("touch /user-mount/denied-source/file") + machine.succeed("chmod -R a+Xr /user-mount/denied-source") + machine.succeed("mkdir /user-mount/denied-point") + machine.succeed("chown alice:users /user-mount/denied-point") + machine.fail("su - alice -c 'mount /user-mount/denied-point'") + with subtest("shell-vars"): machine.succeed('[ -n "$NIX_PATH" ]') diff --git a/nixos/tests/molly-brown.nix b/nixos/tests/molly-brown.nix new file mode 100644 index 00000000000..09ce42726ca --- /dev/null +++ b/nixos/tests/molly-brown.nix @@ -0,0 +1,71 @@ +import ./make-test-python.nix ({ pkgs, ... }: + + let testString = "NixOS Gemini test successful"; + in { + + name = "molly-brown"; + meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ ehmry ]; }; + + nodes = { + + geminiServer = { config, pkgs, ... }: + let + inherit (config.networking) hostName; + cfg = config.services.molly-brown; + in { + + environment.systemPackages = [ + (pkgs.writeScriptBin "test-gemini" '' + #!${pkgs.python3}/bin/python + + import socket + import ssl + import tempfile + import textwrap + import urllib.parse + + url = "gemini://geminiServer/init.gmi" + parsed_url = urllib.parse.urlparse(url) + + s = socket.create_connection((parsed_url.netloc, 1965)) + context = ssl.SSLContext() + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + s = context.wrap_socket(s, server_hostname=parsed_url.netloc) + s.sendall((url + "\r\n").encode("UTF-8")) + fp = s.makefile("rb") + print(fp.readline().strip()) + print(fp.readline().strip()) + print(fp.readline().strip()) + '') + ]; + + networking.firewall.allowedTCPPorts = [ cfg.settings.Port ]; + + services.molly-brown = { + enable = true; + docBase = "/tmp/docs"; + certPath = "/tmp/cert.pem"; + keyPath = "/tmp/key.pem"; + }; + + systemd.services.molly-brown.preStart = '' + ${pkgs.openssl}/bin/openssl genrsa -out "/tmp/key.pem" + ${pkgs.openssl}/bin/openssl req -new \ + -subj "/CN=${config.networking.hostName}" \ + -key "/tmp/key.pem" -out /tmp/request.pem + ${pkgs.openssl}/bin/openssl x509 -req -days 3650 \ + -in /tmp/request.pem -signkey "/tmp/key.pem" -out "/tmp/cert.pem" + + mkdir -p "${cfg.settings.DocBase}" + echo "${testString}" > "${cfg.settings.DocBase}/test.gmi" + ''; + }; + }; + testScript = '' + geminiServer.wait_for_unit("molly-brown") + geminiServer.wait_for_open_port(1965) + geminiServer.succeed("test-gemini") + ''; + + }) diff --git a/nixos/tests/mongodb.nix b/nixos/tests/mongodb.nix index a637ec4bfc0..1a712388301 100644 --- a/nixos/tests/mongodb.nix +++ b/nixos/tests/mongodb.nix @@ -15,7 +15,7 @@ import ./make-test-python.nix ({ pkgs, ... }: node.wait_for_open_port(27017) assert "hello" in node.succeed( - "mongo ${testQuery}" + "${pkg}/bin/mongo ${testQuery}" ) node.execute( @@ -36,6 +36,7 @@ import ./make-test-python.nix ({ pkgs, ... }: mongodb-3_4 mongodb-3_6 mongodb-4_0 + mongodb-4_2 ]; }; }; @@ -44,8 +45,9 @@ import ./make-test-python.nix ({ pkgs, ... }: node.start() '' + runMongoDBTest pkgs.mongodb-3_4 - + runMongoDBTest pkgs.mongodb-3_6 + + runMongoDBTest pkgs.mongodb-3_6 + runMongoDBTest pkgs.mongodb-4_0 + + runMongoDBTest pkgs.mongodb-4_2 + '' node.shutdown() ''; diff --git a/nixos/tests/mysql/mariadb-galera-mariabackup.nix b/nixos/tests/mysql/mariadb-galera-mariabackup.nix index 73abf6c555f..cae55878060 100644 --- a/nixos/tests/mysql/mariadb-galera-mariabackup.nix +++ b/nixos/tests/mysql/mariadb-galera-mariabackup.nix @@ -55,9 +55,9 @@ in { }; galera = { wsrep_on = "ON"; - wsrep_debug = "OFF"; + wsrep_debug = "NONE"; wsrep_retry_autocommit = "3"; - wsrep_provider = "${pkgs.mariadb-galera_25}/lib/galera/libgalera_smm.so"; + wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so"; wsrep_cluster_address = "gcomm://"; wsrep_cluster_name = "galera"; wsrep_node_address = "192.168.1.1"; @@ -102,9 +102,9 @@ in { }; galera = { wsrep_on = "ON"; - wsrep_debug = "OFF"; + wsrep_debug = "NONE"; wsrep_retry_autocommit = "3"; - wsrep_provider = "${pkgs.mariadb-galera_25}/lib/galera/libgalera_smm.so"; + wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so"; wsrep_cluster_address = "gcomm://galera_01,galera_02,galera_03"; wsrep_cluster_name = "galera"; wsrep_node_address = "192.168.1.2"; @@ -149,9 +149,9 @@ in { }; galera = { wsrep_on = "ON"; - wsrep_debug = "OFF"; + wsrep_debug = "NONE"; wsrep_retry_autocommit = "3"; - wsrep_provider = "${pkgs.mariadb-galera_25}/lib/galera/libgalera_smm.so"; + wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so"; wsrep_cluster_address = "gcomm://galera_01,galera_02,galera_03"; wsrep_cluster_name = "galera"; wsrep_node_address = "192.168.1.3"; @@ -184,17 +184,17 @@ in { galera_03.wait_for_unit("mysql") galera_03.wait_for_open_port(3306) galera_02.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; select test_id from db1;' -N | grep 37" + "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 37" ) galera_02.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; create table db2 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'" + "sudo -u testuser mysql -u testuser -e 'use testdb; create table db2 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'" ) galera_02.succeed("systemctl stop mysql") galera_01.succeed( "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db2 values (38);'" ) galera_03.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; create table db3 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'" + "sudo -u testuser mysql -u testuser -e 'use testdb; create table db3 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'" ) galera_01.succeed( "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db3 values (39);'" @@ -202,22 +202,22 @@ in { galera_02.succeed("systemctl start mysql") galera_02.wait_for_open_port(3306) galera_02.succeed( - "sudo -u testuser mysql -u root -e 'show status' -N | grep 'wsrep_cluster_size.*3'" + "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_cluster_size.*3'" ) galera_03.succeed( - "sudo -u testuser mysql -u root -e 'show status' -N | grep 'wsrep_local_state_comment.*Synced'" + "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_local_state_comment.*Synced'" ) galera_01.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; select test_id from db3;' -N | grep 39" + "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db3;' -N | grep 39" ) galera_02.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; select test_id from db2;' -N | grep 38" + "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db2;' -N | grep 38" ) galera_03.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; select test_id from db1;' -N | grep 37" + "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 37" ) galera_01.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db3;'") - galera_02.succeed("sudo -u testuser mysql -u root -e 'use testdb; drop table db2;'") - galera_03.succeed("sudo -u testuser mysql -u root -e 'use testdb; drop table db1;'") + galera_02.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db2;'") + galera_03.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db1;'") ''; }) diff --git a/nixos/tests/mysql/mariadb-galera-rsync.nix b/nixos/tests/mysql/mariadb-galera-rsync.nix index cacae4569b5..4318efae8a9 100644 --- a/nixos/tests/mysql/mariadb-galera-rsync.nix +++ b/nixos/tests/mysql/mariadb-galera-rsync.nix @@ -51,9 +51,9 @@ in { }; galera = { wsrep_on = "ON"; - wsrep_debug = "OFF"; + wsrep_debug = "NONE"; wsrep_retry_autocommit = "3"; - wsrep_provider = "${pkgs.mariadb-galera_25}/lib/galera/libgalera_smm.so"; + wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so"; wsrep_cluster_address = "gcomm://"; wsrep_cluster_name = "galera-rsync"; wsrep_node_address = "192.168.2.1"; @@ -97,9 +97,9 @@ in { }; galera = { wsrep_on = "ON"; - wsrep_debug = "OFF"; + wsrep_debug = "NONE"; wsrep_retry_autocommit = "3"; - wsrep_provider = "${pkgs.mariadb-galera_25}/lib/galera/libgalera_smm.so"; + wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so"; wsrep_cluster_address = "gcomm://galera_04,galera_05,galera_06"; wsrep_cluster_name = "galera-rsync"; wsrep_node_address = "192.168.2.2"; @@ -143,9 +143,9 @@ in { }; galera = { wsrep_on = "ON"; - wsrep_debug = "OFF"; + wsrep_debug = "NONE"; wsrep_retry_autocommit = "3"; - wsrep_provider = "${pkgs.mariadb-galera_25}/lib/galera/libgalera_smm.so"; + wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so"; wsrep_cluster_address = "gcomm://galera_04,galera_05,galera_06"; wsrep_cluster_name = "galera-rsync"; wsrep_node_address = "192.168.2.3"; @@ -177,17 +177,17 @@ in { galera_06.wait_for_unit("mysql") galera_06.wait_for_open_port(3306) galera_05.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; select test_id from db1;' -N | grep 41" + "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 41" ) galera_05.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; create table db2 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'" + "sudo -u testuser mysql -u testuser -e 'use testdb; create table db2 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'" ) galera_05.succeed("systemctl stop mysql") galera_04.succeed( "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db2 values (42);'" ) galera_06.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; create table db3 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'" + "sudo -u testuser mysql -u testuser -e 'use testdb; create table db3 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'" ) galera_04.succeed( "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db3 values (43);'" @@ -195,22 +195,22 @@ in { galera_05.succeed("systemctl start mysql") galera_05.wait_for_open_port(3306) galera_05.succeed( - "sudo -u testuser mysql -u root -e 'show status' -N | grep 'wsrep_cluster_size.*3'" + "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_cluster_size.*3'" ) galera_06.succeed( - "sudo -u testuser mysql -u root -e 'show status' -N | grep 'wsrep_local_state_comment.*Synced'" + "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_local_state_comment.*Synced'" ) galera_04.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; select test_id from db3;' -N | grep 43" + "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db3;' -N | grep 43" ) galera_05.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; select test_id from db2;' -N | grep 42" + "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db2;' -N | grep 42" ) galera_06.succeed( - "sudo -u testuser mysql -u root -e 'use testdb; select test_id from db1;' -N | grep 41" + "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 41" ) galera_04.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db3;'") - galera_05.succeed("sudo -u testuser mysql -u root -e 'use testdb; drop table db2;'") - galera_06.succeed("sudo -u testuser mysql -u root -e 'use testdb; drop table db1;'") + galera_05.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db2;'") + galera_06.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db1;'") ''; }) diff --git a/nixos/tests/mysql/mysql-replication.nix b/nixos/tests/mysql/mysql-replication.nix index 81038dccd94..b5e00325019 100644 --- a/nixos/tests/mysql/mysql-replication.nix +++ b/nixos/tests/mysql/mysql-replication.nix @@ -59,7 +59,7 @@ in master.wait_for_open_port(3306) # Wait for testdb to be fully populated (5 rows). master.wait_until_succeeds( - "mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5" + "sudo -u mysql mysql -u mysql -D testdb -N -B -e 'select count(id) from tests' | grep -q 5" ) slave1.start() @@ -71,19 +71,21 @@ in # wait for replications to finish slave1.wait_until_succeeds( - "mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5" + "sudo -u mysql mysql -u mysql -D testdb -N -B -e 'select count(id) from tests' | grep -q 5" ) slave2.wait_until_succeeds( - "mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5" + "sudo -u mysql mysql -u mysql -D testdb -N -B -e 'select count(id) from tests' | grep -q 5" ) slave2.succeed("systemctl stop mysql") - master.succeed("echo 'insert into testdb.tests values (123, 456);' | mysql -u root -N") + master.succeed( + "echo 'insert into testdb.tests values (123, 456);' | sudo -u mysql mysql -u mysql -N" + ) slave2.succeed("systemctl start mysql") slave2.wait_for_unit("mysql") slave2.wait_for_open_port(3306) slave2.wait_until_succeeds( - "echo 'select * from testdb.tests where Id = 123;' | mysql -u root -N | grep 456" + "echo 'select * from testdb.tests where Id = 123;' | sudo -u mysql mysql -u mysql -N | grep 456" ) ''; }) diff --git a/nixos/tests/mysql/mysql.nix b/nixos/tests/mysql/mysql.nix index d236ce94632..5437a286043 100644 --- a/nixos/tests/mysql/mysql.nix +++ b/nixos/tests/mysql/mysql.nix @@ -5,20 +5,34 @@ import ./../make-test-python.nix ({ pkgs, ...} : { }; nodes = { - mysql = + mysql57 = { pkgs, ... }: { + users.users.testuser = { }; + users.users.testuser2 = { }; services.mysql.enable = true; services.mysql.initialDatabases = [ - { name = "testdb"; schema = ./testdb.sql; } - { name = "empty_testdb"; } + { name = "testdb3"; schema = ./testdb.sql; } ]; # note that using pkgs.writeText here is generally not a good idea, # as it will store the password in world-readable /nix/store ;) services.mysql.initialScript = pkgs.writeText "mysql-init.sql" '' - CREATE USER 'passworduser'@'localhost' IDENTIFIED BY 'password123'; + CREATE USER 'testuser3'@'localhost' IDENTIFIED BY 'secure'; + GRANT ALL PRIVILEGES ON testdb3.* TO 'testuser3'@'localhost'; ''; + services.mysql.ensureDatabases = [ "testdb" "testdb2" ]; + services.mysql.ensureUsers = [{ + name = "testuser"; + ensurePermissions = { + "testdb.*" = "ALL PRIVILEGES"; + }; + } { + name = "testuser2"; + ensurePermissions = { + "testdb2.*" = "ALL PRIVILEGES"; + }; + }]; services.mysql.package = pkgs.mysql57; }; @@ -30,16 +44,30 @@ import ./../make-test-python.nix ({ pkgs, ...} : { # Kernel panic - not syncing: Out of memory: compulsory panic_on_oom is enabled virtualisation.memorySize = 1024; + users.users.testuser = { }; + users.users.testuser2 = { }; services.mysql.enable = true; services.mysql.initialDatabases = [ - { name = "testdb"; schema = ./testdb.sql; } - { name = "empty_testdb"; } + { name = "testdb3"; schema = ./testdb.sql; } ]; # note that using pkgs.writeText here is generally not a good idea, # as it will store the password in world-readable /nix/store ;) services.mysql.initialScript = pkgs.writeText "mysql-init.sql" '' - CREATE USER 'passworduser'@'localhost' IDENTIFIED BY 'password123'; + CREATE USER 'testuser3'@'localhost' IDENTIFIED BY 'secure'; + GRANT ALL PRIVILEGES ON testdb3.* TO 'testuser3'@'localhost'; ''; + services.mysql.ensureDatabases = [ "testdb" "testdb2" ]; + services.mysql.ensureUsers = [{ + name = "testuser"; + ensurePermissions = { + "testdb.*" = "ALL PRIVILEGES"; + }; + } { + name = "testuser2"; + ensurePermissions = { + "testdb2.*" = "ALL PRIVILEGES"; + }; + }]; services.mysql.package = pkgs.mysql80; }; @@ -81,17 +109,49 @@ import ./../make-test-python.nix ({ pkgs, ...} : { testScript = '' start_all() - mysql.wait_for_unit("mysql") - mysql.succeed("echo 'use empty_testdb;' | mysql -u root") - mysql.succeed("echo 'use testdb; select * from tests;' | mysql -u root -N | grep 4") - # ';' acts as no-op, just check whether login succeeds with the user created from the initialScript - mysql.succeed("echo ';' | mysql -u passworduser --password=password123") + mysql57.wait_for_unit("mysql") + mysql57.succeed( + "echo 'use testdb; create table tests (test_id INT, PRIMARY KEY (test_id));' | sudo -u testuser mysql -u testuser" + ) + mysql57.succeed( + "echo 'use testdb; insert into tests values (41);' | sudo -u testuser mysql -u testuser" + ) + # Ensure testuser2 is not able to insert into testdb as mysql testuser2 + mysql57.fail( + "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser2" + ) + # Ensure testuser2 is not able to authenticate as mysql testuser + mysql57.fail( + "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser" + ) + mysql57.succeed( + "echo 'use testdb; select test_id from tests;' | sudo -u testuser mysql -u testuser -N | grep 41" + ) + mysql57.succeed( + "echo 'use testdb3; select * from tests;' | mysql -u testuser3 --password=secure -N | grep 4" + ) mysql80.wait_for_unit("mysql") - mysql80.succeed("echo 'use empty_testdb;' | mysql -u root") - mysql80.succeed("echo 'use testdb; select * from tests;' | mysql -u root -N | grep 4") - # ';' acts as no-op, just check whether login succeeds with the user created from the initialScript - mysql80.succeed("echo ';' | mysql -u passworduser --password=password123") + mysql80.succeed( + "echo 'use testdb; create table tests (test_id INT, PRIMARY KEY (test_id));' | sudo -u testuser mysql -u testuser" + ) + mysql80.succeed( + "echo 'use testdb; insert into tests values (41);' | sudo -u testuser mysql -u testuser" + ) + # Ensure testuser2 is not able to insert into testdb as mysql testuser2 + mysql80.fail( + "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser2" + ) + # Ensure testuser2 is not able to authenticate as mysql testuser + mysql80.fail( + "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser" + ) + mysql80.succeed( + "echo 'use testdb; select test_id from tests;' | sudo -u testuser mysql -u testuser -N | grep 41" + ) + mysql80.succeed( + "echo 'use testdb3; select * from tests;' | mysql -u testuser3 --password=secure -N | grep 4" + ) mariadb.wait_for_unit("mysql") mariadb.succeed( @@ -112,32 +172,32 @@ import ./../make-test-python.nix ({ pkgs, ...} : { "echo 'use testdb; select test_id from tests;' | sudo -u testuser mysql -u testuser -N | grep 42" ) - # Check if TokuDB plugin works + # Check if RocksDB plugin works mariadb.succeed( - "echo 'use testdb; create table tokudb (test_id INT, PRIMARY KEY (test_id)) ENGINE = TokuDB;' | sudo -u testuser mysql -u testuser" + "echo 'use testdb; create table rocksdb (test_id INT, PRIMARY KEY (test_id)) ENGINE = RocksDB;' | sudo -u testuser mysql -u testuser" ) mariadb.succeed( - "echo 'use testdb; insert into tokudb values (25);' | sudo -u testuser mysql -u testuser" + "echo 'use testdb; insert into rocksdb values (28);' | sudo -u testuser mysql -u testuser" ) mariadb.succeed( - "echo 'use testdb; select test_id from tokudb;' | sudo -u testuser mysql -u testuser -N | grep 25" + "echo 'use testdb; select test_id from rocksdb;' | sudo -u testuser mysql -u testuser -N | grep 28" ) mariadb.succeed( - "echo 'use testdb; drop table tokudb;' | sudo -u testuser mysql -u testuser" + "echo 'use testdb; drop table rocksdb;' | sudo -u testuser mysql -u testuser" ) - - # Check if RocksDB plugin works + '' + pkgs.stdenv.lib.optionalString pkgs.stdenv.isx86_64 '' + # Check if TokuDB plugin works mariadb.succeed( - "echo 'use testdb; create table rocksdb (test_id INT, PRIMARY KEY (test_id)) ENGINE = RocksDB;' | sudo -u testuser mysql -u testuser" + "echo 'use testdb; create table tokudb (test_id INT, PRIMARY KEY (test_id)) ENGINE = TokuDB;' | sudo -u testuser mysql -u testuser" ) mariadb.succeed( - "echo 'use testdb; insert into rocksdb values (28);' | sudo -u testuser mysql -u testuser" + "echo 'use testdb; insert into tokudb values (25);' | sudo -u testuser mysql -u testuser" ) mariadb.succeed( - "echo 'use testdb; select test_id from rocksdb;' | sudo -u testuser mysql -u testuser -N | grep 28" + "echo 'use testdb; select test_id from tokudb;' | sudo -u testuser mysql -u testuser -N | grep 25" ) mariadb.succeed( - "echo 'use testdb; drop table rocksdb;' | sudo -u testuser mysql -u testuser" + "echo 'use testdb; drop table tokudb;' | sudo -u testuser mysql -u testuser" ) ''; }) diff --git a/nixos/tests/ncdns.nix b/nixos/tests/ncdns.nix new file mode 100644 index 00000000000..507e20fe7cc --- /dev/null +++ b/nixos/tests/ncdns.nix @@ -0,0 +1,77 @@ +import ./make-test-python.nix ({ pkgs, ... }: +let + fakeReply = pkgs.writeText "namecoin-reply.json" '' + { "error": null, + "id": 1, + "result": { + "address": "T31q8ucJ4dI1xzhxQ5QispfECld5c7Xw", + "expired": false, + "expires_in": 2248, + "height": 438155, + "name": "d/test", + "txid": "db61c0b2540ba0c1a2c8cc92af703a37002e7566ecea4dbf8727c7191421edfb", + "value": "{\"ip\": \"1.2.3.4\", \"email\": \"root@test.bit\",\"info\": \"Fake record\"}", + "vout": 0 + } + } + ''; +in + +{ + name = "ncdns"; + + nodes.server = { ... }: { + networking.nameservers = [ "127.0.0.1" ]; + + services.namecoind.rpc = { + address = "127.0.0.1"; + user = "namecoin"; + password = "secret"; + port = 8332; + }; + + # Fake namecoin RPC server because we can't + # run a full node in a test. + systemd.services.namecoind = { + wantedBy = [ "multi-user.target" ]; + script = '' + while true; do + echo -e "HTTP/1.1 200 OK\n\n $(<${fakeReply})\n" \ + | ${pkgs.netcat}/bin/nc -N -l 127.0.0.1 8332 + done + ''; + }; + + services.ncdns = { + enable = true; + dnssec.enable = true; + }; + + services.pdns-recursor = { + enable = true; + dns.allowFrom = [ "127.0.0.0/8" ]; + settings.loglevel = 8; + resolveNamecoin = true; + }; + + environment.systemPackages = [ pkgs.dnsutils ]; + + }; + + testScript = '' + with subtest("DNSSEC keys have been generated"): + server.wait_for_unit("ncdns") + server.wait_for_file("/var/lib/ncdns/bit.key") + server.wait_for_file("/var/lib/ncdns/bit-zone.key") + + with subtest("DNSKEY bit record is present"): + server.wait_for_unit("pdns-recursor") + server.wait_for_open_port("53") + server.succeed("host -t DNSKEY bit") + + with subtest("can resolve a .bit name"): + server.wait_for_unit("namecoind") + server.wait_for_open_port("8332") + assert "1.2.3.4" in server.succeed("host -t A test.bit") + ''; +}) diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix index 3d8ab761a44..83d4f6465b6 100644 --- a/nixos/tests/networking.nix +++ b/nixos/tests/networking.nix @@ -8,7 +8,9 @@ with import ../lib/testing-python.nix { inherit system pkgs; }; with pkgs.lib; let - router = { config, pkgs, ... }: + qemu-flags = import ../lib/qemu-flags.nix { inherit pkgs; }; + + router = { config, pkgs, lib, ... }: with pkgs.lib; let vlanIfs = range 1 (length config.virtualisation.vlans); @@ -30,17 +32,20 @@ let services.dhcpd4 = { enable = true; interfaces = map (n: "eth${toString n}") vlanIfs; - extraConfig = '' - authoritative; - '' + flip concatMapStrings vlanIfs (n: '' + extraConfig = flip concatMapStrings vlanIfs (n: '' subnet 192.168.${toString n}.0 netmask 255.255.255.0 { option routers 192.168.${toString n}.1; - # XXX: technically it's _not guaranteed_ that IP addresses will be - # issued from the first item in range onwards! We assume that in - # our tests however. range 192.168.${toString n}.2 192.168.${toString n}.254; } - ''); + '') + ; + machines = flip map vlanIfs (vlan: + { + hostName = "client${toString vlan}"; + ethernetAddress = qemu-flags.qemuNicMac vlan 1; + ipAddress = "192.168.${toString vlan}.2"; + } + ); }; services.radvd = { enable = true; diff --git a/nixos/tests/nextcloud/basic.nix b/nixos/tests/nextcloud/basic.nix index 92ac5c46e8f..72fb020dca7 100644 --- a/nixos/tests/nextcloud/basic.nix +++ b/nixos/tests/nextcloud/basic.nix @@ -9,14 +9,30 @@ in { nodes = { # The only thing the client needs to do is download a file. - client = { ... }: {}; + client = { ... }: { + services.davfs2.enable = true; + system.activationScripts.davfs2-secrets = '' + echo "http://nextcloud/remote.php/webdav/ ${adminuser} ${adminpass}" > /tmp/davfs2-secrets + chmod 600 /tmp/davfs2-secrets + ''; + fileSystems = pkgs.lib.mkVMOverride { + "/mnt/dav" = { + device = "http://nextcloud/remote.php/webdav/"; + fsType = "davfs"; + options = let + davfs2Conf = (pkgs.writeText "davfs2.conf" "secrets /tmp/davfs2-secrets"); + in [ "conf=${davfs2Conf}" "x-systemd.automount" "noauto"]; + }; + }; + }; - nextcloud = { config, pkgs, ... }: { + nextcloud = { config, pkgs, ... }: let + cfg = config; + in { networking.firewall.allowedTCPPorts = [ 80 ]; services.nextcloud = { enable = true; - nginx.enable = true; hostName = "nextcloud"; config = { # Don't inherit adminuser since "root" is supposed to be the default @@ -27,6 +43,8 @@ in { startAt = "20:00"; }; }; + + environment.systemPackages = [ cfg.services.nextcloud.occ ]; }; }; @@ -52,6 +70,8 @@ in { in '' start_all() nextcloud.wait_for_unit("multi-user.target") + # This is just to ensure the nextcloud-occ program is working + nextcloud.succeed("nextcloud-occ status") nextcloud.succeed("curl -sSf http://nextcloud/login") nextcloud.succeed( "${withRcloneEnv} ${copySharedFile}" @@ -60,5 +80,6 @@ in { client.succeed( "${withRcloneEnv} ${diffSharedFile}" ) + assert "hi" in client.succeed("cat /mnt/dav/test-shared-file") ''; }) diff --git a/nixos/tests/nextcloud/with-mysql-and-memcached.nix b/nixos/tests/nextcloud/with-mysql-and-memcached.nix index 8db630be893..bec3815a3e1 100644 --- a/nixos/tests/nextcloud/with-mysql-and-memcached.nix +++ b/nixos/tests/nextcloud/with-mysql-and-memcached.nix @@ -17,7 +17,6 @@ in { services.nextcloud = { enable = true; hostName = "nextcloud"; - nginx.enable = true; https = true; caching = { apcu = true; diff --git a/nixos/tests/nextcloud/with-postgresql-and-redis.nix b/nixos/tests/nextcloud/with-postgresql-and-redis.nix index 95219cac9be..40a208115c3 100644 --- a/nixos/tests/nextcloud/with-postgresql-and-redis.nix +++ b/nixos/tests/nextcloud/with-postgresql-and-redis.nix @@ -17,7 +17,6 @@ in { services.nextcloud = { enable = true; hostName = "nextcloud"; - nginx.enable = true; caching = { apcu = false; redis = true; diff --git a/nixos/tests/nfs/kerberos.nix b/nixos/tests/nfs/kerberos.nix index 1f2d0d453ea..078f0b7814c 100644 --- a/nixos/tests/nfs/kerberos.nix +++ b/nixos/tests/nfs/kerberos.nix @@ -3,7 +3,7 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: with lib; let - krb5 = + krb5 = { enable = true; domain_realm."nfs.test" = "NFS.TEST"; libdefaults.default_realm = "NFS.TEST"; @@ -31,7 +31,7 @@ in { name = "nfsv4-with-kerberos"; - + nodes = { client = { lib, ... }: { inherit krb5 users; diff --git a/nixos/tests/nginx-pubhtml.nix b/nixos/tests/nginx-pubhtml.nix index 432913cb42d..6e1e605628e 100644 --- a/nixos/tests/nginx-pubhtml.nix +++ b/nixos/tests/nginx-pubhtml.nix @@ -2,6 +2,7 @@ import ./make-test-python.nix { name = "nginx-pubhtml"; machine = { pkgs, ... }: { + systemd.services.nginx.serviceConfig.ProtectHome = "read-only"; services.nginx.enable = true; services.nginx.virtualHosts.localhost = { locations."~ ^/\\~([a-z0-9_]+)(/.*)?$".alias = "/home/$1/public_html$2"; diff --git a/nixos/tests/nginx-sandbox.nix b/nixos/tests/nginx-sandbox.nix new file mode 100644 index 00000000000..bc9d3ba8add --- /dev/null +++ b/nixos/tests/nginx-sandbox.nix @@ -0,0 +1,66 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "nginx-sandbox"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ izorkin ]; + }; + + # This test checks the creation and reading of a file in sandbox mode. Used simple lua script. + + 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.enableSandbox = true; + 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-variants.nix b/nixos/tests/nginx-variants.nix new file mode 100644 index 00000000000..ca4655391bc --- /dev/null +++ b/nixos/tests/nginx-variants.nix @@ -0,0 +1,33 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing-python.nix { inherit system pkgs; }; + +builtins.listToAttrs ( + builtins.map + (nginxName: + { + name = nginxName; + value = makeTest { + name = "nginx-variant-${nginxName}"; + + machine = { pkgs, ... }: { + services.nginx = { + enable = true; + virtualHosts.localhost.locations."/".return = "200 'foo'"; + package = pkgs."${nginxName}"; + }; + }; + + testScript = '' + machine.wait_for_unit("nginx") + machine.wait_for_open_port(80) + machine.succeed('test "$(curl -fvvv http://localhost/)" = foo') + ''; + }; + } + ) + [ "nginxStable" "nginxUnstable" "nginxShibboleth" "openresty" "tengine" ] +) diff --git a/nixos/tests/pinnwand.nix b/nixos/tests/pinnwand.nix new file mode 100644 index 00000000000..2204e74b2c2 --- /dev/null +++ b/nixos/tests/pinnwand.nix @@ -0,0 +1,86 @@ +import ./make-test-python.nix ({ pkgs, ...}: +let + pythonEnv = pkgs.python3.withPackages (py: with py; [ appdirs toml ]); + + port = 8000; + baseUrl = "http://server:${toString port}"; + + configureSteck = pkgs.writeScript "configure.py" '' + #!${pythonEnv.interpreter} + import appdirs + import toml + import os + + CONFIG = { + "base": "${baseUrl}/", + "confirm": False, + "magic": True, + "ignore": True + } + + os.makedirs(appdirs.user_config_dir('steck')) + with open(os.path.join(appdirs.user_config_dir('steck'), 'steck.toml'), "w") as fd: + toml.dump(CONFIG, fd) + ''; +in +{ + name = "pinnwand"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers =[ hexa ]; + }; + + nodes = { + server = { config, ... }: + { + networking.firewall.allowedTCPPorts = [ + port + ]; + + services.pinnwand = { + enable = true; + port = port; + }; + }; + + client = { pkgs, ... }: + { + environment.systemPackages = [ pkgs.steck ]; + }; + }; + + testScript = '' + start_all() + + server.wait_for_unit("pinnwand.service") + client.wait_for_unit("network.target") + + # create steck.toml config file + client.succeed("${configureSteck}") + + # wait until the server running pinnwand is reachable + client.wait_until_succeeds("ping -c1 server") + + # make sure pinnwand is listening + server.wait_until_succeeds("ss -lnp | grep ${toString port}") + + # send the contents of /etc/machine-id + response = client.succeed("steck paste /etc/machine-id") + + # parse the steck response + raw_url = None + removal_link = None + for line in response.split("\n"): + if line.startswith("View link:"): + raw_url = f"${baseUrl}/raw/{line.split('/')[-1]}" + if line.startswith("Removal link:"): + removal_link = line.split(":", 1)[1] + + # check whether paste matches what we sent + client.succeed(f"curl {raw_url} > /tmp/machine-id") + client.succeed("diff /tmp/machine-id /etc/machine-id") + + # remove paste and check that it's not available any more + client.succeed(f"curl {removal_link}") + client.fail(f"curl --fail {raw_url}") + ''; +}) diff --git a/nixos/tests/plasma5.nix b/nixos/tests/plasma5.nix index 2eccfdf47f5..5a603f8cbfb 100644 --- a/nixos/tests/plasma5.nix +++ b/nixos/tests/plasma5.nix @@ -14,7 +14,7 @@ import ./make-test-python.nix ({ pkgs, ...} : services.xserver.displayManager.sddm.enable = true; services.xserver.displayManager.defaultSession = "plasma5"; services.xserver.desktopManager.plasma5.enable = true; - services.xserver.displayManager.sddm.autoLogin = { + services.xserver.displayManager.autoLogin = { enable = true; user = "alice"; }; diff --git a/nixos/tests/podman.nix b/nixos/tests/podman.nix index 283db71d9a4..cd8c2b4308c 100644 --- a/nixos/tests/podman.nix +++ b/nixos/tests/podman.nix @@ -12,9 +12,6 @@ import ./make-test-python.nix ( { pkgs, ... }: { virtualisation.podman.enable = true; - virtualisation.containers.users = [ - "alice" - ]; users.users.alice = { isNormalUser = true; @@ -38,23 +35,45 @@ import ./make-test-python.nix ( start_all() - with subtest("Run container as root"): + with subtest("Run container as root with runc"): podman.succeed("tar cv --files-from /dev/null | podman import - scratchimg") podman.succeed( - "podman run -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10" + "podman run --runtime=runc -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10" ) podman.succeed("podman ps | grep sleeping") podman.succeed("podman stop sleeping") + podman.succeed("podman rm sleeping") - with subtest("Run container rootless"): + with subtest("Run container as root with crun"): + podman.succeed("tar cv --files-from /dev/null | podman import - scratchimg") + podman.succeed( + "podman run --runtime=crun -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10" + ) + podman.succeed("podman ps | grep sleeping") + podman.succeed("podman stop sleeping") + podman.succeed("podman rm sleeping") + + with subtest("Run container rootless with runc"): + podman.succeed(su_cmd("tar cv --files-from /dev/null | podman import - scratchimg")) + podman.succeed( + su_cmd( + "podman run --runtime=runc -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10" + ) + ) + podman.succeed(su_cmd("podman ps | grep sleeping")) + podman.succeed(su_cmd("podman stop sleeping")) + podman.succeed(su_cmd("podman rm sleeping")) + + with subtest("Run container rootless with crun"): podman.succeed(su_cmd("tar cv --files-from /dev/null | podman import - scratchimg")) podman.succeed( su_cmd( - "podman run -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10" + "podman run --runtime=crun -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10" ) ) podman.succeed(su_cmd("podman ps | grep sleeping")) podman.succeed(su_cmd("podman stop sleeping")) + podman.succeed(su_cmd("podman rm sleeping")) ''; } ) diff --git a/nixos/tests/postfix-raise-smtpd-tls-security-level.nix b/nixos/tests/postfix-raise-smtpd-tls-security-level.nix new file mode 100644 index 00000000000..b3c2156122d --- /dev/null +++ b/nixos/tests/postfix-raise-smtpd-tls-security-level.nix @@ -0,0 +1,44 @@ +let + certs = import ./common/acme/server/snakeoil-certs.nix; +in +import ./make-test-python.nix { + name = "postfix"; + + machine = { pkgs, ... }: { + imports = [ common/user-account.nix ]; + services.postfix = { + enable = true; + enableSubmissions = true; + submissionsOptions = { + smtpd_tls_security_level = "none"; + }; + }; + + environment.systemPackages = let + checkConfig = pkgs.writeScriptBin "check-config" '' + #!${pkgs.python3.interpreter} + import sys + + state = 1 + success = False + + with open("/etc/postfix/master.cf") as masterCf: + for line in masterCf: + if state == 1 and line.startswith("submissions"): + state = 2 + elif state == 2 and line.startswith(" ") and "smtpd_tls_security_level=encrypt" in line: + success = True + elif state == 2 and not line.startswith(" "): + state == 3 + if not success: + sys.exit(1) + ''; + + in [ checkConfig ]; + }; + + testScript = '' + machine.wait_for_unit("postfix.service") + machine.succeed("check-config") + ''; +} diff --git a/nixos/tests/postfix.nix b/nixos/tests/postfix.nix new file mode 100644 index 00000000000..b0674ca3a0d --- /dev/null +++ b/nixos/tests/postfix.nix @@ -0,0 +1,76 @@ +let + certs = import ./common/acme/server/snakeoil-certs.nix; +in +import ./make-test-python.nix { + name = "postfix"; + + machine = { pkgs, ... }: { + imports = [ common/user-account.nix ]; + services.postfix = { + enable = true; + enableSubmission = true; + enableSubmissions = true; + sslCACert = certs.ca.cert; + sslCert = certs."acme.test".cert; + sslKey = certs."acme.test".key; + submissionsOptions = { + smtpd_sasl_auth_enable = "yes"; + smtpd_client_restrictions = "permit"; + milter_macro_daemon_name = "ORIGINATING"; + }; + }; + + security.pki.certificateFiles = [ + certs.ca.cert + ]; + + networking.extraHosts = '' + 127.0.0.1 acme.test + ''; + + environment.systemPackages = let + sendTestMail = pkgs.writeScriptBin "send-testmail" '' + #!${pkgs.python3.interpreter} + import smtplib + + with smtplib.SMTP('acme.test') as smtp: + smtp.sendmail('root@localhost', 'alice@localhost', 'Subject: Test\n\nTest data.') + smtp.quit() + ''; + + sendTestMailStarttls = pkgs.writeScriptBin "send-testmail-starttls" '' + #!${pkgs.python3.interpreter} + import smtplib + import ssl + + ctx = ssl.create_default_context() + + with smtplib.SMTP('acme.test') as smtp: + smtp.ehlo() + smtp.starttls(context=ctx) + smtp.ehlo() + smtp.sendmail('root@localhost', 'alice@localhost', 'Subject: Test STARTTLS\n\nTest data.') + smtp.quit() + ''; + + sendTestMailSmtps = pkgs.writeScriptBin "send-testmail-smtps" '' + #!${pkgs.python3.interpreter} + import smtplib + import ssl + + ctx = ssl.create_default_context() + + with smtplib.SMTP_SSL(host='acme.test', context=ctx) as smtp: + smtp.sendmail('root@localhost', 'alice@localhost', 'Subject: Test SMTPS\n\nTest data.') + smtp.quit() + ''; + in [ sendTestMail sendTestMailStarttls sendTestMailSmtps ]; + }; + + testScript = '' + machine.wait_for_unit("postfix.service") + machine.succeed("send-testmail") + machine.succeed("send-testmail-starttls") + machine.succeed("send-testmail-smtps") + ''; +} diff --git a/nixos/tests/postgresql-wal-receiver.nix b/nixos/tests/postgresql-wal-receiver.nix index 372dd9d8c1c..c50746aa838 100644 --- a/nixos/tests/postgresql-wal-receiver.nix +++ b/nixos/tests/postgresql-wal-receiver.nix @@ -28,6 +28,10 @@ let meta.maintainers = with maintainers; [ pacien ]; machine = { ... }: { + # Needed because this test uses a non-default 'services.postgresql.dataDir'. + systemd.tmpfiles.rules = [ + "d /var/db/postgresql 0700 postgres postgres" + ]; services.postgresql = { package = postgresqlPackage; enable = true; diff --git a/nixos/tests/privacyidea.nix b/nixos/tests/privacyidea.nix new file mode 100644 index 00000000000..45c7cd37c24 --- /dev/null +++ b/nixos/tests/privacyidea.nix @@ -0,0 +1,36 @@ +# Miscellaneous small tests that don't warrant their own VM run. + +import ./make-test-python.nix ({ pkgs, ...} : rec { + name = "privacyidea"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ fpletz ]; + }; + + machine = { ... }: { + virtualisation.cores = 2; + virtualisation.memorySize = 512; + + services.privacyidea = { + enable = true; + secretKey = "testing"; + pepper = "testing"; + adminPasswordFile = pkgs.writeText "admin-password" "testing"; + adminEmail = "root@localhost"; + }; + 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( + "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 4fc3668cfaf..b912e3425e0 100644 --- a/nixos/tests/prometheus-exporters.nix +++ b/nixos/tests/prometheus-exporters.nix @@ -56,6 +56,21 @@ let */ exporterTests = { + apcupsd = { + exporterConfig = { + enable = true; + }; + metricProvider = { + services.apcupsd.enable = true; + }; + exporterTest = '' + wait_for_unit("apcupsd.service") + wait_for_open_port(3551) + wait_for_unit("prometheus-apcupsd-exporter.service") + wait_for_open_port(9162) + succeed("curl -sSf http://localhost:9162/metrics | grep -q 'apcupsd_info'") + ''; + }; bind = { exporterConfig = { @@ -202,6 +217,69 @@ let ''; }; + keylight = { + # A hardware device is required to properly test this exporter, so just + # perform a couple of basic sanity checks that the exporter is running + # and requires a target, but cannot reach a specified target. + exporterConfig = { + enable = true; + }; + exporterTest = '' + wait_for_unit("prometheus-keylight-exporter.service") + wait_for_open_port(9288) + succeed( + "curl -sS --write-out '%{http_code}' -o /dev/null http://localhost:9288/metrics | grep -q '400'" + ) + succeed( + "curl -sS --write-out '%{http_code}' -o /dev/null http://localhost:9288/metrics?target=nosuchdevice | grep -q '500'" + ) + ''; + }; + + lnd = { + exporterConfig = { + enable = true; + lndTlsPath = "/var/lib/lnd/tls.cert"; + lndMacaroonDir = "/var/lib/lnd"; + }; + metricProvider = { + systemd.services.prometheus-lnd-exporter.serviceConfig.DynamicUser = false; + services.bitcoind.enable = true; + services.bitcoind.extraConfig = '' + rpcauth=bitcoinrpc:e8fe33f797e698ac258c16c8d7aadfbe$872bdb8f4d787367c26bcfd75e6c23c4f19d44a69f5d1ad329e5adf3f82710f7 + bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 + bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 + ''; + systemd.services.lnd = { + serviceConfig.ExecStart = '' + ${pkgs.lnd}/bin/lnd \ + --datadir=/var/lib/lnd \ + --tlscertpath=/var/lib/lnd/tls.cert \ + --tlskeypath=/var/lib/lnd/tls.key \ + --logdir=/var/log/lnd \ + --bitcoin.active \ + --bitcoin.mainnet \ + --bitcoin.node=bitcoind \ + --bitcoind.rpcuser=bitcoinrpc \ + --bitcoind.rpcpass=hunter2 \ + --bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 \ + --bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 \ + --readonlymacaroonpath=/var/lib/lnd/readonly.macaroon + ''; + serviceConfig.StateDirectory = "lnd"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + }; + }; + exporterTest = '' + wait_for_unit("lnd.service") + wait_for_open_port(10009) + wait_for_unit("prometheus-lnd-exporter.service") + wait_for_open_port(9092) + succeed("curl -sSf localhost:9092/metrics | grep -q '^promhttp_metric_handler'") + ''; + }; + mail = { exporterConfig = { enable = true; @@ -285,6 +363,31 @@ let ''; }; + modemmanager = { + exporterConfig = { + enable = true; + refreshRate = "10s"; + }; + metricProvider = { + # ModemManager is installed when NetworkManager is enabled. Ensure it is + # started and is wanted by NM and the exporter to start everything up + # in the right order. + networking.networkmanager.enable = true; + systemd.services.ModemManager = { + enable = true; + wantedBy = [ "NetworkManager.service" "prometheus-modemmanager-exporter.service" ]; + }; + }; + exporterTest = '' + wait_for_unit("ModemManager.service") + wait_for_unit("prometheus-modemmanager-exporter.service") + wait_for_open_port(9539) + succeed( + "curl -sSf http://localhost:9539/metrics | grep -q 'modemmanager_info'" + ) + ''; + }; + nextcloud = { exporterConfig = { enable = true; @@ -397,6 +500,20 @@ let ''; }; + redis = { + exporterConfig = { + enable = true; + }; + metricProvider.services.redis.enable = true; + exporterTest = '' + wait_for_unit("redis.service") + wait_for_unit("prometheus-redis-exporter.service") + wait_for_open_port(6379) + wait_for_open_port(9121) + wait_until_succeeds("curl -sSf localhost:9121/metrics | grep -q 'redis_up 1'") + ''; + }; + rspamd = { exporterConfig = { enable = true; diff --git a/nixos/tests/pt2-clone.nix b/nixos/tests/pt2-clone.nix new file mode 100644 index 00000000000..b502172e2ee --- /dev/null +++ b/nixos/tests/pt2-clone.nix @@ -0,0 +1,35 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "pt2-clone"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ fgaz ]; + }; + + machine = { config, pkgs, ... }: { + imports = [ + ./common/x11.nix + ]; + + services.xserver.enable = true; + sound.enable = true; + environment.systemPackages = [ pkgs.pt2-clone ]; + }; + + enableOCR = true; + + testScript = + '' + machine.wait_for_x() + # Add a dummy sound card, or the program won't start + machine.execute("modprobe snd-dummy") + + machine.execute("pt2-clone &") + + machine.wait_for_window(r"ProTracker") + machine.sleep(5) + # One of the few words that actually get recognized + if "LENGTH" not in machine.get_screen_text(): + raise Exception("Program did not start successfully") + machine.screenshot("screen") + ''; +}) + diff --git a/nixos/tests/qboot.nix b/nixos/tests/qboot.nix new file mode 100644 index 00000000000..12aef6decfa --- /dev/null +++ b/nixos/tests/qboot.nix @@ -0,0 +1,13 @@ +import ./make-test-python.nix ({ pkgs, ...} : { + name = "qboot"; + + machine = { ... }: { + virtualisation.bios = pkgs.qboot; + }; + + testScript = + '' + start_all() + machine.wait_for_unit("multi-user.target") + ''; +}) diff --git a/nixos/tests/radicale.nix b/nixos/tests/radicale.nix index c81e78a8f99..1d3679c82a2 100644 --- a/nixos/tests/radicale.nix +++ b/nixos/tests/radicale.nix @@ -14,9 +14,6 @@ let [storage] filesystem_folder = /tmp/collections - - [logging] - debug = True ''; }; # WARNING: DON'T DO THIS IN PRODUCTION! @@ -49,13 +46,18 @@ in services.radicale.extraArgs = [ "--export-storage" "/tmp/collections-new" ]; + system.stateVersion = "17.03"; }; radicale2_verify = lib.recursiveUpdate radicale2 { - services.radicale.extraArgs = [ "--verify-storage" ]; + services.radicale.extraArgs = [ "--debug" "--verify-storage" ]; + system.stateVersion = "17.09"; }; radicale2 = lib.recursiveUpdate (common args) { system.stateVersion = "17.09"; }; + radicale3 = lib.recursiveUpdate (common args) { + system.stateVersion = "20.09"; + }; }; # This tests whether the web interface is accessible to an authenticated user @@ -117,6 +119,22 @@ in retcode == 0 and "VCALENDAR" in output ), "Could not read calendar from Radicale 2" - radicale.succeed("curl --fail http://${user}:${password}@localhost:${port}/.web/") + radicale.succeed("curl --fail http://${user}:${password}@localhost:${port}/.web/") + + with subtest("Check Radicale 3 functionality"): + radicale.succeed( + "${switchToConfig "radicale3"} >&2" + ) + radicale.wait_for_unit("radicale.service") + radicale.wait_for_open_port(${port}) + + (retcode, output) = radicale.execute( + "curl --fail http://${user}:${password}@localhost:${port}/someuser/calendar.ics/" + ) + assert ( + retcode == 0 and "VCALENDAR" in output + ), "Could not read calendar from Radicale 3" + + radicale.succeed("curl --fail http://${user}:${password}@localhost:${port}/.web/") ''; }) diff --git a/nixos/tests/restic.nix b/nixos/tests/restic.nix index 67bb7f1933d..dad5bdfff27 100644 --- a/nixos/tests/restic.nix +++ b/nixos/tests/restic.nix @@ -4,33 +4,50 @@ import ./make-test-python.nix ( let password = "some_password"; repository = "/tmp/restic-backup"; - passwordFile = pkgs.writeText "password" "correcthorsebatterystaple"; + rcloneRepository = "rclone:local:/tmp/restic-rclone-backup"; + + passwordFile = "${pkgs.writeText "password" "correcthorsebatterystaple"}"; + initialize = true; + paths = [ "/opt" ]; + pruneOpts = [ + "--keep-daily 2" + "--keep-weekly 1" + "--keep-monthly 1" + "--keep-yearly 99" + ]; in { name = "restic"; meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ bbigras ]; + maintainers = [ bbigras i077 ]; }; nodes = { server = - { ... }: + { pkgs, ... }: { services.restic.backups = { remotebackup = { - inherit repository; - passwordFile = "${passwordFile}"; - initialize = true; - paths = [ "/opt" ]; - pruneOpts = [ - "--keep-daily 2" - "--keep-weekly 1" - "--keep-monthly 1" - "--keep-yearly 99" - ]; + inherit repository passwordFile initialize paths pruneOpts; + }; + rclonebackup = { + repository = rcloneRepository; + rcloneConfig = { + type = "local"; + one_file_system = true; + }; + + # This gets overridden by rcloneConfig.type + rcloneConfigFile = pkgs.writeText "rclone.conf" '' + [local] + type=ftp + ''; + inherit passwordFile initialize paths pruneOpts; }; }; + + environment.sessionVariables.RCLONE_CONFIG_LOCAL_TYPE = "local"; }; }; @@ -38,25 +55,35 @@ import ./make-test-python.nix ( server.start() server.wait_for_unit("dbus.socket") server.fail( - "${pkgs.restic}/bin/restic -r ${repository} -p ${passwordFile} snapshots" + "${pkgs.restic}/bin/restic -r ${repository} -p ${passwordFile} snapshots", + "${pkgs.restic}/bin/restic -r ${rcloneRepository} -p ${passwordFile} snapshots", ) server.succeed( "mkdir -p /opt", "touch /opt/some_file", + "mkdir -p /tmp/restic-rclone-backup", "timedatectl set-time '2016-12-13 13:45'", "systemctl start restic-backups-remotebackup.service", + "systemctl start restic-backups-rclonebackup.service", '${pkgs.restic}/bin/restic -r ${repository} -p ${passwordFile} snapshots -c | grep -e "^1 snapshot"', + '${pkgs.restic}/bin/restic -r ${rcloneRepository} -p ${passwordFile} snapshots -c | grep -e "^1 snapshot"', "timedatectl set-time '2017-12-13 13:45'", "systemctl start restic-backups-remotebackup.service", + "systemctl start restic-backups-rclonebackup.service", "timedatectl set-time '2018-12-13 13:45'", "systemctl start restic-backups-remotebackup.service", + "systemctl start restic-backups-rclonebackup.service", "timedatectl set-time '2018-12-14 13:45'", "systemctl start restic-backups-remotebackup.service", + "systemctl start restic-backups-rclonebackup.service", "timedatectl set-time '2018-12-15 13:45'", "systemctl start restic-backups-remotebackup.service", + "systemctl start restic-backups-rclonebackup.service", "timedatectl set-time '2018-12-16 13:45'", "systemctl start restic-backups-remotebackup.service", + "systemctl start restic-backups-rclonebackup.service", '${pkgs.restic}/bin/restic -r ${repository} -p ${passwordFile} snapshots -c | grep -e "^4 snapshot"', + '${pkgs.restic}/bin/restic -r ${rcloneRepository} -p ${passwordFile} snapshots -c | grep -e "^4 snapshot"', ) ''; } diff --git a/nixos/tests/sddm.nix b/nixos/tests/sddm.nix index a145705250f..f9b961163c3 100644 --- a/nixos/tests/sddm.nix +++ b/nixos/tests/sddm.nix @@ -44,8 +44,8 @@ let machine = { ... }: { imports = [ ./common/user-account.nix ]; services.xserver.enable = true; - services.xserver.displayManager.sddm = { - enable = true; + services.xserver.displayManager = { + sddm.enable = true; autoLogin = { enable = true; user = "alice"; diff --git a/nixos/tests/shattered-pixel-dungeon.nix b/nixos/tests/shattered-pixel-dungeon.nix new file mode 100644 index 00000000000..cf6ee8db80b --- /dev/null +++ b/nixos/tests/shattered-pixel-dungeon.nix @@ -0,0 +1,29 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "shattered-pixel-dungeon"; + meta = with pkgs.lib.maintainers; { + maintainers = [ fgaz ]; + }; + + machine = { config, pkgs, ... }: { + imports = [ + ./common/x11.nix + ]; + + services.xserver.enable = true; + environment.systemPackages = [ pkgs.shattered-pixel-dungeon ]; + }; + + enableOCR = true; + + testScript = + '' + machine.wait_for_x() + machine.execute("shattered-pixel-dungeon &") + 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.screenshot("screen") + ''; +}) + diff --git a/nixos/tests/slurm.nix b/nixos/tests/slurm.nix index d0e62d15437..a54c5d9db48 100644 --- a/nixos/tests/slurm.nix +++ b/nixos/tests/slurm.nix @@ -1,16 +1,52 @@ -import ./make-test-python.nix ({ lib, ... }: +import ./make-test-python.nix ({ lib, pkgs, ... }: let - mungekey = "mungeverryweakkeybuteasytointegratoinatest"; - slurmconfig = { - controlMachine = "control"; - nodeName = [ "node[1-3] CPUs=1 State=UNKNOWN" ]; - partitionName = [ "debug Nodes=node[1-3] Default=YES MaxTime=INFINITE State=UP" ]; - extraConfig = '' - AccountingStorageHost=dbd - AccountingStorageType=accounting_storage/slurmdbd - ''; + services.slurm = { + controlMachine = "control"; + nodeName = [ "node[1-3] CPUs=1 State=UNKNOWN" ]; + partitionName = [ "debug Nodes=node[1-3] Default=YES MaxTime=INFINITE State=UP" ]; + extraConfig = '' + AccountingStorageHost=dbd + AccountingStorageType=accounting_storage/slurmdbd + ''; + }; + environment.systemPackages = [ mpitest ]; + networking.firewall.enable = false; + systemd.tmpfiles.rules = [ + "f /etc/munge/munge.key 0400 munge munge - mungeverryweakkeybuteasytointegratoinatest" + ]; }; + + mpitest = let + mpitestC = pkgs.writeText "mpitest.c" '' + #include <stdio.h> + #include <stdlib.h> + #include <mpi.h> + + int + main (int argc, char *argv[]) + { + int rank, size, length; + char name[512]; + + MPI_Init (&argc, &argv); + MPI_Comm_rank (MPI_COMM_WORLD, &rank); + MPI_Comm_size (MPI_COMM_WORLD, &size); + MPI_Get_processor_name (name, &length); + + if ( rank == 0 ) printf("size=%d\n", size); + + printf ("%s: hello world from process %d of %d\n", name, rank, size); + + MPI_Finalize (); + + return EXIT_SUCCESS; + } + ''; + in pkgs.runCommandNoCC "mpitest" {} '' + mkdir -p $out/bin + ${pkgs.openmpi}/bin/mpicc ${mpitestC} -o $out/bin/mpitest + ''; in { name = "slurm"; @@ -21,37 +57,40 @@ in { computeNode = { ...}: { + imports = [ slurmconfig ]; # TODO slurmd port and slurmctld port should be configurations and # automatically allowed by the firewall. - networking.firewall.enable = false; services.slurm = { client.enable = true; - } // slurmconfig; + }; }; in { control = { ...}: { - networking.firewall.enable = false; + imports = [ slurmconfig ]; services.slurm = { server.enable = true; - } // slurmconfig; + }; }; submit = { ...}: { - networking.firewall.enable = false; + imports = [ slurmconfig ]; services.slurm = { enableStools = true; - } // slurmconfig; + }; }; dbd = { pkgs, ... } : { networking.firewall.enable = false; + systemd.tmpfiles.rules = [ + "f /etc/munge/munge.key 0400 munge munge - mungeverryweakkeybuteasytointegratoinatest" + ]; services.slurm.dbdserver = { enable = true; storagePass = "password123"; @@ -87,24 +126,7 @@ in { '' start_all() - # Set up authentification across the cluster - for node in [submit, control, dbd, node1, node2, node3]: - - node.wait_for_unit("default.target") - - node.succeed("mkdir /etc/munge") - node.succeed( - "echo '${mungekey}' > /etc/munge/munge.key" - ) - node.succeed("chmod 0400 /etc/munge/munge.key") - node.succeed("chown munge:munge /etc/munge/munge.key") - node.succeed("systemctl restart munged") - - node.wait_for_unit("munged") - - - # Restart the services since they have probably failed due to the munge init - # failure + # Make sure DBD is up after DB initialzation with subtest("can_start_slurmdbd"): dbd.succeed("systemctl restart slurmdbd") dbd.wait_for_unit("slurmdbd.service") @@ -137,5 +159,8 @@ in { # find the srun job from above in the database control.succeed("sleep 5") control.succeed("sacct | grep hostname") + + with subtest("run_PMIx_mpitest"): + submit.succeed("srun -N 3 --mpi=pmix mpitest | grep size=3") ''; }) diff --git a/nixos/tests/snapcast.nix b/nixos/tests/snapcast.nix new file mode 100644 index 00000000000..92534f10281 --- /dev/null +++ b/nixos/tests/snapcast.nix @@ -0,0 +1,58 @@ +import ./make-test-python.nix ({ pkgs, ...} : + +let + port = 10004; + tcpPort = 10005; + httpPort = 10080; +in { + name = "snapcast"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ hexa ]; + }; + + nodes = { + server = { + services.snapserver = { + enable = true; + port = port; + tcp.port = tcpPort; + http.port = httpPort; + streams = { + mpd = { + type = "pipe"; + location = "/run/snapserver/mpd"; + }; + bluetooth = { + type = "pipe"; + location = "/run/snapserver/bluetooth"; + }; + }; + }; + }; + }; + + testScript = '' + import json + + get_rpc_version = {"id": "1", "jsonrpc": "2.0", "method": "Server.GetRPCVersion"} + + start_all() + + server.wait_for_unit("snapserver.service") + server.wait_until_succeeds("ss -ntl | grep -q ${toString port}") + server.wait_until_succeeds("ss -ntl | grep -q ${toString tcpPort}") + server.wait_until_succeeds("ss -ntl | grep -q ${toString httpPort}") + + with subtest("check that pipes are created"): + server.succeed("test -p /run/snapserver/mpd") + server.succeed("test -p /run/snapserver/bluetooth") + + with subtest("test tcp json-rpc"): + server.succeed(f"echo '{json.dumps(get_rpc_version)}' | nc -w 1 localhost ${toString tcpPort}") + + with subtest("test http json-rpc"): + server.succeed( + "curl --fail http://localhost:${toString httpPort}/jsonrpc -d '{json.dumps(get_rpc_version)}'" + ) + ''; +}) diff --git a/nixos/tests/sogo.nix b/nixos/tests/sogo.nix new file mode 100644 index 00000000000..016331a9eed --- /dev/null +++ b/nixos/tests/sogo.nix @@ -0,0 +1,58 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "sogo"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ajs124 das_j ]; + }; + + nodes = { + sogo = { config, pkgs, ... }: { + services.nginx.enable = true; + + services.mysql = { + enable = true; + package = pkgs.mysql; + ensureDatabases = [ "sogo" ]; + ensureUsers = [{ + name = "sogo"; + ensurePermissions = { + "sogo.*" = "ALL PRIVILEGES"; + }; + }]; + }; + + services.sogo = { + enable = true; + timezone = "Europe/Berlin"; + extraConfig = '' + WOWorkersCount = 1; + + SOGoUserSources = ( + { + type = sql; + userPasswordAlgorithm = md5; + viewURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_users"; + canAuthenticate = YES; + id = users; + } + ); + + SOGoProfileURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_user_profile"; + OCSFolderInfoURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_folder_info"; + OCSSessionsFolderURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_sessions_folder"; + OCSEMailAlarmsFolderURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_alarms_folder"; + OCSStoreURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_store"; + OCSAclURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_acl"; + OCSCacheFolderURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_cache_folder"; + ''; + }; + }; + }; + + testScript = '' + start_all() + sogo.wait_for_unit("multi-user.target") + sogo.wait_for_open_port(20000) + sogo.wait_for_open_port(80) + sogo.succeed("curl -sSfL http://sogo/SOGo") + ''; +}) diff --git a/nixos/tests/sslh.nix b/nixos/tests/sslh.nix new file mode 100644 index 00000000000..2a800aa52d0 --- /dev/null +++ b/nixos/tests/sslh.nix @@ -0,0 +1,83 @@ +import ./make-test-python.nix { + name = "sslh"; + + nodes = { + server = { pkgs, lib, ... }: { + networking.firewall.allowedTCPPorts = [ 443 ]; + networking.interfaces.eth1.ipv6.addresses = [ + { + address = "fe00:aa:bb:cc::2"; + 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"; }, + ); + ''; + }; + services.openssh.enable = true; + users.users.root.openssh.authorizedKeys.keyFiles = [ ./initrd-network-ssh/id_ed25519.pub ]; + services.nginx = { + enable = true; + virtualHosts."localhost" = { + addSSL = false; + default = true; + root = pkgs.runCommand "testdir" {} '' + mkdir "$out" + echo hello world > "$out/index.html" + ''; + }; + }; + }; + client = { ... }: { + networking.interfaces.eth1.ipv6.addresses = [ + { + address = "fe00:aa:bb:cc::1"; + prefixLength = 64; + } + ]; + networking.hosts."fe00:aa:bb:cc::2" = [ "server" ]; + environment.etc.sshKey = { + source = ./initrd-network-ssh/id_ed25519; # dont use this anywhere else + mode = "0600"; + }; + }; + }; + + testScript = '' + start_all() + + server.wait_for_unit("sslh.service") + server.wait_for_unit("nginx.service") + server.wait_for_unit("sshd.service") + server.wait_for_open_port(80) + server.wait_for_open_port(443) + server.wait_for_open_port(22) + + for arg in ["-6", "-4"]: + client.wait_until_succeeds(f"ping {arg} -c1 server") + + # check that ssh through sslh works + client.succeed( + f"ssh {arg} -p 443 -i /etc/sshKey -o StrictHostKeyChecking=accept-new server 'echo $SSH_CONNECTION > /tmp/foo{arg}'" + ) + + # check that 1/ the above ssh command had an effect 2/ transparent proxying really works + ip = "fe00:aa:bb:cc::1" if arg == "-6" else "192.168.1." + server.succeed(f"grep '{ip}' /tmp/foo{arg}") + + # check that http through sslh works + assert client.succeed(f"curl {arg} http://server:443").strip() == "hello world" + ''; +} diff --git a/nixos/tests/sudo.nix b/nixos/tests/sudo.nix index 5bbec3d5726..8c38f1b47ef 100644 --- a/nixos/tests/sudo.nix +++ b/nixos/tests/sudo.nix @@ -74,7 +74,7 @@ in with subtest("test5 user should not be able to run commands under root"): machine.fail("sudo -u test5 sudo -n -u root true") - with subtest("test5 user should be able to keep his environment"): + with subtest("test5 user should be able to keep their environment"): machine.succeed("sudo -u test5 sudo -n -E -u test1 true") with subtest("users in group 'barfoo' should not be able to keep their environment"): diff --git a/nixos/tests/syncthing-init.nix b/nixos/tests/syncthing-init.nix index 9c8e0a3d087..0a01da52b68 100644 --- a/nixos/tests/syncthing-init.nix +++ b/nixos/tests/syncthing-init.nix @@ -24,9 +24,8 @@ in { testScript = '' machine.wait_for_unit("syncthing-init.service") config = machine.succeed("cat /var/lib/syncthing/.config/syncthing/config.xml") - + assert "testFolder" in config assert "${testId}" in config ''; }) - diff --git a/nixos/tests/syncthing.nix b/nixos/tests/syncthing.nix new file mode 100644 index 00000000000..9e2a8e01e3f --- /dev/null +++ b/nixos/tests/syncthing.nix @@ -0,0 +1,65 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: { + name = "syncthing"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ chkno ]; + + nodes = rec { + a = { + environment.systemPackages = with pkgs; [ curl libxml2 syncthing ]; + services.syncthing = { + enable = true; + openDefaultPorts = true; + }; + }; + b = a; + }; + + testScript = '' + import json + import shlex + + confdir = "/var/lib/syncthing/.config/syncthing" + + + def addPeer(host, name, deviceID): + APIKey = host.succeed( + "xmllint --xpath 'string(configuration/gui/apikey)' %s/config.xml" % confdir + ).strip() + oldConf = host.succeed( + "curl -Ss -H 'X-API-Key: %s' 127.0.0.1:8384/rest/system/config" % APIKey + ) + conf = json.loads(oldConf) + conf["devices"].append({"deviceID": deviceID, "id": name}) + conf["folders"].append( + { + "devices": [{"deviceID": deviceID}], + "id": "foo", + "path": "/var/lib/syncthing/foo", + "rescanIntervalS": 1, + } + ) + newConf = json.dumps(conf) + host.succeed( + "curl -Ss -H 'X-API-Key: %s' 127.0.0.1:8384/rest/system/config -d %s" + % (APIKey, shlex.quote(newConf)) + ) + + + start_all() + a.wait_for_unit("syncthing.service") + b.wait_for_unit("syncthing.service") + a.wait_for_open_port(22000) + b.wait_for_open_port(22000) + + aDeviceID = a.succeed("syncthing -home=%s -device-id" % confdir).strip() + bDeviceID = b.succeed("syncthing -home=%s -device-id" % confdir).strip() + addPeer(a, "b", bDeviceID) + addPeer(b, "a", aDeviceID) + + a.wait_for_file("/var/lib/syncthing/foo") + b.wait_for_file("/var/lib/syncthing/foo") + a.succeed("echo a2b > /var/lib/syncthing/foo/a2b") + b.succeed("echo b2a > /var/lib/syncthing/foo/b2a") + a.wait_for_file("/var/lib/syncthing/foo/b2a") + b.wait_for_file("/var/lib/syncthing/foo/a2b") + ''; +}) diff --git a/nixos/tests/systemd-binfmt.nix b/nixos/tests/systemd-binfmt.nix new file mode 100644 index 00000000000..2a676f3da98 --- /dev/null +++ b/nixos/tests/systemd-binfmt.nix @@ -0,0 +1,24 @@ +# Teach the kernel how to run armv7l and aarch64-linux binaries, +# and run GNU Hello for these architectures. +import ./make-test-python.nix ({ pkgs, ... }: { + name = "systemd-binfmt"; + machine = { + boot.binfmt.emulatedSystems = [ + "armv7l-linux" + "aarch64-linux" + ]; + }; + + testScript = let + helloArmv7l = pkgs.pkgsCross.armv7l-hf-multiplatform.hello; + helloAarch64 = pkgs.pkgsCross.aarch64-multiplatform.hello; + in '' + machine.start() + assert "world" in machine.succeed( + "${helloArmv7l}/bin/hello" + ) + assert "world" in machine.succeed( + "${helloAarch64}/bin/hello" + ) + ''; +}) diff --git a/nixos/tests/systemd-boot.nix b/nixos/tests/systemd-boot.nix index e911c393361..7a663dd9b42 100644 --- a/nixos/tests/systemd-boot.nix +++ b/nixos/tests/systemd-boot.nix @@ -6,26 +6,85 @@ with import ../lib/testing-python.nix { inherit system pkgs; }; with pkgs.lib; -makeTest { - name = "systemd-boot"; - meta.maintainers = with pkgs.stdenv.lib.maintainers; [ danielfullmer ]; - - machine = { pkgs, lib, ... }: { +let + common = { virtualisation.useBootLoader = true; virtualisation.useEFIBoot = true; boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + environment.systemPackages = [ pkgs.efibootmgr ]; }; +in +{ + basic = makeTest { + name = "systemd-boot"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ danielfullmer ]; + + machine = common; + + testScript = '' + machine.start() + machine.wait_for_unit("multi-user.target") + + machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf") + + # Ensure we actually booted using systemd-boot + # Magic number is the vendor UUID used by systemd-boot. + machine.succeed( + "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" + ) + + # "bootctl install" should have created an EFI entry + machine.succeed('efibootmgr | grep "Linux Boot Manager"') + ''; + }; + + # Boot without having created an EFI entry--instead using default "/EFI/BOOT/BOOTX64.EFI" + fallback = makeTest { + name = "systemd-boot-fallback"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ danielfullmer ]; + + machine = { pkgs, lib, ... }: { + imports = [ common ]; + boot.loader.efi.canTouchEfiVariables = mkForce false; + }; - testScript = '' - machine.start() - machine.wait_for_unit("multi-user.target") + testScript = '' + machine.start() + machine.wait_for_unit("multi-user.target") - machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf") + machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf") - # Ensure we actually booted using systemd-boot. - # Magic number is the vendor UUID used by systemd-boot. - machine.succeed( - "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" - ) - ''; + # Ensure we actually booted using systemd-boot + # Magic number is the vendor UUID used by systemd-boot. + machine.succeed( + "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" + ) + + # "bootctl install" should _not_ have created an EFI entry + machine.fail('efibootmgr | grep "Linux Boot Manager"') + ''; + }; + + update = makeTest { + name = "systemd-boot-update"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ danielfullmer ]; + + machine = common; + + testScript = '' + machine.succeed("mount -o remount,rw /boot") + + # Replace version inside sd-boot with something older. See magic[] string in systemd src/boot/efi/boot.c + machine.succeed( + """ + find /boot -iname '*.efi' -print0 | \ + xargs -0 -I '{}' sed -i 's/#### LoaderInfo: systemd-boot .* ####/#### LoaderInfo: systemd-boot 001 ####/' '{}' + """ + ) + + output = machine.succeed("/run/current-system/bin/switch-to-configuration boot") + assert "updating systemd-boot from 001 to " in output + ''; + }; } diff --git a/nixos/tests/systemd-networkd-vrf.nix b/nixos/tests/systemd-networkd-vrf.nix index af7813a2e60..bd4751f8e43 100644 --- a/nixos/tests/systemd-networkd-vrf.nix +++ b/nixos/tests/systemd-networkd-vrf.nix @@ -159,6 +159,8 @@ in { node2.wait_for_unit("network.target") node3.wait_for_unit("network.target") + # NOTE: please keep in mind that the trailing whitespaces in the following strings + # are intentional as the output is compared against the raw `iproute2`-output. client_ipv4_table = """ 192.168.1.2 dev vrf1 proto static metric 100 192.168.2.3 dev vrf2 proto static metric 100 @@ -194,18 +196,16 @@ in { client.succeed("ping -c5 192.168.1.2") client.succeed("ping -c5 192.168.2.3") - # Test whether SSH through a VRF IP is possible. - # (Note: this seems to be an issue on Linux 5.x, so I decided to add this to - # ensure that we catch this when updating the default kernel). - # with subtest("tcp traffic through vrf works"): - # node1.wait_for_open_port(22) - # 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 root@192.168.1.2 true" - # ) + # Test whether TCP through a VRF IP is possible. + with subtest("tcp traffic through vrf works"): + node1.wait_for_open_port(22) + client.succeed( + "cat ${snakeOilPrivateKey} > privkey.snakeoil" + ) + client.succeed("chmod 600 privkey.snakeoil") + client.succeed( + "ulimit -l 2048; ip vrf exec vrf1 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil root@192.168.1.2 true" + ) # Only configured routes through the VRF from the main routing table should # work. Additional IPs are only reachable when binding to the vrf interface. diff --git a/nixos/tests/systemd.nix b/nixos/tests/systemd.nix index ca2e36a443e..a653932fb37 100644 --- a/nixos/tests/systemd.nix +++ b/nixos/tests/systemd.nix @@ -50,6 +50,13 @@ import ./make-test-python.nix ({ pkgs, ... }: { fi ''; }; + + systemd.watchdog = { + device = "/dev/watchdog"; + runtimeTime = "30s"; + rebootTime = "10min"; + kexecTime = "5min"; + }; }; testScript = '' @@ -97,6 +104,11 @@ import ./make-test-python.nix ({ pkgs, ... }: { re.search(r"^Filesystem state: *clean$", extinfo, re.MULTILINE) is not None ), ("File system was not cleanly unmounted: " + extinfo) + # Regression test for https://github.com/NixOS/nixpkgs/pull/91232 + with subtest("setting transient hostnames works"): + machine.succeed("hostnamectl set-hostname --transient machine-transient") + machine.fail("hostnamectl set-hostname machine-all") + with subtest("systemd-shutdown works"): machine.shutdown() machine.wait_for_unit("multi-user.target") @@ -117,5 +129,20 @@ import ./make-test-python.nix ({ pkgs, ... }: { retcode, output = machine.execute("systemctl status testservice1.service") assert retcode in [0, 3] # https://bugs.freedesktop.org/show_bug.cgi?id=77507 assert "CPU:" in output + + # Test systemd is configured to manage a watchdog + with subtest("systemd manages hardware watchdog"): + machine.wait_for_unit("multi-user.target") + + # It seems that the device's path doesn't appear in 'systemctl show' so + # check it separately. + assert "WatchdogDevice=/dev/watchdog" in machine.succeed( + "cat /etc/systemd/system.conf" + ) + + output = machine.succeed("systemctl show | grep Watchdog") + assert "RuntimeWatchdogUSec=30s" in output + assert "RebootWatchdogUSec=10m" in output + assert "KExecWatchdogUSec=5m" in output ''; }) diff --git a/nixos/tests/taskserver.nix b/nixos/tests/taskserver.nix index ab9b589f859..f34782c7059 100644 --- a/nixos/tests/taskserver.nix +++ b/nixos/tests/taskserver.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, ... }: let +import ./make-test-python.nix ({ pkgs, ... }: let snakeOil = pkgs.runCommand "snakeoil-certs" { outputs = [ "out" "cacert" "cert" "key" "crl" ]; buildInputs = [ pkgs.gnutls.bin ]; @@ -105,186 +105,178 @@ in { newServerSystem = nodes.newServer.config.system.build.toplevel; switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test"; in '' - sub su ($$) { - my ($user, $cmd) = @_; - my $esc = $cmd =~ s/'/'\\${"'"}'/gr; - return "su - $user -c '$esc'"; - } - - sub setupClientsFor ($$;$) { - my ($org, $user, $extraInit) = @_; - - for my $client ($client1, $client2) { - $client->nest("initialize client for user $user", sub { - $client->succeed( - (su $user, "rm -rf /home/$user/.task"), - (su $user, "task rc.confirmation=no config confirmation no") - ); - - my $exportinfo = $server->succeed( - "nixos-taskserver user export $org $user" - ); - - $exportinfo =~ s/'/'\\'''/g; - - $client->nest("importing taskwarrior configuration", sub { - my $cmd = su $user, "eval '$exportinfo' >&2"; - my ($status, $out) = $client->execute_($cmd); - if ($status != 0) { - $client->log("output: $out"); - die "command `$cmd' did not succeed (exit code $status)\n"; - } - }); - - eval { &$extraInit($client, $org, $user) }; - - $client->succeed(su $user, - "task config taskd.server server:${portStr} >&2" - ); - - $client->succeed(su $user, "task sync init >&2"); - }); - } - } - - sub restartServer { - $server->succeed("systemctl restart taskserver.service"); - $server->waitForOpenPort(${portStr}); - } - - sub readdImperativeUser { - $server->nest("(re-)add imperative user bar", sub { - $server->execute("nixos-taskserver org remove imperativeOrg"); - $server->succeed( - "nixos-taskserver org add imperativeOrg", - "nixos-taskserver user add imperativeOrg bar" - ); - setupClientsFor "imperativeOrg", "bar"; - }); - } - - sub testSync ($) { - my $user = $_[0]; - subtest "sync for user $user", sub { - $client1->succeed(su $user, "task add foo >&2"); - $client1->succeed(su $user, "task sync >&2"); - $client2->fail(su $user, "task list >&2"); - $client2->succeed(su $user, "task sync >&2"); - $client2->succeed(su $user, "task list >&2"); - }; - } - - sub checkClientCert ($) { - my $user = $_[0]; - my $cmd = "gnutls-cli". - " --x509cafile=/home/$user/.task/keys/ca.cert". - " --x509keyfile=/home/$user/.task/keys/private.key". - " --x509certfile=/home/$user/.task/keys/public.cert". - " --port=${portStr} server < /dev/null"; - return su $user, $cmd; - } + from shlex import quote + + + def su(user, cmd): + return f"su - {user} -c {quote(cmd)}" + + + def no_extra_init(client, org, user): + pass + + + def setup_clients_for(org, user, extra_init=no_extra_init): + for client in [client1, client2]: + with client.nested(f"initialize client for user {user}"): + client.succeed( + su(user, f"rm -rf /home/{user}/.task"), + su(user, "task rc.confirmation=no config confirmation no"), + ) + + exportinfo = server.succeed(f"nixos-taskserver user export {org} {user}") + + with client.nested("importing taskwarrior configuration"): + client.succeed(su(user, f"eval {quote(exportinfo)} >&2")) + + extra_init(client, org, user) + + client.succeed(su(user, "task config taskd.server server:${portStr} >&2")) + + client.succeed(su(user, "task sync init >&2")) + + + def restart_server(): + server.systemctl("restart taskserver.service") + server.wait_for_open_port(${portStr}) + + + def re_add_imperative_user(): + with server.nested("(re-)add imperative user bar"): + server.execute("nixos-taskserver org remove imperativeOrg") + server.succeed( + "nixos-taskserver org add imperativeOrg", + "nixos-taskserver user add imperativeOrg bar", + ) + setup_clients_for("imperativeOrg", "bar") + + + def test_sync(user): + with subtest(f"sync for user {user}"): + client1.succeed(su(user, "task add foo >&2")) + client1.succeed(su(user, "task sync >&2")) + client2.fail(su(user, "task list >&2")) + client2.succeed(su(user, "task sync >&2")) + client2.succeed(su(user, "task list >&2")) + + + def check_client_cert(user): + # debug level 3 is a workaround for gnutls issue https://gitlab.com/gnutls/gnutls/-/issues/1040 + cmd = ( + f"gnutls-cli -d 3" + f" --x509cafile=/home/{user}/.task/keys/ca.cert" + f" --x509keyfile=/home/{user}/.task/keys/private.key" + f" --x509certfile=/home/{user}/.task/keys/public.cert" + f" --port=${portStr} server < /dev/null" + ) + return su(user, cmd) + # Explicitly start the VMs so that we don't accidentally start newServer - $server->start; - $client1->start; - $client2->start; + server.start() + client1.start() + client2.start() - $server->waitForUnit("taskserver.service"); + server.wait_for_unit("taskserver.service") - $server->succeed( - "nixos-taskserver user list testOrganisation | grep -qxF alice", - "nixos-taskserver user list testOrganisation | grep -qxF foo", - "nixos-taskserver user list anotherOrganisation | grep -qxF bob" - ); + server.succeed( + "nixos-taskserver user list testOrganisation | grep -qxF alice", + "nixos-taskserver user list testOrganisation | grep -qxF foo", + "nixos-taskserver user list anotherOrganisation | grep -qxF bob", + ) - $server->waitForOpenPort(${portStr}); + server.wait_for_open_port(${portStr}) - $client1->waitForUnit("multi-user.target"); - $client2->waitForUnit("multi-user.target"); + client1.wait_for_unit("multi-user.target") + client2.wait_for_unit("multi-user.target") - setupClientsFor "testOrganisation", "alice"; - setupClientsFor "testOrganisation", "foo"; - setupClientsFor "anotherOrganisation", "bob"; + setup_clients_for("testOrganisation", "alice") + setup_clients_for("testOrganisation", "foo") + setup_clients_for("anotherOrganisation", "bob") - testSync $_ for ("alice", "bob", "foo"); + for user in ["alice", "bob", "foo"]: + test_sync(user) - $server->fail("nixos-taskserver user add imperativeOrg bar"); - readdImperativeUser; + server.fail("nixos-taskserver user add imperativeOrg bar") + re_add_imperative_user() - testSync "bar"; + test_sync("bar") - subtest "checking certificate revocation of user bar", sub { - $client1->succeed(checkClientCert "bar"); + with subtest("checking certificate revocation of user bar"): + client1.succeed(check_client_cert("bar")) - $server->succeed("nixos-taskserver user remove imperativeOrg bar"); - restartServer; + server.succeed("nixos-taskserver user remove imperativeOrg bar") + restart_server() - $client1->fail(checkClientCert "bar"); + client1.fail(check_client_cert("bar")) - $client1->succeed(su "bar", "task add destroy everything >&2"); - $client1->fail(su "bar", "task sync >&2"); - }; + client1.succeed(su("bar", "task add destroy everything >&2")) + client1.fail(su("bar", "task sync >&2")) - readdImperativeUser; + re_add_imperative_user() - subtest "checking certificate revocation of org imperativeOrg", sub { - $client1->succeed(checkClientCert "bar"); + with subtest("checking certificate revocation of org imperativeOrg"): + client1.succeed(check_client_cert("bar")) - $server->succeed("nixos-taskserver org remove imperativeOrg"); - restartServer; + server.succeed("nixos-taskserver org remove imperativeOrg") + restart_server() - $client1->fail(checkClientCert "bar"); + client1.fail(check_client_cert("bar")) - $client1->succeed(su "bar", "task add destroy even more >&2"); - $client1->fail(su "bar", "task sync >&2"); - }; + client1.succeed(su("bar", "task add destroy even more >&2")) + client1.fail(su("bar", "task sync >&2")) - readdImperativeUser; + re_add_imperative_user() - subtest "check whether declarative config overrides user bar", sub { - restartServer; - testSync "bar"; - }; + with subtest("check whether declarative config overrides user bar"): + restart_server() + test_sync("bar") - subtest "check manual configuration", sub { - # Remove the keys from automatic CA creation, to make sure the new - # generation doesn't use keys from before. - $server->succeed('rm -rf ${cfg.dataDir}/keys/* >&2'); - - $server->succeed('${switchToNewServer} >&2'); - $server->waitForUnit("taskserver.service"); - $server->waitForOpenPort(${portStr}); - - $server->succeed( - "nixos-taskserver org add manualOrg", - "nixos-taskserver user add manualOrg alice" - ); - - setupClientsFor "manualOrg", "alice", sub { - my ($client, $org, $user) = @_; - my $cfgpath = "/home/$user/.task"; - - $client->copyFileFromHost("${snakeOil.cacert}", "$cfgpath/ca.cert"); - for my $file ('alice.key', 'alice.cert') { - $client->copyFileFromHost("${snakeOil}/$file", "$cfgpath/$file"); - } - - for my $file ("$user.key", "$user.cert") { - $client->copyFileFromHost( - "${snakeOil}/$file", "$cfgpath/$file" - ); - } - $client->copyFileFromHost( - "${snakeOil.cacert}", "$cfgpath/ca.cert" - ); - $client->succeed( - (su "alice", "task config taskd.ca $cfgpath/ca.cert"), - (su "alice", "task config taskd.key $cfgpath/$user.key"), - (su $user, "task config taskd.certificate $cfgpath/$user.cert") - ); - }; - testSync "alice"; - }; + def init_manual_config(client, org, user): + cfgpath = f"/home/{user}/.task" + + client.copy_from_host( + "${snakeOil.cacert}", + f"{cfgpath}/ca.cert", + ) + for file in ["alice.key", "alice.cert"]: + client.copy_from_host( + f"${snakeOil}/{file}", + f"{cfgpath}/{file}", + ) + + for file in [f"{user}.key", f"{user}.cert"]: + client.copy_from_host( + f"${snakeOil}/{file}", + f"{cfgpath}/{file}", + ) + + client.succeed( + su("alice", f"task config taskd.ca {cfgpath}/ca.cert"), + su("alice", f"task config taskd.key {cfgpath}/{user}.key"), + su(user, f"task config taskd.certificate {cfgpath}/{user}.cert"), + ) + + + with subtest("check manual configuration"): + # Remove the keys from automatic CA creation, to make sure the new + # generation doesn't use keys from before. + server.succeed("rm -rf ${cfg.dataDir}/keys/* >&2") + + server.succeed( + "${switchToNewServer} >&2" + ) + server.wait_for_unit("taskserver.service") + server.wait_for_open_port(${portStr}) + + server.succeed( + "nixos-taskserver org add manualOrg", + "nixos-taskserver user add manualOrg alice", + ) + + setup_clients_for("manualOrg", "alice", init_manual_config) + + test_sync("alice") ''; }) diff --git a/nixos/tests/teeworlds.nix b/nixos/tests/teeworlds.nix new file mode 100644 index 00000000000..edf58896878 --- /dev/null +++ b/nixos/tests/teeworlds.nix @@ -0,0 +1,55 @@ +import ./make-test-python.nix ({ pkgs, ... }: + +let + client = + { pkgs, ... }: + + { imports = [ ./common/x11.nix ]; + environment.systemPackages = [ pkgs.teeworlds ]; + }; + +in { + name = "teeworlds"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ hax404 ]; + }; + + nodes = + { server = + { services.teeworlds = { + enable = true; + openPorts = true; + }; + }; + + client1 = client; + client2 = client; + }; + + testScript = + '' + start_all() + + server.wait_for_unit("teeworlds.service") + server.wait_until_succeeds("ss --numeric --udp --listening | grep -q 8303") + + client1.wait_for_x() + client2.wait_for_x() + + client1.execute("teeworlds 'player_name Alice;connect server'&") + server.wait_until_succeeds( + 'journalctl -u teeworlds -e | grep --extended-regexp -q "team_join player=\'[0-9]:Alice"' + ) + + client2.execute("teeworlds 'player_name Bob;connect server'&") + server.wait_until_succeeds( + 'journalctl -u teeworlds -e | grep --extended-regexp -q "team_join player=\'[0-9]:Bob"' + ) + + server.sleep(10) # wait for a while to get a nice screenshot + + client1.screenshot("screen_client1") + client2.screenshot("screen_client2") + ''; + +}) diff --git a/nixos/tests/tiddlywiki.nix b/nixos/tests/tiddlywiki.nix index cf45578b0f9..822711b8939 100644 --- a/nixos/tests/tiddlywiki.nix +++ b/nixos/tests/tiddlywiki.nix @@ -44,7 +44,7 @@ import ./make-test-python.nix ({ ... }: { configured.succeed( "curl --fail -o /dev/null 127.0.0.1:3000 --user somelogin:somesecret" ) - + with subtest("restart preserves changes"): # given running wiki default.wait_for_unit("tiddlywiki.service") diff --git a/nixos/tests/transmission.nix b/nixos/tests/transmission.nix index f4f2186be1f..37c0352dcfb 100644 --- a/nixos/tests/transmission.nix +++ b/nixos/tests/transmission.nix @@ -9,6 +9,8 @@ import ./make-test-python.nix ({ pkgs, ...} : { networking.firewall.allowedTCPPorts = [ 9091 ]; + security.apparmor.enable = true; + services.transmission.enable = true; }; diff --git a/nixos/tests/trezord.nix b/nixos/tests/trezord.nix index 8d908a52249..b7b3dd31942 100644 --- a/nixos/tests/trezord.nix +++ b/nixos/tests/trezord.nix @@ -1,9 +1,8 @@ import ./make-test-python.nix ({ pkgs, ... }: { name = "trezord"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ mmahut "1000101" ]; + meta = with pkgs.stdenv.lib; { + maintainers = with maintainers; [ mmahut _1000101 ]; }; - nodes = { machine = { ... }: { services.trezord.enable = true; diff --git a/nixos/tests/trickster.nix b/nixos/tests/trickster.nix index e2ca00980d5..713ac8f0b2f 100644 --- a/nixos/tests/trickster.nix +++ b/nixos/tests/trickster.nix @@ -1,7 +1,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { name = "trickster"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ "1000101" ]; + meta = with pkgs.stdenv.lib; { + maintainers = with maintainers; [ _1000101 ]; }; nodes = { @@ -34,4 +34,4 @@ import ./make-test-python.nix ({ pkgs, ... }: { "curl -L http://localhost:9090/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'" ) ''; -}) \ No newline at end of file +}) diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix index aec8da6a2af..af76e6f9844 100644 --- a/nixos/tests/virtualbox.nix +++ b/nixos/tests/virtualbox.nix @@ -198,7 +198,6 @@ let systemd.services."vboxtestlog-${name}@" = { description = "VirtualBox Test Machine Log For ${name}"; serviceConfig.StandardInput = "socket"; - serviceConfig.StandardOutput = "syslog"; serviceConfig.SyslogIdentifier = "GUEST-${name}"; serviceConfig.ExecStart = "${pkgs.coreutils}/bin/cat"; }; diff --git a/nixos/tests/wasabibackend.nix b/nixos/tests/wasabibackend.nix new file mode 100644 index 00000000000..d169ad15272 --- /dev/null +++ b/nixos/tests/wasabibackend.nix @@ -0,0 +1,38 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "wasabibackend"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ mmahut ]; + }; + + nodes = { + machine = { ... }: { + services.wasabibackend = { + enable = true; + network = "testnet"; + rpc = { + user = "alice"; + port = 18332; + }; + }; + services.bitcoind = { + enable = true; + testnet = true; + rpc.users = { + alice.passwordHMAC = "e7096bc21da60b29ecdbfcdb2c3acc62$f948e61cb587c399358ed99c6ed245a41460b4bf75125d8330c9f6fcc13d7ae7"; + }; + }; + }; + }; + + testScript = '' + start_all() + machine.wait_for_unit("wasabibackend.service") + machine.wait_until_succeeds( + "grep 'Wasabi Backend started' /var/lib/wasabibackend/.walletwasabi/backend/Logs.txt" + ) + machine.sleep(5) + machine.succeed( + "grep 'Config is successfully initialized' /var/lib/wasabibackend/.walletwasabi/backend/Logs.txt" + ) + ''; +}) diff --git a/nixos/tests/web-servers/unit-php.nix b/nixos/tests/web-servers/unit-php.nix index c6327a1f825..2a0a5bdaa5d 100644 --- a/nixos/tests/web-servers/unit-php.nix +++ b/nixos/tests/web-servers/unit-php.nix @@ -23,7 +23,10 @@ in { "user": "testuser", "group": "testgroup", "root": "${testdir}/www", - "index": "info.php" + "index": "info.php", + "options": { + "file": "${pkgs.unit.usedPhp74}/lib/php.ini" + } } } } @@ -42,6 +45,13 @@ in { }; testScript = '' machine.wait_for_unit("unit.service") - assert "PHP Version ${pkgs.php74.version}" in machine.succeed("curl -vvv -s http://127.0.0.1:9074/") + + # Check so we get an evaluated PHP back + response = machine.succeed("curl -vvv -s http://127.0.0.1:9074/") + assert "PHP Version ${pkgs.unit.usedPhp74.version}" in response, "PHP version not detected" + + # Check so we have database and some other extensions loaded + for ext in ["json", "opcache", "pdo_mysql", "pdo_pgsql", "pdo_sqlite"]: + assert ext in response, f"Missing {ext} extension" ''; }) diff --git a/nixos/tests/wireguard/basic.nix b/nixos/tests/wireguard/basic.nix new file mode 100644 index 00000000000..25d706ae2e5 --- /dev/null +++ b/nixos/tests/wireguard/basic.nix @@ -0,0 +1,74 @@ +{ kernelPackages ? null }: +import ../make-test-python.nix ({ pkgs, lib, ...} : + let + wg-snakeoil-keys = import ./snakeoil-keys.nix; + peer = (import ./make-peer.nix) { inherit lib; }; + in + { + name = "wireguard"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ma27 ]; + }; + + nodes = { + peer0 = peer { + ip4 = "192.168.0.1"; + ip6 = "fd00::1"; + extraConfig = { + boot = lib.mkIf (kernelPackages != null) { inherit kernelPackages; }; + networking.firewall.allowedUDPPorts = [ 23542 ]; + networking.wireguard.interfaces.wg0 = { + ips = [ "10.23.42.1/32" "fc00::1/128" ]; + listenPort = 23542; + + inherit (wg-snakeoil-keys.peer0) privateKey; + + peers = lib.singleton { + allowedIPs = [ "10.23.42.2/32" "fc00::2/128" ]; + + inherit (wg-snakeoil-keys.peer1) publicKey; + }; + }; + }; + }; + + peer1 = peer { + ip4 = "192.168.0.2"; + ip6 = "fd00::2"; + extraConfig = { + boot = lib.mkIf (kernelPackages != null) { inherit kernelPackages; }; + networking.wireguard.interfaces.wg0 = { + ips = [ "10.23.42.2/32" "fc00::2/128" ]; + listenPort = 23542; + allowedIPsAsRoutes = false; + + inherit (wg-snakeoil-keys.peer1) privateKey; + + peers = lib.singleton { + allowedIPs = [ "0.0.0.0/0" "::/0" ]; + endpoint = "192.168.0.1:23542"; + persistentKeepalive = 25; + + inherit (wg-snakeoil-keys.peer0) publicKey; + }; + + postSetup = let inherit (pkgs) iproute; in '' + ${iproute}/bin/ip route replace 10.23.42.1/32 dev wg0 + ${iproute}/bin/ip route replace fc00::1/128 dev wg0 + ''; + }; + }; + }; + }; + + testScript = '' + start_all() + + peer0.wait_for_unit("wireguard-wg0.service") + peer1.wait_for_unit("wireguard-wg0.service") + + peer1.succeed("ping -c5 fc00::1") + peer1.succeed("ping -c5 10.23.42.1") + ''; + } +) diff --git a/nixos/tests/wireguard/default.nix b/nixos/tests/wireguard/default.nix index e3bc31c600f..dedb321ff2e 100644 --- a/nixos/tests/wireguard/default.nix +++ b/nixos/tests/wireguard/default.nix @@ -1,71 +1,27 @@ -import ../make-test-python.nix ({ pkgs, lib, ...} : - let - wg-snakeoil-keys = import ./snakeoil-keys.nix; - peer = (import ./make-peer.nix) { inherit lib; }; - in - { - name = "wireguard"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ ma27 ]; - }; - - nodes = { - peer0 = peer { - ip4 = "192.168.0.1"; - ip6 = "fd00::1"; - extraConfig = { - networking.firewall.allowedUDPPorts = [ 23542 ]; - networking.wireguard.interfaces.wg0 = { - ips = [ "10.23.42.1/32" "fc00::1/128" ]; - listenPort = 23542; - - inherit (wg-snakeoil-keys.peer0) privateKey; - - peers = lib.singleton { - allowedIPs = [ "10.23.42.2/32" "fc00::2/128" ]; - - inherit (wg-snakeoil-keys.peer1) publicKey; - }; - }; - }; - }; - - peer1 = peer { - ip4 = "192.168.0.2"; - ip6 = "fd00::2"; - extraConfig = { - networking.wireguard.interfaces.wg0 = { - ips = [ "10.23.42.2/32" "fc00::2/128" ]; - listenPort = 23542; - allowedIPsAsRoutes = false; - - inherit (wg-snakeoil-keys.peer1) privateKey; - - peers = lib.singleton { - allowedIPs = [ "0.0.0.0/0" "::/0" ]; - endpoint = "192.168.0.1:23542"; - persistentKeepalive = 25; - - inherit (wg-snakeoil-keys.peer0) publicKey; - }; - - postSetup = let inherit (pkgs) iproute; in '' - ${iproute}/bin/ip route replace 10.23.42.1/32 dev wg0 - ${iproute}/bin/ip route replace fc00::1/128 dev wg0 - ''; - }; - }; - }; - }; - - testScript = '' - start_all() - - peer0.wait_for_unit("wireguard-wg0.service") - peer1.wait_for_unit("wireguard-wg0.service") - - peer1.succeed("ping -c5 fc00::1") - peer1.succeed("ping -c5 10.23.42.1") - ''; - } +{ system ? builtins.currentSystem +, config ? { } +, pkgs ? import ../../.. { inherit system config; } +, kernelVersionsToTest ? [ "5.4" "latest" ] +}: + +with pkgs.lib; + +let + tests = let callTest = p: flip (import p) { inherit system pkgs; }; in { + basic = callTest ./basic.nix; + namespaces = callTest ./namespaces.nix; + wg-quick = callTest ./wg-quick.nix; + generated = callTest ./generated.nix; + }; +in + +listToAttrs ( + flip concatMap kernelVersionsToTest (version: + let + v' = replaceStrings [ "." ] [ "_" ] version; + in + flip mapAttrsToList tests (name: test: + nameValuePair "wireguard-${name}-linux-${v'}" (test { kernelPackages = pkgs."linuxPackages_${v'}"; }) + ) + ) ) diff --git a/nixos/tests/wireguard/generated.nix b/nixos/tests/wireguard/generated.nix index a29afd2d466..cdf15483265 100644 --- a/nixos/tests/wireguard/generated.nix +++ b/nixos/tests/wireguard/generated.nix @@ -1,4 +1,5 @@ -import ../make-test-python.nix ({ pkgs, ...} : { +{ kernelPackages ? null }: +import ../make-test-python.nix ({ pkgs, lib, ... } : { name = "wireguard-generated"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ ma27 grahamc ]; @@ -6,6 +7,7 @@ import ../make-test-python.nix ({ pkgs, ...} : { nodes = { peer1 = { + boot = lib.mkIf (kernelPackages != null) { inherit kernelPackages; }; networking.firewall.allowedUDPPorts = [ 12345 ]; networking.wireguard.interfaces.wg0 = { ips = [ "10.10.10.1/24" ]; @@ -17,6 +19,7 @@ import ../make-test-python.nix ({ pkgs, ...} : { }; peer2 = { + boot = lib.mkIf (kernelPackages != null) { inherit kernelPackages; }; networking.firewall.allowedUDPPorts = [ 12345 ]; networking.wireguard.interfaces.wg0 = { ips = [ "10.10.10.2/24" ]; diff --git a/nixos/tests/wireguard/namespaces.nix b/nixos/tests/wireguard/namespaces.nix index c8a4e3bb52a..c47175ceafc 100644 --- a/nixos/tests/wireguard/namespaces.nix +++ b/nixos/tests/wireguard/namespaces.nix @@ -1,3 +1,5 @@ +{ kernelPackages ? null }: + let listenPort = 12345; socketNamespace = "foo"; @@ -13,7 +15,7 @@ let in -import ../make-test-python.nix ({ pkgs, ...} : { +import ../make-test-python.nix ({ pkgs, lib, ... } : { name = "wireguard-with-namespaces"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ asymmetric ]; @@ -23,6 +25,7 @@ import ../make-test-python.nix ({ pkgs, ...} : { # interface should be created in the socketNamespace # and not moved from there peer0 = pkgs.lib.attrsets.recursiveUpdate node { + boot = lib.mkIf (kernelPackages != null) { inherit kernelPackages; }; networking.wireguard.interfaces.wg0 = { preSetup = '' ip netns add ${socketNamespace} @@ -33,6 +36,7 @@ import ../make-test-python.nix ({ pkgs, ...} : { # interface should be created in the init namespace # and moved to the interfaceNamespace peer1 = pkgs.lib.attrsets.recursiveUpdate node { + boot = lib.mkIf (kernelPackages != null) { inherit kernelPackages; }; networking.wireguard.interfaces.wg0 = { preSetup = '' ip netns add ${interfaceNamespace} @@ -43,6 +47,7 @@ import ../make-test-python.nix ({ pkgs, ...} : { # interface should be created in the socketNamespace # and moved to the interfaceNamespace peer2 = pkgs.lib.attrsets.recursiveUpdate node { + boot = lib.mkIf (kernelPackages != null) { inherit kernelPackages; }; networking.wireguard.interfaces.wg0 = { preSetup = '' ip netns add ${socketNamespace} @@ -54,6 +59,7 @@ import ../make-test-python.nix ({ pkgs, ...} : { # interface should be created in the socketNamespace # and moved to the init namespace peer3 = pkgs.lib.attrsets.recursiveUpdate node { + boot = lib.mkIf (kernelPackages != null) { inherit kernelPackages; }; networking.wireguard.interfaces.wg0 = { preSetup = '' ip netns add ${socketNamespace} diff --git a/nixos/tests/wireguard/wg-quick.nix b/nixos/tests/wireguard/wg-quick.nix index 7354dd01a34..5472d21cd1e 100644 --- a/nixos/tests/wireguard/wg-quick.nix +++ b/nixos/tests/wireguard/wg-quick.nix @@ -1,3 +1,5 @@ +{ kernelPackages ? null }: + import ../make-test-python.nix ({ pkgs, lib, ... }: let wg-snakeoil-keys = import ./snakeoil-keys.nix; @@ -14,6 +16,7 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: ip4 = "192.168.0.1"; ip6 = "fd00::1"; extraConfig = { + boot = lib.mkIf (kernelPackages != null) { inherit kernelPackages; }; networking.firewall.allowedUDPPorts = [ 23542 ]; networking.wg-quick.interfaces.wg0 = { address = [ "10.23.42.1/32" "fc00::1/128" ]; @@ -34,6 +37,7 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: ip4 = "192.168.0.2"; ip6 = "fd00::2"; extraConfig = { + boot = lib.mkIf (kernelPackages != null) { inherit kernelPackages; }; networking.wg-quick.interfaces.wg0 = { address = [ "10.23.42.2/32" "fc00::2/128" ]; inherit (wg-snakeoil-keys.peer1) privateKey; diff --git a/nixos/tests/xandikos.nix b/nixos/tests/xandikos.nix index 0fded20ff1a..48c770a3d16 100644 --- a/nixos/tests/xandikos.nix +++ b/nixos/tests/xandikos.nix @@ -4,7 +4,7 @@ import ./make-test-python.nix ( { name = "xandikos"; - meta.maintainers = [ lib.maintainers."0x4A6F" ]; + meta.maintainers = with lib.maintainers; [ _0x4A6F ]; nodes = { xandikos_client = {}; @@ -17,7 +17,7 @@ import ./make-test-python.nix ( services.xandikos.enable = true; services.xandikos.address = "localhost"; services.xandikos.port = 8080; - services.xandikos.routePrefix = "/xandikos/"; + services.xandikos.routePrefix = "/xandikos-prefix/"; services.xandikos.extraOptions = [ "--defaults" ]; @@ -28,7 +28,7 @@ import ./make-test-python.nix ( serverName = "xandikos.local"; basicAuth.xandikos = "snakeOilPassword"; locations."/xandikos/" = { - proxyPass = "http://localhost:8080/"; + proxyPass = "http://localhost:8080/xandikos-prefix/"; }; }; }; diff --git a/nixos/tests/xfce.nix b/nixos/tests/xfce.nix index 99065669661..99e30342e59 100644 --- a/nixos/tests/xfce.nix +++ b/nixos/tests/xfce.nix @@ -11,8 +11,8 @@ import ./make-test-python.nix ({ pkgs, ...} : { services.xserver.enable = true; - services.xserver.displayManager.lightdm = { - enable = true; + services.xserver.displayManager = { + lightdm.enable = true; autoLogin = { enable = true; user = "alice"; diff --git a/nixos/tests/yggdrasil.nix b/nixos/tests/yggdrasil.nix index 468fcf67127..1d7541308b4 100644 --- a/nixos/tests/yggdrasil.nix +++ b/nixos/tests/yggdrasil.nix @@ -7,6 +7,7 @@ let SigningPrivateKey = "fe3add8da35316c05f6d90d3ca79bd2801e6ccab6d37e5339fef4152589398abe2c43349083bc1e998e4ec4535b4c6a8f44ca9a5a8e07336561267253b2be5f4"; }; bobIp6 = "201:ebbd:bde9:f138:c302:4afa:1fb6:a19a"; + bobPrefix = "301:ebbd:bde9:f138"; bobConfig = { InterfacePeers = { eth1 = [ "tcp://192.168.1.200:12345" ]; @@ -18,6 +19,7 @@ let SigningPublicKey = "de111da0ec781e45bf6c63ecb45a78c24d7d4655abfaeea83b26c36eb5c0fd5b"; SigningPrivateKey = "2a6c21550f3fca0331df50668ffab66b6dce8237bcd5728e571e8033b363e247de111da0ec781e45bf6c63ecb45a78c24d7d4655abfaeea83b26c36eb5c0fd5b"; }; + danIp6 = bobPrefix + "::2"; in import ./make-test-python.nix ({ pkgs, ...} : { name = "yggdrasil"; @@ -69,6 +71,41 @@ in import ./make-test-python.nix ({ pkgs, ...} : { text = builtins.toJSON bobConfig; }); }; + + boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = 1; + + networking = { + bridges.br0.interfaces = [ ]; + interfaces.br0 = { + ipv6.addresses = [{ + address = bobPrefix + "::1"; + prefixLength = 64; + }]; + }; + }; + + # dan is a node inside a container running on bob's host. + containers.dan = { + autoStart = true; + privateNetwork = true; + hostBridge = "br0"; + config = { config, pkgs, ... }: { + networking.interfaces.eth0.ipv6 = { + addresses = [{ + address = bobPrefix + "::2"; + prefixLength = 64; + }]; + routes = [{ + address = "200::"; + prefixLength = 7; + via = bobPrefix + "::1"; + }]; + }; + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + }; }; # Carol only does local peering. Carol's yggdrasil config is all Nix. @@ -85,6 +122,7 @@ in import ./make-test-python.nix ({ pkgs, ...} : { MulticastInterfaces = [ "eth1" ]; LinkLocalTCPPort = 43210; }; + persistentKeys = true; }; }; }; @@ -99,7 +137,7 @@ in import ./make-test-python.nix ({ pkgs, ...} : { bob.start() carol.start() - bob.wait_for_unit("yggdrasil.service") + bob.wait_for_unit("default.target") carol.wait_for_unit("yggdrasil.service") ip_addr_show = "ip -o -6 addr show dev ygg0 scope global" @@ -116,10 +154,13 @@ in import ./make-test-python.nix ({ pkgs, ...} : { carol.succeed("ping -c 1 ${aliceIp6}") carol.succeed("ping -c 1 ${bobIp6}") + carol.succeed("ping -c 1 ${bobPrefix}::1") + carol.succeed("ping -c 8 ${danIp6}") carol.fail("journalctl -u dhcpcd | grep ygg0") alice.wait_for_unit("httpd.service") carol.succeed("curl --fail -g http://[${aliceIp6}]") + carol.succeed("curl --fail -g http://[${danIp6}]") ''; }) diff --git a/nixos/tests/zfs.nix b/nixos/tests/zfs.nix index 7ba60ee9806..87e6c900c98 100644 --- a/nixos/tests/zfs.nix +++ b/nixos/tests/zfs.nix @@ -46,6 +46,17 @@ let "zpool destroy rpool", "udevadm settle", ) + + machine.succeed( + 'echo password | zpool create -o altroot="/tmp/mnt" ' + + "-O encryption=aes-256-gcm -O keyformat=passphrase rpool /dev/vdb1", + "zfs create -o mountpoint=legacy rpool/root", + "mount -t zfs rpool/root /tmp/mnt", + "udevadm settle", + "umount /tmp/mnt", + "zpool destroy rpool", + "udevadm settle", + ) '' + extraTest; }; @@ -57,18 +68,6 @@ in { unstable = makeZfsTest "unstable" { enableUnstable = true; - extraTest = '' - machine.succeed( - 'echo password | zpool create -o altroot="/tmp/mnt" ' - + "-O encryption=aes-256-gcm -O keyformat=passphrase rpool /dev/vdb1", - "zfs create -o mountpoint=legacy rpool/root", - "mount -t zfs rpool/root /tmp/mnt", - "udevadm settle", - "umount /tmp/mnt", - "zpool destroy rpool", - "udevadm settle", - ) - ''; }; installer = (import ./installer.nix { }).zfsroot; diff --git a/nixos/tests/zigbee2mqtt.nix b/nixos/tests/zigbee2mqtt.nix new file mode 100644 index 00000000000..b7bb21f9227 --- /dev/null +++ b/nixos/tests/zigbee2mqtt.nix @@ -0,0 +1,19 @@ +import ./make-test-python.nix ({ pkgs, ... }: + + { + machine = { pkgs, ... }: + { + services.zigbee2mqtt = { + enable = true; + }; + }; + + testScript = '' + machine.wait_for_unit("zigbee2mqtt.service") + machine.wait_until_fails("systemctl status zigbee2mqtt.service") + machine.succeed( + "journalctl -eu zigbee2mqtt | grep \"Error: Error while opening serialport 'Error: Error: No such file or directory, cannot open /dev/ttyACM0'\"" + ) + ''; + } +) diff --git a/nixos/tests/zoneminder.nix b/nixos/tests/zoneminder.nix new file mode 100644 index 00000000000..a4e1a05ec0e --- /dev/null +++ b/nixos/tests/zoneminder.nix @@ -0,0 +1,23 @@ +import ./make-test-python.nix ({ lib, ...}: + +{ + name = "zoneminder"; + meta.maintainers = with lib.maintainers; [ danielfullmer ]; + + machine = { ... }: + { + services.zoneminder = { + enable = true; + database.createLocally = true; + database.username = "zoneminder"; + }; + time.timeZone = "America/New_York"; + }; + + testScript = '' + machine.wait_for_unit("zoneminder.service") + machine.wait_for_unit("nginx.service") + machine.wait_for_open_port(8095) + machine.succeed("curl --fail http://localhost:8095/") + ''; +}) |