diff options
Diffstat (limited to 'nixpkgs/nixos/tests')
254 files changed, 18439 insertions, 0 deletions
diff --git a/nixpkgs/nixos/tests/acme.nix b/nixpkgs/nixos/tests/acme.nix new file mode 100644 index 000000000000..4669a092433e --- /dev/null +++ b/nixpkgs/nixos/tests/acme.nix @@ -0,0 +1,39 @@ +let + commonConfig = ./common/letsencrypt/common.nix; +in import ./make-test.nix { + name = "acme"; + + nodes = { + letsencrypt = ./common/letsencrypt; + + webserver = { config, pkgs, ... }: { + imports = [ commonConfig ]; + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + networking.extraHosts = '' + ${config.networking.primaryIPAddress} example.com + ''; + + services.nginx.enable = true; + services.nginx.virtualHosts."example.com" = { + enableACME = true; + forceSSL = true; + locations."/".root = pkgs.runCommand "docroot" {} '' + mkdir -p "$out" + echo hello world > "$out/index.html" + ''; + }; + }; + + client = commonConfig; + }; + + testScript = '' + $letsencrypt->waitForUnit("default.target"); + $letsencrypt->waitForUnit("boulder.service"); + $webserver->waitForUnit("default.target"); + $webserver->waitForUnit("acme-certificates.target"); + $client->waitForUnit("default.target"); + $client->succeed('curl https://example.com/ | grep -qF "hello world"'); + ''; +} diff --git a/nixpkgs/nixos/tests/all-tests.nix b/nixpkgs/nixos/tests/all-tests.nix new file mode 100644 index 000000000000..4450cafd2800 --- /dev/null +++ b/nixpkgs/nixos/tests/all-tests.nix @@ -0,0 +1,229 @@ +{ system, pkgs, callTest }: +# The return value of this function will be an attrset with arbitrary depth and +# the `anything` returned by callTest at its test leafs. +# The tests not supported by `system` will be replaced with `{}`, so that +# `passthru.tests` can contain links to those without breaking on architectures +# where said tests are unsupported. +# Example callTest that just extracts the derivation from the test: +# callTest = t: t.test; + +with pkgs.lib; + +let + discoverTests = val: + if !isAttrs val then val + else if hasAttr "test" val then callTest val + else mapAttrs (n: s: discoverTests s) val; + handleTest = path: args: + discoverTests (import path ({ inherit system pkgs; } // args)); + handleTestOn = systems: path: args: + if elem system systems then handleTest path args + else {}; +in +{ + acme = handleTestOn ["x86_64-linux"] ./acme.nix {}; + atd = handleTest ./atd.nix {}; + avahi = handleTest ./avahi.nix {}; + bcachefs = handleTestOn ["x86_64-linux"] ./bcachefs.nix {}; # linux-4.18.2018.10.12 is unsupported on aarch64 + beegfs = handleTestOn ["x86_64-linux"] ./beegfs.nix {}; # beegfs is unsupported on aarch64 + bind = handleTest ./bind.nix {}; + bittorrent = handleTest ./bittorrent.nix {}; + #blivet = handleTest ./blivet.nix {}; # broken since 2017-07024 + boot = handleTestOn ["x86_64-linux"] ./boot.nix {}; # syslinux is unsupported on aarch64 + boot-stage1 = handleTest ./boot-stage1.nix {}; + borgbackup = handleTest ./borgbackup.nix {}; + buildbot = handleTest ./buildbot.nix {}; + cadvisor = handleTestOn ["x86_64-linux"] ./cadvisor.nix {}; + ceph = handleTestOn ["x86_64-linux"] ./ceph.nix {}; + certmgr = handleTest ./certmgr.nix {}; + cfssl = handleTestOn ["x86_64-linux"] ./cfssl.nix {}; + chromium = (handleTestOn ["x86_64-linux"] ./chromium.nix {}).stable or {}; + cjdns = handleTest ./cjdns.nix {}; + clickhouse = handleTest ./clickhouse.nix {}; + cloud-init = handleTest ./cloud-init.nix {}; + codimd = handleTest ./codimd.nix {}; + containers-bridge = handleTest ./containers-bridge.nix {}; + containers-extra_veth = handleTest ./containers-extra_veth.nix {}; + containers-hosts = handleTest ./containers-hosts.nix {}; + containers-imperative = handleTest ./containers-imperative.nix {}; + containers-ipv4 = handleTest ./containers-ipv4.nix {}; + containers-ipv6 = handleTest ./containers-ipv6.nix {}; + containers-macvlans = handleTest ./containers-macvlans.nix {}; + containers-physical_interfaces = handleTest ./containers-physical_interfaces.nix {}; + containers-restart_networking = handleTest ./containers-restart_networking.nix {}; + containers-tmpfs = handleTest ./containers-tmpfs.nix {}; + #couchdb = handleTest ./couchdb.nix {}; # spidermonkey-1.8.5 is marked as broken + deluge = handleTest ./deluge.nix {}; + dhparams = handleTest ./dhparams.nix {}; + dnscrypt-proxy = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy.nix {}; + docker = handleTestOn ["x86_64-linux"] ./docker.nix {}; + docker-edge = handleTestOn ["x86_64-linux"] ./docker-edge.nix {}; + docker-preloader = handleTestOn ["x86_64-linux"] ./docker-preloader.nix {}; + docker-registry = handleTest ./docker-registry.nix {}; + docker-tools = handleTestOn ["x86_64-linux"] ./docker-tools.nix {}; + docker-tools-overlay = handleTestOn ["x86_64-linux"] ./docker-tools-overlay.nix {}; + dovecot = handleTest ./dovecot.nix {}; + # ec2-config doesn't work in a sandbox as the simulated ec2 instance needs network access + #ec2-config = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-config or {}; + ec2-nixops = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-nixops or {}; + ecryptfs = handleTest ./ecryptfs.nix {}; + elk = handleTestOn ["x86_64-linux"] ./elk.nix {}; + env = handleTest ./env.nix {}; + etcd = handleTestOn ["x86_64-linux"] ./etcd.nix {}; + ferm = handleTest ./ferm.nix {}; + firefox = handleTest ./firefox.nix {}; + firewall = handleTest ./firewall.nix {}; + flatpak = handleTest ./flatpak.nix {}; + fsck = handleTest ./fsck.nix {}; + fwupd = handleTestOn ["x86_64-linux"] ./fwupd.nix {}; # libsmbios is unsupported on aarch64 + gdk-pixbuf = handleTest ./gdk-pixbuf.nix {}; + gitea = handleTest ./gitea.nix {}; + gitlab = handleTest ./gitlab.nix {}; + gitolite = handleTest ./gitolite.nix {}; + gjs = handleTest ./gjs.nix {}; + google-oslogin = handleTest ./google-oslogin {}; + gnome3 = handleTestOn ["x86_64-linux"] ./gnome3.nix {}; # libsmbios is unsupported on aarch64 + gnome3-gdm = handleTestOn ["x86_64-linux"] ./gnome3-gdm.nix {}; # libsmbios is unsupported on aarch64 + gocd-agent = handleTest ./gocd-agent.nix {}; + gocd-server = handleTest ./gocd-server.nix {}; + grafana = handleTest ./grafana.nix {}; + graphite = handleTest ./graphite.nix {}; + hadoop.hdfs = handleTestOn [ "x86_64-linux" ] ./hadoop/hdfs.nix {}; + hadoop.yarn = handleTestOn [ "x86_64-linux" ] ./hadoop/yarn.nix {}; + handbrake = handleTestOn ["x86_64-linux"] ./handbrake.nix {}; + haproxy = handleTest ./haproxy.nix {}; + hardened = handleTest ./hardened.nix {}; + hibernate = handleTest ./hibernate.nix {}; + hitch = handleTest ./hitch {}; + hocker-fetchdocker = handleTest ./hocker-fetchdocker {}; + home-assistant = handleTest ./home-assistant.nix {}; + hound = handleTest ./hound.nix {}; + hydra = handleTest ./hydra {}; + i3wm = handleTest ./i3wm.nix {}; + iftop = handleTest ./iftop.nix {}; + incron = handleTest ./incron.nix {}; + influxdb = handleTest ./influxdb.nix {}; + initrd-network-ssh = handleTest ./initrd-network-ssh {}; + initrdNetwork = handleTest ./initrd-network.nix {}; + installer = handleTest ./installer.nix {}; + ipv6 = handleTest ./ipv6.nix {}; + jackett = handleTest ./jackett.nix {}; + jenkins = handleTest ./jenkins.nix {}; + #kafka = handleTest ./kafka.nix {}; # broken since openjdk: 8u181 -> 8u192 + kerberos = handleTest ./kerberos/default.nix {}; + kernel-latest = handleTest ./kernel-latest.nix {}; + kernel-lts = handleTest ./kernel-lts.nix {}; + keymap = handleTest ./keymap.nix {}; + kubernetes.dns = handleTestOn ["x86_64-linux"] ./kubernetes/dns.nix {}; + # kubernetes.e2e should eventually replace kubernetes.rbac when it works + #kubernetes.e2e = handleTestOn ["x86_64-linux"] ./kubernetes/e2e.nix {}; + kubernetes.rbac = handleTestOn ["x86_64-linux"] ./kubernetes/rbac.nix {}; + latestKernel.login = handleTest ./login.nix { latestKernel = true; }; + ldap = handleTest ./ldap.nix {}; + leaps = handleTest ./leaps.nix {}; + lidarr = handleTest ./lidarr.nix {}; + #lightdm = handleTest ./lightdm.nix {}; + login = handleTest ./login.nix {}; + #logstash = handleTest ./logstash.nix {}; + mathics = handleTest ./mathics.nix {}; + matrix-synapse = handleTest ./matrix-synapse.nix {}; + memcached = handleTest ./memcached.nix {}; + mesos = handleTest ./mesos.nix {}; + minio = handleTest ./minio.nix {}; + misc = handleTest ./misc.nix {}; + mongodb = handleTest ./mongodb.nix {}; + morty = handleTest ./morty.nix {}; + mpd = handleTest ./mpd.nix {}; + mumble = handleTest ./mumble.nix {}; + munin = handleTest ./munin.nix {}; + mutableUsers = handleTest ./mutable-users.nix {}; + mysql = handleTest ./mysql.nix {}; + mysqlBackup = handleTest ./mysql-backup.nix {}; + mysqlReplication = handleTest ./mysql-replication.nix {}; + nat.firewall = handleTest ./nat.nix { withFirewall = true; }; + nat.firewall-conntrack = handleTest ./nat.nix { withFirewall = true; withConntrackHelpers = true; }; + nat.standalone = handleTest ./nat.nix { withFirewall = false; }; + ndppd = handleTest ./ndppd.nix {}; + neo4j = handleTest ./neo4j.nix {}; + netdata = handleTest ./netdata.nix {}; + networking.networkd = handleTest ./networking.nix { networkd = true; }; + networking.scripted = handleTest ./networking.nix { networkd = false; }; + # TODO: put in networking.nix after the test becomes more complete + networkingProxy = handleTest ./networking-proxy.nix {}; + nextcloud = handleTest ./nextcloud {}; + nexus = handleTest ./nexus.nix {}; + nfs3 = handleTest ./nfs.nix { version = 3; }; + nfs4 = handleTest ./nfs.nix { version = 4; }; + nghttpx = handleTest ./nghttpx.nix {}; + nginx = handleTest ./nginx.nix {}; + nginx-sso = handleTest ./nginx-sso.nix {}; + nix-ssh-serve = handleTest ./nix-ssh-serve.nix {}; + novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {}; + nsd = handleTest ./nsd.nix {}; + openldap = handleTest ./openldap.nix {}; + opensmtpd = handleTest ./opensmtpd.nix {}; + openssh = handleTest ./openssh.nix {}; + osquery = handleTest ./osquery.nix {}; + ostree = handleTest ./ostree.nix {}; + pam-oath-login = handleTest ./pam-oath-login.nix {}; + pam-u2f = handleTest ./pam-u2f.nix {}; + peerflix = handleTest ./peerflix.nix {}; + pgjwt = handleTest ./pgjwt.nix {}; + pgmanage = handleTest ./pgmanage.nix {}; + php-pcre = handleTest ./php-pcre.nix {}; + plasma5 = handleTest ./plasma5.nix {}; + plotinus = handleTest ./plotinus.nix {}; + postgis = handleTest ./postgis.nix {}; + postgresql = handleTest ./postgresql.nix {}; + powerdns = handleTest ./powerdns.nix {}; + predictable-interface-names = handleTest ./predictable-interface-names.nix {}; + printing = handleTest ./printing.nix {}; + prometheus = handleTest ./prometheus.nix {}; + prometheus-exporters = handleTest ./prometheus-exporters.nix {}; + prosody = handleTest ./prosody.nix {}; + proxy = handleTest ./proxy.nix {}; + quagga = handleTest ./quagga.nix {}; + quake3 = handleTest ./quake3.nix {}; + rabbitmq = handleTest ./rabbitmq.nix {}; + radarr = handleTest ./radarr.nix {}; + radicale = handleTest ./radicale.nix {}; + redmine = handleTest ./redmine.nix {}; + roundcube = handleTest ./roundcube.nix {}; + rspamd = handleTest ./rspamd.nix {}; + rss2email = handleTest ./rss2email.nix {}; + rsyslogd = handleTest ./rsyslogd.nix {}; + runInMachine = handleTest ./run-in-machine.nix {}; + rxe = handleTest ./rxe.nix {}; + samba = handleTest ./samba.nix {}; + sddm = handleTest ./sddm.nix {}; + simple = handleTest ./simple.nix {}; + slim = handleTest ./slim.nix {}; + slurm = handleTest ./slurm.nix {}; + smokeping = handleTest ./smokeping.nix {}; + snapper = handleTest ./snapper.nix {}; + solr = handleTest ./solr.nix {}; + sonarr = handleTest ./sonarr.nix {}; + strongswan-swanctl = handleTest ./strongswan-swanctl.nix {}; + sudo = handleTest ./sudo.nix {}; + switchTest = handleTest ./switch-test.nix {}; + syncthing-relay = handleTest ./syncthing-relay.nix {}; + systemd = handleTest ./systemd.nix {}; + taskserver = handleTest ./taskserver.nix {}; + telegraf = handleTest ./telegraf.nix {}; + tomcat = handleTest ./tomcat.nix {}; + tor = handleTest ./tor.nix {}; + transmission = handleTest ./transmission.nix {}; + udisks2 = handleTest ./udisks2.nix {}; + upnp = handleTest ./upnp.nix {}; + vault = handleTest ./vault.nix {}; + virtualbox = handleTestOn ["x86_64-linux"] ./virtualbox.nix {}; + wordpress = handleTest ./wordpress.nix {}; + xautolock = handleTest ./xautolock.nix {}; + xdg-desktop-portal = handleTest ./xdg-desktop-portal.nix {}; + xfce = handleTest ./xfce.nix {}; + xmonad = handleTest ./xmonad.nix {}; + xrdp = handleTest ./xrdp.nix {}; + xss-lock = handleTest ./xss-lock.nix {}; + yabar = handleTest ./yabar.nix {}; + zookeeper = handleTest ./zookeeper.nix {}; +} diff --git a/nixpkgs/nixos/tests/ammonite.nix b/nixpkgs/nixos/tests/ammonite.nix new file mode 100644 index 000000000000..fedfde233e8d --- /dev/null +++ b/nixpkgs/nixos/tests/ammonite.nix @@ -0,0 +1,20 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "ammonite"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ nequissimus ]; + }; + + nodes = { + amm = + { pkgs, ... }: + { + environment.systemPackages = [ pkgs.ammonite ]; + }; + }; + + testScript = '' + startAll; + + $amm->succeed("amm -c 'val foo = 21; println(foo * 2)' | grep 42") + ''; +}) diff --git a/nixpkgs/nixos/tests/atd.nix b/nixpkgs/nixos/tests/atd.nix new file mode 100644 index 000000000000..25db72799241 --- /dev/null +++ b/nixpkgs/nixos/tests/atd.nix @@ -0,0 +1,31 @@ +import ./make-test.nix ({ pkgs, ... }: + +{ + name = "atd"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ bjornfor ]; + }; + + machine = + { ... }: + { services.atd.enable = true; + users.users.alice = { isNormalUser = true; }; + }; + + # "at" has a resolution of 1 minute + testScript = '' + startAll; + + $machine->waitForUnit('atd.service'); # wait for atd to start + $machine->fail("test -f ~root/at-1"); + $machine->fail("test -f ~alice/at-1"); + + $machine->succeed("echo 'touch ~root/at-1' | at now+1min"); + $machine->succeed("su - alice -c \"echo 'touch at-1' | at now+1min\""); + + $machine->succeed("sleep 1.5m"); + + $machine->succeed("test -f ~root/at-1"); + $machine->succeed("test -f ~alice/at-1"); + ''; +}) diff --git a/nixpkgs/nixos/tests/avahi.nix b/nixpkgs/nixos/tests/avahi.nix new file mode 100644 index 000000000000..dfb60998941b --- /dev/null +++ b/nixpkgs/nixos/tests/avahi.nix @@ -0,0 +1,60 @@ +# Test whether `avahi-daemon' and `libnss-mdns' work as expected. +import ./make-test.nix ({ pkgs, ... } : { + name = "avahi"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + nodes = let + cfg = { ... }: { + services.avahi = { + enable = true; + nssmdns = true; + publish.addresses = true; + publish.domain = true; + publish.enable = true; + publish.userServices = true; + publish.workstation = true; + }; + }; + in { + one = cfg; + two = cfg; + }; + + testScript = + '' startAll; + + # mDNS. + $one->waitForUnit("network.target"); + $two->waitForUnit("network.target"); + + $one->succeed("avahi-resolve-host-name one.local | tee out >&2"); + $one->succeed("test \"`cut -f1 < out`\" = one.local"); + $one->succeed("avahi-resolve-host-name two.local | tee out >&2"); + $one->succeed("test \"`cut -f1 < out`\" = two.local"); + + $two->succeed("avahi-resolve-host-name one.local | tee out >&2"); + $two->succeed("test \"`cut -f1 < out`\" = one.local"); + $two->succeed("avahi-resolve-host-name two.local | tee out >&2"); + $two->succeed("test \"`cut -f1 < out`\" = two.local"); + + # Basic DNS-SD. + $one->succeed("avahi-browse -r -t _workstation._tcp | tee out >&2"); + $one->succeed("test `wc -l < out` -gt 0"); + $two->succeed("avahi-browse -r -t _workstation._tcp | tee out >&2"); + $two->succeed("test `wc -l < out` -gt 0"); + + # More DNS-SD. + $one->execute("avahi-publish -s \"This is a test\" _test._tcp 123 one=1 &"); + $one->sleep(5); + $two->succeed("avahi-browse -r -t _test._tcp | tee out >&2"); + $two->succeed("test `wc -l < out` -gt 0"); + + # NSS-mDNS. + $one->succeed("getent hosts one.local >&2"); + $one->succeed("getent hosts two.local >&2"); + $two->succeed("getent hosts one.local >&2"); + $two->succeed("getent hosts two.local >&2"); + ''; +}) diff --git a/nixpkgs/nixos/tests/bcachefs.nix b/nixpkgs/nixos/tests/bcachefs.nix new file mode 100644 index 000000000000..658676ef0ab9 --- /dev/null +++ b/nixpkgs/nixos/tests/bcachefs.nix @@ -0,0 +1,38 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "bcachefs"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ chiiruno ]; + + machine = { pkgs, ... }: { + virtualisation.emptyDiskImages = [ 4096 ]; + networking.hostId = "deadbeef"; + boot.supportedFilesystems = [ "bcachefs" ]; + environment.systemPackages = with pkgs; [ parted ]; + }; + + testScript = '' + $machine->succeed("modprobe bcachefs"); + $machine->succeed("bcachefs version"); + $machine->succeed("ls /dev"); + + $machine->succeed( + "mkdir /tmp/mnt", + + "udevadm settle", + "parted --script /dev/vdb mklabel msdos", + "parted --script /dev/vdb -- mkpart primary 1024M -1s", + "udevadm settle", + + # Due to #32279, we cannot use encryption for this test yet + # "echo password | bcachefs format --encrypted /dev/vdb1", + # "echo password | bcachefs unlock /dev/vdb1", + "bcachefs format /dev/vdb1", + "mount -t bcachefs /dev/vdb1 /tmp/mnt", + "udevadm settle", + + "bcachefs fs usage /tmp/mnt", + + "umount /tmp/mnt", + "udevadm settle" + ); + ''; +}) diff --git a/nixpkgs/nixos/tests/beegfs.nix b/nixpkgs/nixos/tests/beegfs.nix new file mode 100644 index 000000000000..9c241fd2301a --- /dev/null +++ b/nixpkgs/nixos/tests/beegfs.nix @@ -0,0 +1,115 @@ +import ./make-test.nix ({ ... } : + +let + connAuthFile="beegfs/auth-def.key"; + + client = { pkgs, ... } : { + networking.firewall.enable = false; + services.beegfsEnable = true; + services.beegfs.default = { + mgmtdHost = "mgmt"; + connAuthFile = "/etc/${connAuthFile}"; + client = { + mount = false; + enable = true; + }; + }; + + fileSystems = pkgs.lib.mkVMOverride # FIXME: this should be creatd by the module + [ { mountPoint = "/beegfs"; + device = "default"; + fsType = "beegfs"; + options = [ "cfgFile=/etc/beegfs/client-default.conf" "_netdev" ]; + } + ]; + + environment.etc."${connAuthFile}" = { + enable = true; + text = "ThisIsALousySecret"; + mode = "0600"; + }; + }; + + + server = service : { pkgs, ... } : { + networking.firewall.enable = false; + boot.initrd.postDeviceCommands = '' + ${pkgs.e2fsprogs}/bin/mkfs.ext4 -L data /dev/vdb + ''; + + virtualisation.emptyDiskImages = [ 4096 ]; + + fileSystems = pkgs.lib.mkVMOverride + [ { mountPoint = "/data"; + device = "/dev/disk/by-label/data"; + fsType = "ext4"; + } + ]; + + environment.systemPackages = with pkgs; [ beegfs ]; + environment.etc."${connAuthFile}" = { + enable = true; + text = "ThisIsALousySecret"; + mode = "0600"; + }; + + services.beegfsEnable = true; + services.beegfs.default = { + mgmtdHost = "mgmt"; + connAuthFile = "/etc/${connAuthFile}"; + "${service}" = { + enable = true; + storeDir = "/data"; + }; + }; + }; + +in +{ + name = "beegfs"; + + nodes = { + meta = server "meta"; + mgmt = server "mgmtd"; + storage1 = server "storage"; + storage2 = server "storage"; + client1 = client; + client2 = client; + }; + + testScript = '' + # Initalize the data directories + $mgmt->waitForUnit("default.target"); + $mgmt->succeed("beegfs-setup-mgmtd -C -f -p /data"); + $mgmt->succeed("systemctl start beegfs-mgmtd-default"); + + $meta->waitForUnit("default.target"); + $meta->succeed("beegfs-setup-meta -C -f -s 1 -p /data"); + $meta->succeed("systemctl start beegfs-meta-default"); + + $storage1->waitForUnit("default.target"); + $storage1->succeed("beegfs-setup-storage -C -f -s 1 -i 1 -p /data"); + $storage1->succeed("systemctl start beegfs-storage-default"); + + $storage2->waitForUnit("default.target"); + $storage2->succeed("beegfs-setup-storage -C -f -s 2 -i 2 -p /data"); + $storage2->succeed("systemctl start beegfs-storage-default"); + + # + + # Basic test + $client1->waitForUnit("beegfs.mount"); + $client1->succeed("beegfs-check-servers-default"); + $client1->succeed("echo test > /beegfs/test"); + $client2->waitForUnit("beegfs.mount"); + $client2->succeed("test -e /beegfs/test"); + $client2->succeed("cat /beegfs/test | grep test"); + + # test raid0/stripping + $client1->succeed("dd if=/dev/urandom bs=1M count=10 of=/beegfs/striped"); + $client2->succeed("cat /beegfs/striped > /dev/null"); + + # check if fs is still healthy + $client1->succeed("beegfs-fsck-default --checkfs"); + ''; +}) diff --git a/nixpkgs/nixos/tests/bees.nix b/nixpkgs/nixos/tests/bees.nix new file mode 100644 index 000000000000..6f68c2f834f1 --- /dev/null +++ b/nixpkgs/nixos/tests/bees.nix @@ -0,0 +1,55 @@ +import ./make-test.nix ({ lib, ... }: +{ + name = "bees"; + + machine = { config, pkgs, ... }: { + boot.initrd.postDeviceCommands = '' + ${pkgs.btrfs-progs}/bin/mkfs.btrfs -f -L aux1 /dev/vdb + ${pkgs.btrfs-progs}/bin/mkfs.btrfs -f -L aux2 /dev/vdc + ''; + virtualisation.emptyDiskImages = [ 4096 4096 ]; + fileSystems = lib.mkVMOverride { + "/aux1" = { # filesystem configured to be deduplicated + device = "/dev/disk/by-label/aux1"; + fsType = "btrfs"; + }; + "/aux2" = { # filesystem not configured to be deduplicated + device = "/dev/disk/by-label/aux2"; + fsType = "btrfs"; + }; + }; + services.beesd.filesystems = { + aux1 = { + spec = "LABEL=aux1"; + hashTableSizeMB = 16; + verbosity = "debug"; + }; + }; + }; + + testScript = + let + withRetry = content: maxTests: sleepTime: '' + max_tests=${lib.escapeShellArg maxTests}; sleep_time=${lib.escapeShellArg sleepTime}; for ((i=0; i<max_tests; i++)); do ${content} && exit 0; sleep "$sleep_time"; done; exit 1; + ''; + someContentIsShared = loc: ''[[ $(btrfs fi du -s --raw ${lib.escapeShellArg loc}/dedup-me-{1,2} | awk 'BEGIN { count=0; } NR>1 && $3 == 0 { count++ } END { print count }') -eq 0 ]]''; + in '' + # shut down the instance started by systemd at boot, so we can test our test procedure + $machine->succeed("systemctl stop beesd\@aux1.service"); + + $machine->succeed("dd if=/dev/urandom of=/aux1/dedup-me-1 bs=1M count=8"); + $machine->succeed("cp --reflink=never /aux1/dedup-me-1 /aux1/dedup-me-2"); + $machine->succeed("cp --reflink=never /aux1/* /aux2/"); + $machine->succeed("sync"); + $machine->fail(q(${someContentIsShared "/aux1"})); + $machine->fail(q(${someContentIsShared "/aux2"})); + $machine->succeed("systemctl start beesd\@aux1.service"); + + # assert that "Set Shared" column is nonzero + $machine->succeed(q(${withRetry (someContentIsShared "/aux1") 20 2})); + $machine->fail(q(${someContentIsShared "/aux2"})); + + # assert that 16MB hash table size requested was honored + $machine->succeed(q([[ $(stat -c %s /aux1/.beeshome/beeshash.dat) = $(( 16 * 1024 * 1024)) ]])) + ''; +}) diff --git a/nixpkgs/nixos/tests/bind.nix b/nixpkgs/nixos/tests/bind.nix new file mode 100644 index 000000000000..1f8c1dc7be40 --- /dev/null +++ b/nixpkgs/nixos/tests/bind.nix @@ -0,0 +1,27 @@ +import ./make-test.nix { + name = "bind"; + + machine = { pkgs, lib, ... }: { + services.bind.enable = true; + services.bind.extraOptions = "empty-zones-enable no;"; + services.bind.zones = lib.singleton { + name = "."; + file = pkgs.writeText "root.zone" '' + $TTL 3600 + . IN SOA ns.example.org. admin.example.org. ( 1 3h 1h 1w 1d ) + . IN NS ns.example.org. + + ns.example.org. IN A 192.168.0.1 + ns.example.org. IN AAAA abcd::1 + + 1.0.168.192.in-addr.arpa IN PTR ns.example.org. + ''; + }; + }; + + testScript = '' + $machine->waitForUnit('bind.service'); + $machine->waitForOpenPort(53); + $machine->succeed('host 192.168.0.1 127.0.0.1 | grep -qF ns.example.org'); + ''; +} diff --git a/nixpkgs/nixos/tests/bittorrent.nix b/nixpkgs/nixos/tests/bittorrent.nix new file mode 100644 index 000000000000..27871f72b4e5 --- /dev/null +++ b/nixpkgs/nixos/tests/bittorrent.nix @@ -0,0 +1,149 @@ +# This test runs a Bittorrent tracker on one machine, and verifies +# that two client machines can download the torrent using +# `transmission'. The first client (behind a NAT router) downloads +# from the initial seeder running on the tracker. Then we kill the +# initial seeder. The second client downloads from the first client, +# which only works if the first client successfully uses the UPnP-IGD +# protocol to poke a hole in the NAT. + +import ./make-test.nix ({ pkgs, ... }: + +let + + # Some random file to serve. + file = pkgs.hello.src; + + internalRouterAddress = "192.168.3.1"; + internalClient1Address = "192.168.3.2"; + externalRouterAddress = "80.100.100.1"; + externalClient2Address = "80.100.100.2"; + externalTrackerAddress = "80.100.100.3"; +in + +{ + name = "bittorrent"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ domenkozar eelco chaoflow rob bobvanderlinden ]; + }; + + nodes = + { tracker = + { pkgs, ... }: + { environment.systemPackages = [ pkgs.transmission ]; + + virtualisation.vlans = [ 1 ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalTrackerAddress; prefixLength = 24; } + ]; + + # We need Apache on the tracker to serve the torrents. + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + services.httpd.documentRoot = "/tmp"; + + networking.firewall.enable = false; + + services.opentracker.enable = true; + + services.transmission.enable = true; + services.transmission.settings.dht-enabled = false; + services.transmission.settings.port-forwaring-enabled = false; + }; + + router = + { pkgs, nodes, ... }: + { virtualisation.vlans = [ 1 2 ]; + networking.nat.enable = true; + networking.nat.internalInterfaces = [ "eth2" ]; + networking.nat.externalInterface = "eth1"; + networking.firewall.enable = true; + networking.firewall.trustedInterfaces = [ "eth2" ]; + networking.interfaces.eth0.ipv4.addresses = []; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalRouterAddress; prefixLength = 24; } + ]; + networking.interfaces.eth2.ipv4.addresses = [ + { address = internalRouterAddress; prefixLength = 24; } + ]; + services.miniupnpd = { + enable = true; + externalInterface = "eth1"; + internalIPs = [ "eth2" ]; + appendConfig = '' + ext_ip=${externalRouterAddress} + ''; + }; + }; + + client1 = + { pkgs, nodes, ... }: + { environment.systemPackages = [ pkgs.transmission pkgs.miniupnpc ]; + virtualisation.vlans = [ 2 ]; + networking.interfaces.eth0.ipv4.addresses = []; + networking.interfaces.eth1.ipv4.addresses = [ + { address = internalClient1Address; prefixLength = 24; } + ]; + networking.defaultGateway = internalRouterAddress; + networking.firewall.enable = false; + services.transmission.enable = true; + services.transmission.settings.dht-enabled = false; + services.transmission.settings.message-level = 3; + }; + + client2 = + { pkgs, ... }: + { environment.systemPackages = [ pkgs.transmission ]; + virtualisation.vlans = [ 1 ]; + networking.interfaces.eth0.ipv4.addresses = []; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalClient2Address; prefixLength = 24; } + ]; + networking.firewall.enable = false; + services.transmission.enable = true; + services.transmission.settings.dht-enabled = false; + services.transmission.settings.port-forwaring-enabled = false; + }; + }; + + testScript = + { nodes, ... }: + '' + startAll; + + # Wait for network and miniupnpd. + $router->waitForUnit("network-online.target"); + $router->waitForUnit("miniupnpd"); + + # Create the torrent. + $tracker->succeed("mkdir /tmp/data"); + $tracker->succeed("cp ${file} /tmp/data/test.tar.bz2"); + $tracker->succeed("transmission-create /tmp/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent"); + $tracker->succeed("chmod 644 /tmp/test.torrent"); + + # Start the tracker. !!! use a less crappy tracker + $tracker->waitForUnit("network-online.target"); + $tracker->waitForUnit("opentracker.service"); + $tracker->waitForOpenPort(6969); + + # Start the initial seeder. + $tracker->succeed("transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir /tmp/data"); + + # Now we should be able to download from the client behind the NAT. + $tracker->waitForUnit("httpd"); + $client1->waitForUnit("network-online.target"); + $client1->succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent --download-dir /tmp >&2 &"); + $client1->waitForFile("/tmp/test.tar.bz2"); + $client1->succeed("cmp /tmp/test.tar.bz2 ${file}"); + + # Bring down the initial seeder. + # $tracker->stopJob("transmission"); + + # Now download from the second client. This can only succeed if + # the first client created a NAT hole in the router. + $client2->waitForUnit("network-online.target"); + $client2->succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht --download-dir /tmp >&2 &"); + $client2->waitForFile("/tmp/test.tar.bz2"); + $client2->succeed("cmp /tmp/test.tar.bz2 ${file}"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/blivet.nix b/nixpkgs/nixos/tests/blivet.nix new file mode 100644 index 000000000000..2adc2ee1eeea --- /dev/null +++ b/nixpkgs/nixos/tests/blivet.nix @@ -0,0 +1,87 @@ +import ./make-test.nix ({ pkgs, ... }: with pkgs.python2Packages; rec { + name = "blivet"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aszlig ]; + }; + + machine = { + environment.systemPackages = [ pkgs.python blivet mock ]; + boot.supportedFilesystems = [ "btrfs" "jfs" "reiserfs" "xfs" ]; + virtualisation.memorySize = 768; + }; + + debugBlivet = false; + debugProgramCalls = false; + + pythonTestRunner = pkgs.writeText "run-blivet-tests.py" '' + import sys + import logging + + from unittest import TestLoader + from unittest.runner import TextTestRunner + + ${pkgs.lib.optionalString debugProgramCalls '' + blivet_program_log = logging.getLogger("program") + blivet_program_log.setLevel(logging.DEBUG) + blivet_program_log.addHandler(logging.StreamHandler(sys.stderr)) + ''} + + ${pkgs.lib.optionalString debugBlivet '' + blivet_log = logging.getLogger("blivet") + blivet_log.setLevel(logging.DEBUG) + blivet_log.addHandler(logging.StreamHandler(sys.stderr)) + ''} + + runner = TextTestRunner(verbosity=2, failfast=False, buffer=False) + result = runner.run(TestLoader().discover('tests/', pattern='*_test.py')) + sys.exit(not result.wasSuccessful()) + ''; + + blivetTest = pkgs.writeScript "blivet-test.sh" '' + #!${pkgs.stdenv.shell} -e + + # Use the hosts temporary directory, because we have a tmpfs within the VM + # and we don't want to increase the memory size of the VM for no reason. + mkdir -p /tmp/xchg/bigtmp + TMPDIR=/tmp/xchg/bigtmp + export TMPDIR + + cp -Rd "${blivet.src}/tests" . + + # Skip SELinux tests + rm -f tests/formats_test/selinux_test.py + + # Race conditions in growing/shrinking during resync + rm -f tests/devicelibs_test/mdraid_* + + # Deactivate small BTRFS device test, because it fails with newer btrfsprogs + sed -i -e '/^class *BTRFSAsRootTestCase3(/,/^[^ ]/ { + /^class *BTRFSAsRootTestCase3(/d + /^$/d + /^ /d + }' tests/devicelibs_test/btrfs_test.py + + # How on earth can these tests ever work even upstream? O_o + sed -i -e '/def testDiskChunk[12]/,/^ *[^ ]/{n; s/^ */&return # /}' \ + tests/partitioning_test.py + + # fix hardcoded temporary directory + sed -i \ + -e '1i import tempfile' \ + -e 's|_STORE_FILE_PATH = .*|_STORE_FILE_PATH = tempfile.gettempdir()|' \ + -e 's|DEFAULT_STORE_SIZE = .*|DEFAULT_STORE_SIZE = 409600|' \ + tests/loopbackedtestcase.py + + PYTHONPATH=".:$(< "${pkgs.stdenv.mkDerivation { + name = "blivet-pythonpath"; + buildInputs = [ blivet mock ]; + buildCommand = "echo \"$PYTHONPATH\" > \"$out\""; + }}")" python "${pythonTestRunner}" + ''; + + testScript = '' + $machine->waitForUnit("multi-user.target"); + $machine->succeed("${blivetTest}"); + $machine->execute("rm -rf /tmp/xchg/bigtmp"); + ''; +}) diff --git a/nixpkgs/nixos/tests/boot-stage1.nix b/nixpkgs/nixos/tests/boot-stage1.nix new file mode 100644 index 000000000000..b2e74bff6fcd --- /dev/null +++ b/nixpkgs/nixos/tests/boot-stage1.nix @@ -0,0 +1,162 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "boot-stage1"; + + machine = { config, pkgs, lib, ... }: { + boot.extraModulePackages = let + compileKernelModule = name: source: pkgs.runCommandCC name rec { + inherit source; + kdev = config.boot.kernelPackages.kernel.dev; + kver = config.boot.kernelPackages.kernel.modDirVersion; + ksrc = "${kdev}/lib/modules/${kver}/build"; + hardeningDisable = [ "pic" ]; + nativeBuildInputs = kdev.moduleBuildDependencies; + } '' + echo "obj-m += $name.o" > Makefile + echo "$source" > "$name.c" + make -C "$ksrc" M=$(pwd) modules + install -vD "$name.ko" "$out/lib/modules/$kver/$name.ko" + ''; + + # This spawns a kthread which just waits until it gets a signal and + # terminates if that is the case. We want to make sure that nothing during + # the boot process kills any kthread by accident, like what happened in + # issue #15226. + kcanary = compileKernelModule "kcanary" '' + #include <linux/version.h> + #include <linux/init.h> + #include <linux/module.h> + #include <linux/kernel.h> + #include <linux/kthread.h> + #include <linux/sched.h> + #include <linux/signal.h> + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + #include <linux/sched/signal.h> + #endif + + struct task_struct *canaryTask; + + static int kcanary(void *nothing) + { + allow_signal(SIGINT); + allow_signal(SIGTERM); + allow_signal(SIGKILL); + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout_interruptible(msecs_to_jiffies(100)); + if (signal_pending(current)) break; + } + return 0; + } + + static int kcanaryInit(void) + { + kthread_run(&kcanary, NULL, "kcanary"); + return 0; + } + + static void kcanaryExit(void) + { + kthread_stop(canaryTask); + } + + module_init(kcanaryInit); + module_exit(kcanaryExit); + ''; + + in lib.singleton kcanary; + + boot.initrd.kernelModules = [ "kcanary" ]; + + boot.initrd.extraUtilsCommands = let + compile = name: source: pkgs.runCommandCC name { inherit source; } '' + mkdir -p "$out/bin" + echo "$source" | gcc -Wall -o "$out/bin/$name" -xc - + ''; + + daemonize = name: source: compile name '' + #include <stdio.h> + #include <unistd.h> + + void runSource(void) { + ${source} + } + + int main(void) { + if (fork() > 0) return 0; + setsid(); + runSource(); + return 1; + } + ''; + + mkCmdlineCanary = { name, cmdline ? "", source ? "" }: (daemonize name '' + char *argv[] = {"${cmdline}", NULL}; + execvp("${name}-child", argv); + '') // { + child = compile "${name}-child" '' + #include <stdio.h> + #include <unistd.h> + + int main(void) { + ${source} + while (1) sleep(1); + return 1; + } + ''; + }; + + copyCanaries = with lib; concatMapStrings (canary: '' + ${optionalString (canary ? child) '' + copy_bin_and_libs "${canary.child}/bin/${canary.child.name}" + ''} + copy_bin_and_libs "${canary}/bin/${canary.name}" + ''); + + in copyCanaries [ + # Simple canary process which just sleeps forever and should be killed by + # stage 2. + (daemonize "canary1" "while (1) sleep(1);") + + # We want this canary process to try mimicking a kthread using a cmdline + # with a zero length so we can make sure that the process is properly + # killed in stage 1. + (mkCmdlineCanary { + name = "canary2"; + source = '' + FILE *f; + f = fopen("/run/canary2.pid", "w"); + fprintf(f, "%d\n", getpid()); + fclose(f); + ''; + }) + + # This canary process mimicks a storage daemon, which we do NOT want to be + # killed before going into stage 2. For more on root storage daemons, see: + # https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/ + (mkCmdlineCanary { + name = "canary3"; + cmdline = "@canary3"; + }) + ]; + + boot.initrd.postMountCommands = '' + canary1 + canary2 + canary3 + # Make sure the pidfile of canary 2 is created so that we still can get + # its former pid after the killing spree starts next within stage 1. + while [ ! -s /run/canary2.pid ]; do sleep 0.1; done + ''; + }; + + testScript = '' + $machine->waitForUnit("multi-user.target"); + $machine->succeed('test -s /run/canary2.pid'); + $machine->fail('pgrep -a canary1'); + $machine->fail('kill -0 $(< /run/canary2.pid)'); + $machine->succeed('pgrep -a -f \'^@canary3$\'''); + $machine->succeed('pgrep -a -f \'^kcanary$\'''); + ''; + + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ aszlig ]; +}) diff --git a/nixpkgs/nixos/tests/boot.nix b/nixpkgs/nixos/tests/boot.nix new file mode 100644 index 000000000000..c9bb1e77c6d0 --- /dev/null +++ b/nixpkgs/nixos/tests/boot.nix @@ -0,0 +1,99 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + + iso = + (import ../lib/eval-config.nix { + inherit system; + modules = + [ ../modules/installer/cd-dvd/installation-cd-minimal.nix + ../modules/testing/test-instrumentation.nix + ]; + }).config.system.build.isoImage; + + makeBootTest = name: machineConfig: + makeTest { + inherit iso; + name = "boot-" + name; + nodes = { }; + testScript = + '' + my $machine = createMachine({ ${machineConfig}, qemuFlags => '-m 768' }); + $machine->start; + $machine->waitForUnit("multi-user.target"); + $machine->succeed("nix verify -r --no-trust /run/current-system"); + + # Test whether the channel got installed correctly. + $machine->succeed("nix-instantiate --dry-run '<nixpkgs>' -A hello"); + $machine->succeed("nix-env --dry-run -iA nixos.procps"); + + $machine->shutdown; + ''; + }; +in { + + biosCdrom = makeBootTest "bios-cdrom" '' + cdrom => glob("${iso}/iso/*.iso") + ''; + + biosUsb = makeBootTest "bios-usb" '' + usb => glob("${iso}/iso/*.iso") + ''; + + uefiCdrom = makeBootTest "uefi-cdrom" '' + cdrom => glob("${iso}/iso/*.iso"), + bios => '${pkgs.OVMF.fd}/FV/OVMF.fd' + ''; + + uefiUsb = makeBootTest "uefi-usb" '' + usb => glob("${iso}/iso/*.iso"), + bios => '${pkgs.OVMF.fd}/FV/OVMF.fd' + ''; + + netboot = let + config = (import ../lib/eval-config.nix { + inherit system; + modules = + [ ../modules/installer/netboot/netboot.nix + ../modules/testing/test-instrumentation.nix + { key = "serial"; } + ]; + }).config; + ipxeScriptDir = pkgs.writeTextFile { + name = "ipxeScriptDir"; + text = '' + #!ipxe + dhcp + kernel bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} console=ttyS0 + initrd initrd + boot + ''; + destination = "/boot.ipxe"; + }; + ipxeBootDir = pkgs.symlinkJoin { + name = "ipxeBootDir"; + paths = [ + config.system.build.netbootRamdisk + config.system.build.kernel + ipxeScriptDir + ]; + }; + in + makeTest { + name = "boot-netboot"; + nodes = { }; + testScript = + '' + my $machine = createMachine({ qemuFlags => '-boot order=n -net nic,model=e1000 -net user,tftp=${ipxeBootDir}/,bootfile=boot.ipxe -m 2000M' }); + $machine->start; + $machine->waitForUnit("multi-user.target"); + $machine->shutdown; + ''; + }; +} diff --git a/nixpkgs/nixos/tests/borgbackup.nix b/nixpkgs/nixos/tests/borgbackup.nix new file mode 100644 index 000000000000..fdb87dbea438 --- /dev/null +++ b/nixpkgs/nixos/tests/borgbackup.nix @@ -0,0 +1,165 @@ +import ./make-test.nix ({ pkgs, ... }: + +let + passphrase = "supersecret"; + dataDir = "/ran:dom/data"; + excludeFile = "not_this_file"; + keepFile = "important_file"; + keepFileData = "important_data"; + localRepo = "/root/back:up"; + archiveName = "my_archive"; + remoteRepo = "borg@server:."; # No need to specify path + privateKey = pkgs.writeText "id_ed25519" '' + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrwAAAJB+cF5HfnBe + RwAAAAtzc2gtZWQyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrw + AAAEBN75NsJZSpt63faCuaD75Unko0JjlSDxMhYHAPJk2/xXHxQHThDpD9/AMWNqQer3Tg + 9gXMb2lTZMn0pelo8xyvAAAADXJzY2h1ZXR6QGt1cnQ= + -----END OPENSSH PRIVATE KEY----- + ''; + publicKey = '' + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHHxQHThDpD9/AMWNqQer3Tg9gXMb2lTZMn0pelo8xyv root@client + ''; + privateKeyAppendOnly = pkgs.writeText "id_ed25519" '' + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLwAAAJC9YTxxvWE8 + cQAAAAtzc2gtZWQyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLw + AAAEAAhV7wTl5dL/lz+PF/d4PnZXuG1Id6L/mFEiGT1tZsuFpxm7PUQsZB2Ejs8Xp0YVp8 + IOW+HylIRzhweORbRCMvAAAADXJzY2h1ZXR6QGt1cnQ= + -----END OPENSSH PRIVATE KEY----- + ''; + publicKeyAppendOnly = '' + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFpxm7PUQsZB2Ejs8Xp0YVp8IOW+HylIRzhweORbRCMv root@client + ''; + +in { + name = "borgbackup"; + meta = with pkgs.stdenv.lib; { + maintainers = with maintainers; [ dotlambda ]; + }; + + nodes = { + client = { ... }: { + services.borgbackup.jobs = { + + local = rec { + paths = dataDir; + repo = localRepo; + preHook = '' + # Don't append a timestamp + archiveName="${archiveName}" + ''; + encryption = { + mode = "repokey"; + inherit passphrase; + }; + compression = "auto,zlib,9"; + prune.keep = { + within = "1y"; + yearly = 5; + }; + exclude = [ "*/${excludeFile}" ]; + postHook = "echo post"; + startAt = [ ]; # Do not run automatically + }; + + remote = { + paths = dataDir; + repo = remoteRepo; + encryption.mode = "none"; + startAt = [ ]; + environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519"; + }; + + remoteAppendOnly = { + paths = dataDir; + repo = remoteRepo; + encryption.mode = "none"; + startAt = [ ]; + environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly"; + }; + + }; + }; + + server = { ... }: { + services.openssh = { + enable = true; + passwordAuthentication = false; + challengeResponseAuthentication = false; + }; + + services.borgbackup.repos.repo1 = { + authorizedKeys = [ publicKey ]; + path = "/data/borgbackup"; + }; + + # Second repo to make sure the authorizedKeys options are merged correctly + services.borgbackup.repos.repo2 = { + authorizedKeysAppendOnly = [ publicKeyAppendOnly ]; + path = "/data/borgbackup"; + quota = ".5G"; + }; + }; + }; + + testScript = '' + startAll; + + $client->fail('test -d "${remoteRepo}"'); + + $client->succeed("cp ${privateKey} /root/id_ed25519"); + $client->succeed("chmod 0600 /root/id_ed25519"); + $client->succeed("cp ${privateKeyAppendOnly} /root/id_ed25519.appendOnly"); + $client->succeed("chmod 0600 /root/id_ed25519.appendOnly"); + + $client->succeed("mkdir -p ${dataDir}"); + $client->succeed("touch ${dataDir}/${excludeFile}"); + $client->succeed("echo '${keepFileData}' > ${dataDir}/${keepFile}"); + + subtest "local", sub { + my $borg = "BORG_PASSPHRASE='${passphrase}' borg"; + $client->systemctl("start --wait borgbackup-job-local"); + $client->fail("systemctl is-failed borgbackup-job-local"); + # Make sure exactly one archive has been created + $client->succeed("c=\$($borg list '${localRepo}' | wc -l) && [[ \$c == '1' ]]"); + # Make sure excludeFile has been excluded + $client->fail("$borg list '${localRepo}::${archiveName}' | grep -qF '${excludeFile}'"); + # Make sure keepFile has the correct content + $client->succeed("$borg extract '${localRepo}::${archiveName}'"); + $client->succeed('c=$(cat ${dataDir}/${keepFile}) && [[ "$c" == "${keepFileData}" ]]'); + # Make sure the same is true when using `borg mount` + $client->succeed("mkdir -p /mnt/borg && $borg mount '${localRepo}::${archiveName}' /mnt/borg"); + $client->succeed('c=$(cat /mnt/borg/${dataDir}/${keepFile}) && [[ "$c" == "${keepFileData}" ]]'); + }; + + subtest "remote", sub { + my $borg = "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519' borg"; + $server->waitForUnit("sshd.service"); + $client->waitForUnit("network.target"); + $client->systemctl("start --wait borgbackup-job-remote"); + $client->fail("systemctl is-failed borgbackup-job-remote"); + + # Make sure we can't access repos other than the specified one + $client->fail("$borg list borg\@server:wrong"); + + #TODO: Make sure that data is actually deleted + }; + + subtest "remoteAppendOnly", sub { + my $borg = "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly' borg"; + $server->waitForUnit("sshd.service"); + $client->waitForUnit("network.target"); + $client->systemctl("start --wait borgbackup-job-remoteAppendOnly"); + $client->fail("systemctl is-failed borgbackup-job-remoteAppendOnly"); + + # Make sure we can't access repos other than the specified one + $client->fail("$borg list borg\@server:wrong"); + + #TODO: Make sure that data is not actually deleted + }; + + ''; +}) diff --git a/nixpkgs/nixos/tests/buildbot.nix b/nixpkgs/nixos/tests/buildbot.nix new file mode 100644 index 000000000000..210ad8e91df7 --- /dev/null +++ b/nixpkgs/nixos/tests/buildbot.nix @@ -0,0 +1,120 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; + +let + # Test ensures buildbot master comes up correctly and workers can connect + mkBuildbotTest = python: makeTest { + name = "buildbot"; + + nodes = { + bbmaster = { pkgs, ... }: { + services.buildbot-master = { + enable = true; + package = python.pkgs.buildbot-full; + + # NOTE: use fake repo due to no internet in hydra ci + factorySteps = [ + "steps.Git(repourl='git://gitrepo/fakerepo.git', mode='incremental')" + "steps.ShellCommand(command=['bash', 'fakerepo.sh'])" + ]; + changeSource = [ + "changes.GitPoller('git://gitrepo/fakerepo.git', workdir='gitpoller-workdir', branch='master', pollinterval=300)" + ]; + }; + networking.firewall.allowedTCPPorts = [ 8010 8011 9989 ]; + environment.systemPackages = with pkgs; [ git python.pkgs.buildbot-full ]; + }; + + bbworker = { pkgs, ... }: { + services.buildbot-worker = { + enable = true; + masterUrl = "bbmaster:9989"; + }; + environment.systemPackages = with pkgs; [ git python.pkgs.buildbot-worker ]; + }; + + gitrepo = { pkgs, ... }: { + services.openssh.enable = true; + networking.firewall.allowedTCPPorts = [ 22 9418 ]; + environment.systemPackages = with pkgs; [ git ]; + }; + }; + + testScript = '' + #Start up and populate fake repo + $gitrepo->waitForUnit("multi-user.target"); + print($gitrepo->execute(" \ + git config --global user.name 'Nobody Fakeuser' && \ + git config --global user.email 'nobody\@fakerepo.com' && \ + rm -rvf /srv/repos/fakerepo.git /tmp/fakerepo && \ + mkdir -pv /srv/repos/fakerepo ~/.ssh && \ + ssh-keyscan -H gitrepo > ~/.ssh/known_hosts && \ + cat ~/.ssh/known_hosts && \ + cd /srv/repos/fakerepo && \ + git init && \ + echo -e '#!/bin/sh\necho fakerepo' > fakerepo.sh && \ + cat fakerepo.sh && \ + touch .git/git-daemon-export-ok && \ + git add fakerepo.sh .git/git-daemon-export-ok && \ + git commit -m fakerepo && \ + git daemon --verbose --export-all --base-path=/srv/repos --reuseaddr & \ + ")); + + # Test gitrepo + $bbmaster->waitForUnit("network-online.target"); + #$bbmaster->execute("nc -z gitrepo 9418"); + print($bbmaster->execute(" \ + rm -rfv /tmp/fakerepo && \ + git clone git://gitrepo/fakerepo /tmp/fakerepo && \ + pwd && \ + ls -la && \ + ls -la /tmp/fakerepo \ + ")); + + # Test start master and connect worker + $bbmaster->waitForUnit("buildbot-master.service"); + $bbmaster->waitUntilSucceeds("curl -s --head http://bbmaster:8010") =~ /200 OK/; + $bbworker->waitForUnit("network-online.target"); + $bbworker->execute("nc -z bbmaster 8010"); + $bbworker->execute("nc -z bbmaster 9989"); + $bbworker->waitForUnit("buildbot-worker.service"); + print($bbworker->execute("ls -la /home/bbworker/worker")); + + + # Test stop buildbot master and worker + print($bbmaster->execute(" \ + systemctl -l --no-pager status buildbot-master && \ + systemctl stop buildbot-master \ + ")); + $bbworker->fail("nc -z bbmaster 8010"); + $bbworker->fail("nc -z bbmaster 9989"); + print($bbworker->execute(" \ + systemctl -l --no-pager status buildbot-worker && \ + systemctl stop buildbot-worker && \ + ls -la /home/bbworker/worker \ + ")); + + + # Test buildbot daemon mode + $bbmaster->execute("buildbot create-master /tmp"); + $bbmaster->execute("mv -fv /tmp/master.cfg.sample /tmp/master.cfg"); + $bbmaster->execute("sed -i 's/8010/8011/' /tmp/master.cfg"); + $bbmaster->execute("buildbot start /tmp"); + $bbworker->execute("nc -z bbmaster 8011"); + $bbworker->waitUntilSucceeds("curl -s --head http://bbmaster:8011") =~ /200 OK/; + $bbmaster->execute("buildbot stop /tmp"); + $bbworker->fail("nc -z bbmaster 8011"); + + ''; + + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ nand0p ]; + + }; +in { + python2 = mkBuildbotTest pkgs.python2; + python3 = mkBuildbotTest pkgs.python3; +} diff --git a/nixpkgs/nixos/tests/cadvisor.nix b/nixpkgs/nixos/tests/cadvisor.nix new file mode 100644 index 000000000000..e60bae4b7003 --- /dev/null +++ b/nixpkgs/nixos/tests/cadvisor.nix @@ -0,0 +1,35 @@ +import ./make-test.nix ({ pkgs, ... } : { + name = "cadvisor"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ offline ]; + }; + + nodes = { + machine = { ... }: { + services.cadvisor.enable = true; + }; + + influxdb = { lib, ... }: with lib; { + services.cadvisor.enable = true; + services.cadvisor.storageDriver = "influxdb"; + services.influxdb.enable = true; + }; + }; + + testScript = + '' + startAll; + $machine->waitForUnit("cadvisor.service"); + $machine->succeed("curl http://localhost:8080/containers/"); + + $influxdb->waitForUnit("influxdb.service"); + + # create influxdb database + $influxdb->succeed(q~ + curl -XPOST http://localhost:8086/query --data-urlencode "q=CREATE DATABASE root" + ~); + + $influxdb->waitForUnit("cadvisor.service"); + $influxdb->succeed("curl http://localhost:8080/containers/"); + ''; +}) diff --git a/nixpkgs/nixos/tests/cassandra.nix b/nixpkgs/nixos/tests/cassandra.nix new file mode 100644 index 000000000000..60d0c6d76068 --- /dev/null +++ b/nixpkgs/nixos/tests/cassandra.nix @@ -0,0 +1,71 @@ +import ./make-test.nix ({ pkgs, ...}: +let + # Change this to test a different version of Cassandra: + testPackage = pkgs.cassandra; + cassandraCfg = + { enable = true; + listenAddress = null; + listenInterface = "eth1"; + rpcAddress = null; + rpcInterface = "eth1"; + extraConfig = + { start_native_transport = true; + seed_provider = + [{ class_name = "org.apache.cassandra.locator.SimpleSeedProvider"; + parameters = [ { seeds = "cass0"; } ]; + }]; + }; + package = testPackage; + }; + nodeCfg = extra: {pkgs, config, ...}: + { environment.systemPackages = [ testPackage ]; + networking.firewall.enable = false; + services.cassandra = cassandraCfg // extra; + virtualisation.memorySize = 1024; + }; +in +{ + name = "cassandra-ci"; + + nodes = { + cass0 = nodeCfg {}; + cass1 = nodeCfg {}; + cass2 = nodeCfg { jvmOpts = [ "-Dcassandra.replace_address=cass1" ]; }; + }; + + testScript = '' + subtest "timers exist", sub { + $cass0->succeed("systemctl list-timers | grep cassandra-full-repair.timer"); + $cass0->succeed("systemctl list-timers | grep cassandra-incremental-repair.timer"); + }; + subtest "can connect via cqlsh", sub { + $cass0->waitForUnit("cassandra.service"); + $cass0->waitUntilSucceeds("nc -z cass0 9042"); + $cass0->succeed("echo 'show version;' | cqlsh cass0"); + }; + subtest "nodetool is operational", sub { + $cass0->waitForUnit("cassandra.service"); + $cass0->waitUntilSucceeds("nc -z localhost 7199"); + $cass0->succeed("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass0'"); + }; + subtest "bring up cluster", sub { + $cass1->waitForUnit("cassandra.service"); + $cass1->waitUntilSucceeds("nodetool status | egrep -c '^UN' | grep 2"); + $cass0->succeed("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass1'"); + }; + subtest "break and fix node", sub { + $cass1->block; + $cass0->waitUntilSucceeds("nodetool status --resolve-ip | egrep -c '^DN[[:space:]]+cass1'"); + $cass0->succeed("nodetool status | egrep -c '^UN' | grep 1"); + $cass1->unblock; + $cass1->waitUntilSucceeds("nodetool status | egrep -c '^UN' | grep 2"); + $cass0->succeed("nodetool status | egrep -c '^UN' | grep 2"); + }; + subtest "replace crashed node", sub { + $cass1->crash; + $cass2->waitForUnit("cassandra.service"); + $cass0->waitUntilFails("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass1'"); + $cass0->waitUntilSucceeds("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass2'"); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/ceph.nix b/nixpkgs/nixos/tests/ceph.nix new file mode 100644 index 000000000000..7408029c460e --- /dev/null +++ b/nixpkgs/nixos/tests/ceph.nix @@ -0,0 +1,139 @@ +import ./make-test.nix ({pkgs, ...}: rec { + name = "All-in-one-basic-ceph-cluster"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ lejonet ]; + }; + + nodes = { + aio = { pkgs, ... }: { + virtualisation = { + emptyDiskImages = [ 20480 20480 ]; + vlans = [ 1 ]; + }; + + networking = { + useDHCP = false; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [ + { address = "192.168.1.1"; prefixLength = 24; } + ]; + }; + + environment.systemPackages = with pkgs; [ + bash + sudo + ceph + xfsprogs + ]; + nixpkgs.config.packageOverrides = super: { + ceph = super.ceph.override({ nss = super.nss; libxfs = super.libxfs; libaio = super.libaio; jemalloc = super.jemalloc; }); + }; + + boot.kernelModules = [ "xfs" ]; + + services.ceph.enable = true; + services.ceph.global = { + fsid = "066ae264-2a5d-4729-8001-6ad265f50b03"; + monInitialMembers = "aio"; + monHost = "192.168.1.1"; + }; + + services.ceph.mon = { + enable = true; + daemons = [ "aio" ]; + }; + + services.ceph.mgr = { + enable = true; + daemons = [ "aio" ]; + }; + + services.ceph.osd = { + enable = true; + daemons = [ "0" "1" ]; + }; + }; + }; + + testScript = { ... }: '' + startAll; + + $aio->waitForUnit("network.target"); + + # Create the ceph-related directories + $aio->mustSucceed( + "mkdir -p /var/lib/ceph/mgr/ceph-aio/", + "mkdir -p /var/lib/ceph/mon/ceph-aio/", + "mkdir -p /var/lib/ceph/osd/ceph-{0..1}/", + "chown ceph:ceph -R /var/lib/ceph/" + ); + + # Bootstrap ceph-mon daemon + $aio->mustSucceed( + "mkdir -p /var/lib/ceph/bootstrap-osd && chown ceph:ceph /var/lib/ceph/bootstrap-osd", + "sudo -u ceph ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'", + "ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --set-uid=0 --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'", + "ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring", + "monmaptool --create --add aio 192.168.1.1 --fsid 066ae264-2a5d-4729-8001-6ad265f50b03 /tmp/monmap", + "sudo -u ceph ceph-mon --mkfs -i aio --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring", + "touch /var/lib/ceph/mon/ceph-aio/done", + "systemctl start ceph-mon-aio" + ); + $aio->waitForUnit("ceph-mon-aio"); + + # Can't check ceph status until a mon is up + $aio->succeed("ceph -s | grep 'mon: 1 daemons'"); + + # Start the ceph-mgr daemon, it has no deps and hardly any setup + $aio->mustSucceed( + "ceph auth get-or-create mgr.aio mon 'allow profile mgr' osd 'allow *' mds 'allow *' > /var/lib/ceph/mgr/ceph-aio/keyring", + "systemctl start ceph-mgr-aio" + ); + $aio->waitForUnit("ceph-mgr-aio"); + $aio->waitUntilSucceeds("ceph -s | grep 'quorum aio'"); + + # Bootstrap both OSDs + $aio->mustSucceed( + "mkfs.xfs /dev/vdb", + "mkfs.xfs /dev/vdc", + "mount /dev/vdb /var/lib/ceph/osd/ceph-0", + "mount /dev/vdc /var/lib/ceph/osd/ceph-1", + "ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-0/keyring --name osd.0 --add-key AQBCEJNa3s8nHRAANvdsr93KqzBznuIWm2gOGg==", + "ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-1/keyring --name osd.1 --add-key AQBEEJNac00kExAAXEgy943BGyOpVH1LLlHafQ==", + "echo '{\"cephx_secret\": \"AQBCEJNa3s8nHRAANvdsr93KqzBznuIWm2gOGg==\"}' | ceph osd new 55ba2294-3e24-478f-bee0-9dca4c231dd9 -i -", + "echo '{\"cephx_secret\": \"AQBEEJNac00kExAAXEgy943BGyOpVH1LLlHafQ==\"}' | ceph osd new 5e97a838-85b6-43b0-8950-cb56d554d1e5 -i -" + ); + + # Initialize the OSDs with regular filestore + $aio->mustSucceed( + "ceph-osd -i 0 --mkfs --osd-uuid 55ba2294-3e24-478f-bee0-9dca4c231dd9", + "ceph-osd -i 1 --mkfs --osd-uuid 5e97a838-85b6-43b0-8950-cb56d554d1e5", + "chown -R ceph:ceph /var/lib/ceph/osd", + "systemctl start ceph-osd-0", + "systemctl start ceph-osd-1" + ); + + $aio->waitUntilSucceeds("ceph osd stat | grep '2 osds: 2 up, 2 in'"); + $aio->waitUntilSucceeds("ceph -s | grep 'mgr: aio(active)'"); + $aio->waitUntilSucceeds("ceph -s | grep 'HEALTH_OK'"); + + $aio->mustSucceed( + "ceph osd pool create aio-test 100 100", + "ceph osd pool ls | grep 'aio-test'", + "ceph osd pool rename aio-test aio-other-test", + "ceph osd pool ls | grep 'aio-other-test'", + "ceph -s | grep '1 pools, 100 pgs'", + "ceph osd getcrushmap -o crush", + "crushtool -d crush -o decrushed", + "sed 's/step chooseleaf firstn 0 type host/step chooseleaf firstn 0 type osd/' decrushed > modcrush", + "crushtool -c modcrush -o recrushed", + "ceph osd setcrushmap -i recrushed", + "ceph osd pool set aio-other-test size 2" + ); + $aio->waitUntilSucceeds("ceph -s | grep 'HEALTH_OK'"); + $aio->waitUntilSucceeds("ceph -s | grep '100 active+clean'"); + $aio->mustFail( + "ceph osd pool ls | grep 'aio-test'", + "ceph osd pool delete aio-other-test aio-other-test --yes-i-really-really-mean-it" + ); + ''; +}) diff --git a/nixpkgs/nixos/tests/certmgr.nix b/nixpkgs/nixos/tests/certmgr.nix new file mode 100644 index 000000000000..fe67833808ce --- /dev/null +++ b/nixpkgs/nixos/tests/certmgr.nix @@ -0,0 +1,151 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +let + mkSpec = { host, service ? null, action }: { + inherit action; + authority = { + file = { + group = "nobody"; + owner = "nobody"; + path = "/tmp/${host}-ca.pem"; + }; + label = "www_ca"; + profile = "three-month"; + remote = "localhost:8888"; + }; + certificate = { + group = "nobody"; + owner = "nobody"; + path = "/tmp/${host}-cert.pem"; + }; + private_key = { + group = "nobody"; + mode = "0600"; + owner = "nobody"; + path = "/tmp/${host}-key.pem"; + }; + request = { + CN = host; + hosts = [ host "www.${host}" ]; + key = { + algo = "rsa"; + size = 2048; + }; + names = [ + { + C = "US"; + L = "San Francisco"; + O = "Example, LLC"; + ST = "CA"; + } + ]; + }; + inherit service; + }; + + mkCertmgrTest = { svcManager, specs, testScript }: makeTest { + name = "certmgr-" + svcManager; + nodes = { + machine = { config, lib, pkgs, ... }: { + networking.firewall.allowedTCPPorts = with config.services; [ cfssl.port certmgr.metricsPort ]; + networking.extraHosts = "127.0.0.1 imp.example.org decl.example.org"; + + services.cfssl.enable = true; + systemd.services.cfssl.after = [ "cfssl-init.service" "networking.target" ]; + + systemd.services.cfssl-init = { + description = "Initialize the cfssl CA"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "cfssl"; + Type = "oneshot"; + WorkingDirectory = config.services.cfssl.dataDir; + }; + script = '' + ${pkgs.cfssl}/bin/cfssl genkey -initca ${pkgs.writeText "ca.json" (builtins.toJSON { + hosts = [ "ca.example.com" ]; + key = { + algo = "rsa"; size = 4096; }; + names = [ + { + C = "US"; + L = "San Francisco"; + O = "Internet Widgets, LLC"; + OU = "Certificate Authority"; + ST = "California"; + } + ]; + })} | ${pkgs.cfssl}/bin/cfssljson -bare ca + ''; + }; + + services.nginx = { + enable = true; + virtualHosts = lib.mkMerge (map (host: { + ${host} = { + sslCertificate = "/tmp/${host}-cert.pem"; + sslCertificateKey = "/tmp/${host}-key.pem"; + extraConfig = '' + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ''; + onlySSL = true; + serverName = host; + root = pkgs.writeTextDir "index.html" "It works!"; + }; + }) [ "imp.example.org" "decl.example.org" ]); + }; + + systemd.services.nginx.wantedBy = lib.mkForce []; + + systemd.services.certmgr.after = [ "cfssl.service" ]; + services.certmgr = { + enable = true; + inherit svcManager; + inherit specs; + }; + + }; + }; + inherit testScript; + }; +in +{ + systemd = mkCertmgrTest { + svcManager = "systemd"; + specs = { + decl = mkSpec { host = "decl.example.org"; service = "nginx"; action ="restart"; }; + imp = toString (pkgs.writeText "test.json" (builtins.toJSON ( + mkSpec { host = "imp.example.org"; service = "nginx"; action = "restart"; } + ))); + }; + testScript = '' + $machine->waitForUnit('cfssl.service'); + $machine->waitUntilSucceeds('ls /tmp/decl.example.org-ca.pem'); + $machine->waitUntilSucceeds('ls /tmp/decl.example.org-key.pem'); + $machine->waitUntilSucceeds('ls /tmp/decl.example.org-cert.pem'); + $machine->waitUntilSucceeds('ls /tmp/imp.example.org-ca.pem'); + $machine->waitUntilSucceeds('ls /tmp/imp.example.org-key.pem'); + $machine->waitUntilSucceeds('ls /tmp/imp.example.org-cert.pem'); + $machine->waitForUnit('nginx.service'); + $machine->succeed('[ "1" -lt "$(journalctl -u nginx | grep "Starting Nginx" | wc -l)" ]'); + $machine->succeed('curl --cacert /tmp/imp.example.org-ca.pem https://imp.example.org'); + $machine->succeed('curl --cacert /tmp/decl.example.org-ca.pem https://decl.example.org'); + ''; + }; + + command = mkCertmgrTest { + svcManager = "command"; + specs = { + test = mkSpec { host = "command.example.org"; action = "touch /tmp/command.executed"; }; + }; + testScript = '' + $machine->waitForUnit('cfssl.service'); + $machine->waitUntilSucceeds('stat /tmp/command.executed'); + ''; + }; + +} diff --git a/nixpkgs/nixos/tests/cfssl.nix b/nixpkgs/nixos/tests/cfssl.nix new file mode 100644 index 000000000000..513ed8c45741 --- /dev/null +++ b/nixpkgs/nixos/tests/cfssl.nix @@ -0,0 +1,67 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "cfssl"; + + machine = { config, lib, pkgs, ... }: + { + networking.firewall.allowedTCPPorts = [ config.services.cfssl.port ]; + + services.cfssl.enable = true; + systemd.services.cfssl.after = [ "cfssl-init.service" ]; + + systemd.services.cfssl-init = { + description = "Initialize the cfssl CA"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "cfssl"; + Type = "oneshot"; + WorkingDirectory = config.services.cfssl.dataDir; + }; + script = with pkgs; '' + ${cfssl}/bin/cfssl genkey -initca ${pkgs.writeText "ca.json" (builtins.toJSON { + hosts = [ "ca.example.com" ]; + key = { + algo = "rsa"; size = 4096; }; + names = [ + { + C = "US"; + L = "San Francisco"; + O = "Internet Widgets, LLC"; + OU = "Certificate Authority"; + ST = "California"; + } + ]; + })} | ${cfssl}/bin/cfssljson -bare ca + ''; + }; + }; + + testScript = + let + cfsslrequest = with pkgs; writeScript "cfsslrequest" '' + curl -X POST -H "Content-Type: application/json" -d @${csr} \ + http://localhost:8888/api/v1/cfssl/newkey | ${cfssl}/bin/cfssljson /tmp/certificate + ''; + csr = pkgs.writeText "csr.json" (builtins.toJSON { + CN = "www.example.com"; + hosts = [ "example.com" "www.example.com" ]; + key = { + algo = "rsa"; + size = 2048; + }; + names = [ + { + C = "US"; + L = "San Francisco"; + O = "Example Company, LLC"; + OU = "Operations"; + ST = "California"; + } + ]; + }); + in + '' + $machine->waitForUnit('cfssl.service'); + $machine->waitUntilSucceeds('${cfsslrequest}'); + $machine->succeed('ls /tmp/certificate-key.pem'); + ''; +}) diff --git a/nixpkgs/nixos/tests/chromium.nix b/nixpkgs/nixos/tests/chromium.nix new file mode 100644 index 000000000000..af5db2a3dbe1 --- /dev/null +++ b/nixpkgs/nixos/tests/chromium.nix @@ -0,0 +1,201 @@ +{ system ? builtins.currentSystem +, config ? {} +, pkgs ? import ../.. { inherit system config; } +, channelMap ? { + stable = pkgs.chromium; + beta = pkgs.chromiumBeta; + dev = pkgs.chromiumDev; + } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +mapAttrs (channel: chromiumPkg: makeTest rec { + name = "chromium-${channel}"; + meta = { + maintainers = with maintainers; [ aszlig ]; + # https://github.com/NixOS/hydra/issues/591#issuecomment-435125621 + inherit (chromiumPkg.meta) timeout; + }; + + enableOCR = true; + + machine.imports = [ ./common/user-account.nix ./common/x11.nix ]; + machine.virtualisation.memorySize = 2047; + machine.services.xserver.displayManager.auto.user = "alice"; + machine.environment.systemPackages = [ chromiumPkg ]; + + startupHTML = pkgs.writeText "chromium-startup.html" '' + <!DOCTYPE html> + <html> + <head> + <meta charset="UTF-8"> + <title>Chromium startup notifier</title> + </head> + <body onload="javascript:document.title='startup done'"> + <img src="file://${pkgs.fetchurl { + url = "http://nixos.org/logo/nixos-hex.svg"; + sha256 = "0wxpp65npdw2cg8m0cxc9qff1sb3b478cxpg1741d8951g948rg8"; + }}" /> + </body> + </html> + ''; + + testScript = let + xdo = name: text: let + xdoScript = pkgs.writeText "${name}.xdo" text; + in "${pkgs.xdotool}/bin/xdotool '${xdoScript}'"; + in '' + # Run as user alice + sub ru ($) { + my $esc = $_[0] =~ s/'/'\\${"'"}'/gr; + return "su - alice -c '$esc'"; + } + + sub createNewWin { + $machine->nest("creating a new Chromium window", sub { + $machine->execute(ru "${xdo "new-window" '' + search --onlyvisible --name "startup done" + windowfocus --sync + windowactivate --sync + ''}"); + $machine->execute(ru "${xdo "new-window" '' + key Ctrl+n + ''}"); + }); + } + + sub closeWin { + Machine::retry sub { + $machine->execute(ru "${xdo "close-window" '' + search --onlyvisible --name "new tab" + windowfocus --sync + windowactivate --sync + ''}"); + $machine->execute(ru "${xdo "close-window" '' + key Ctrl+w + ''}"); + for (1..20) { + my ($status, $out) = $machine->execute(ru "${xdo "wait-for-close" '' + search --onlyvisible --name "new tab" + ''}"); + return 1 if $status != 0; + $machine->sleep(1); + } + } + } + + sub waitForNewWin { + my $ret = 0; + $machine->nest("waiting for new Chromium window to appear", sub { + for (1..20) { + my ($status, $out) = $machine->execute(ru "${xdo "wait-for-window" '' + search --onlyvisible --name "new tab" + windowfocus --sync + windowactivate --sync + ''}"); + if ($status == 0) { + $ret = 1; + + # XXX: Somehow Chromium is not accepting keystrokes for a few + # seconds after a new window has appeared, so let's wait a while. + $machine->sleep(10); + + last; + } + $machine->sleep(1); + } + }); + return $ret; + } + + sub createAndWaitForNewWin { + for (1..3) { + createNewWin; + return 1 if waitForNewWin; + } + die "new window didn't appear within 60 seconds"; + } + + sub testNewWin { + my ($desc, $code) = @_; + createAndWaitForNewWin; + subtest($desc, $code); + closeWin; + } + + $machine->waitForX; + + my $url = "file://${startupHTML}"; + $machine->execute(ru "ulimit -c unlimited; chromium \"$url\" & disown"); + $machine->waitForText(qr/startup done/); + $machine->waitUntilSucceeds(ru "${xdo "check-startup" '' + search --sync --onlyvisible --name "startup done" + # close first start help popup + key -delay 1000 Escape + windowfocus --sync + windowactivate --sync + ''}"); + + createAndWaitForNewWin; + $machine->screenshot("empty_windows"); + closeWin; + + $machine->screenshot("startup_done"); + + testNewWin "check sandbox", sub { + $machine->succeed(ru "${xdo "type-url" '' + search --sync --onlyvisible --name "new tab" + windowfocus --sync + type --delay 1000 "chrome://sandbox" + ''}"); + + $machine->succeed(ru "${xdo "submit-url" '' + search --sync --onlyvisible --name "new tab" + windowfocus --sync + key --delay 1000 Return + ''}"); + + $machine->screenshot("sandbox_info"); + + $machine->succeed(ru "${xdo "find-window" '' + search --sync --onlyvisible --name "sandbox status" + windowfocus --sync + ''}"); + $machine->succeed(ru "${xdo "copy-sandbox-info" '' + key --delay 1000 Ctrl+a Ctrl+c + ''}"); + + my $clipboard = $machine->succeed(ru "${pkgs.xclip}/bin/xclip -o"); + die "sandbox not working properly: $clipboard" + unless $clipboard =~ /layer 1 sandbox.*namespace/mi + && $clipboard =~ /pid namespaces.*yes/mi + && $clipboard =~ /network namespaces.*yes/mi + && $clipboard =~ /seccomp.*sandbox.*yes/mi + && $clipboard =~ /you are adequately sandboxed/mi; + + $machine->sleep(1); + $machine->succeed(ru "${xdo "find-window-after-copy" '' + search --onlyvisible --name "sandbox status" + ''}"); + + my $clipboard = $machine->succeed(ru "echo void | ${pkgs.xclip}/bin/xclip -i"); + $machine->succeed(ru "${xdo "copy-sandbox-info" '' + key --delay 1000 Ctrl+a Ctrl+c + ''}"); + + my $clipboard = $machine->succeed(ru "${pkgs.xclip}/bin/xclip -o"); + die "copying twice in a row does not work properly: $clipboard" + unless $clipboard =~ /layer 1 sandbox.*namespace/mi + && $clipboard =~ /pid namespaces.*yes/mi + && $clipboard =~ /network namespaces.*yes/mi + && $clipboard =~ /seccomp.*sandbox.*yes/mi + && $clipboard =~ /you are adequately sandboxed/mi; + + $machine->screenshot("afer_copy_from_chromium"); + }; + + $machine->shutdown; + ''; +}) channelMap diff --git a/nixpkgs/nixos/tests/cjdns.nix b/nixpkgs/nixos/tests/cjdns.nix new file mode 100644 index 000000000000..e03bb9882540 --- /dev/null +++ b/nixpkgs/nixos/tests/cjdns.nix @@ -0,0 +1,119 @@ +let + carolKey = "2d2a338b46f8e4a8c462f0c385b481292a05f678e19a2b82755258cf0f0af7e2"; + carolPubKey = "n932l3pjvmhtxxcdrqq2qpw5zc58f01vvjx01h4dtd1bb0nnu2h0.k"; + carolPassword = "678287829ce4c67bc8b227e56d94422ee1b85fa11618157b2f591de6c6322b52"; + + basicConfig = + { ... }: + { services.cjdns.enable = true; + + # Turning off DHCP isn't very realistic but makes + # the sequence of address assignment less stochastic. + networking.useDHCP = false; + + # CJDNS output is incompatible with the XML log. + systemd.services.cjdns.serviceConfig.StandardOutput = "null"; + }; + +in + +import ./make-test.nix ({ pkgs, ...} : { + name = "cjdns"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ehmry ]; + }; + + nodes = rec + { # Alice finds peers over over ETHInterface. + alice = + { ... }: + { imports = [ basicConfig ]; + + services.cjdns.ETHInterface.bind = "eth1"; + + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + + # Bob explicitly connects to Carol over UDPInterface. + bob = + { ... }: + + { imports = [ basicConfig ]; + + networking.interfaces.eth1.ipv4.addresses = [ + { address = "192.168.0.2"; prefixLength = 24; } + ]; + + services.cjdns = + { UDPInterface = + { bind = "0.0.0.0:1024"; + connectTo."192.168.0.1:1024" = + { password = carolPassword; + publicKey = carolPubKey; + }; + }; + }; + }; + + # Carol listens on ETHInterface and UDPInterface, + # but knows neither Alice or Bob. + carol = + { ... }: + { imports = [ basicConfig ]; + + environment.etc."cjdns.keys".text = '' + CJDNS_PRIVATE_KEY=${carolKey} + CJDNS_ADMIN_PASSWORD=FOOBAR + ''; + + networking.interfaces.eth1.ipv4.addresses = [ + { address = "192.168.0.1"; prefixLength = 24; } + ]; + + services.cjdns = + { authorizedPasswords = [ carolPassword ]; + ETHInterface.bind = "eth1"; + UDPInterface.bind = "192.168.0.1:1024"; + }; + networking.firewall.allowedUDPPorts = [ 1024 ]; + }; + + }; + + testScript = + '' + startAll; + + $alice->waitForUnit("cjdns.service"); + $bob->waitForUnit("cjdns.service"); + $carol->waitForUnit("cjdns.service"); + + sub cjdnsIp { + my ($machine) = @_; + my $ip = (split /[ \/]+/, $machine->succeed("ip -o -6 addr show dev tun0"))[3]; + $machine->log("has ip $ip"); + return $ip; + } + + my $aliceIp6 = cjdnsIp $alice; + my $bobIp6 = cjdnsIp $bob; + my $carolIp6 = cjdnsIp $carol; + + # ping a few times each to let the routing table establish itself + + $alice->succeed("ping -c 4 $carolIp6"); + $bob->succeed("ping -c 4 $carolIp6"); + + $carol->succeed("ping -c 4 $aliceIp6"); + $carol->succeed("ping -c 4 $bobIp6"); + + $alice->succeed("ping -c 4 $bobIp6"); + $bob->succeed("ping -c 4 $aliceIp6"); + + $alice->waitForUnit("httpd.service"); + + $bob->succeed("curl --fail -g http://[$aliceIp6]"); + ''; +}) diff --git a/nixpkgs/nixos/tests/clickhouse.nix b/nixpkgs/nixos/tests/clickhouse.nix new file mode 100644 index 000000000000..7d835069ec4d --- /dev/null +++ b/nixpkgs/nixos/tests/clickhouse.nix @@ -0,0 +1,25 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "clickhouse"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ ma27 ]; + + machine = { + services.clickhouse.enable = true; + }; + + testScript = + let + # work around quote/substitution complexity by Nix, Perl, bash and SQL. + tableDDL = pkgs.writeText "ddl.sql" "CREATE TABLE `demo` (`value` FixedString(10)) engine = MergeTree PARTITION BY value ORDER BY tuple();"; + insertQuery = pkgs.writeText "insert.sql" "INSERT INTO `demo` (`value`) VALUES ('foo');"; + selectQuery = pkgs.writeText "select.sql" "SELECT * from `demo`"; + in + '' + $machine->start(); + $machine->waitForUnit("clickhouse.service"); + $machine->waitForOpenPort(9000); + + $machine->succeed("cat ${tableDDL} | clickhouse-client"); + $machine->succeed("cat ${insertQuery} | clickhouse-client"); + $machine->succeed("cat ${selectQuery} | clickhouse-client | grep foo"); + ''; +}) diff --git a/nixpkgs/nixos/tests/cloud-init.nix b/nixpkgs/nixos/tests/cloud-init.nix new file mode 100644 index 000000000000..516d29c9036b --- /dev/null +++ b/nixpkgs/nixos/tests/cloud-init.nix @@ -0,0 +1,49 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + metadataDrive = pkgs.stdenv.mkDerivation { + name = "metadata"; + buildCommand = '' + mkdir -p $out/iso + + cat << EOF > $out/iso/user-data + #cloud-config + write_files: + - content: | + cloudinit + path: /tmp/cloudinit-write-file + EOF + + cat << EOF > $out/iso/meta-data + instance-id: iid-local01 + local-hostname: "test" + public-keys: + - "should be a key!" + EOF + ${pkgs.cdrkit}/bin/genisoimage -volid cidata -joliet -rock -o $out/metadata.iso $out/iso + ''; + }; +in makeTest { + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ lewo ]; + }; + machine = + { ... }: + { + virtualisation.qemu.options = [ "-cdrom" "${metadataDrive}/metadata.iso" ]; + services.cloud-init.enable = true; + }; + testScript = '' + $machine->start; + $machine->waitForUnit("cloud-init.service"); + $machine->succeed("cat /tmp/cloudinit-write-file | grep -q 'cloudinit'"); + + $machine->waitUntilSucceeds("cat /root/.ssh/authorized_keys | grep -q 'should be a key!'"); + ''; +} diff --git a/nixpkgs/nixos/tests/cockroachdb.nix b/nixpkgs/nixos/tests/cockroachdb.nix new file mode 100644 index 000000000000..56c624d8cf2f --- /dev/null +++ b/nixpkgs/nixos/tests/cockroachdb.nix @@ -0,0 +1,126 @@ +# This performs a full 'end-to-end' test of a multi-node CockroachDB cluster +# using the built-in 'cockroach workload' command, to simulate a semi-realistic +# test load. It generally takes anywhere from 3-5 minutes to run and 1-2GB of +# RAM (though each of 3 workers gets 1GB allocated) +# +# CockroachDB requires synchronized system clocks within a small error window +# (~500ms by default) on each node in order to maintain a multi-node cluster. +# Cluster joins that are outside this window will fail, and nodes that skew +# outside the window after joining will promptly get kicked out. +# +# To accomodate this, we use QEMU/virtio infrastructure and load the 'ptp_kvm' +# driver inside a guest. This driver allows the host machine to pass its clock +# through to the guest as a hardware clock that appears as a Precision Time +# Protocol (PTP) Clock device, generally /dev/ptp0. PTP devices can be measured +# and used as hardware reference clocks (similar to an on-board GPS clock) by +# NTP software. In our case, we use Chrony to synchronize to the reference +# clock. +# +# This test is currently NOT enabled as a continuously-checked NixOS test. +# Ideally, this test would be run by Hydra and Borg on all relevant changes, +# except: +# +# - Not every build machine is compatible with the ptp_kvm driver. +# Virtualized EC2 instances, for example, do not support loading the ptp_kvm +# driver into guests. However, bare metal builders (e.g. Packet) do seem to +# work just fine. In practice, this means x86_64-linux builds would fail +# randomly, depending on which build machine got the job. (This is probably +# worth some investigation; I imagine it's based on ptp_kvm's usage of paravirt +# support which may not be available in 'nested' environments.) +# +# - ptp_kvm is not supported on aarch64, otherwise it seems likely Cockroach +# could be tested there, as well. This seems to be due to the usage of +# the TSC in ptp_kvm, which isn't supported (easily) on AArch64. (And: +# testing stuff, not just making sure it builds, is important to ensure +# aarch64 support remains viable.) +# +# For future developers who are reading this message, are daring and would want +# to fix this, some options are: +# +# - Just test a single node cluster instead (boring and less thorough). +# - Move all CI to bare metal packet builders, and we can at least do x86_64-linux. +# - Get virtualized clocking working in aarch64, somehow. +# - Add a 4th node that acts as an NTP service and uses no PTP clocks for +# references, at the client level. This bloats the node and memory +# requirements, but would probably allow both aarch64/x86_64 to work. +# + +let + + # Creates a node. If 'joinNode' parameter, a string containing an IP address, + # is non-null, then the CockroachDB server will attempt to join/connect to + # the cluster node specified at that address. + makeNode = locality: myAddr: joinNode: + { nodes, pkgs, lib, config, ... }: + + { + # Bank/TPC-C benchmarks take some memory to complete + virtualisation.memorySize = 1024; + + # Install the KVM PTP "Virtualized Clock" driver. This allows a /dev/ptp0 + # device to appear as a reference clock, synchronized to the host clock. + # Because CockroachDB *requires* a time-synchronization mechanism for + # the system time in a cluster scenario, this is necessary to work. + boot.kernelModules = [ "ptp_kvm" ]; + + # Enable and configure Chrony, using the given virtualized clock passed + # through by KVM. + services.chrony.enable = true; + services.chrony.servers = lib.mkForce [ ]; + services.chrony.extraConfig = '' + refclock PHC /dev/ptp0 poll 2 prefer require refid KVM + makestep 0.1 3 + ''; + + # Enable CockroachDB. In order to ensure that Chrony has performed its + # first synchronization at boot-time (which may take ~10 seconds) before + # starting CockroachDB, we block the ExecStartPre directive using the + # 'waitsync' command. This ensures Cockroach doesn't have its system time + # leap forward out of nowhere during startup/execution. + # + # Note that the default threshold for NTP-based skew in CockroachDB is + # ~500ms by default, so making sure it's started *after* accurate time + # synchronization is extremely important. + services.cockroachdb.enable = true; + services.cockroachdb.insecure = true; + services.cockroachdb.openPorts = true; + services.cockroachdb.locality = locality; + services.cockroachdb.listen.address = myAddr; + services.cockroachdb.join = lib.mkIf (joinNode != null) joinNode; + + # Hold startup until Chrony has performed its first measurement (which + # will probably result in a full timeskip, thanks to makestep) + systemd.services.cockroachdb.preStart = '' + ${pkgs.chrony}/bin/chronyc waitsync + ''; + }; + +in import ./make-test.nix ({ pkgs, ...} : { + name = "cockroachdb"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; + [ thoughtpolice ]; + + nodes = rec { + node1 = makeNode "country=us,region=east,dc=1" "192.168.1.1" null; + node2 = makeNode "country=us,region=west,dc=2b" "192.168.1.2" "192.168.1.1"; + node3 = makeNode "country=eu,region=west,dc=2" "192.168.1.3" "192.168.1.1"; + }; + + # NOTE: All the nodes must start in order and you must NOT use startAll, because + # there's otherwise no way to guarantee that node1 will start before the others try + # to join it. + testScript = '' + $node1->start; + $node1->waitForUnit("cockroachdb"); + + $node2->start; + $node2->waitForUnit("cockroachdb"); + + $node3->start; + $node3->waitForUnit("cockroachdb"); + + $node1->mustSucceed("cockroach sql --host=192.168.1.1 --insecure -e 'SHOW ALL CLUSTER SETTINGS' 2>&1"); + $node1->mustSucceed("cockroach workload init bank 'postgresql://root\@192.168.1.1:26257?sslmode=disable'"); + $node1->mustSucceed("cockroach workload run bank --duration=1m 'postgresql://root\@192.168.1.1:26257?sslmode=disable'"); + ''; +}) diff --git a/nixpkgs/nixos/tests/codimd.nix b/nixpkgs/nixos/tests/codimd.nix new file mode 100644 index 000000000000..562f6f24f999 --- /dev/null +++ b/nixpkgs/nixos/tests/codimd.nix @@ -0,0 +1,54 @@ +import ./make-test.nix ({ pkgs, lib, ... }: +{ + name = "codimd"; + + meta = with lib.maintainers; { + maintainers = [ willibutz ]; + }; + + nodes = { + codimdSqlite = { ... }: { + services = { + codimd = { + enable = true; + configuration.dbURL = "sqlite:///var/lib/codimd/codimd.db"; + }; + }; + }; + + codimdPostgres = { ... }: { + systemd.services.codimd.after = [ "postgresql.service" ]; + services = { + codimd = { + enable = true; + configuration.dbURL = "postgres://codimd:snakeoilpassword@localhost:5432/codimddb"; + }; + postgresql = { + enable = true; + initialScript = pkgs.writeText "pg-init-script.sql" '' + CREATE ROLE codimd LOGIN PASSWORD 'snakeoilpassword'; + CREATE DATABASE codimddb OWNER codimd; + ''; + }; + }; + }; + }; + + testScript = '' + startAll(); + + subtest "CodiMD sqlite", sub { + $codimdSqlite->waitForUnit("codimd.service"); + $codimdSqlite->waitForOpenPort(3000); + $codimdSqlite->waitUntilSucceeds("curl -sSf http://localhost:3000/new"); + }; + + subtest "CodiMD postgres", sub { + $codimdPostgres->waitForUnit("postgresql.service"); + $codimdPostgres->waitForUnit("codimd.service"); + $codimdPostgres->waitForOpenPort(5432); + $codimdPostgres->waitForOpenPort(3000); + $codimdPostgres->waitUntilSucceeds("curl -sSf http://localhost:3000/new"); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/common/letsencrypt/common.nix b/nixpkgs/nixos/tests/common/letsencrypt/common.nix new file mode 100644 index 000000000000..798a749f7f9b --- /dev/null +++ b/nixpkgs/nixos/tests/common/letsencrypt/common.nix @@ -0,0 +1,27 @@ +{ lib, nodes, ... }: { + networking.nameservers = [ + nodes.letsencrypt.config.networking.primaryIPAddress + ]; + + nixpkgs.overlays = lib.singleton (self: super: { + cacert = super.cacert.overrideDerivation (drv: { + installPhase = (drv.installPhase or "") + '' + cat "${nodes.letsencrypt.config.test-support.letsencrypt.caCert}" \ + >> "$out/etc/ssl/certs/ca-bundle.crt" + ''; + }); + + # Override certifi so that it accepts fake certificate for Let's Encrypt + # Need to override the attribute used by simp_le, which is python3Packages + python3Packages = (super.python3.override { + packageOverrides = lib.const (pysuper: { + certifi = pysuper.certifi.overridePythonAttrs (attrs: { + postPatch = (attrs.postPatch or "") + '' + cat "${self.cacert}/etc/ssl/certs/ca-bundle.crt" \ + > certifi/cacert.pem + ''; + }); + }); + }).pkgs; + }); +} diff --git a/nixpkgs/nixos/tests/common/letsencrypt/default.nix b/nixpkgs/nixos/tests/common/letsencrypt/default.nix new file mode 100644 index 000000000000..73aac51a0126 --- /dev/null +++ b/nixpkgs/nixos/tests/common/letsencrypt/default.nix @@ -0,0 +1,445 @@ +# Fully pluggable module to have Letsencrypt's Boulder ACME service running in +# a test environment. +# +# The certificate for the ACME service is exported as: +# +# config.test-support.letsencrypt.caCert +# +# This value can be used inside the configuration of other test nodes to inject +# the snakeoil certificate into security.pki.certificateFiles or into package +# overlays. +# +# Another value that's needed if you don't use a custom resolver (see below for +# notes on that) is to add the letsencrypt node as a nameserver to every node +# that needs to acquire certificates using ACME, because otherwise the API host +# for letsencrypt.org can't be resolved. +# +# A configuration example of a full node setup using this would be this: +# +# { +# letsencrypt = import ./common/letsencrypt; +# +# example = { nodes, ... }: { +# networking.nameservers = [ +# nodes.letsencrypt.config.networking.primaryIPAddress +# ]; +# security.pki.certificateFiles = [ +# nodes.letsencrypt.config.test-support.letsencrypt.caCert +# ]; +# }; +# } +# +# By default, this module runs a local resolver, generated using resolver.nix +# from the parent directory to automatically discover all zones in the network. +# +# If you do not want this and want to use your own resolver, you can just +# override networking.nameservers like this: +# +# { +# letsencrypt = { nodes, ... }: { +# imports = [ ./common/letsencrypt ]; +# networking.nameservers = [ +# nodes.myresolver.config.networking.primaryIPAddress +# ]; +# }; +# +# myresolver = ...; +# } +# +# Keep in mind, that currently only _one_ resolver is supported, if you have +# more than one resolver in networking.nameservers only the first one will be +# used. +# +# Also make sure that whenever you use a resolver from a different test node +# that it has to be started _before_ the ACME service. +{ config, pkgs, lib, ... }: + +let + softhsm = pkgs.stdenv.mkDerivation rec { + name = "softhsm-${version}"; + version = "1.3.8"; + + src = pkgs.fetchurl { + url = "https://dist.opendnssec.org/source/${name}.tar.gz"; + sha256 = "0flmnpkgp65ym7w3qyg78d3fbmvq3aznmi66rgd420n33shf7aif"; + }; + + configureFlags = [ "--with-botan=${pkgs.botan}" ]; + buildInputs = [ pkgs.sqlite ]; + }; + + pkcs11-proxy = pkgs.stdenv.mkDerivation { + name = "pkcs11-proxy"; + + src = pkgs.fetchFromGitHub { + owner = "SUNET"; + repo = "pkcs11-proxy"; + rev = "944684f78bca0c8da6cabe3fa273fed3db44a890"; + sha256 = "1nxgd29y9wmifm11pjcdpd2y293p0dgi0x5ycis55miy97n0f5zy"; + }; + + postPatch = "patchShebangs mksyscalls.sh"; + + nativeBuildInputs = [ pkgs.cmake ]; + buildInputs = [ pkgs.openssl pkgs.libseccomp ]; + }; + + mkGoDep = { goPackagePath, url ? "https://${goPackagePath}", rev, sha256 }: { + inherit goPackagePath; + src = pkgs.fetchgit { inherit url rev sha256; }; + }; + + goose = let + owner = "liamstask"; + repo = "goose"; + rev = "8488cc47d90c8a502b1c41a462a6d9cc8ee0a895"; + version = "20150116"; + + in pkgs.buildGoPackage rec { + name = "${repo}-${version}"; + + src = pkgs.fetchFromBitbucket { + name = "${name}-src"; + inherit rev owner repo; + sha256 = "1jy0pscxjnxjdg3hj111w21g8079rq9ah2ix5ycxxhbbi3f0wdhs"; + }; + + goPackagePath = "bitbucket.org/${owner}/${repo}"; + subPackages = [ "cmd/goose" ]; + extraSrcs = map mkGoDep [ + { goPackagePath = "github.com/go-sql-driver/mysql"; + rev = "2e00b5cd70399450106cec6431c2e2ce3cae5034"; + sha256 = "085g48jq9hzmlcxg122n0c4pi41sc1nn2qpx1vrl2jfa8crsppa5"; + } + { goPackagePath = "github.com/kylelemons/go-gypsy"; + rev = "08cad365cd28a7fba23bb1e57aa43c5e18ad8bb8"; + sha256 = "1djv7nii3hy451n5jlslk0dblqzb1hia1cbqpdwhnps1g8hqjy8q"; + } + { goPackagePath = "github.com/lib/pq"; + rev = "ba5d4f7a35561e22fbdf7a39aa0070f4d460cfc0"; + sha256 = "1mfbqw9g00bk24bfmf53wri5c2wqmgl0qh4sh1qv2da13a7cwwg3"; + } + { goPackagePath = "github.com/mattn/go-sqlite3"; + rev = "2acfafad5870400156f6fceb12852c281cbba4d5"; + sha256 = "1rpgil3w4hh1cibidskv1js898hwz83ps06gh0hm3mym7ki8d5h7"; + } + { goPackagePath = "github.com/ziutek/mymysql"; + rev = "0582bcf675f52c0c2045c027fd135bd726048f45"; + sha256 = "0bkc9x8sgqbzgdimsmsnhb0qrzlzfv33fgajmmjxl4hcb21qz3rf"; + } + { goPackagePath = "golang.org/x/net"; + url = "https://go.googlesource.com/net"; + rev = "10c134ea0df15f7e34d789338c7a2d76cc7a3ab9"; + sha256 = "14cbr2shl08gyg85n5gj7nbjhrhhgrd52h073qd14j97qcxsakcz"; + } + ]; + }; + + boulder = let + owner = "letsencrypt"; + repo = "boulder"; + rev = "9c6a1f2adc4c26d925588f5ae366cfd4efb7813a"; + version = "20180129"; + + in pkgs.buildGoPackage rec { + name = "${repo}-${version}"; + + src = pkgs.fetchFromGitHub { + name = "${name}-src"; + inherit rev owner repo; + sha256 = "09kszswrifm9rc6idfaq0p1mz5w21as2qbc8gd5pphrq9cf9pn55"; + }; + + postPatch = '' + # compat for go < 1.8 + sed -i -e 's/time\.Until(\([^)]\+\))/\1.Sub(time.Now())/' \ + test/ocsp/helper/helper.go + + find test -type f -exec sed -i -e '/libpkcs11-proxy.so/ { + s,/usr/local,${pkcs11-proxy}, + }' {} + + + sed -i -r \ + -e '/^def +install/a \ return True' \ + -e 's,exec \./bin/,,' \ + test/startservers.py + + cat ${lib.escapeShellArg snakeOilCerts.ca.key} > test/test-ca.key + cat ${lib.escapeShellArg snakeOilCerts.ca.cert} > test/test-ca.pem + ''; + + # Until vendored pkcs11 is go 1.9 compatible + preBuild = '' + rm -r go/src/github.com/letsencrypt/boulder/vendor/github.com/miekg/pkcs11 + ''; + + # XXX: Temporarily brought back putting the source code in the output, + # since e95f17e2720e67e2eabd59d7754c814d3e27a0b2 was removing that from + # buildGoPackage. + preInstall = '' + mkdir -p $out + pushd "$NIX_BUILD_TOP/go" + while read f; do + echo "$f" | grep -q '^./\(src\|pkg/[^/]*\)/${goPackagePath}' \ + || continue + mkdir -p "$(dirname "$out/share/go/$f")" + cp "$NIX_BUILD_TOP/go/$f" "$out/share/go/$f" + done < <(find . -type f) + popd + ''; + + extraSrcs = map mkGoDep [ + { goPackagePath = "github.com/miekg/pkcs11"; + rev = "6dbd569b952ec150d1425722dbbe80f2c6193f83"; + sha256 = "1m8g6fx7df6hf6q6zsbyw1icjmm52dmsx28rgb0h930wagvngfwb"; + } + ]; + + goPackagePath = "github.com/${owner}/${repo}"; + buildInputs = [ pkgs.libtool ]; + }; + + boulderSource = "${boulder.out}/share/go/src/${boulder.goPackagePath}"; + + softHsmConf = pkgs.writeText "softhsm.conf" '' + 0:/var/lib/softhsm/slot0.db + 1:/var/lib/softhsm/slot1.db + ''; + + snakeOilCerts = import ./snakeoil-certs.nix; + + wfeDomain = "acme-v01.api.letsencrypt.org"; + wfeCertFile = snakeOilCerts.${wfeDomain}.cert; + wfeKeyFile = snakeOilCerts.${wfeDomain}.key; + + siteDomain = "letsencrypt.org"; + siteCertFile = snakeOilCerts.${siteDomain}.cert; + siteKeyFile = snakeOilCerts.${siteDomain}.key; + + # Retrieved via: + # curl -s -I https://acme-v01.api.letsencrypt.org/terms \ + # | sed -ne 's/^[Ll]ocation: *//p' + tosUrl = "https://letsencrypt.org/documents/2017.11.15-LE-SA-v1.2.pdf"; + tosPath = builtins.head (builtins.match "https?://[^/]+(.*)" tosUrl); + + tosFile = pkgs.fetchurl { + url = tosUrl; + sha256 = "0yvyckqzj0b1xi61sypcha82nanizzlm8yqy828h2jbza7cxi26c"; + }; + + resolver = let + message = "You need to define a resolver for the letsencrypt test module."; + firstNS = lib.head config.networking.nameservers; + in if config.networking.nameservers == [] then throw message else firstNS; + + cfgDir = pkgs.stdenv.mkDerivation { + name = "boulder-config"; + src = "${boulderSource}/test/config"; + nativeBuildInputs = [ pkgs.jq ]; + phases = [ "unpackPhase" "patchPhase" "installPhase" ]; + postPatch = '' + sed -i -e 's/5002/80/' -e 's/5002/443/' va.json + sed -i -e '/listenAddress/s/:4000/:80/' wfe.json + sed -i -r \ + -e ${lib.escapeShellArg "s,http://boulder:4000/terms/v1,${tosUrl},g"} \ + -e 's,http://(boulder|127\.0\.0\.1):4000,https://${wfeDomain},g' \ + -e '/dnsResolver/s/127\.0\.0\.1:8053/${resolver}:53/' \ + *.json + if grep 4000 *.json; then exit 1; fi + + # Change all ports from 1909X to 909X, because the 1909X range of ports is + # allocated by startservers.py in order to intercept gRPC communication. + sed -i -e 's/\<1\(909[0-9]\)\>/\1/' *.json + + # Patch out all additional issuer certs + jq '. + {ca: (.ca + {Issuers: + [.ca.Issuers[] | select(.CertFile == "test/test-ca.pem")] + })}' ca.json > tmp + mv tmp ca.json + ''; + installPhase = "cp -r . \"$out\""; + }; + + components = { + gsb-test-srv.args = "-apikey my-voice-is-my-passport"; + gsb-test-srv.waitForPort = 6000; + gsb-test-srv.first = true; + boulder-sa.args = "--config ${cfgDir}/sa.json"; + boulder-wfe.args = "--config ${cfgDir}/wfe.json"; + boulder-ra.args = "--config ${cfgDir}/ra.json"; + boulder-ca.args = "--config ${cfgDir}/ca.json"; + boulder-va.args = "--config ${cfgDir}/va.json"; + boulder-publisher.args = "--config ${cfgDir}/publisher.json"; + boulder-publisher.waitForPort = 9091; + ocsp-updater.args = "--config ${cfgDir}/ocsp-updater.json"; + ocsp-updater.after = [ "boulder-publisher" ]; + ocsp-responder.args = "--config ${cfgDir}/ocsp-responder.json"; + ct-test-srv = {}; + mail-test-srv.args = let + key = "${boulderSource}/test/mail-test-srv/minica-key.pem"; + crt = "${boulderSource}/test/mail-test-srv/minica.pem"; + in + "--closeFirst 5 --cert ${crt} --key ${key}"; + }; + + commonPath = [ softhsm pkgs.mariadb goose boulder ]; + + mkServices = a: b: with lib; listToAttrs (concatLists (mapAttrsToList a b)); + + componentServices = mkServices (name: attrs: let + mkSrvName = n: "boulder-${n}.service"; + firsts = lib.filterAttrs (lib.const (c: c.first or false)) components; + firstServices = map mkSrvName (lib.attrNames firsts); + firstServicesNoSelf = lib.remove "boulder-${name}.service" firstServices; + additionalAfter = firstServicesNoSelf ++ map mkSrvName (attrs.after or []); + needsPort = attrs ? waitForPort; + inits = map (n: "boulder-init-${n}.service") [ "mysql" "softhsm" ]; + portWaiter = { + name = "boulder-${name}"; + value = { + description = "Wait For Port ${toString attrs.waitForPort} (${name})"; + after = [ "boulder-real-${name}.service" "bind.service" ]; + requires = [ "boulder-real-${name}.service" ]; + requiredBy = [ "boulder.service" ]; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + script = let + netcat = "${pkgs.libressl.nc}/bin/nc"; + portCheck = "${netcat} -z 127.0.0.1 ${toString attrs.waitForPort}"; + in "while ! ${portCheck}; do :; done"; + }; + }; + in lib.optional needsPort portWaiter ++ lib.singleton { + name = if needsPort then "boulder-real-${name}" else "boulder-${name}"; + value = { + description = "Boulder ACME Component (${name})"; + after = inits ++ additionalAfter; + requires = inits; + requiredBy = [ "boulder.service" ]; + path = commonPath; + environment.GORACE = "halt_on_error=1"; + environment.SOFTHSM_CONF = softHsmConf; + environment.PKCS11_PROXY_SOCKET = "tcp://127.0.0.1:5657"; + serviceConfig.WorkingDirectory = boulderSource; + serviceConfig.ExecStart = "${boulder}/bin/${name} ${attrs.args or ""}"; + serviceConfig.Restart = "on-failure"; + }; + }) components; + +in { + imports = [ ../resolver.nix ]; + + options.test-support.letsencrypt.caCert = lib.mkOption { + type = lib.types.path; + description = '' + A certificate file to use with the <literal>nodes</literal> attribute to + inject the snakeoil CA certificate used in the ACME server into + <option>security.pki.certificateFiles</option>. + ''; + }; + + config = { + test-support = { + resolver.enable = let + isLocalResolver = config.networking.nameservers == [ "127.0.0.1" ]; + in lib.mkOverride 900 isLocalResolver; + letsencrypt.caCert = snakeOilCerts.ca.cert; + }; + + # This has priority 140, because modules/testing/test-instrumentation.nix + # already overrides this with priority 150. + networking.nameservers = lib.mkOverride 140 [ "127.0.0.1" ]; + networking.firewall.enable = false; + + networking.extraHosts = '' + 127.0.0.1 ${toString [ + "sa.boulder" "ra.boulder" "wfe.boulder" "ca.boulder" "va.boulder" + "publisher.boulder" "ocsp-updater.boulder" "admin-revoker.boulder" + "boulder" "boulder-mysql" wfeDomain + ]} + ${config.networking.primaryIPAddress} ${wfeDomain} ${siteDomain} + ''; + + services.mysql.enable = true; + services.mysql.package = pkgs.mariadb; + + services.nginx.enable = true; + services.nginx.recommendedProxySettings = true; + # This fixes the test on i686 + services.nginx.commonHttpConfig = '' + server_names_hash_bucket_size 64; + ''; + services.nginx.virtualHosts.${wfeDomain} = { + onlySSL = true; + enableACME = false; + sslCertificate = wfeCertFile; + sslCertificateKey = wfeKeyFile; + locations."/".proxyPass = "http://127.0.0.1:80"; + }; + services.nginx.virtualHosts.${siteDomain} = { + onlySSL = true; + enableACME = false; + sslCertificate = siteCertFile; + sslCertificateKey = siteKeyFile; + locations.${tosPath}.extraConfig = "alias ${tosFile};"; + }; + + systemd.services = { + pkcs11-daemon = { + description = "PKCS11 Daemon"; + after = [ "boulder-init-softhsm.service" ]; + before = map (n: "${n}.service") (lib.attrNames componentServices); + wantedBy = [ "multi-user.target" ]; + environment.SOFTHSM_CONF = softHsmConf; + environment.PKCS11_DAEMON_SOCKET = "tcp://127.0.0.1:5657"; + serviceConfig.ExecStart = let + softhsmLib = "${softhsm}/lib/softhsm/libsofthsm.so"; + in "${pkcs11-proxy}/bin/pkcs11-daemon ${softhsmLib}"; + }; + + boulder-init-mysql = { + description = "Boulder ACME Init (MySQL)"; + after = [ "mysql.service" ]; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + serviceConfig.WorkingDirectory = boulderSource; + path = commonPath; + script = "${pkgs.bash}/bin/sh test/create_db.sh"; + }; + + boulder-init-softhsm = { + description = "Boulder ACME Init (SoftHSM)"; + environment.SOFTHSM_CONF = softHsmConf; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + serviceConfig.WorkingDirectory = boulderSource; + preStart = "mkdir -p /var/lib/softhsm"; + path = commonPath; + script = '' + softhsm --slot 0 --init-token \ + --label intermediate --pin 5678 --so-pin 1234 + softhsm --slot 0 --import test/test-ca.key \ + --label intermediate_key --pin 5678 --id FB + softhsm --slot 1 --init-token \ + --label root --pin 5678 --so-pin 1234 + softhsm --slot 1 --import test/test-root.key \ + --label root_key --pin 5678 --id FA + ''; + }; + + boulder = { + description = "Boulder ACME Server"; + after = map (n: "${n}.service") (lib.attrNames componentServices); + wantedBy = [ "multi-user.target" ]; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + script = let + ports = lib.range 8000 8005 ++ lib.singleton 80; + netcat = "${pkgs.libressl.nc}/bin/nc"; + mkPortCheck = port: "${netcat} -z 127.0.0.1 ${toString port}"; + checks = "(${lib.concatMapStringsSep " && " mkPortCheck ports})"; + in "while ! ${checks}; do :; done"; + }; + } // componentServices; + }; +} diff --git a/nixpkgs/nixos/tests/common/letsencrypt/mkcerts.nix b/nixpkgs/nixos/tests/common/letsencrypt/mkcerts.nix new file mode 100644 index 000000000000..3b4a589e4142 --- /dev/null +++ b/nixpkgs/nixos/tests/common/letsencrypt/mkcerts.nix @@ -0,0 +1,69 @@ +{ pkgs ? import <nixpkgs> {} +, lib ? pkgs.lib + +, domains ? [ "acme-v01.api.letsencrypt.org" "letsencrypt.org" ] +}: + +pkgs.runCommand "letsencrypt-snakeoil-ca" { + nativeBuildInputs = [ pkgs.openssl ]; +} '' + addpem() { + local file="$1"; shift + local storeFileName="$(IFS=.; echo "$*")" + + echo -n " " >> "$out" + + # Every following argument is an attribute, so let's recurse and check + # every attribute whether it must be quoted and write it into $out. + while [ -n "$1" ]; do + if expr match "$1" '^[a-zA-Z][a-zA-Z0-9]*$' > /dev/null; then + echo -n "$1" >> "$out" + else + echo -n '"' >> "$out" + echo -n "$1" | sed -e 's/["$]/\\&/g' >> "$out" + echo -n '"' >> "$out" + fi + shift + [ -z "$1" ] || echo -n . >> "$out" + done + + echo " = builtins.toFile \"$storeFileName\" '''" >> "$out" + sed -e 's/^/ /' "$file" >> "$out" + + echo " ''';" >> "$out" + } + + echo '# Generated via mkcert.sh in the same directory.' > "$out" + echo '{' >> "$out" + + openssl req -newkey rsa:4096 -x509 -sha256 -days 36500 \ + -subj '/CN=Snakeoil CA' -nodes -out ca.pem -keyout ca.key + + addpem ca.key ca key + addpem ca.pem ca cert + + ${lib.concatMapStrings (fqdn: let + opensslConfig = pkgs.writeText "snakeoil.cnf" '' + [req] + default_bits = 4096 + prompt = no + default_md = sha256 + req_extensions = req_ext + distinguished_name = dn + [dn] + CN = ${fqdn} + [req_ext] + subjectAltName = DNS:${fqdn} + ''; + in '' + export OPENSSL_CONF=${lib.escapeShellArg opensslConfig} + openssl genrsa -out snakeoil.key 4096 + openssl req -new -key snakeoil.key -out snakeoil.csr + openssl x509 -req -in snakeoil.csr -sha256 -set_serial 666 \ + -CA ca.pem -CAkey ca.key -out snakeoil.pem -days 36500 + addpem snakeoil.key ${lib.escapeShellArg fqdn} key + addpem snakeoil.pem ${lib.escapeShellArg fqdn} cert + '') domains} + + echo '}' >> "$out" +'' diff --git a/nixpkgs/nixos/tests/common/letsencrypt/mkcerts.sh b/nixpkgs/nixos/tests/common/letsencrypt/mkcerts.sh new file mode 100755 index 000000000000..cc7f8ca650dd --- /dev/null +++ b/nixpkgs/nixos/tests/common/letsencrypt/mkcerts.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env nix-shell +#!nix-shell -p nix bash -i bash +set -e +cd "$(dirname "$0")" +storepath="$(nix-build --no-out-link mkcerts.nix)" +cat "$storepath" > snakeoil-certs.nix diff --git a/nixpkgs/nixos/tests/common/letsencrypt/snakeoil-certs.nix b/nixpkgs/nixos/tests/common/letsencrypt/snakeoil-certs.nix new file mode 100644 index 000000000000..c3d29ab8f163 --- /dev/null +++ b/nixpkgs/nixos/tests/common/letsencrypt/snakeoil-certs.nix @@ -0,0 +1,253 @@ +# Generated via mkcert.sh in the same directory. +{ + ca.key = builtins.toFile "ca.key" '' + -----BEGIN PRIVATE KEY----- + MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDfdVxC/4HwhuzD + 9or9CDDu3TBQE5lirJI5KYmfMZtfgdzEjgOzmR9AVSkn2rQeCqzM5m+YCzPO+2y7 + 0Fdk7vDORi1OdhYfUQIW6/TZ27xEjx4t82j9i705yUqTJZKjMbD830geXImJ6VGj + Nv/WisTHmwBspWKefYQPN68ZvYNCn0d5rYJg9uROZPJHSI0MYj9iERWIPN+xhZoS + xN74ILJ0rEOQfx2GHDhTr99vZYAFqbAIfh35fYulRWarUSekI+rDxa83FD8q9cMg + OP84KkLep2dRXXTbUWErGUOpHP55M9M7ws0RVNdl9PUSbDgChl7yYlHCde3261q/ + zGp5dMV/t/jXXNUgRurvXc4gUKKjS4Sffvg0XVnPs3sMlZ4JNmycK9klgISVmbTK + VcjRRJv8Bva2NQVsJ9TIryV0QEk94DucgsC3LbhQfQdmnWVcEdzwrZHNpk9az5mn + w42RuvZW9L19T7xpIrdLSHaOis4VEquZjkWIhfIz0DVMeXtYEQmwqFG23Ww0utcp + mCW4FPvpyYs5GAPmGWfrlMxsLD/7eteot3AheC+56ZBoVBnI8FFvIX2qci+gfVDu + CjvDmbyS/0NvxLGqvSC1GUPmWP3TR5Fb1H8Rp+39zJHRmH+qYWlhcv6p7FlY2/6d + 9Rkw8WKRTSCB7yeUdNNPiPopk6N4NwIDAQABAoICAQCzV0ei5dntpvwjEp3eElLj + glYiDnjOPt5kTjgLsg6XCmyau7ewzrXMNgz/1YE1ky+4i0EI8AS2nAdafQ2HDlXp + 11zJWfDLVYKtztYGe1qQU6TPEEo1I4/M7waRLliP7XO0n6cL5wzjyIQi0CNolprz + 8CzZBasutGHmrLQ1nmnYcGk2+NBo7f2yBUaFe27of3mLRVbYrrKBkU5kveiNkABp + r0/SipKxbbivQbm7d+TVpqiHSGDaOa54CEksOcfs7n6efOvw8qj326KtG9GJzDE6 + 7XP4U19UHe40XuR0t7Zso/FmRyO6QzNUutJt5LjXHezZ75razTcdMyr0QCU8MUHH + jXZxQCsbt+9AmdxUMBm1SMNVBdHYM8oiNHynlgsEj9eM6jxDEss/Uc3FeKoHl+XL + L6m28guIB8NivqjVzZcwhxvdiQCzYxjyqMC+/eX7aaK4NIlX2QRMoDL6mJ58Bz/8 + V2Qxp2UNVwKJFWAmpgXC+sq6XV/TP3HkOvd0OK82Nid2QxEvfE/EmOhU63qAjgUR + QnteLEcJ3MkGGurs05pYBDE7ejKVz6uu2tHahFMOv+yanGP2gfivnT9a323/nTqH + oR5ffMEI1u/ufpWU7sWXZfL/mH1L47x87k+9wwXHCPeSigcy+hFI7t1+rYsdCmz9 + V6QtmxZHMLanwzh5R0ipcQKCAQEA8kuZIz9JyYP6L+5qmIUxiWESihVlRCSKIqLB + fJ5sQ06aDBV2sqS4XnoWsHuJWUd39rulks8cg8WIQu8oJwVkFI9EpARt/+a1fRP0 + Ncc9qiBdP6VctQGgKfe5KyOfMzIBUl3zj2cAmU6q+CW1OgdhnEl4QhgBe5XQGquZ + Alrd2P2jhJbMO3sNFgzTy7xPEr3KqUy+L4gtRnGOegKIh8EllmsyMRO4eIrZV2z3 + XI+S2ZLyUn3WHYkaJqvUFrbfekgBBmbk5Ead6ImlsLsBla6MolKrVYV1kN6KT+Y+ + plcxNpWY8bnWfw5058OWPLPa9LPfReu9rxAeGT2ZLmAhSkjGxQKCAQEA7BkBzT3m + SIzop9RKl5VzYbVysCYDjFU9KYMW5kBIw5ghSMnRmU7kXIZUkc6C1L/v9cTNFFLw + ZSF4vCHLdYLmDysW2d4DU8fS4qdlDlco5A00g8T1FS7nD9CzdkVN/oix6ujw7RuI + 7pE1K3JELUYFBc8AZ7mIGGbddeCwnM+NdPIlhWzk5s4x4/r31cdk0gzor0kE4e+d + 5m0s1T4O/Iak6rc0MGDeTejZQg04p1eAJFYQ6OY23tJhH/kO8CMYnQ4fidfCkf8v + 85v4EC1MCorFR7J65uSj8MiaL7LTXPvLAkgFls1c3ijQ2tJ8qXvqmfo0by33T1OF + ZGyaOP9/1WQSywKCAQB47m6CfyYO5EZNAgxGD8SHsuGT9dXTSwF/BAjacB/NAEA2 + 48eYpko3LWyBrUcCPn+LsGCVg7XRtxepgMBjqXcoI9G4o1VbsgTHZtwus0D91qV0 + DM7WsPcFu1S6SU8+OCkcuTPFUT2lRvRiYj+vtNttK+ZP5rdmvYFermLyH/Q2R3ID + zVgmH+aKKODVASneSsgJ8/nAs5EVZbwc/YKzbx2Zk+s7P4KE95g+4G4dzrMW0RcN + QS1LFJDu2DhFFgU4fRO15Ek9/lj2JS2DpfLGiJY8tlI5nyDsq4YRFvQSBdbUTZpG + m+CJDegffSlRJtuT4ur/dQf5hmvfYTVBRk2XS/eZAoIBAB143a22PWnvFRfmO02C + 3X1j/iYZCLZa6aCl+ZTSj4LDGdyRPPXrUDxwlFwDMHfIYfcHEyanV9T4Aa9SdKh9 + p6RbF6YovbeWqS+b/9RzcupM77JHQuTbDwL9ZXmtGxhcDgGqBHFEz6ogPEfpIrOY + GwZnmcBY+7E4HgsZ+lII4rqng6GNP2HEeZvg91Eba+2AqQdAkTh3Bfn+xOr1rT8+ + u5WFOyGS5g1JtN0280yIcrmWeNPp8Q2Nq4wnNgMqDmeEnNFDOsmo1l6NqMC0NtrW + CdxyXj82aXSkRgMQSqw/zk7BmNkDV8VvyOqX/fHWQynnfuYmEco4Pd2UZQgadOW5 + cVMCggEBANGz1fC+QQaangUzsVNOJwg2+CsUFYlAKYA3pRKZPIyMob2CBXk3Oln/ + YqOq6j373kG2AX74EZT07JFn28F27JF3r+zpyS/TYrfZyO1lz/5ZejPtDTmqBiVd + qa2coaPKwCOz64s77A9KSPyvpvyuTfRVa8UoArHcrQsPXMHgEhnFRsbxgmdP582A + kfYfoJBSse6dQtS9ZnREJtyWJlBNIBvsuKwzicuIgtE3oCBcIUZpEa6rBSN7Om2d + ex8ejCcS7qpHeULYspXbm5ZcwE4glKlQbJDTKaJ9mjiMdvuNFUZnv1BdMQ3Tb8zf + Gvfq54FbDuB10XP8JdLrsy9Z6GEsmoE= + -----END PRIVATE KEY----- + ''; + ca.cert = builtins.toFile "ca.cert" '' + -----BEGIN CERTIFICATE----- + MIIFATCCAumgAwIBAgIJANydi4uFZr0LMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV + BAMMC1NuYWtlb2lsIENBMCAXDTE4MDcxMjAwMjIxNloYDzIxMTgwNjE4MDAyMjE2 + WjAWMRQwEgYDVQQDDAtTbmFrZW9pbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP + ADCCAgoCggIBAN91XEL/gfCG7MP2iv0IMO7dMFATmWKskjkpiZ8xm1+B3MSOA7OZ + H0BVKSfatB4KrMzmb5gLM877bLvQV2Tu8M5GLU52Fh9RAhbr9NnbvESPHi3zaP2L + vTnJSpMlkqMxsPzfSB5ciYnpUaM2/9aKxMebAGylYp59hA83rxm9g0KfR3mtgmD2 + 5E5k8kdIjQxiP2IRFYg837GFmhLE3vggsnSsQ5B/HYYcOFOv329lgAWpsAh+Hfl9 + i6VFZqtRJ6Qj6sPFrzcUPyr1wyA4/zgqQt6nZ1FddNtRYSsZQ6kc/nkz0zvCzRFU + 12X09RJsOAKGXvJiUcJ17fbrWr/Manl0xX+3+Ndc1SBG6u9dziBQoqNLhJ9++DRd + Wc+zewyVngk2bJwr2SWAhJWZtMpVyNFEm/wG9rY1BWwn1MivJXRAST3gO5yCwLct + uFB9B2adZVwR3PCtkc2mT1rPmafDjZG69lb0vX1PvGkit0tIdo6KzhUSq5mORYiF + 8jPQNUx5e1gRCbCoUbbdbDS61ymYJbgU++nJizkYA+YZZ+uUzGwsP/t616i3cCF4 + L7npkGhUGcjwUW8hfapyL6B9UO4KO8OZvJL/Q2/Esaq9ILUZQ+ZY/dNHkVvUfxGn + 7f3MkdGYf6phaWFy/qnsWVjb/p31GTDxYpFNIIHvJ5R000+I+imTo3g3AgMBAAGj + UDBOMB0GA1UdDgQWBBQ3vPWzjLmu5krbSpfhBAht9KL3czAfBgNVHSMEGDAWgBQ3 + vPWzjLmu5krbSpfhBAht9KL3czAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA + A4ICAQDF9HyC1ZFN3Ob+JA9Dj5+Rcobi7JIA5F8uW3Q92LfPoVaUGEkBrwJSiTFX + 47zvP/ySBJIpZ9rzHMbJ+1L+eJgczF1uQ91inthCKo1THTPo5TgBrpJj0YAIunsj + 9eH1tBnfWFYdVIDZoTSiwPtgIvglpyuK/eJXEe+FRzubhtdc9w1Hlzox1sd0TQuy + Pl9KFHg7BlFZfCPig1mkB8pfwjBDgVhv5DKJ9cJXh3R5zSoiyuS2b+qYSvw8YTHq + 0WNKWUthb7BVAYE3OmcbOHgUAUjtJ6EIGIB9z/SoLe90CofXLXFR5dppuVLKCMBA + kgL4luBIu7t8mcnN2yzobvcGHy8RVY6F5abCCy6gackLzjOzvH1SYOxP8yN74aKB + ANgcqdWspb8JYoU8lEbA8dhBVrsgBf7XeJlrZvMdcUENlJ2PI0JWr9WvlRAM9rYY + EY1alJqBCp6530Ggd6/f0V64cEqptejUdmN9L0zboxKjQf4LjpUNraGvg8tw/xkY + 4dT1U2HlVnhOyBVkx/tE6zIK/RU16oMqwpjCdfbK/TuWCNc/emJz5PMlp81zm83+ + dExpWwuV4rt6OQbZ/GSatNLJXOw+pkLjaEhnHgrsgI+HqAUXg3ByKol+1e76wN51 + k1ZKpB6mk4kejySGPYBHiJwED0IyXu9gUfalSczXFO4ySAvhCg== + -----END CERTIFICATE----- + ''; + "acme-v01.api.letsencrypt.org".key = builtins.toFile "acme-v01.api.letsencrypt.org.key" '' + -----BEGIN RSA PRIVATE KEY----- + MIIJKQIBAAKCAgEAvG+sL4q0VkgSClBTn4NkPiUrtXx5oLyZ+CCM1jrQx/xotUt5 + X2S4/7vMnAK/yRLsR7R2PhXO8CZPqJ7B6OfAgaDTgvipJkZYPZQSMP3KOinM3WJL + ssqKh7/HOxZIf0iyUXewrnX5eTAo/CLsUnhBjBD7E99nmQz/leLWSl82sSYDkO3n + Uk3/1qJZA8iddb4uH0IEQWcNKev3WoQQzwiVrXBiftlRQOJy5JJXm5m8229MCpMA + 1AUWmpdu6sl3/gFFdsDhUFq/a7LFrVyaUCMRIHg9szAB7ZFkixr9umQs8jKwuo98 + 3JHB11h2SirwgfIzHHmyhaWhCt22ucTwEXGhq63LtrzZvLsfP8Ql5S+AuqGTH0v8 + meuc784leAjulBZjkpuIFwDnVv9+YeUEbqJeo1hSHrILddora3nkH4E2dJWmLpqp + iPr++GRi+BNgYKW/BQLTJ7C6v+vUs+kdPgYJH5z7oP6f0YZkT0Wkubp/UEz7UV2d + fjz57d77DYx5rFWGYzJriWR/xltgL1zDpjwjwG1FDpRqwlyYbBFpjQhxI+X0aT98 + m6fCzBDQHDb/+JgvsjTHh6OZatahFAwzFIEfrceDv1BG8sBWIaZGhLzYiWQxafl8 + oXbWv1T6I1jpsTlCdCSkWzaJb4ZjxI9Ga1ynVu8F16+GR2a71wKWu7UbZQsCAwEA + AQKCAgBYvrs4FLoD3KNqahRIDqhaQEVKjtn1Yn2dBy9tAXwsg2qI34fE7nnWLwsY + +o56U0gmKQ57BOhV36Uqg8JNP0BBjI2wpA19simCrsa2fgAMznzmUpHWHV+KuT5K + TJ9OGt2oUpdKQtOASLc0r/neiTZNkf29iTyQLzf7zj4f/qGSYpXRXsnP0F5KJmGH + z6agujWckQnSB4eCk9gFsCb+akubyE8K8Kw8w6lajrVl2czBB7SnUj5UnCTeH62k + M8goP08Is6QppON8BFDm6bLfRPSe9yIPzu9JhGz2unp+mwkz872Zz1P9yUOieM4U + 9g4ZFQkPQx1ZpfynUm3pJZ/uhzadBabnIvMe/1qwDAEDifh/WzEM76/2kBpQkHtS + qcjwjAElfWnP8aBr1Pj42/cVJy3dbDqb0OawFHx/8xSO2CkY4Gq2h3OYv1XpPv3g + S9qqKhvuaT+aD0YjKhP4FYc2vvQSJwdZL8vqOyma8JGmc+r7jakIPCyOx3oPVqnS + L2P7DuJ1FcGIZyYOU3UUSzKndDU9fVC8YoLWvHDlwm4RK9UPtdsBY8mEu6BlaAwL + zEQG+fbcFnEkHPiJeAohYUCHiqCihLt0pqGwZi+QrudPQE6C47YijGZWJu4VVLjB + B2L9iDQKsN4FnBJ9egJIwWBLX3XXQfjC43UGm1A5sBvD+ScsCQKCAQEA7GxU7/SW + 4YJ+wBXrp7Z3vzlc5mTT5U4L2muWZLhIjT/jmpHpZ4c9a5DY/K9OYcu8XJ+7kx2B + N40cU3ZkT2ZbB5/BUCEmi3Wzy3R/KZshHDzvvSZHcXJqVBtv+HGJgR5ssFqAw8c6 + gJtDls+JE9Sz+nhLk0ZZ4658vbTQfG1lmtzrbC3Kz2xK8RPTdOU5Or7fayeaEKEW + ECBJPE41ME2UTdB/E85vyYoee0MBijjAs19QKqvoNbyrsZ5bihcIDYsrvjCmkdW1 + 20IUrSF3ZYJ9bb+CxHeRyNqwvRxPYSkzdMjZHx+xEAvJgw51QqmIi2QQf/qB+ych + cSbE/0Jhx4QbDQKCAQEAzAoenEOgmZvUegFUu8C6gWeibMjl3Y9SikQ4CoQO/zWr + aoCr5BpbzbtOffwnPfgk9wCGvXf6smOdrLUP1K2QAhBr/vJh7ih2MonvpYr5HPP7 + maVARR66IgtxXP2ER2I9+9p2OQdecGRP2fUn2KCDQIASHSSY/VjBb8LLJgryC/DS + r2b0+m1e2qXfNWt/BYTQZhD/8B/jl/2pl/jI2ne3rkeiwEm7lqZaDt3Q8gC+qoP5 + /IdG1Gob7UTMCbICWy1aGuzRYUmbpg0Vq4DAV1RtgBySB5oNq5PMBHYpOxedM2nM + NxHvf0u6wsxVULwQ4IfWUqUTspjxDmIgogSzmOGadwKCAQEA558if4tynjBImUtg + egirvG4oc5doeQhDWJN63eYlPizPgUleD41RQSbBTp04/1qoiV38WJ7ZT2Ex1Rry + H0+58vgyXZx8tLh1kufpBQv0HkQc44SzDZP4U7olspMZEaSK+yNPb36p9AEo8IEW + XJVQVhywffK4cfUqRHj2oFBU8KlrA6rBPQFtUk4IJkfED6ecHtDHgW8vvFDFLw23 + 0kDPAIU5WmAu6JYmUsBMq+v57kF8urF8Z9kVpIfuSpVR0GL+UfA74DgtWEefFhbp + cEutMm4jYPN7ofmOmVc49Yl13f4/qNxVjdDedUUe4FZTbax09cyotzOY8c/3w9R3 + Ew57qQKCAQAa5jqi30eM+L5KV2KUXhQ4ezEupk2np/15vQSmXkKb4rd2kwAWUmNH + /Cmc8mE6CjzVU3xv/iFO41MmMbikkT0rCH80XUAL5cmvX//4ExpEduX0m5SdiC+B + zYBkggeuYYVKbsKnQhFxP8hHM8rNBFxJZJj+vpRs0gaudT/TBB5k9JrSBQDHAyQ+ + Lx/+Ku3UDG5tBlC3l3ypzQdOwb25D49nqooKT64rbkLxMs0ZGoAIet26LRtpZZPI + 9AjyPkWRP6lhY1c3PD0I5zC0K4Uv/jFxclLOLcEfnZyH+gv1fmd7H7eMixDH93Pn + uoiE3EZdU4st2hV+tisRel5S/cuvnA6BAoIBAQDJISK8H0hwYp+J4/WUv/WLtrm4 + Mhmn8ItdEPAyCljycU6oLHJy4fgmmfRHeoO1i3jb87ks2GghegFBbJNzugfoGxIM + dLWIV+uFXWs24fMJ/J6lqN1JtAj7HjvqkXp061X+MdIJ0DsACygzFfJOjv+Ij77Q + Q1OBTSPfb0EWFNOuIJr9i2TwdN9eW/2ZMo1bPuwe4ttPEIBssfIC02dn2KD1RTqM + 1l+L97vVFk7CoSJZf5rLeysLVyUeGdDcoEcRA6fKhfB/55h+iqrZNvySX1HrR6on + PQcxDRPJD7f9rMsTzVl3DOxzvXAU3lIcZtPZps97IwXceAAh2e1kZNNv/cxj + -----END RSA PRIVATE KEY----- + ''; + "acme-v01.api.letsencrypt.org".cert = builtins.toFile "acme-v01.api.letsencrypt.org.cert" '' + -----BEGIN CERTIFICATE----- + MIIEtDCCApwCAgKaMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNVBAMMC1NuYWtlb2ls + IENBMCAXDTE4MDcxMjAwMjIxN1oYDzIxMTgwNjE4MDAyMjE3WjAnMSUwIwYDVQQD + DBxhY21lLXYwMS5hcGkubGV0c2VuY3J5cHQub3JnMIICIjANBgkqhkiG9w0BAQEF + AAOCAg8AMIICCgKCAgEAvG+sL4q0VkgSClBTn4NkPiUrtXx5oLyZ+CCM1jrQx/xo + tUt5X2S4/7vMnAK/yRLsR7R2PhXO8CZPqJ7B6OfAgaDTgvipJkZYPZQSMP3KOinM + 3WJLssqKh7/HOxZIf0iyUXewrnX5eTAo/CLsUnhBjBD7E99nmQz/leLWSl82sSYD + kO3nUk3/1qJZA8iddb4uH0IEQWcNKev3WoQQzwiVrXBiftlRQOJy5JJXm5m8229M + CpMA1AUWmpdu6sl3/gFFdsDhUFq/a7LFrVyaUCMRIHg9szAB7ZFkixr9umQs8jKw + uo983JHB11h2SirwgfIzHHmyhaWhCt22ucTwEXGhq63LtrzZvLsfP8Ql5S+AuqGT + H0v8meuc784leAjulBZjkpuIFwDnVv9+YeUEbqJeo1hSHrILddora3nkH4E2dJWm + LpqpiPr++GRi+BNgYKW/BQLTJ7C6v+vUs+kdPgYJH5z7oP6f0YZkT0Wkubp/UEz7 + UV2dfjz57d77DYx5rFWGYzJriWR/xltgL1zDpjwjwG1FDpRqwlyYbBFpjQhxI+X0 + aT98m6fCzBDQHDb/+JgvsjTHh6OZatahFAwzFIEfrceDv1BG8sBWIaZGhLzYiWQx + afl8oXbWv1T6I1jpsTlCdCSkWzaJb4ZjxI9Ga1ynVu8F16+GR2a71wKWu7UbZQsC + AwEAATANBgkqhkiG9w0BAQsFAAOCAgEAzeGlFMz1Bo+bbpZDQ60HLdw7qDp3SPJi + x5LYG860yzbh9ghvyc59MIm5E6vB140LRJAs+Xo6VdVSTC4jUA2kI9k1BQsbZKds + XT0RqA7HkqcLS3t3JWFkkKbCshMGZTSZ//hpbaUG1qEAfUfmZw1lAxqSa0kqavbP + awf7k8qHbqcj7WORCdH7fjKAjntEQwIpl1GEkAdCSghOJz2/o9aWmiGZt27OM/sG + MLSrcmL3QBElCjOxg14P8rnsmZ+VEp6MO93otoJ4dJL7fN7vTIh5ThbS384at/4l + 4KK/y7XctUzAtWzhnodjk/NSgrrGX2kseOGOWEM1sZc9xtinHH2tpOMqtLVOkgHD + Lul+TArqgqeoOdEM/9OL64kgOrO/JzxBq+egLUi4wgAul2wmtecKZK1dkwYZHeqW + 74i55yeBp+TTomnPr0ZBns6xKFYldJVzC34OB+2YVDxe8y9XtWtuQOxFw0LQHhNb + zy5aBverWzZFwiIIjJoVHTQq848uKBJec0YILfMinS1Wjif4xqW/IMfi+GFS0oka + sKCGNE/8ur9u/Jm6cbto3f2dtV8/vkhiITQgwzM2jalyuVJ9jyPxG7EvbTvZORgw + pRvBRTd4/eE7I1L+UDe6x8EjR/MrqfF9FWVGOZo4vPTyNbrSWYBh6s9kYy56ds1l + IRxst1BXEfI= + -----END CERTIFICATE----- + ''; + "letsencrypt.org".key = builtins.toFile "letsencrypt.org.key" '' + -----BEGIN RSA PRIVATE KEY----- + MIIJKAIBAAKCAgEAwPvhlwemgPi6919sSD7Pz6l6CRfU1G/fDc0AvsMN/nTmiGND + pqn9ef1CA+RtLtOuPc1LLyEovcfu75/V+6KSgO4k19E2CrFCFwjEOWDGF4DgclT3 + 751WGmFJgzPEfZfhbOrmQfQau86KxAtNZVp9FxcKbuLyQ/sNNxfNMB+7IHbVhwvz + VcndHpYZEP6kdnwvNLP22bouX5q3avxWStln01uZ0BfUm4XwxaUNIU7t0Dv56FK9 + C9hW9AZae0do0BJBWRF7xSwLeDJqn9uZz+sX0X/tIaaSQSBuZySj0He5ZKzdUO0t + px2xTS2Brl3Y2BOJaOE98HubWvdKoslLt4X2rVrMxGa86SmFzcyDL1RSowcP/ruy + y555l7pepL5s4cmMgRBBXj5tXhqUTVOn5WO+JClLk+rtvtAT4rogJmMqEKmMw2t7 + LNy1W9ri/378QG/i3AGaLIL/7GsPbuRO51Sdti4QMVe2zNFze72mzNmj1SXokWy7 + +ZvjUMp55oEjRRsTPUZdNOEHJWy6Os2znuqL7ZpIHCxBG8FKnkCViXRJqAA8bzcE + hR+pLamLIOHlv4kdzJ6phHkSvK68qvbRReUmOjJgSupVBI9jhK+fHay/UWR4zfJQ + ed99H8ZOoiXlrLCVs+VPDynUUKrzF1nYyolNzi/NS4e4AbnfWgyC5JKRpjUCAwEA + AQKCAgB0fNYL+zM3MGxy+2d6KGf6GnuuV3NBlBGY3ACyJT0iNmAdPYXNaVi2tPeP + L+fz1xSa+3uBhEt6Wt/QRrO8g8JZDuawWvl69MpG6yS+2bpY35MbkExkl50sqULd + bncRtIb+3r+EWht099RtR8E9B6TwNhk3G8hO3pB4i+ZwQQcMLo7vSHhmdUYCu2mA + B6UwW/+GmYbMoARz8wj6DDzuS1LPksBCis/r3KqcMue9Dk6gXkOYR7ETIFBEVj1x + ooYS6qIFaHdEajS2JgCUY9LxXR/wdn6lzE0GANSDb+tt34bJzUp+Gdxvvo2SX4Ci + xsUokIpmA2gG7CW3gAPORSFuMu/VYZtvt+owNYlODXRPuGi/eLDknFRB/S4Nx0J0 + WZZq5uTgJdQainyKYtDZALia5X4cc5I2hNetCorG9jNZIsSunbIAG+htx2FI3eqK + jwOUiHE8SCZ6YdXoDQjg2w+g8jeB23eqkPyzunpZphYiKay7VFeLwQEMC2a791ln + +MbHhhpRAc1uAoU2reB2fxKyaPlOfAWVMgUOGlgpVOuEVeMoc1CwjajaFztGG7fI + 8EHNoyAftCdXnTaLZk2KZnnIDHHzFXR62TE1GJFD1fdI1pHAloCbgA4h+Dtwm1Uu + iAEEfvVU/E5wbtAzv6pY32+OKX5kyHAbM5/e918B8ZxmHG1J9QKCAQEA6FwxsRG3 + 526NnZak540yboht5kV12BNBChjmARv/XgZ7o1VsfwjaosErMvasUBcHDEYOC/oE + ZgPAyrMVsYm0xe/5FSIFLJVeYXTr0rmCNhVtBCHx3IS94BCXreNnz0qoEWnb5E09 + Z1O42D0yGcLXklg6QaJfb7EdHh03F3dSVMHyDR3JlAQHRINeuP6LlQpbvRD3adH5 + QWr2M3k+Stuq2OJdG7eUS1dreCxRShLuDjDhiZekdl/TB3LM0prOaWrKBrryN2g6 + mjiasH6I5zRD3LQP5zg57Thb8afHqA4Fb85Frt6ltfFlPTIoxXZ5drVhmRWfXXnQ + POnj8T+w4zVjvwKCAQEA1J4ivyFkCL0JTSY3/PtwAQvBBj3GazzU6P+urWeH74Vh + WK17Ae40iOUHGyy80Db/fVY4VLQTpxvAeG91Gj5Nd/AucXJgOrisabcEz6N/xUs5 + sjJNgXuNKTAgjYBu0bqLXxgZj43zT8JhA6KW7RuYU0PtHMRragz4RbK9NWDaVvJb + xSR5QoVLS00PerUa0SfupEYKCrlSTP6FOM5YNkCuSMt7X6/m9cR0WwVINKvUQBiT + ObrN+KeBmF9awpQQnQOq/GbCl3kf6VyPQqYFhdrWSg52w33c2tBVYrtHJpeXGcin + akw4KKcj4rdU2qxMuuRiD5paagshbLdGsYMTbSzjCwKCAQEAh89DGAyUIcfDLAWd + st0bSfGh0oJsw3NVg3JUFPfpRWqiny/Rr1pcd95RwoLc6h7bdrgHg8aJBZtR9ue/ + WTp0l3CQdGKjBZD0TiAJqevViIjzZAP3Gn3XgPwRu4f75/Pp0eu+o2zl49vSYUk7 + XEU+vIGm4y/leiHaM/y9c5DBZVrKgBIV/NZx7QCfv56/tMgOIK6m/YnFlw/OgP1v + hE9qR0PfSdD98x9QaDf290WjMFYvrL0eWjXd4S+fOcVTude55z8jTXE1N2i4OUpr + +D7bH0d7OBjr+pQDYXZAQyCW2ueEYRYvYu2Jz7/ehrOdgN25AsHZmMgXB1NpcFta + pyJQfwKCAQByoPMwworRH0GVg4Zp8RFYrwKZH9MK29gZ6kc9m/Sw0OND0PvhdZCD + QZ8MKpl9VDl4VHS4TgHOdWrWQ5kJ1g8kG6yeY0C4R/pEYHTKkWaAcucfSHl61qar + TxQt1dFpZz5evXqCZ9CG7tApCo5+NQNx2MxMVyVmHqn3wb66uYXdnHqXlet+Tqji + ZyByUpOrsfC6RjyBvZo+gnZGwxDR5xtPiczxML+/PvRQYk+kfgNHrzgoxqrnZT+8 + a6ReBT/TtzeHLsu4qIfo44slLqcJnIstkBC9ouzgV7PBMCDTEKVZNFH2QDOCz2HM + iHTKFFyl4h1wNhKK24dguor1hyqBENMzAoIBAAQvQHwRWIVlfCMRI170Ls8AXB9Z + MMdZJ37bh6kmJpkV3+HB1ZkKwofHKR9h/3xLt5iYXzqT+/zA4EAsFFs1A93+tkzh + yPrN5iTSJicophZSlA4ObX1hMkgshvl7ZB1fRM5WyiszBOfm8W7eAxaK8nY2oAoP + tI7rioo6CFBNMCGbOl4gEX6YJ4OsVSm+efCRSDDw+3HW8H2YgqufBzAULk1Jcj5t + ZvraXpC5qZ92VtsH0cGA1ovNDAmoOV4AAvtZVpLQsXwaphad/Fbn/ItGrrluvvFC + HuldRzYtl/AQtoirK86LTY3aAmcwVFuiYvDQMzjzkJvVMmRCFZBcUIaz2oI= + -----END RSA PRIVATE KEY----- + ''; + "letsencrypt.org".cert = builtins.toFile "letsencrypt.org.cert" '' + -----BEGIN CERTIFICATE----- + MIIEpzCCAo8CAgKaMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNVBAMMC1NuYWtlb2ls + IENBMCAXDTE4MDcxMjAwMjIxOVoYDzIxMTgwNjE4MDAyMjE5WjAaMRgwFgYDVQQD + DA9sZXRzZW5jcnlwdC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC + AQDA++GXB6aA+Lr3X2xIPs/PqXoJF9TUb98NzQC+ww3+dOaIY0Omqf15/UID5G0u + 0649zUsvISi9x+7vn9X7opKA7iTX0TYKsUIXCMQ5YMYXgOByVPfvnVYaYUmDM8R9 + l+Fs6uZB9Bq7zorEC01lWn0XFwpu4vJD+w03F80wH7sgdtWHC/NVyd0elhkQ/qR2 + fC80s/bZui5fmrdq/FZK2WfTW5nQF9SbhfDFpQ0hTu3QO/noUr0L2Fb0Blp7R2jQ + EkFZEXvFLAt4Mmqf25nP6xfRf+0hppJBIG5nJKPQd7lkrN1Q7S2nHbFNLYGuXdjY + E4lo4T3we5ta90qiyUu3hfatWszEZrzpKYXNzIMvVFKjBw/+u7LLnnmXul6kvmzh + yYyBEEFePm1eGpRNU6flY74kKUuT6u2+0BPiuiAmYyoQqYzDa3ss3LVb2uL/fvxA + b+LcAZosgv/saw9u5E7nVJ22LhAxV7bM0XN7vabM2aPVJeiRbLv5m+NQynnmgSNF + GxM9Rl004QclbLo6zbOe6ovtmkgcLEEbwUqeQJWJdEmoADxvNwSFH6ktqYsg4eW/ + iR3MnqmEeRK8rryq9tFF5SY6MmBK6lUEj2OEr58drL9RZHjN8lB5330fxk6iJeWs + sJWz5U8PKdRQqvMXWdjKiU3OL81Lh7gBud9aDILkkpGmNQIDAQABMA0GCSqGSIb3 + DQEBCwUAA4ICAQAkx3jcryukAuYP7PQxMy3LElOl65ZFVqxDtTDlr7DvAkWJzVCb + g08L6Tu+K0rKh2RbG/PqS0+8/jBgc4IwSOPfDDAX+sinfj0kwXG34WMzB0G3fQzU + 2BMplJDOaBcNqHG8pLP1BG+9HAtR/RHe9p2Jw8LG2qmZs6uemPT/nCTNoyIL4oxh + UncjETV4ayCHDKD1XA7/icgddYsnfLQHWuIMuCrmQCHo0uQAd7qVHfUWZ+gcsZx0 + jTNCcaI8OTS2S65Bjaq2HaM7GMcUYNUD2vSyNQeQbha4ZeyZ9bPyFzznPMmrPXQe + MJdkbJ009RQIG9As79En4m+l+/6zrdx4DNdROqaL6YNiSebWMnuFHpMW/rCnhrT/ + HYadijHOiJJGj9tWSdC4XJs7fvZW3crMPUYxpOvl01xW2ZlgaekILi1FAjSMQVoV + NhWstdGCKJdthJqLL5MtNdfgihKcmgkJqKFXTkPv7sgAQCopu6X+S+srCgn856Lv + 21haRWZa8Ml+E0L/ticT8Fd8Luysc6K9TJ4mT8ENC5ywvgDlEkwBD3yvINXm5lg1 + xOIxv/Ye5gFk1knuM7OzpUFBrXUHdVVxflCUqNAhFPbcXwjgEQ+A+S5B0vI6Ohue + ZnR/wuiou6Y+Yzh8XfqL/3H18mGDdjyMXI1B6l4Judk000UVyr46cnI7mw== + -----END CERTIFICATE----- + ''; +} diff --git a/nixpkgs/nixos/tests/common/resolver.nix b/nixpkgs/nixos/tests/common/resolver.nix new file mode 100644 index 000000000000..6be8d1d18e62 --- /dev/null +++ b/nixpkgs/nixos/tests/common/resolver.nix @@ -0,0 +1,141 @@ +# This module automatically discovers zones in BIND and NSD NixOS +# configurations and creates zones for all definitions of networking.extraHosts +# (except those that point to 127.0.0.1 or ::1) within the current test network +# and delegates these zones using a fake root zone served by a BIND recursive +# name server. +{ config, nodes, pkgs, lib, ... }: + +{ + options.test-support.resolver.enable = lib.mkOption { + type = lib.types.bool; + default = true; + internal = true; + description = '' + Whether to enable the resolver that automatically discovers zone in the + test network. + + This option is <literal>true</literal> by default, because the module + defining this option needs to be explicitly imported. + + The reason this option exists is for the + <filename>nixos/tests/common/letsencrypt</filename> module, which + needs that option to disable the resolver once the user has set its own + resolver. + ''; + }; + + config = lib.mkIf config.test-support.resolver.enable { + networking.firewall.enable = false; + services.bind.enable = true; + services.bind.cacheNetworks = lib.mkForce [ "any" ]; + services.bind.forwarders = lib.mkForce []; + services.bind.zones = lib.singleton { + name = "."; + file = let + addDot = zone: zone + lib.optionalString (!lib.hasSuffix "." zone) "."; + mkNsdZoneNames = zones: map addDot (lib.attrNames zones); + mkBindZoneNames = zones: map (zone: addDot zone.name) zones; + getZones = cfg: mkNsdZoneNames cfg.services.nsd.zones + ++ mkBindZoneNames cfg.services.bind.zones; + + getZonesForNode = attrs: { + ip = attrs.config.networking.primaryIPAddress; + zones = lib.filter (zone: zone != ".") (getZones attrs.config); + }; + + zoneInfo = lib.mapAttrsToList (lib.const getZonesForNode) nodes; + + # A and AAAA resource records for all the definitions of + # networking.extraHosts except those for 127.0.0.1 or ::1. + # + # The result is an attribute set with keys being the host name and the + # values are either { ipv4 = ADDR; } or { ipv6 = ADDR; } where ADDR is + # the IP address for the corresponding key. + recordsFromExtraHosts = let + getHostsForNode = lib.const (n: n.config.networking.extraHosts); + allHostsList = lib.mapAttrsToList getHostsForNode nodes; + allHosts = lib.concatStringsSep "\n" allHostsList; + + reIp = "[a-fA-F0-9.:]+"; + reHost = "[a-zA-Z0-9.-]+"; + + matchAliases = str: let + matched = builtins.match "[ \t]+(${reHost})(.*)" str; + continue = lib.singleton (lib.head matched) + ++ matchAliases (lib.last matched); + in if matched == null then [] else continue; + + matchLine = str: let + result = builtins.match "[ \t]*(${reIp})[ \t]+(${reHost})(.*)" str; + in if result == null then null else { + ipAddr = lib.head result; + hosts = lib.singleton (lib.elemAt result 1) + ++ matchAliases (lib.last result); + }; + + skipLine = str: let + rest = builtins.match "[^\n]*\n(.*)" str; + in if rest == null then "" else lib.head rest; + + getEntries = str: acc: let + result = matchLine str; + next = getEntries (skipLine str); + newEntry = acc ++ lib.singleton result; + continue = if result == null then next acc else next newEntry; + in if str == "" then acc else continue; + + isIPv6 = str: builtins.match ".*:.*" str != null; + loopbackIps = [ "127.0.0.1" "::1" ]; + filterLoopback = lib.filter (e: !lib.elem e.ipAddr loopbackIps); + + allEntries = lib.concatMap (entry: map (host: { + inherit host; + ${if isIPv6 entry.ipAddr then "ipv6" else "ipv4"} = entry.ipAddr; + }) entry.hosts) (filterLoopback (getEntries (allHosts + "\n") [])); + + mkRecords = entry: let + records = lib.optional (entry ? ipv6) "AAAA ${entry.ipv6}" + ++ lib.optional (entry ? ipv4) "A ${entry.ipv4}"; + mkRecord = typeAndData: "${entry.host}. IN ${typeAndData}"; + in lib.concatMapStringsSep "\n" mkRecord records; + + in lib.concatMapStringsSep "\n" mkRecords allEntries; + + # All of the zones that are subdomains of existing zones. + # For example if there is only "example.com" the following zones would + # be 'subZones': + # + # * foo.example.com. + # * bar.example.com. + # + # While the following would *not* be 'subZones': + # + # * example.com. + # * com. + # + subZones = let + allZones = lib.concatMap (zi: zi.zones) zoneInfo; + isSubZoneOf = z1: z2: lib.hasSuffix z2 z1 && z1 != z2; + in lib.filter (z: lib.any (isSubZoneOf z) allZones) allZones; + + # All the zones without 'subZones'. + filteredZoneInfo = map (zi: zi // { + zones = lib.filter (x: !lib.elem x subZones) zi.zones; + }) zoneInfo; + + in pkgs.writeText "fake-root.zone" '' + $TTL 3600 + . IN SOA ns.fakedns. admin.fakedns. ( 1 3h 1h 1w 1d ) + ns.fakedns. IN A ${config.networking.primaryIPAddress} + . IN NS ns.fakedns. + ${lib.concatImapStrings (num: { ip, zones }: '' + ns${toString num}.fakedns. IN A ${ip} + ${lib.concatMapStrings (zone: '' + ${zone} IN NS ns${toString num}.fakedns. + '') zones} + '') (lib.filter (zi: zi.zones != []) filteredZoneInfo)} + ${recordsFromExtraHosts} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/tests/common/user-account.nix b/nixpkgs/nixos/tests/common/user-account.nix new file mode 100644 index 000000000000..9cd531a1f96c --- /dev/null +++ b/nixpkgs/nixos/tests/common/user-account.nix @@ -0,0 +1,14 @@ +{ ... }: + +{ users.users.alice = + { isNormalUser = true; + description = "Alice Foobar"; + password = "foobar"; + }; + + users.users.bob = + { isNormalUser = true; + description = "Bob Foobar"; + password = "foobar"; + }; +} diff --git a/nixpkgs/nixos/tests/common/webroot/news-rss.xml b/nixpkgs/nixos/tests/common/webroot/news-rss.xml new file mode 100644 index 000000000000..28e6fa7da1f3 --- /dev/null +++ b/nixpkgs/nixos/tests/common/webroot/news-rss.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<rss xmlns:blogChannel="http://backend.userland.com/blogChannelModule" version="2.0"><channel><title>NixOS News</title><link>https://nixos.org</link><description>News for NixOS, the purely functional Linux distribution.</description><image><title>NixOS</title><url>https://nixos.org/logo/nixos-logo-only-hires.png</url><link>https://nixos.org/</link></image><item><title> + NixOS 18.09 released + </title><link>https://nixos.org/news.html</link><description> + <a href="https://github.com/NixOS/nixos-artwork/blob/master/releases/18.09-jellyfish/jellyfish.png"> + <img class="inline" src="logo/nixos-logo-18.09-jellyfish-lores.png" alt="18.09 Jellyfish logo" with="100" height="87"/> + </a> + NixOS 18.09 “Jellyfish” has been released, the tenth stable release branch. + See the <a href="/nixos/manual/release-notes.html#sec-release-18.09">release notes</a> + for details. You can get NixOS 18.09 ISOs and VirtualBox appliances + from the <a href="nixos/download.html">download page</a>. + For information on how to upgrade from older release branches + to 18.09, check out the + <a href="/nixos/manual/index.html#sec-upgrading">manual section on upgrading</a>. + </description><pubDate>Sat Oct 06 2018 00:00:00 GMT</pubDate></item></channel></rss> diff --git a/nixpkgs/nixos/tests/common/x11.nix b/nixpkgs/nixos/tests/common/x11.nix new file mode 100644 index 000000000000..c5a7c165d126 --- /dev/null +++ b/nixpkgs/nixos/tests/common/x11.nix @@ -0,0 +1,12 @@ +{ services.xserver.enable = true; + + # Automatically log in. + services.xserver.displayManager.auto.enable = true; + + # Use IceWM as the window manager. + services.xserver.windowManager.default = "icewm"; + services.xserver.windowManager.icewm.enable = true; + + # Don't use a desktop manager. + services.xserver.desktopManager.default = "none"; +} diff --git a/nixpkgs/nixos/tests/containers-bridge.nix b/nixpkgs/nixos/tests/containers-bridge.nix new file mode 100644 index 000000000000..0eae51433d20 --- /dev/null +++ b/nixpkgs/nixos/tests/containers-bridge.nix @@ -0,0 +1,103 @@ +# Test for NixOS' container support. + +let + hostIp = "192.168.0.1"; + containerIp = "192.168.0.100/24"; + hostIp6 = "fc00::1"; + containerIp6 = "fc00::2/7"; +in + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-bridge"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aristid aszlig eelco chaoflow kampfschlaefer ]; + }; + + machine = + { pkgs, ... }: + { imports = [ ../modules/installer/cd-dvd/channel.nix ]; + virtualisation.writableStore = true; + virtualisation.memorySize = 768; + + networking.bridges = { + br0 = { + interfaces = []; + }; + }; + networking.interfaces = { + br0 = { + ipv4.addresses = [{ address = hostIp; prefixLength = 24; }]; + ipv6.addresses = [{ address = hostIp6; prefixLength = 7; }]; + }; + }; + + containers.webserver = + { + autoStart = true; + privateNetwork = true; + hostBridge = "br0"; + localAddress = containerIp; + localAddress6 = containerIp6; + config = + { services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + }; + + containers.web-noip = + { + autoStart = true; + privateNetwork = true; + hostBridge = "br0"; + config = + { services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + }; + + + virtualisation.pathsInNixDB = [ pkgs.stdenv ]; + }; + + testScript = + '' + $machine->waitForUnit("default.target"); + $machine->succeed("nixos-container list") =~ /webserver/ or die; + + # Start the webserver container. + $machine->succeed("nixos-container status webserver") =~ /up/ or die; + + # Check if bridges exist inside containers + $machine->succeed("nixos-container run webserver -- ip link show eth0"); + $machine->succeed("nixos-container run web-noip -- ip link show eth0"); + + "${containerIp}" =~ /([^\/]+)\/([0-9+])/; + my $ip = $1; + chomp $ip; + $machine->succeed("ping -n -c 1 $ip"); + $machine->succeed("curl --fail http://$ip/ > /dev/null"); + + "${containerIp6}" =~ /([^\/]+)\/([0-9+])/; + my $ip6 = $1; + chomp $ip6; + $machine->succeed("ping -n -c 1 $ip6"); + $machine->succeed("curl --fail http://[$ip6]/ > /dev/null"); + + # Check that nixos-container show-ip works in case of an ipv4 address with + # subnetmask in CIDR notation. + my $result = $machine->succeed("nixos-container show-ip webserver"); + chomp $result; + $result eq $ip or die; + + # Stop the container. + $machine->succeed("nixos-container stop webserver"); + $machine->fail("curl --fail --connect-timeout 2 http://$ip/ > /dev/null"); + $machine->fail("curl --fail --connect-timeout 2 http://[$ip6]/ > /dev/null"); + + # Destroying a declarative container should fail. + $machine->fail("nixos-container destroy webserver"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/containers-extra_veth.nix b/nixpkgs/nixos/tests/containers-extra_veth.nix new file mode 100644 index 000000000000..b3d3bce87579 --- /dev/null +++ b/nixpkgs/nixos/tests/containers-extra_veth.nix @@ -0,0 +1,103 @@ +# Test for NixOS' container support. + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-bridge"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ kampfschlaefer ]; + }; + + machine = + { pkgs, ... }: + { imports = [ ../modules/installer/cd-dvd/channel.nix ]; + virtualisation.writableStore = true; + virtualisation.memorySize = 768; + virtualisation.vlans = []; + + networking.useDHCP = false; + networking.bridges = { + br0 = { + interfaces = []; + }; + br1 = { interfaces = []; }; + }; + networking.interfaces = { + br0 = { + ipv4.addresses = [{ address = "192.168.0.1"; prefixLength = 24; }]; + ipv6.addresses = [{ address = "fc00::1"; prefixLength = 7; }]; + }; + br1 = { + ipv4.addresses = [{ address = "192.168.1.1"; prefixLength = 24; }]; + }; + }; + + containers.webserver = + { + autoStart = true; + privateNetwork = true; + hostBridge = "br0"; + localAddress = "192.168.0.100/24"; + localAddress6 = "fc00::2/7"; + extraVeths = { + veth1 = { hostBridge = "br1"; localAddress = "192.168.1.100/24"; }; + veth2 = { hostAddress = "192.168.2.1"; localAddress = "192.168.2.100"; }; + }; + config = + { + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + }; + + virtualisation.pathsInNixDB = [ pkgs.stdenv ]; + }; + + testScript = + '' + $machine->waitForUnit("default.target"); + $machine->succeed("nixos-container list") =~ /webserver/ or die; + + # Status of the webserver container. + $machine->succeed("nixos-container status webserver") =~ /up/ or die; + + # Debug + #$machine->succeed("nixos-container run webserver -- ip link >&2"); + + # Ensure that the veths are inside the container + $machine->succeed("nixos-container run webserver -- ip link show veth1") =~ /state UP/ or die; + $machine->succeed("nixos-container run webserver -- ip link show veth2") =~ /state UP/ or die; + + # Debug + #$machine->succeed("ip link >&2"); + + # Ensure the presence of the extra veths + $machine->succeed("ip link show veth1") =~ /state UP/ or die; + $machine->succeed("ip link show veth2") =~ /state UP/ or die; + + # Ensure the veth1 is part of br1 on the host + $machine->succeed("ip link show veth1") =~ /master br1/ or die; + + # Debug + #$machine->succeed("ip -4 a >&2"); + #$machine->succeed("ip -4 r >&2"); + #$machine->succeed("nixos-container run webserver -- ip link >&2"); + #$machine->succeed("nixos-container run webserver -- ip -4 a >&2"); + #$machine->succeed("nixos-container run webserver -- ip -4 r >&2"); + + # Ping on main veth + $machine->succeed("ping -n -c 1 192.168.0.100"); + $machine->succeed("ping -n -c 1 fc00::2"); + + # Ping on the first extra veth + $machine->succeed("ping -n -c 1 192.168.1.100 >&2"); + + # Ping on the second extra veth + $machine->succeed("ping -n -c 1 192.168.2.100 >&2"); + + # Stop the container. + $machine->succeed("nixos-container stop webserver"); + $machine->fail("ping -n -c 1 192.168.1.100 >&2"); + $machine->fail("ping -n -c 1 192.168.2.100 >&2"); + + # Destroying a declarative container should fail. + $machine->fail("nixos-container destroy webserver"); + ''; +}) diff --git a/nixpkgs/nixos/tests/containers-hosts.nix b/nixpkgs/nixos/tests/containers-hosts.nix new file mode 100644 index 000000000000..8cf298c62258 --- /dev/null +++ b/nixpkgs/nixos/tests/containers-hosts.nix @@ -0,0 +1,52 @@ +# Test for NixOS' container support. + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-hosts"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ montag451 ]; + }; + + machine = + { lib, ... }: + { + virtualisation.memorySize = 256; + virtualisation.vlans = []; + + networking.bridges.br0.interfaces = []; + networking.interfaces.br0.ipv4.addresses = [ + { address = "10.11.0.254"; prefixLength = 24; } + ]; + + # Force /etc/hosts to be the only source for host name resolution + environment.etc."nsswitch.conf".text = lib.mkForce '' + hosts: files + ''; + + containers.simple = { + autoStart = true; + privateNetwork = true; + localAddress = "10.10.0.1"; + hostAddress = "10.10.0.254"; + + config = {}; + }; + + containers.netmask = { + autoStart = true; + privateNetwork = true; + hostBridge = "br0"; + localAddress = "10.11.0.1/24"; + + config = {}; + }; + }; + + testScript = '' + startAll; + $machine->waitForUnit("default.target"); + + # Ping the containers using the entries added in /etc/hosts + $machine->succeed("ping -n -c 1 simple.containers"); + $machine->succeed("ping -n -c 1 netmask.containers"); + ''; +}) diff --git a/nixpkgs/nixos/tests/containers-imperative.nix b/nixpkgs/nixos/tests/containers-imperative.nix new file mode 100644 index 000000000000..782095a09dad --- /dev/null +++ b/nixpkgs/nixos/tests/containers-imperative.nix @@ -0,0 +1,111 @@ +# Test for NixOS' container support. + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-imperative"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aristid aszlig eelco chaoflow kampfschlaefer ]; + }; + + machine = + { config, pkgs, lib, ... }: + { imports = [ ../modules/installer/cd-dvd/channel.nix ]; + + # XXX: Sandbox setup fails while trying to hardlink files from the host's + # store file system into the prepared chroot directory. + nix.useSandbox = false; + nix.binaryCaches = []; # don't try to access cache.nixos.org + + virtualisation.writableStore = true; + virtualisation.memorySize = 1024; + # Make sure we always have all the required dependencies for creating a + # container available within the VM, because we don't have network access. + virtualisation.pathsInNixDB = let + emptyContainer = import ../lib/eval-config.nix { + inherit (config.nixpkgs.localSystem) system; + modules = lib.singleton { + containers.foo.config = { + system.stateVersion = "18.03"; + }; + }; + }; + in with pkgs; [ + stdenv stdenvNoCC emptyContainer.config.containers.foo.path + libxslt desktop-file-utils texinfo docbook5 libxml2 + docbook_xsl_ns xorg.lndir documentation-highlighter + ]; + }; + + testScript = + '' + # Make sure we have a NixOS tree (required by ‘nixos-container create’). + $machine->succeed("PAGER=cat nix-env -qa -A nixos.hello >&2"); + + # Create some containers imperatively. + my $id1 = $machine->succeed("nixos-container create foo --ensure-unique-name"); + chomp $id1; + $machine->log("created container $id1"); + + my $id2 = $machine->succeed("nixos-container create foo --ensure-unique-name"); + chomp $id2; + $machine->log("created container $id2"); + + die if $id1 eq $id2; + + # Put the root of $id2 into a bind mount. + $machine->succeed( + "mv /var/lib/containers/$id2 /id2-bindmount", + "mount --bind /id2-bindmount /var/lib/containers/$id1" + ); + + my $ip1 = $machine->succeed("nixos-container show-ip $id1"); + chomp $ip1; + my $ip2 = $machine->succeed("nixos-container show-ip $id2"); + chomp $ip2; + die if $ip1 eq $ip2; + + # Create a directory and a file we can later check if it still exists + # after destruction of the container. + $machine->succeed( + "mkdir /nested-bindmount", + "echo important data > /nested-bindmount/dummy", + ); + + # Create a directory with a dummy file and bind-mount it into both + # containers. + foreach ($id1, $id2) { + my $importantPath = "/var/lib/containers/$_/very/important/data"; + $machine->succeed( + "mkdir -p $importantPath", + "mount --bind /nested-bindmount $importantPath" + ); + } + + # Start one of them. + $machine->succeed("nixos-container start $id1"); + + # Execute commands via the root shell. + $machine->succeed("nixos-container run $id1 -- uname") =~ /Linux/ or die; + + # Execute a nix command via the root shell. (regression test for #40355) + $machine->succeed("nixos-container run $id1 -- nix-instantiate -E 'derivation { name = \"empty\"; builder = \"false\"; system = \"false\"; }'"); + + # Stop and start (regression test for #4989) + $machine->succeed("nixos-container stop $id1"); + $machine->succeed("nixos-container start $id1"); + + # Execute commands via the root shell. + $machine->succeed("nixos-container run $id1 -- uname") =~ /Linux/ or die; + + # Destroy the containers. + $machine->succeed("nixos-container destroy $id1"); + $machine->succeed("nixos-container destroy $id2"); + + $machine->succeed( + # Check whether destruction of any container has killed important data + "grep -qF 'important data' /nested-bindmount/dummy", + # Ensure that the container path is gone + "test ! -e /var/lib/containers/$id1" + ); + ''; + +}) diff --git a/nixpkgs/nixos/tests/containers-ipv4.nix b/nixpkgs/nixos/tests/containers-ipv4.nix new file mode 100644 index 000000000000..5f83a33b1079 --- /dev/null +++ b/nixpkgs/nixos/tests/containers-ipv4.nix @@ -0,0 +1,55 @@ +# Test for NixOS' container support. + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-ipv4"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aristid aszlig eelco chaoflow kampfschlaefer ]; + }; + + machine = + { pkgs, ... }: + { imports = [ ../modules/installer/cd-dvd/channel.nix ]; + virtualisation.writableStore = true; + virtualisation.memorySize = 768; + + containers.webserver = + { privateNetwork = true; + hostAddress = "10.231.136.1"; + localAddress = "10.231.136.2"; + config = + { services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.allowedTCPPorts = [ 80 ]; + system.stateVersion = "18.03"; + }; + }; + + virtualisation.pathsInNixDB = [ pkgs.stdenv ]; + }; + + testScript = + '' + $machine->succeed("nixos-container list") =~ /webserver/ or die; + + # Start the webserver container. + $machine->succeed("nixos-container start webserver"); + + # wait two seconds for the container to start and the network to be up + sleep 2; + + # Since "start" returns after the container has reached + # multi-user.target, we should now be able to access it. + my $ip = $machine->succeed("nixos-container show-ip webserver"); + chomp $ip; + $machine->succeed("ping -n -c1 $ip"); + $machine->succeed("curl --fail http://$ip/ > /dev/null"); + + # Stop the container. + $machine->succeed("nixos-container stop webserver"); + $machine->fail("curl --fail --connect-timeout 2 http://$ip/ > /dev/null"); + + # Destroying a declarative container should fail. + $machine->fail("nixos-container destroy webserver"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/containers-ipv6.nix b/nixpkgs/nixos/tests/containers-ipv6.nix new file mode 100644 index 000000000000..5866e51b731d --- /dev/null +++ b/nixpkgs/nixos/tests/containers-ipv6.nix @@ -0,0 +1,60 @@ +# Test for NixOS' container support. + +let + hostIp = "fc00::2"; + localIp = "fc00::1"; +in + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-ipv6"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aristid aszlig eelco chaoflow kampfschlaefer ]; + }; + + machine = + { pkgs, ... }: + { imports = [ ../modules/installer/cd-dvd/channel.nix ]; + virtualisation.writableStore = true; + virtualisation.memorySize = 768; + + containers.webserver = + { privateNetwork = true; + hostAddress6 = hostIp; + localAddress6 = localIp; + config = + { services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + }; + + virtualisation.pathsInNixDB = [ pkgs.stdenv ]; + }; + + testScript = + '' + $machine->waitForUnit("default.target"); + $machine->succeed("nixos-container list") =~ /webserver/ or die; + + # Start the webserver container. + $machine->succeed("nixos-container start webserver"); + + # wait two seconds for the container to start and the network to be up + sleep 2; + + # Since "start" returns after the container has reached + # multi-user.target, we should now be able to access it. + my $ip = "${localIp}"; + chomp $ip; + $machine->succeed("ping -n -c 1 $ip"); + $machine->succeed("curl --fail http://[$ip]/ > /dev/null"); + + # Stop the container. + $machine->succeed("nixos-container stop webserver"); + $machine->fail("curl --fail --connect-timeout 2 http://[$ip]/ > /dev/null"); + + # Destroying a declarative container should fail. + $machine->fail("nixos-container destroy webserver"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/containers-macvlans.nix b/nixpkgs/nixos/tests/containers-macvlans.nix new file mode 100644 index 000000000000..2bdb926a8e2b --- /dev/null +++ b/nixpkgs/nixos/tests/containers-macvlans.nix @@ -0,0 +1,82 @@ +# Test for NixOS' container support. + +let + # containers IP on VLAN 1 + containerIp1 = "192.168.1.253"; + containerIp2 = "192.168.1.254"; +in + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-macvlans"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ montag451 ]; + }; + + nodes = { + + machine1 = + { lib, ... }: + { + virtualisation.memorySize = 256; + virtualisation.vlans = [ 1 ]; + + # To be able to ping containers from the host, it is necessary + # to create a macvlan on the host on the VLAN 1 network. + networking.macvlans.mv-eth1-host = { + interface = "eth1"; + mode = "bridge"; + }; + networking.interfaces.eth1.ipv4.addresses = lib.mkForce []; + networking.interfaces.mv-eth1-host = { + ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ]; + }; + + containers.test1 = { + autoStart = true; + macvlans = [ "eth1" ]; + + config = { + networking.interfaces.mv-eth1 = { + ipv4.addresses = [ { address = containerIp1; prefixLength = 24; } ]; + }; + }; + }; + + containers.test2 = { + autoStart = true; + macvlans = [ "eth1" ]; + + config = { + networking.interfaces.mv-eth1 = { + ipv4.addresses = [ { address = containerIp2; prefixLength = 24; } ]; + }; + }; + }; + }; + + machine2 = + { ... }: + { + virtualisation.memorySize = 256; + virtualisation.vlans = [ 1 ]; + }; + + }; + + testScript = '' + startAll; + $machine1->waitForUnit("default.target"); + $machine2->waitForUnit("default.target"); + + # Ping between containers to check that macvlans are created in bridge mode + $machine1->succeed("nixos-container run test1 -- ping -n -c 1 ${containerIp2}"); + + # Ping containers from the host (machine1) + $machine1->succeed("ping -n -c 1 ${containerIp1}"); + $machine1->succeed("ping -n -c 1 ${containerIp2}"); + + # Ping containers from the second machine to check that containers are reachable from the outside + $machine2->succeed("ping -n -c 1 ${containerIp1}"); + $machine2->succeed("ping -n -c 1 ${containerIp2}"); + ''; +}) diff --git a/nixpkgs/nixos/tests/containers-physical_interfaces.nix b/nixpkgs/nixos/tests/containers-physical_interfaces.nix new file mode 100644 index 000000000000..1e312f59f437 --- /dev/null +++ b/nixpkgs/nixos/tests/containers-physical_interfaces.nix @@ -0,0 +1,133 @@ + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-physical_interfaces"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ kampfschlaefer ]; + }; + + nodes = { + server = { ... }: + { + virtualisation.memorySize = 256; + virtualisation.vlans = [ 1 ]; + + containers.server = { + privateNetwork = true; + interfaces = [ "eth1" ]; + + config = { + networking.interfaces.eth1.ipv4.addresses = [ + { address = "10.10.0.1"; prefixLength = 24; } + ]; + networking.firewall.enable = false; + }; + }; + }; + bridged = { ... }: { + virtualisation.memorySize = 128; + virtualisation.vlans = [ 1 ]; + + containers.bridged = { + privateNetwork = true; + interfaces = [ "eth1" ]; + + config = { + networking.bridges.br0.interfaces = [ "eth1" ]; + networking.interfaces.br0.ipv4.addresses = [ + { address = "10.10.0.2"; prefixLength = 24; } + ]; + networking.firewall.enable = false; + }; + }; + }; + + bonded = { ... }: { + virtualisation.memorySize = 128; + virtualisation.vlans = [ 1 ]; + + containers.bonded = { + privateNetwork = true; + interfaces = [ "eth1" ]; + + config = { + networking.bonds.bond0 = { + interfaces = [ "eth1" ]; + driverOptions.mode = "active-backup"; + }; + networking.interfaces.bond0.ipv4.addresses = [ + { address = "10.10.0.3"; prefixLength = 24; } + ]; + networking.firewall.enable = false; + }; + }; + }; + + bridgedbond = { ... }: { + virtualisation.memorySize = 128; + virtualisation.vlans = [ 1 ]; + + containers.bridgedbond = { + privateNetwork = true; + interfaces = [ "eth1" ]; + + config = { + networking.bonds.bond0 = { + interfaces = [ "eth1" ]; + driverOptions.mode = "active-backup"; + }; + networking.bridges.br0.interfaces = [ "bond0" ]; + networking.interfaces.br0.ipv4.addresses = [ + { address = "10.10.0.4"; prefixLength = 24; } + ]; + networking.firewall.enable = false; + }; + }; + }; + }; + + testScript = '' + startAll; + + subtest "prepare server", sub { + $server->waitForUnit("default.target"); + $server->succeed("ip link show dev eth1 >&2"); + }; + + subtest "simple physical interface", sub { + $server->succeed("nixos-container start server"); + $server->waitForUnit("container\@server"); + $server->succeed("systemctl -M server list-dependencies network-addresses-eth1.service >&2"); + + # The other tests will ping this container on its ip. Here we just check + # that the device is present in the container. + $server->succeed("nixos-container run server -- ip a show dev eth1 >&2"); + }; + + subtest "physical device in bridge in container", sub { + $bridged->waitForUnit("default.target"); + $bridged->succeed("nixos-container start bridged"); + $bridged->waitForUnit("container\@bridged"); + $bridged->succeed("systemctl -M bridged list-dependencies network-addresses-br0.service >&2"); + $bridged->succeed("systemctl -M bridged status -n 30 -l network-addresses-br0.service"); + $bridged->succeed("nixos-container run bridged -- ping -w 10 -c 1 -n 10.10.0.1"); + }; + + subtest "physical device in bond in container", sub { + $bonded->waitForUnit("default.target"); + $bonded->succeed("nixos-container start bonded"); + $bonded->waitForUnit("container\@bonded"); + $bonded->succeed("systemctl -M bonded list-dependencies network-addresses-bond0 >&2"); + $bonded->succeed("systemctl -M bonded status -n 30 -l network-addresses-bond0 >&2"); + $bonded->succeed("nixos-container run bonded -- ping -w 10 -c 1 -n 10.10.0.1"); + }; + + subtest "physical device in bond in bridge in container", sub { + $bridgedbond->waitForUnit("default.target"); + $bridgedbond->succeed("nixos-container start bridgedbond"); + $bridgedbond->waitForUnit("container\@bridgedbond"); + $bridgedbond->succeed("systemctl -M bridgedbond list-dependencies network-addresses-br0.service >&2"); + $bridgedbond->succeed("systemctl -M bridgedbond status -n 30 -l network-addresses-br0.service"); + $bridgedbond->succeed("nixos-container run bridgedbond -- ping -w 10 -c 1 -n 10.10.0.1"); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/containers-portforward.nix b/nixpkgs/nixos/tests/containers-portforward.nix new file mode 100644 index 000000000000..d2dda926fc0e --- /dev/null +++ b/nixpkgs/nixos/tests/containers-portforward.nix @@ -0,0 +1,62 @@ +# Test for NixOS' container support. + +let + hostIp = "192.168.0.1"; + hostPort = 10080; + containerIp = "192.168.0.100"; + containerPort = 80; +in + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-portforward"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aristid aszlig eelco chaoflow kampfschlaefer ianwookim ]; + }; + + machine = + { pkgs, ... }: + { imports = [ ../modules/installer/cd-dvd/channel.nix ]; + virtualisation.writableStore = true; + virtualisation.memorySize = 768; + + containers.webserver = + { privateNetwork = true; + hostAddress = hostIp; + localAddress = containerIp; + forwardPorts = [ { protocol = "tcp"; hostPort = hostPort; containerPort = containerPort; } ]; + config = + { services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + }; + + virtualisation.pathsInNixDB = [ pkgs.stdenv ]; + }; + + testScript = + '' + $machine->succeed("nixos-container list") =~ /webserver/ or die; + + # Start the webserver container. + $machine->succeed("nixos-container start webserver"); + + # wait two seconds for the container to start and the network to be up + sleep 2; + + # Since "start" returns after the container has reached + # multi-user.target, we should now be able to access it. + #my $ip = $machine->succeed("nixos-container show-ip webserver"); + #chomp $ip; + $machine->succeed("ping -n -c1 ${hostIp}"); + $machine->succeed("curl --fail http://${hostIp}:${toString hostPort}/ > /dev/null"); + + # Stop the container. + $machine->succeed("nixos-container stop webserver"); + $machine->fail("curl --fail --connect-timeout 2 http://${hostIp}:${toString hostPort}/ > /dev/null"); + + # Destroying a declarative container should fail. + $machine->fail("nixos-container destroy webserver"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/containers-reloadable.nix b/nixpkgs/nixos/tests/containers-reloadable.nix new file mode 100644 index 000000000000..9726ca0cb0e7 --- /dev/null +++ b/nixpkgs/nixos/tests/containers-reloadable.nix @@ -0,0 +1,65 @@ +import ./make-test.nix ({ pkgs, lib, ...} : +let + client_base = rec { + + containers.test1 = { + autoStart = true; + config = { + environment.etc."check".text = "client_base"; + }; + }; + + # prevent make-test.nix to change IP + networking.interfaces = { + eth1.ipv4.addresses = lib.mkOverride 0 [ ]; + }; + }; +in { + name = "cotnainers-reloadable"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ danbst ]; + }; + + nodes = { + client = { ... }: { + imports = [ client_base ]; + }; + + client_c1 = { lib, ... }: { + imports = [ client_base ]; + + containers.test1.config = { + environment.etc."check".text = lib.mkForce "client_c1"; + services.httpd.enable = true; + services.httpd.adminAddr = "nixos@example.com"; + }; + }; + client_c2 = { lib, ... }: { + imports = [ client_base ]; + + containers.test1.config = { + environment.etc."check".text = lib.mkForce "client_c2"; + services.nginx.enable = true; + }; + }; + }; + + testScript = {nodes, ...}: let + c1System = nodes.client_c1.config.system.build.toplevel; + c2System = nodes.client_c2.config.system.build.toplevel; + in '' + $client->start(); + $client->waitForUnit("default.target"); + $client->succeed("[[ \$(nixos-container run test1 cat /etc/check) == client_base ]] >&2"); + + $client->succeed("${c1System}/bin/switch-to-configuration test >&2"); + $client->succeed("[[ \$(nixos-container run test1 cat /etc/check) == client_c1 ]] >&2"); + $client->succeed("systemctl status httpd -M test1 >&2"); + + $client->succeed("${c2System}/bin/switch-to-configuration test >&2"); + $client->succeed("[[ \$(nixos-container run test1 cat /etc/check) == client_c2 ]] >&2"); + $client->fail("systemctl status httpd -M test1 >&2"); + $client->succeed("systemctl status nginx -M test1 >&2"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/containers-restart_networking.nix b/nixpkgs/nixos/tests/containers-restart_networking.nix new file mode 100644 index 000000000000..0fb3b591e9f9 --- /dev/null +++ b/nixpkgs/nixos/tests/containers-restart_networking.nix @@ -0,0 +1,113 @@ +# Test for NixOS' container support. + +let + client_base = rec { + networking.firewall.enable = false; + + containers.webserver = { + autoStart = true; + privateNetwork = true; + hostBridge = "br0"; + config = { + networking.firewall.enable = false; + networking.interfaces.eth0.ipv4.addresses = [ + { address = "192.168.1.122"; prefixLength = 24; } + ]; + }; + }; + }; +in import ./make-test.nix ({ pkgs, ...} : +{ + name = "containers-restart_networking"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ kampfschlaefer ]; + }; + + nodes = { + client = { lib, ... }: client_base // { + virtualisation.vlans = [ 1 ]; + + networking.bridges.br0 = { + interfaces = []; + rstp = false; + }; + networking.interfaces = { + eth1.ipv4.addresses = lib.mkOverride 0 [ ]; + br0.ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ]; + }; + + }; + client_eth1 = { lib, ... }: client_base // { + networking.bridges.br0 = { + interfaces = [ "eth1" ]; + rstp = false; + }; + networking.interfaces = { + eth1.ipv4.addresses = lib.mkOverride 0 [ ]; + br0.ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ]; + }; + }; + client_eth1_rstp = { lib, ... }: client_base // { + networking.bridges.br0 = { + interfaces = [ "eth1" ]; + rstp = true; + }; + networking.interfaces = { + eth1.ipv4.addresses = lib.mkOverride 0 [ ]; + br0.ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ]; + }; + }; + }; + + testScript = {nodes, ...}: let + originalSystem = nodes.client.config.system.build.toplevel; + eth1_bridged = nodes.client_eth1.config.system.build.toplevel; + eth1_rstp = nodes.client_eth1_rstp.config.system.build.toplevel; + in '' + $client->start(); + + $client->waitForUnit("default.target"); + + subtest "initial state", sub { + $client->succeed("ping 192.168.1.122 -c 1 -n >&2"); + $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.1 >&2"); + + $client->fail("ip l show eth1 |grep \"master br0\" >&2"); + $client->fail("grep eth1 /run/br0.interfaces >&2"); + }; + + subtest "interfaces without stp", sub { + $client->succeed("${eth1_bridged}/bin/switch-to-configuration test >&2"); + + $client->succeed("ping 192.168.1.122 -c 1 -n >&2"); + $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.2 >&2"); + + $client->succeed("ip l show eth1 |grep \"master br0\" >&2"); + $client->succeed("grep eth1 /run/br0.interfaces >&2"); + }; + + # activating rstp needs another service, therefor the bridge will restart and the container will loose its connectivity + #subtest "interfaces with rstp", sub { + # $client->succeed("${eth1_rstp}/bin/switch-to-configuration test >&2"); + # $client->execute("ip -4 a >&2"); + # $client->execute("ip l >&2"); + # + # $client->succeed("ping 192.168.1.122 -c 1 -n >&2"); + # $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.2 >&2"); + # + # $client->succeed("ip l show eth1 |grep \"master br0\" >&2"); + # $client->succeed("grep eth1 /run/br0.interfaces >&2"); + #}; + + subtest "back to no interfaces and no stp", sub { + $client->succeed("${originalSystem}/bin/switch-to-configuration test >&2"); + + $client->succeed("ping 192.168.1.122 -c 1 -n >&2"); + $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.1 >&2"); + + $client->fail("ip l show eth1 |grep \"master br0\" >&2"); + $client->fail("grep eth1 /run/br0.interfaces >&2"); + }; + ''; + +}) diff --git a/nixpkgs/nixos/tests/containers-tmpfs.nix b/nixpkgs/nixos/tests/containers-tmpfs.nix new file mode 100644 index 000000000000..05c21f4907bf --- /dev/null +++ b/nixpkgs/nixos/tests/containers-tmpfs.nix @@ -0,0 +1,79 @@ +# Test for NixOS' container support. + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-tmpfs"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ckampka ]; + }; + + machine = + { pkgs, ... }: + { imports = [ ../modules/installer/cd-dvd/channel.nix ]; + virtualisation.writableStore = true; + virtualisation.memorySize = 768; + + containers.tmpfs = + { + autoStart = true; + tmpfs = [ + # Mount var as a tmpfs + "/var" + + # Add a nested mount inside a tmpfs + "/var/log" + + # Add a tmpfs on a path that does not exist + "/some/random/path" + ]; + config = { }; + }; + + virtualisation.pathsInNixDB = [ pkgs.stdenv ]; + }; + + testScript = + '' + $machine->waitForUnit("default.target"); + $machine->succeed("nixos-container list") =~ /tmpfs/ or die; + + # Start the tmpfs container. + #$machine->succeed("nixos-container status tmpfs") =~ /up/ or die; + + # Verify that /var is mounted as a tmpfs + #$machine->succeed("nixos-container run tmpfs -- systemctl status var.mount --no-pager 2>/dev/null") =~ /What: tmpfs/ or die; + $machine->succeed("nixos-container run tmpfs -- mountpoint -q /var 2>/dev/null"); + + # Verify that /var/log is mounted as a tmpfs + $machine->succeed("nixos-container run tmpfs -- systemctl status var-log.mount --no-pager 2>/dev/null") =~ /What: tmpfs/ or die; + $machine->succeed("nixos-container run tmpfs -- mountpoint -q /var/log 2>/dev/null"); + + # Verify that /some/random/path is mounted as a tmpfs + $machine->succeed("nixos-container run tmpfs -- systemctl status some-random-path.mount --no-pager 2>/dev/null") =~ /What: tmpfs/ or die; + $machine->succeed("nixos-container run tmpfs -- mountpoint -q /some/random/path 2>/dev/null"); + + # Verify that files created in the container in a non-tmpfs directory are visible on the host. + # This establishes legitimacy for the following tests + $machine->succeed("nixos-container run tmpfs -- touch /root/test.file 2>/dev/null"); + $machine->succeed("nixos-container run tmpfs -- ls -l /root | grep -q test.file 2>/dev/null"); + $machine->succeed("test -e /var/lib/containers/tmpfs/root/test.file"); + + + # Verify that /some/random/path is writable and that files created there + # are not in the hosts container dir but in the tmpfs + $machine->succeed("nixos-container run tmpfs -- touch /some/random/path/test.file 2>/dev/null"); + $machine->succeed("nixos-container run tmpfs -- test -e /some/random/path/test.file 2>/dev/null"); + + $machine->fail("test -e /var/lib/containers/tmpfs/some/random/path/test.file"); + + # Verify that files created in the hosts container dir in a path where a tmpfs file system has been mounted + # are not visible to the container as the do not exist in the tmpfs + $machine->succeed("touch /var/lib/containers/tmpfs/var/test.file"); + + $machine->succeed("test -e /var/lib/containers/tmpfs/var/test.file"); + $machine->succeed("ls -l /var/lib/containers/tmpfs/var/ | grep -q test.file 2>/dev/null"); + + $machine->fail("nixos-container run tmpfs -- ls -l /var | grep -q test.file 2>/dev/null"); + + ''; + +}) diff --git a/nixpkgs/nixos/tests/couchdb.nix b/nixpkgs/nixos/tests/couchdb.nix new file mode 100644 index 000000000000..48ea48eebbb3 --- /dev/null +++ b/nixpkgs/nixos/tests/couchdb.nix @@ -0,0 +1,56 @@ +import ./make-test.nix ({ pkgs, lib, ...}: + +with lib; + +{ + name = "couchdb"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ fpletz ]; + }; + + nodes = { + couchdb1 = + { pkgs, ... }: + + { environment.systemPackages = with pkgs; [ jq ]; + services.couchdb.enable = true; + }; + + couchdb2 = + { pkgs, ... }: + + { environment.systemPackages = with pkgs; [ jq ]; + services.couchdb.enable = true; + services.couchdb.package = pkgs.couchdb2; + }; + }; + + testScript = let + curlJqCheck = action: path: jqexpr: result: + pkgs.writeScript "curl-jq-check-${action}-${path}.sh" '' + RESULT=$(curl -X ${action} http://127.0.0.1:5984/${path} | jq -r '${jqexpr}') + echo $RESULT >&2 + if [ "$RESULT" != "${result}" ]; then + exit 1 + fi + ''; + in '' + startAll; + + $couchdb1->waitForUnit("couchdb.service"); + $couchdb1->waitUntilSucceeds("${curlJqCheck "GET" "" ".couchdb" "Welcome"}"); + $couchdb1->waitUntilSucceeds("${curlJqCheck "GET" "_all_dbs" ". | length" "2"}"); + $couchdb1->succeed("${curlJqCheck "PUT" "foo" ".ok" "true"}"); + $couchdb1->succeed("${curlJqCheck "GET" "_all_dbs" ". | length" "3"}"); + $couchdb1->succeed("${curlJqCheck "DELETE" "foo" ".ok" "true"}"); + $couchdb1->succeed("${curlJqCheck "GET" "_all_dbs" ". | length" "2"}"); + + $couchdb2->waitForUnit("couchdb.service"); + $couchdb2->waitUntilSucceeds("${curlJqCheck "GET" "" ".couchdb" "Welcome"}"); + $couchdb2->waitUntilSucceeds("${curlJqCheck "GET" "_all_dbs" ". | length" "0"}"); + $couchdb2->succeed("${curlJqCheck "PUT" "foo" ".ok" "true"}"); + $couchdb2->succeed("${curlJqCheck "GET" "_all_dbs" ". | length" "1"}"); + $couchdb2->succeed("${curlJqCheck "DELETE" "foo" ".ok" "true"}"); + $couchdb2->succeed("${curlJqCheck "GET" "_all_dbs" ". | length" "0"}"); + ''; +}) diff --git a/nixpkgs/nixos/tests/deluge.nix b/nixpkgs/nixos/tests/deluge.nix new file mode 100644 index 000000000000..b4be5e465cc0 --- /dev/null +++ b/nixpkgs/nixos/tests/deluge.nix @@ -0,0 +1,29 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "deluge"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ flokli ]; + }; + + nodes = { + server = + { ... }: + + { services.deluge = { + enable = true; + web.enable = true; + }; + networking.firewall.allowedTCPPorts = [ 8112 ]; + }; + + client = { }; + }; + + testScript = '' + startAll; + + $server->waitForUnit("deluged"); + $server->waitForUnit("delugeweb"); + $client->waitForUnit("network.target"); + $client->waitUntilSucceeds("curl --fail http://server:8112"); + ''; +}) diff --git a/nixpkgs/nixos/tests/dhparams.nix b/nixpkgs/nixos/tests/dhparams.nix new file mode 100644 index 000000000000..d11dfeec5d0c --- /dev/null +++ b/nixpkgs/nixos/tests/dhparams.nix @@ -0,0 +1,144 @@ +let + common = { pkgs, ... }: { + security.dhparams.enable = true; + environment.systemPackages = [ pkgs.openssl ]; + }; + +in import ./make-test.nix { + name = "dhparams"; + + nodes.generation1 = { pkgs, config, ... }: { + imports = [ common ]; + security.dhparams.params = { + # Use low values here because we don't want the test to run for ages. + foo.bits = 16; + # Also use the old format to make sure the type is coerced in the right + # way. + bar = 17; + }; + + systemd.services.foo = { + description = "Check systemd Ordering"; + wantedBy = [ "multi-user.target" ]; + unitConfig = { + # This is to make sure that the dhparams generation of foo occurs + # before this service so we need this service to start as early as + # possible to provoke a race condition. + DefaultDependencies = false; + + # We check later whether the service has been started or not. + ConditionPathExists = config.security.dhparams.params.foo.path; + }; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + # The reason we only provide an ExecStop here is to ensure that we don't + # accidentally trigger an error because a file system is not yet ready + # during very early startup (we might not even have the Nix store + # available, for example if future changes in NixOS use systemd mount + # units to do early file system initialisation). + serviceConfig.ExecStop = "${pkgs.coreutils}/bin/true"; + }; + }; + + nodes.generation2 = { + imports = [ common ]; + security.dhparams.params.foo.bits = 18; + }; + + nodes.generation3 = common; + + nodes.generation4 = { + imports = [ common ]; + security.dhparams.stateful = false; + security.dhparams.params.foo2.bits = 18; + security.dhparams.params.bar2.bits = 19; + }; + + nodes.generation5 = { + imports = [ common ]; + security.dhparams.defaultBitSize = 30; + security.dhparams.params.foo3 = {}; + security.dhparams.params.bar3 = {}; + }; + + testScript = { nodes, ... }: let + getParamPath = gen: name: let + node = "generation${toString gen}"; + in nodes.${node}.config.security.dhparams.params.${name}.path; + + assertParamBits = gen: name: bits: let + path = getParamPath gen name; + in '' + $machine->nest('check bit size of ${path}', sub { + my $out = $machine->succeed('openssl dhparam -in ${path} -text'); + $out =~ /^\s*DH Parameters:\s+\((\d+)\s+bit\)\s*$/m; + die "bit size should be ${toString bits} but it is $1 instead." + if $1 != ${toString bits}; + }); + ''; + + switchToGeneration = gen: let + node = "generation${toString gen}"; + inherit (nodes.${node}.config.system.build) toplevel; + switchCmd = "${toplevel}/bin/switch-to-configuration test"; + in '' + $machine->nest('switch to generation ${toString gen}', sub { + $machine->succeed('${switchCmd}'); + $main::machine = ''$${node}; + }); + ''; + + in '' + my $machine = $generation1; + + $machine->waitForUnit('multi-user.target'); + + subtest "verify startup order", sub { + $machine->succeed('systemctl is-active foo.service'); + }; + + subtest "check bit sizes of dhparam files", sub { + ${assertParamBits 1 "foo" 16} + ${assertParamBits 1 "bar" 17} + }; + + ${switchToGeneration 2} + + subtest "check whether bit size has changed", sub { + ${assertParamBits 2 "foo" 18} + }; + + subtest "ensure that dhparams file for 'bar' was deleted", sub { + $machine->fail('test -e ${getParamPath 1 "bar"}'); + }; + + ${switchToGeneration 3} + + subtest "ensure that 'security.dhparams.path' has been deleted", sub { + $machine->fail( + 'test -e ${nodes.generation3.config.security.dhparams.path}' + ); + }; + + ${switchToGeneration 4} + + subtest "check bit sizes dhparam files", sub { + ${assertParamBits 4 "foo2" 18} + ${assertParamBits 4 "bar2" 19} + }; + + subtest "check whether dhparam files are in the Nix store", sub { + $machine->succeed( + 'expr match ${getParamPath 4 "foo2"} ${builtins.storeDir}', + 'expr match ${getParamPath 4 "bar2"} ${builtins.storeDir}', + ); + }; + + ${switchToGeneration 5} + + subtest "check whether defaultBitSize works as intended", sub { + ${assertParamBits 5 "foo3" 30} + ${assertParamBits 5 "bar3" 30} + }; + ''; +} diff --git a/nixpkgs/nixos/tests/dnscrypt-proxy.nix b/nixpkgs/nixos/tests/dnscrypt-proxy.nix new file mode 100644 index 000000000000..13bc9d3d9168 --- /dev/null +++ b/nixpkgs/nixos/tests/dnscrypt-proxy.nix @@ -0,0 +1,33 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "dnscrypt-proxy"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ joachifm ]; + }; + + nodes = { + # A client running the recommended setup: DNSCrypt proxy as a forwarder + # for a caching DNS client. + client = + { ... }: + let localProxyPort = 43; in + { + security.apparmor.enable = true; + + services.dnscrypt-proxy.enable = true; + services.dnscrypt-proxy.localPort = localProxyPort; + services.dnscrypt-proxy.extraArgs = [ "-X libdcplugin_example.so" ]; + + services.dnsmasq.enable = true; + services.dnsmasq.servers = [ "127.0.0.1#${toString localProxyPort}" ]; + }; + }; + + testScript = '' + $client->waitForUnit("dnsmasq"); + + # The daemon is socket activated; sending a single ping should activate it. + $client->fail("systemctl is-active dnscrypt-proxy"); + $client->execute("${pkgs.iputils}/bin/ping -c1 example.com"); + $client->waitUntilSucceeds("systemctl is-active dnscrypt-proxy"); + ''; +}) diff --git a/nixpkgs/nixos/tests/docker-edge.nix b/nixpkgs/nixos/tests/docker-edge.nix new file mode 100644 index 000000000000..b306c149be91 --- /dev/null +++ b/nixpkgs/nixos/tests/docker-edge.nix @@ -0,0 +1,47 @@ +# This test runs docker and checks if simple container starts + +import ./make-test.nix ({ pkgs, ...} : { + name = "docker"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ nequissimus offline ]; + }; + + nodes = { + docker = + { pkgs, ... }: + { + virtualisation.docker.enable = true; + virtualisation.docker.package = pkgs.docker-edge; + + users.users = { + noprivs = { + isNormalUser = true; + description = "Can't access the docker daemon"; + password = "foobar"; + }; + + hasprivs = { + isNormalUser = true; + description = "Can access the docker daemon"; + password = "foobar"; + extraGroups = [ "docker" ]; + }; + }; + }; + }; + + testScript = '' + startAll; + + $docker->waitForUnit("sockets.target"); + $docker->succeed("tar cv --files-from /dev/null | docker import - scratchimg"); + $docker->succeed("docker run -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"); + $docker->succeed("docker ps | grep sleeping"); + $docker->succeed("sudo -u hasprivs docker ps"); + $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-edge.version} | wc -l) = "2" ]'); + ''; +}) diff --git a/nixpkgs/nixos/tests/docker-preloader.nix b/nixpkgs/nixos/tests/docker-preloader.nix new file mode 100644 index 000000000000..eeedec9a392e --- /dev/null +++ b/nixpkgs/nixos/tests/docker-preloader.nix @@ -0,0 +1,27 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "docker-preloader"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ lewo ]; + }; + + nodes = { + docker = + { pkgs, ... }: + { + virtualisation.docker.enable = true; + virtualisation.dockerPreloader.images = [ pkgs.dockerTools.examples.nix pkgs.dockerTools.examples.bash ]; + + services.openssh.enable = true; + services.openssh.permitRootLogin = "yes"; + 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/nixpkgs/nixos/tests/docker-registry.nix b/nixpkgs/nixos/tests/docker-registry.nix new file mode 100644 index 000000000000..8936421072a9 --- /dev/null +++ b/nixpkgs/nixos/tests/docker-registry.nix @@ -0,0 +1,63 @@ +# This test runs docker-registry and check if it works + +import ./make-test.nix ({ pkgs, ...} : { + name = "docker-registry"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ globin ma27 ironpinguin ]; + }; + + nodes = { + registry = { ... }: { + services.dockerRegistry.enable = true; + services.dockerRegistry.enableDelete = true; + services.dockerRegistry.port = 8080; + services.dockerRegistry.listenAddress = "0.0.0.0"; + services.dockerRegistry.enableGarbageCollect = true; + networking.firewall.allowedTCPPorts = [ 8080 ]; + }; + + client1 = { ... }: { + virtualisation.docker.enable = true; + virtualisation.docker.extraOptions = "--insecure-registry registry:8080"; + }; + + client2 = { ... }: { + virtualisation.docker.enable = true; + virtualisation.docker.extraOptions = "--insecure-registry registry:8080"; + }; + }; + + testScript = '' + $client1->start(); + $client1->waitForUnit("docker.service"); + $client1->succeed("tar cv --files-from /dev/null | docker import - scratch"); + $client1->succeed("docker tag scratch registry:8080/scratch"); + + $registry->start(); + $registry->waitForUnit("docker-registry.service"); + $registry->waitForOpenPort("8080"); + $client1->succeed("docker push registry:8080/scratch"); + + $client2->start(); + $client2->waitForUnit("docker.service"); + $client2->succeed("docker pull registry:8080/scratch"); + $client2->succeed("docker images | grep scratch"); + + $client2->succeed( + 'curl -fsS -X DELETE registry:8080/v2/scratch/manifests/$(curl -fsS -I -H"Accept: application/vnd.docker.distribution.manifest.v2+json" registry:8080/v2/scratch/manifests/latest | grep Docker-Content-Digest | sed -e \'s/Docker-Content-Digest: //\' | tr -d \'\r\')' + ); + + $registry->systemctl("start docker-registry-garbage-collect.service"); + $registry->waitUntilFails("systemctl status docker-registry-garbage-collect.service"); + $registry->waitForUnit("docker-registry.service"); + + $registry->fail( + 'ls -l /var/lib/docker-registry/docker/registry/v2/blobs/sha256/*/*/data' + ); + + $client1->succeed("docker push registry:8080/scratch"); + $registry->succeed( + 'ls -l /var/lib/docker-registry/docker/registry/v2/blobs/sha256/*/*/data' + ); + ''; +}) diff --git a/nixpkgs/nixos/tests/docker-tools-overlay.nix b/nixpkgs/nixos/tests/docker-tools-overlay.nix new file mode 100644 index 000000000000..637957bd3e8b --- /dev/null +++ b/nixpkgs/nixos/tests/docker-tools-overlay.nix @@ -0,0 +1,32 @@ +# this test creates a simple GNU image with docker tools and sees if it executes + +import ./make-test.nix ({ pkgs, ... }: +{ + name = "docker-tools-overlay"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ lnl7 ]; + }; + + nodes = { + docker = + { ... }: + { + virtualisation.docker.enable = true; + virtualisation.docker.storageDriver = "overlay"; # defaults to overlay2 + }; + }; + + testScript = + '' + $docker->waitForUnit("sockets.target"); + + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.bash}'"); + $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.bash.imageName} bash --version"); + + # Check if the nix store has correct user permissions depending on what + # storage driver is used, incorrectly built images can show up as readonly. + # drw------- 3 0 0 3 Apr 14 11:36 /nix + # drw------- 99 0 0 100 Apr 14 11:36 /nix/store + $docker->succeed("docker run --rm -u 1000:1000 ${pkgs.dockerTools.examples.bash.imageName} bash --version"); + ''; +}) diff --git a/nixpkgs/nixos/tests/docker-tools.nix b/nixpkgs/nixos/tests/docker-tools.nix new file mode 100644 index 000000000000..58f106314ab3 --- /dev/null +++ b/nixpkgs/nixos/tests/docker-tools.nix @@ -0,0 +1,71 @@ +# this test creates a simple GNU image with docker tools and sees if it executes + +import ./make-test.nix ({ pkgs, ... }: { + name = "docker-tools"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ lnl7 ]; + }; + + nodes = { + docker = + { ... }: { + virtualisation = { + diskSize = 2048; + docker.enable = true; + }; + }; + }; + + testScript = + '' + $docker->waitForUnit("sockets.target"); + + # Ensure Docker images use a stable date by default + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.bash}'"); + $docker->succeed("[ '1970-01-01T00:00:01Z' = \"\$(docker inspect ${pkgs.dockerTools.examples.bash.imageName} | ${pkgs.jq}/bin/jq -r .[].Created)\" ]"); + + $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.bash.imageName} bash --version"); + $docker->succeed("docker rmi ${pkgs.dockerTools.examples.bash.imageName}"); + + # Check if the nix store is correctly initialized by listing dependencies of the installed Nix binary + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nix}'"); + $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.nix.imageName} nix-store -qR ${pkgs.nix}"); + $docker->succeed("docker rmi ${pkgs.dockerTools.examples.nix.imageName}"); + + # To test the pullImage tool + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nixFromDockerHub}'"); + $docker->succeed("docker run --rm nixos/nix:1.11 nix-store --version"); + $docker->succeed("docker rmi nixos/nix:1.11"); + + # To test runAsRoot and entry point + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nginx}'"); + $docker->succeed("docker run --name nginx -d -p 8000:80 ${pkgs.dockerTools.examples.nginx.imageName}"); + $docker->waitUntilSucceeds('curl http://localhost:8000/'); + $docker->succeed("docker rm --force nginx"); + $docker->succeed("docker rmi '${pkgs.dockerTools.examples.nginx.imageName}'"); + + # An pulled image can be used as base image + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.onTopOfPulledImage}'"); + $docker->succeed("docker run --rm ontopofpulledimage hello"); + $docker->succeed("docker rmi ontopofpulledimage"); + + # Regression test for issue #34779 + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.runAsRootExtraCommands}'"); + $docker->succeed("docker run --rm runasrootextracommands cat extraCommands"); + $docker->succeed("docker run --rm runasrootextracommands cat runAsRoot"); + $docker->succeed("docker rmi '${pkgs.dockerTools.examples.runAsRootExtraCommands.imageName}'"); + + # Ensure Docker images can use an unstable date + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.bash}'"); + $docker->succeed("[ '1970-01-01T00:00:01Z' != \"\$(docker inspect ${pkgs.dockerTools.examples.unstableDate.imageName} | ${pkgs.jq}/bin/jq -r .[].Created)\" ]"); + + # Ensure Layered Docker images work + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.layered-image}'"); + $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.layered-image.imageName}"); + $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.layered-image.imageName} cat extraCommands"); + + # Ensure building an image on top of a layered Docker images work + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.layered-on-top}'"); + $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.layered-on-top.imageName}"); + ''; +}) diff --git a/nixpkgs/nixos/tests/docker.nix b/nixpkgs/nixos/tests/docker.nix new file mode 100644 index 000000000000..d67b2f8743d8 --- /dev/null +++ b/nixpkgs/nixos/tests/docker.nix @@ -0,0 +1,47 @@ +# This test runs docker and checks if simple container starts + +import ./make-test.nix ({ pkgs, ...} : { + name = "docker"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ nequissimus offline ]; + }; + + nodes = { + docker = + { pkgs, ... }: + { + virtualisation.docker.enable = true; + virtualisation.docker.package = pkgs.docker; + + users.users = { + noprivs = { + isNormalUser = true; + description = "Can't access the docker daemon"; + password = "foobar"; + }; + + hasprivs = { + isNormalUser = true; + description = "Can access the docker daemon"; + password = "foobar"; + extraGroups = [ "docker" ]; + }; + }; + }; + }; + + testScript = '' + startAll; + + $docker->waitForUnit("sockets.target"); + $docker->succeed("tar cv --files-from /dev/null | docker import - scratchimg"); + $docker->succeed("docker run -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"); + $docker->succeed("docker ps | grep sleeping"); + $docker->succeed("sudo -u hasprivs docker ps"); + $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" ]'); + ''; +}) diff --git a/nixpkgs/nixos/tests/dovecot.nix b/nixpkgs/nixos/tests/dovecot.nix new file mode 100644 index 000000000000..156079d1d585 --- /dev/null +++ b/nixpkgs/nixos/tests/dovecot.nix @@ -0,0 +1,77 @@ +import ./make-test.nix { + name = "dovecot"; + + machine = { pkgs, ... }: { + imports = [ common/user-account.nix ]; + services.postfix.enable = true; + services.dovecot2.enable = true; + services.dovecot2.protocols = [ "imap" "pop3" ]; + environment.systemPackages = let + sendTestMail = pkgs.writeScriptBin "send-testmail" '' + #!${pkgs.stdenv.shell} + exec sendmail -vt <<MAIL + From: root@localhost + To: alice@localhost + Subject: Very important! + + Hello world! + MAIL + ''; + + sendTestMailViaDeliveryAgent = pkgs.writeScriptBin "send-lda" '' + #!${pkgs.stdenv.shell} + + exec ${pkgs.dovecot}/libexec/dovecot/deliver -d bob <<MAIL + From: root@localhost + To: bob@localhost + Subject: Something else... + + I'm running short of ideas! + MAIL + ''; + + testImap = pkgs.writeScriptBin "test-imap" '' + #!${pkgs.python3.interpreter} + import imaplib + + with imaplib.IMAP4('localhost') as imap: + imap.login('alice', 'foobar') + imap.select() + status, refs = imap.search(None, 'ALL') + assert status == 'OK' + assert len(refs) == 1 + status, msg = imap.fetch(refs[0], 'BODY[TEXT]') + assert status == 'OK' + assert msg[0][1].strip() == b'Hello world!' + ''; + + testPop = pkgs.writeScriptBin "test-pop" '' + #!${pkgs.python3.interpreter} + import poplib + + pop = poplib.POP3('localhost') + try: + pop.user('bob') + pop.pass_('foobar') + assert len(pop.list()[1]) == 1 + status, fullmail, size = pop.retr(1) + assert status.startswith(b'+OK ') + body = b"".join(fullmail[fullmail.index(b""):]).strip() + assert body == b"I'm running short of ideas!" + finally: + pop.quit() + ''; + + in [ sendTestMail sendTestMailViaDeliveryAgent testImap testPop ]; + }; + + testScript = '' + $machine->waitForUnit('postfix.service'); + $machine->waitForUnit('dovecot2.service'); + $machine->succeed('send-testmail'); + $machine->succeed('send-lda'); + $machine->waitUntilFails('[ "$(postqueue -p)" != "Mail queue is empty" ]'); + $machine->succeed('test-imap'); + $machine->succeed('test-pop'); + ''; +} diff --git a/nixpkgs/nixos/tests/ec2.nix b/nixpkgs/nixos/tests/ec2.nix new file mode 100644 index 000000000000..ed6bf7da988c --- /dev/null +++ b/nixpkgs/nixos/tests/ec2.nix @@ -0,0 +1,178 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + image = + (import ../lib/eval-config.nix { + inherit system; + modules = [ + ../maintainers/scripts/ec2/amazon-image.nix + ../modules/testing/test-instrumentation.nix + ../modules/profiles/qemu-guest.nix + { ec2.hvm = true; + + # Hack to make the partition resizing work in QEMU. + boot.initrd.postDeviceCommands = mkBefore + '' + ln -s vda /dev/xvda + ln -s vda1 /dev/xvda1 + ''; + + # Needed by nixos-rebuild due to the lack of network + # access. Mostly copied from + # modules/profiles/installation-device.nix. + system.extraDependencies = + with pkgs; [ + stdenv busybox perlPackages.ArchiveCpio unionfs-fuse mkinitcpio-nfs-utils + + # These are used in the configure-from-userdata tests for EC2. Httpd and valgrind are requested + # directly by the configuration we set, and libxslt.bin is used indirectly as a build dependency + # of the derivation for dbus configuration files. + apacheHttpd valgrind.doc libxslt.bin + ]; + } + ]; + }).config.system.build.amazonImage; + + makeEc2Test = { name, userData, script, hostname ? "ec2-instance", sshPublicKey ? null }: + let + metaData = pkgs.stdenv.mkDerivation { + name = "metadata"; + buildCommand = '' + mkdir -p $out/1.0/meta-data + ln -s ${pkgs.writeText "userData" userData} $out/1.0/user-data + echo "${hostname}" > $out/1.0/meta-data/hostname + echo "(unknown)" > $out/1.0/meta-data/ami-manifest-path + '' + optionalString (sshPublicKey != null) '' + mkdir -p $out/1.0/meta-data/public-keys/0 + ln -s ${pkgs.writeText "sshPublicKey" sshPublicKey} $out/1.0/meta-data/public-keys/0/openssh-key + ''; + }; + in makeTest { + name = "ec2-" + name; + nodes = {}; + testScript = + '' + my $imageDir = ($ENV{'TMPDIR'} // "/tmp") . "/vm-state-machine"; + mkdir $imageDir, 0700; + my $diskImage = "$imageDir/machine.qcow2"; + system("qemu-img create -f qcow2 -o backing_file=${image}/nixos.qcow2 $diskImage") == 0 or die; + system("qemu-img resize $diskImage 10G") == 0 or die; + + # Note: we use net=169.0.0.0/8 rather than + # net=169.254.0.0/16 to prevent dhcpcd from getting horribly + # confused. (It would get a DHCP lease in the 169.254.* + # range, which it would then configure and prompty delete + # again when it deletes link-local addresses.) Ideally we'd + # turn off the DHCP server, but qemu does not have an option + # to do that. + my $startCommand = "qemu-kvm -m 768"; + $startCommand .= " -device virtio-net-pci,netdev=vlan0"; + $startCommand .= " -netdev 'user,id=vlan0,net=169.0.0.0/8,guestfwd=tcp:169.254.169.254:80-cmd:${pkgs.micro-httpd}/bin/micro_httpd ${metaData}'"; + $startCommand .= " -drive file=$diskImage,if=virtio,werror=report"; + $startCommand .= " \$QEMU_OPTS"; + + my $machine = createMachine({ startCommand => $startCommand }); + + ${script} + ''; + }; + + snakeOilPrivateKey = '' + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACDEPmwZv5dDPrMUaq0dDP+6eBTTe+QNrz14KBEIdhHd1QAAAJDufJ4S7nye + EgAAAAtzc2gtZWQyNTUxOQAAACDEPmwZv5dDPrMUaq0dDP+6eBTTe+QNrz14KBEIdhHd1Q + AAAECgwbDlYATM5/jypuptb0GF/+zWZcJfoVIFBG3LQeRyGsQ+bBm/l0M+sxRqrR0M/7p4 + FNN75A2vPXgoEQh2Ed3VAAAADEVDMiB0ZXN0IGtleQE= + -----END OPENSSH PRIVATE KEY----- + ''; + + snakeOilPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMQ+bBm/l0M+sxRqrR0M/7p4FNN75A2vPXgoEQh2Ed3V EC2 test key"; + +in { + boot-ec2-nixops = makeEc2Test { + name = "nixops-userdata"; + sshPublicKey = snakeOilPublicKey; # That's right folks! My user's key is also the host key! + + userData = '' + SSH_HOST_ED25519_KEY_PUB:${snakeOilPublicKey} + SSH_HOST_ED25519_KEY:${replaceStrings ["\n"] ["|"] snakeOilPrivateKey} + ''; + script = '' + $machine->start; + $machine->waitForFile("/etc/ec2-metadata/user-data"); + $machine->waitForUnit("sshd.service"); + + $machine->succeed("grep unknown /etc/ec2-metadata/ami-manifest-path"); + + # We have no keys configured on the client side yet, so this should fail + $machine->fail("ssh -o BatchMode=yes localhost exit"); + + # Let's install our client private key + $machine->succeed("mkdir -p ~/.ssh"); + + $machine->succeed("echo '${snakeOilPrivateKey}' > ~/.ssh/id_ed25519"); + $machine->succeed("chmod 600 ~/.ssh/id_ed25519"); + + # We haven't configured the host key yet, so this should still fail + $machine->fail("ssh -o BatchMode=yes localhost exit"); + + # Add the host key; ssh should finally succeed + $machine->succeed("echo localhost,127.0.0.1 ${snakeOilPublicKey} > ~/.ssh/known_hosts"); + $machine->succeed("ssh -o BatchMode=yes localhost exit"); + + # Test whether the root disk was resized. + my $blocks = $machine->succeed("stat -c %b -f /"); + my $bsize = $machine->succeed("stat -c %S -f /"); + my $size = $blocks * $bsize; + die "wrong free space $size" if $size < 9.7 * 1024 * 1024 * 1024 || $size > 10 * 1024 * 1024 * 1024; + + # Just to make sure resizing is idempotent. + $machine->shutdown; + $machine->start; + $machine->waitForFile("/etc/ec2-metadata/user-data"); + ''; + }; + + boot-ec2-config = makeEc2Test { + name = "config-userdata"; + sshPublicKey = snakeOilPublicKey; + + # ### http://nixos.org/channels/nixos-unstable nixos + userData = '' + { pkgs, ... }: + + { + imports = [ + <nixpkgs/nixos/modules/virtualisation/amazon-image.nix> + <nixpkgs/nixos/modules/testing/test-instrumentation.nix> + <nixpkgs/nixos/modules/profiles/qemu-guest.nix> + ]; + environment.etc.testFile = { + text = "whoa"; + }; + + services.httpd = { + enable = true; + adminAddr = "test@example.org"; + documentRoot = "${pkgs.valgrind.doc}/share/doc/valgrind/html"; + }; + networking.firewall.allowedTCPPorts = [ 80 ]; + } + ''; + script = '' + $machine->start; + $machine->waitForFile("/etc/testFile"); + $machine->succeed("cat /etc/testFile | grep -q 'whoa'"); + + $machine->waitForUnit("httpd.service"); + $machine->succeed("curl http://localhost | grep Valgrind"); + ''; + }; +} diff --git a/nixpkgs/nixos/tests/ecryptfs.nix b/nixpkgs/nixos/tests/ecryptfs.nix new file mode 100644 index 000000000000..3f02cecb8662 --- /dev/null +++ b/nixpkgs/nixos/tests/ecryptfs.nix @@ -0,0 +1,84 @@ +import ./make-test.nix ({ ... }: +{ + name = "ecryptfs"; + + machine = { pkgs, ... }: { + imports = [ ./common/user-account.nix ]; + boot.kernelModules = [ "ecryptfs" ]; + security.pam.enableEcryptfs = true; + environment.systemPackages = with pkgs; [ keyutils ]; + }; + + testScript = '' + $machine->waitForUnit("default.target"); + + # Set alice up with a password and a home + $machine->succeed("(echo foobar; echo foobar) | passwd alice"); + $machine->succeed("chown -R alice.users ~alice"); + + # Migrate alice's home + my $out = $machine->succeed("echo foobar | ecryptfs-migrate-home -u alice"); + $machine->log("ecryptfs-migrate-home said: $out"); + + # Log alice in (ecryptfs passwhrase is wrapped during first login) + $machine->waitUntilTTYMatches(1, "login: "); + $machine->sendChars("alice\n"); + $machine->waitUntilTTYMatches(1, "Password: "); + $machine->sendChars("foobar\n"); + $machine->waitUntilTTYMatches(1, "alice\@machine"); + $machine->sendChars("logout\n"); + $machine->waitUntilTTYMatches(1, "login: "); + + # Why do I need to do this?? + $machine->succeed("su alice -c ecryptfs-umount-private || true"); + $machine->sleep(1); + $machine->fail("mount | grep ecryptfs"); # check that encrypted home is not mounted + + # Show contents of the user keyring + my $out = $machine->succeed("su - alice -c 'keyctl list \@u'"); + $machine->log("keyctl unlink said: " . $out); + + # Log alice again + $machine->waitUntilTTYMatches(1, "login: "); + $machine->sendChars("alice\n"); + $machine->waitUntilTTYMatches(1, "Password: "); + $machine->sendChars("foobar\n"); + $machine->waitUntilTTYMatches(1, "alice\@machine"); + + # Create some files in encrypted home + $machine->succeed("su alice -c 'touch ~alice/a'"); + $machine->succeed("su alice -c 'echo c > ~alice/b'"); + + # Logout + $machine->sendChars("logout\n"); + $machine->waitUntilTTYMatches(1, "login: "); + + # Why do I need to do this?? + $machine->succeed("su alice -c ecryptfs-umount-private || true"); + $machine->sleep(1); + + # Check that the filesystem is not accessible + $machine->fail("mount | grep ecryptfs"); + $machine->succeed("su alice -c 'test \! -f ~alice/a'"); + $machine->succeed("su alice -c 'test \! -f ~alice/b'"); + + # Log alice once more + $machine->waitUntilTTYMatches(1, "login: "); + $machine->sendChars("alice\n"); + $machine->waitUntilTTYMatches(1, "Password: "); + $machine->sendChars("foobar\n"); + $machine->waitUntilTTYMatches(1, "alice\@machine"); + + # Check that the files are there + $machine->sleep(1); + $machine->succeed("su alice -c 'test -f ~alice/a'"); + $machine->succeed("su alice -c 'test -f ~alice/b'"); + $machine->succeed(qq%test "\$(cat ~alice/b)" = "c"%); + + # Catch https://github.com/NixOS/nixpkgs/issues/16766 + $machine->succeed("su alice -c 'ls -lh ~alice/'"); + + $machine->sendChars("logout\n"); + $machine->waitUntilTTYMatches(1, "login: "); + ''; +}) diff --git a/nixpkgs/nixos/tests/elk.nix b/nixpkgs/nixos/tests/elk.nix new file mode 100644 index 000000000000..d787ac973005 --- /dev/null +++ b/nixpkgs/nixos/tests/elk.nix @@ -0,0 +1,152 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; }, + enableUnfree ? false +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + esUrl = "http://localhost:9200"; + + mkElkTest = name : elk : makeTest { + inherit name; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow offline basvandijk ]; + }; + nodes = { + one = + { pkgs, ... }: { + # Not giving the machine at least 2060MB results in elasticsearch failing with the following error: + # + # OpenJDK 64-Bit Server VM warning: + # INFO: os::commit_memory(0x0000000085330000, 2060255232, 0) + # failed; error='Cannot allocate memory' (errno=12) + # + # There is insufficient memory for the Java Runtime Environment to continue. + # Native memory allocation (mmap) failed to map 2060255232 bytes for committing reserved memory. + # + # When setting this to 2500 I got "Kernel panic - not syncing: Out of + # memory: compulsory panic_on_oom is enabled" so lets give it even a + # bit more room: + virtualisation.memorySize = 3000; + + # For querying JSON objects returned from elasticsearch and kibana. + environment.systemPackages = [ pkgs.jq ]; + + services = { + logstash = { + enable = true; + package = elk.logstash; + inputConfig = '' + exec { command => "echo -n flowers" interval => 1 type => "test" } + exec { command => "echo -n dragons" interval => 1 type => "test" } + ''; + filterConfig = '' + if [message] =~ /dragons/ { + drop {} + } + ''; + outputConfig = '' + file { + path => "/tmp/logstash.out" + codec => line { format => "%{message}" } + } + elasticsearch { + hosts => [ "${esUrl}" ] + } + ''; + }; + + elasticsearch = { + enable = true; + package = elk.elasticsearch; + }; + + kibana = { + enable = true; + package = elk.kibana; + elasticsearch.url = esUrl; + }; + + elasticsearch-curator = { + enable = true; + actionYAML = '' + --- + actions: + 1: + action: delete_indices + description: >- + Delete indices older than 1 second (based on index name), for logstash- + prefixed indices. Ignore the error if the filter does not result in an + actionable list of indices (ignore_empty_list) and exit cleanly. + options: + ignore_empty_list: True + disable_action: False + filters: + - filtertype: pattern + kind: prefix + value: logstash- + - filtertype: age + source: name + direction: older + timestring: '%Y.%m.%d' + unit: seconds + unit_count: 1 + ''; + }; + }; + }; + }; + + testScript = '' + startAll; + + $one->waitForUnit("elasticsearch.service"); + + # Continue as long as the status is not "red". The status is probably + # "yellow" instead of "green" because we are using a single elasticsearch + # node which elasticsearch considers risky. + # + # TODO: extend this test with multiple elasticsearch nodes and see if the status turns "green". + $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_cluster/health' | jq .status | grep -v red"); + + # Perform some simple logstash tests. + $one->waitForUnit("logstash.service"); + $one->waitUntilSucceeds("cat /tmp/logstash.out | grep flowers"); + $one->waitUntilSucceeds("cat /tmp/logstash.out | grep -v dragons"); + + # See if kibana is healthy. + $one->waitForUnit("kibana.service"); + $one->waitUntilSucceeds("curl --silent --show-error 'http://localhost:5601/api/status' | jq .status.overall.state | grep green"); + + # See if logstash messages arive in elasticsearch. + $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"flowers\"}}}' | jq .hits.total | grep -v 0"); + $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"dragons\"}}}' | jq .hits.total | grep 0"); + + # Test elasticsearch-curator. + $one->systemctl("stop logstash"); + $one->systemctl("start elasticsearch-curator"); + $one->waitUntilSucceeds("! curl --silent --show-error '${esUrl}/_cat/indices' | grep logstash | grep -q ^$1"); + ''; + }; +in mapAttrs mkElkTest { + "ELK-5" = { + elasticsearch = pkgs.elasticsearch5; + logstash = pkgs.logstash5; + kibana = pkgs.kibana5; + }; + "ELK-6" = + if enableUnfree + then { + elasticsearch = pkgs.elasticsearch6; + logstash = pkgs.logstash6; + kibana = pkgs.kibana6; + } + else { + elasticsearch = pkgs.elasticsearch6-oss; + logstash = pkgs.logstash6-oss; + kibana = pkgs.kibana6-oss; + }; +} diff --git a/nixpkgs/nixos/tests/emacs-daemon.nix b/nixpkgs/nixos/tests/emacs-daemon.nix new file mode 100644 index 000000000000..3594e35e343c --- /dev/null +++ b/nixpkgs/nixos/tests/emacs-daemon.nix @@ -0,0 +1,45 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "emacs-daemon"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ]; + }; + + enableOCR = true; + + machine = + { ... }: + + { imports = [ ./common/x11.nix ]; + services.emacs = { + enable = true; + defaultEditor = true; + }; + + # Important to get the systemd service running for root + environment.variables.XDG_RUNTIME_DIR = "/run/user/0"; + + environment.variables.TEST_SYSTEM_VARIABLE = "system variable"; + }; + + testScript = + '' + $machine->waitForUnit("multi-user.target"); + + # checks that the EDITOR environment variable is set + $machine->succeed("test \$(basename \"\$EDITOR\") = emacseditor"); + + # waits for the emacs service to be ready + $machine->waitUntilSucceeds("systemctl --user status emacs.service | grep 'Active: active'"); + + # connects to the daemon + $machine->succeed("emacsclient --create-frame \$EDITOR &"); + + # checks that Emacs shows the edited filename + $machine->waitForText("emacseditor"); + + # makes sure environment variables are accessible from Emacs + $machine->succeed("emacsclient --eval '(getenv \"TEST_SYSTEM_VARIABLE\")'") =~ /system variable/ or die; + + $machine->screenshot("emacsclient"); + ''; +}) diff --git a/nixpkgs/nixos/tests/env.nix b/nixpkgs/nixos/tests/env.nix new file mode 100644 index 000000000000..064c498204ae --- /dev/null +++ b/nixpkgs/nixos/tests/env.nix @@ -0,0 +1,35 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "environment"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ nequissimus ]; + }; + + machine = { pkgs, ... }: + { + boot.kernelPackages = pkgs.linuxPackages; + environment.etc."plainFile".text = '' + Hello World + ''; + environment.etc."folder/with/file".text = '' + Foo Bar! + ''; + + environment.sessionVariables = { + TERMINFO_DIRS = "/run/current-system/sw/share/terminfo"; + NIXCON = "awesome"; + }; + }; + + testScript = + '' + $machine->succeed('[ -L "/etc/plainFile" ]'); + $machine->succeed('cat "/etc/plainFile" | grep "Hello World"'); + $machine->succeed('[ -d "/etc/folder" ]'); + $machine->succeed('[ -d "/etc/folder/with" ]'); + $machine->succeed('[ -L "/etc/folder/with/file" ]'); + $machine->succeed('cat "/etc/plainFile" | grep "Hello World"'); + + $machine->succeed('echo ''${TERMINFO_DIRS} | grep "/run/current-system/sw/share/terminfo"'); + $machine->succeed('echo ''${NIXCON} | grep "awesome"'); + ''; +}) diff --git a/nixpkgs/nixos/tests/etcd-cluster.nix b/nixpkgs/nixos/tests/etcd-cluster.nix new file mode 100644 index 000000000000..3c4de5950a79 --- /dev/null +++ b/nixpkgs/nixos/tests/etcd-cluster.nix @@ -0,0 +1,157 @@ +# This test runs simple etcd cluster + +import ./make-test.nix ({ pkgs, ... } : let + + runWithOpenSSL = file: cmd: pkgs.runCommand file { + buildInputs = [ pkgs.openssl ]; + } cmd; + + ca_key = runWithOpenSSL "ca-key.pem" "openssl genrsa -out $out 2048"; + ca_pem = runWithOpenSSL "ca.pem" '' + openssl req \ + -x509 -new -nodes -key ${ca_key} \ + -days 10000 -out $out -subj "/CN=etcd-ca" + ''; + etcd_key = runWithOpenSSL "etcd-key.pem" "openssl genrsa -out $out 2048"; + etcd_csr = runWithOpenSSL "etcd.csr" '' + openssl req \ + -new -key ${etcd_key} \ + -out $out -subj "/CN=etcd" \ + -config ${openssl_cnf} + ''; + etcd_cert = runWithOpenSSL "etcd.pem" '' + openssl x509 \ + -req -in ${etcd_csr} \ + -CA ${ca_pem} -CAkey ${ca_key} \ + -CAcreateserial -out $out \ + -days 365 -extensions v3_req \ + -extfile ${openssl_cnf} + ''; + + etcd_client_key = runWithOpenSSL "etcd-client-key.pem" + "openssl genrsa -out $out 2048"; + + etcd_client_csr = runWithOpenSSL "etcd-client-key.pem" '' + openssl req \ + -new -key ${etcd_client_key} \ + -out $out -subj "/CN=etcd-client" \ + -config ${client_openssl_cnf} + ''; + + etcd_client_cert = runWithOpenSSL "etcd-client.crt" '' + openssl x509 \ + -req -in ${etcd_client_csr} \ + -CA ${ca_pem} -CAkey ${ca_key} -CAcreateserial \ + -out $out -days 365 -extensions v3_req \ + -extfile ${client_openssl_cnf} + ''; + + openssl_cnf = pkgs.writeText "openssl.cnf" '' + ions = v3_req + distinguished_name = req_distinguished_name + [req_distinguished_name] + [ v3_req ] + basicConstraints = CA:FALSE + keyUsage = digitalSignature, keyEncipherment + extendedKeyUsage = serverAuth + subjectAltName = @alt_names + [alt_names] + DNS.1 = node1 + DNS.2 = node2 + DNS.3 = node3 + IP.1 = 127.0.0.1 + ''; + + client_openssl_cnf = pkgs.writeText "client-openssl.cnf" '' + ions = v3_req + distinguished_name = req_distinguished_name + [req_distinguished_name] + [ v3_req ] + basicConstraints = CA:FALSE + keyUsage = digitalSignature, keyEncipherment + extendedKeyUsage = clientAuth + ''; + + nodeConfig = { + services = { + etcd = { + enable = true; + keyFile = etcd_key; + certFile = etcd_cert; + trustedCaFile = ca_pem; + peerClientCertAuth = true; + listenClientUrls = ["https://127.0.0.1:2379"]; + listenPeerUrls = ["https://0.0.0.0:2380"]; + }; + }; + + environment.variables = { + ETCDCTL_CERT_FILE = "${etcd_client_cert}"; + ETCDCTL_KEY_FILE = "${etcd_client_key}"; + ETCDCTL_CA_FILE = "${ca_pem}"; + ETCDCTL_PEERS = "https://127.0.0.1:2379"; + }; + + networking.firewall.allowedTCPPorts = [ 2380 ]; + }; +in { + name = "etcd"; + + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ offline ]; + }; + + nodes = { + node1 = { ... }: { + require = [nodeConfig]; + services.etcd = { + initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380"]; + initialAdvertisePeerUrls = ["https://node1:2380"]; + }; + }; + + node2 = { ... }: { + require = [nodeConfig]; + services.etcd = { + initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380"]; + initialAdvertisePeerUrls = ["https://node2:2380"]; + }; + }; + + node3 = { ... }: { + require = [nodeConfig]; + services.etcd = { + initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380" "node3=https://node3:2380"]; + initialAdvertisePeerUrls = ["https://node3:2380"]; + initialClusterState = "existing"; + }; + }; + }; + + testScript = '' + subtest "should start etcd cluster", sub { + $node1->start(); + $node2->start(); + $node1->waitForUnit("etcd.service"); + $node2->waitForUnit("etcd.service"); + $node2->waitUntilSucceeds("etcdctl cluster-health"); + $node1->succeed("etcdctl set /foo/bar 'Hello world'"); + $node2->succeed("etcdctl get /foo/bar | grep 'Hello world'"); + }; + + subtest "should add another member", sub { + $node1->succeed("etcdctl member add node3 https://node3:2380"); + $node3->start(); + $node3->waitForUnit("etcd.service"); + $node3->waitUntilSucceeds("etcdctl member list | grep 'node3'"); + $node3->succeed("etcdctl cluster-health"); + }; + + subtest "should survive member crash", sub { + $node3->crash; + $node1->succeed("etcdctl cluster-health"); + $node1->succeed("etcdctl set /foo/bar 'Hello degraded world'"); + $node1->succeed("etcdctl get /foo/bar | grep 'Hello degraded world'"); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/etcd.nix b/nixpkgs/nixos/tests/etcd.nix new file mode 100644 index 000000000000..6c23b31779bc --- /dev/null +++ b/nixpkgs/nixos/tests/etcd.nix @@ -0,0 +1,27 @@ +# This test runs simple etcd node + +import ./make-test.nix ({ pkgs, ... } : { + name = "etcd"; + + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ offline ]; + }; + + nodes = { + node = { ... }: { + services.etcd.enable = true; + }; + }; + + testScript = '' + subtest "should start etcd node", sub { + $node->start(); + $node->waitForUnit("etcd.service"); + }; + + subtest "should write and read some values to etcd", sub { + $node->succeed("etcdctl set /foo/bar 'Hello world'"); + $node->succeed("etcdctl get /foo/bar | grep 'Hello world'"); + } + ''; +}) diff --git a/nixpkgs/nixos/tests/ferm.nix b/nixpkgs/nixos/tests/ferm.nix new file mode 100644 index 000000000000..b8e8663e3ad2 --- /dev/null +++ b/nixpkgs/nixos/tests/ferm.nix @@ -0,0 +1,74 @@ + +import ./make-test.nix ({ pkgs, ...} : { + name = "ferm"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ mic92 ]; + }; + + nodes = + { client = + { pkgs, ... }: + with pkgs.lib; + { + networking = { + dhcpcd.enable = false; + interfaces.eth1.ipv6.addresses = mkOverride 0 [ { address = "fd00::2"; prefixLength = 64; } ]; + interfaces.eth1.ipv4.addresses = mkOverride 0 [ { address = "192.168.1.2"; prefixLength = 24; } ]; + }; + }; + server = + { pkgs, ... }: + with pkgs.lib; + { + networking = { + dhcpcd.enable = false; + interfaces.eth1.ipv6.addresses = mkOverride 0 [ { address = "fd00::1"; prefixLength = 64; } ]; + interfaces.eth1.ipv4.addresses = mkOverride 0 [ { address = "192.168.1.1"; prefixLength = 24; } ]; + }; + + services = { + ferm.enable = true; + ferm.config = '' + domain (ip ip6) table filter chain INPUT { + interface lo ACCEPT; + proto tcp dport 8080 REJECT reject-with tcp-reset; + } + ''; + nginx.enable = true; + nginx.httpConfig = '' + server { + listen 80; + listen [::]:80; + listen 8080; + listen [::]:8080; + + location /status { stub_status on; } + } + ''; + }; + }; + }; + + testScript = + '' + startAll; + + $client->waitForUnit("network-online.target"); + $server->waitForUnit("ferm.service"); + $server->waitForUnit("nginx.service"); + $server->waitUntilSucceeds("ss -ntl | grep -q 80"); + + subtest "port 80 is allowed", sub { + $client->succeed("curl --fail -g http://192.168.1.1:80/status"); + $client->succeed("curl --fail -g http://[fd00::1]:80/status"); + }; + + subtest "port 8080 is not allowed", sub { + $server->succeed("curl --fail -g http://192.168.1.1:8080/status"); + $server->succeed("curl --fail -g http://[fd00::1]:8080/status"); + + $client->fail("curl --fail -g http://192.168.1.1:8080/status"); + $client->fail("curl --fail -g http://[fd00::1]:8080/status"); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/firefox.nix b/nixpkgs/nixos/tests/firefox.nix new file mode 100644 index 000000000000..58a80243ea9c --- /dev/null +++ b/nixpkgs/nixos/tests/firefox.nix @@ -0,0 +1,29 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "firefox"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow shlevy ]; + }; + + machine = + { pkgs, ... }: + + { imports = [ ./common/x11.nix ]; + environment.systemPackages = [ pkgs.firefox pkgs.xdotool ]; + }; + + testScript = + '' + $machine->waitForX; + $machine->execute("xterm -e 'firefox file://${pkgs.valgrind.doc}/share/doc/valgrind/html/index.html' &"); + $machine->waitForWindow(qr/Valgrind/); + $machine->sleep(40); # wait until Firefox has finished loading the page + $machine->execute("xdotool key space"); # do I want to make Firefox the + # default browser? I just want to close the dialog + $machine->sleep(2); # wait until Firefox hides the default browser window + $machine->execute("xdotool key F12"); + $machine->sleep(10); # wait until Firefox draws the developer tool panel + $machine->succeed("xwininfo -root -tree | grep Valgrind"); + $machine->screenshot("screen"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/firewall.nix b/nixpkgs/nixos/tests/firewall.nix new file mode 100644 index 000000000000..7207a880d8e2 --- /dev/null +++ b/nixpkgs/nixos/tests/firewall.nix @@ -0,0 +1,65 @@ +# Test the firewall module. + +import ./make-test.nix ( { pkgs, ... } : { + name = "firewall"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + nodes = + { walled = + { ... }: + { networking.firewall.enable = true; + networking.firewall.logRefusedPackets = true; + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + }; + + # Dummy configuration to check whether firewall.service will be honored + # during system activation. This only needs to be different to the + # original walled configuration so that there is a change in the service + # file. + walled2 = + { ... }: + { networking.firewall.enable = true; + networking.firewall.rejectPackets = true; + }; + + attacker = + { ... }: + { services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.enable = false; + }; + }; + + testScript = { nodes, ... }: let + newSystem = nodes.walled2.config.system.build.toplevel; + in '' + $walled->start; + $attacker->start; + + $walled->waitForUnit("firewall"); + $walled->waitForUnit("httpd"); + $attacker->waitForUnit("network.target"); + + # Local connections should still work. + $walled->succeed("curl -v http://localhost/ >&2"); + + # Connections to the firewalled machine should fail, but ping should succeed. + $attacker->fail("curl --fail --connect-timeout 2 http://walled/ >&2"); + $attacker->succeed("ping -c 1 walled >&2"); + + # Outgoing connections/pings should still work. + $walled->succeed("curl -v http://attacker/ >&2"); + $walled->succeed("ping -c 1 attacker >&2"); + + # If we stop the firewall, then connections should succeed. + $walled->stopJob("firewall"); + $attacker->succeed("curl -v http://walled/ >&2"); + + # Check whether activation of a new configuration reloads the firewall. + $walled->succeed("${newSystem}/bin/switch-to-configuration test 2>&1" . + " | grep -qF firewall.service"); + ''; +}) diff --git a/nixpkgs/nixos/tests/flannel.nix b/nixpkgs/nixos/tests/flannel.nix new file mode 100644 index 000000000000..fb66fe282090 --- /dev/null +++ b/nixpkgs/nixos/tests/flannel.nix @@ -0,0 +1,55 @@ +import ./make-test.nix ({ pkgs, ...} : rec { + name = "flannel"; + + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ offline ]; + }; + + nodes = let + flannelConfig = { + services.flannel = { + enable = true; + network = "10.1.0.0/16"; + iface = "eth1"; + etcd.endpoints = ["http://etcd:2379"]; + }; + + networking.firewall.allowedUDPPorts = [ 8472 ]; + }; + in { + etcd = { ... }: { + services = { + etcd = { + enable = true; + listenClientUrls = ["http://etcd:2379"]; + listenPeerUrls = ["http://etcd:2380"]; + initialAdvertisePeerUrls = ["http://etcd:2379"]; + initialCluster = ["etcd=http://etcd:2379"]; + }; + }; + + networking.firewall.allowedTCPPorts = [ 2379 ]; + }; + + node1 = { ... }: { + require = [flannelConfig]; + }; + + node2 = { ... }: { + require = [flannelConfig]; + }; + }; + + testScript = '' + startAll; + + $node1->waitForUnit("flannel.service"); + $node2->waitForUnit("flannel.service"); + + my $ip1 = $node1->succeed("ip -4 addr show flannel.1 | grep -oP '(?<=inet).*(?=/)'"); + my $ip2 = $node2->succeed("ip -4 addr show flannel.1 | grep -oP '(?<=inet).*(?=/)'"); + + $node1->waitUntilSucceeds("ping -c 1 $ip2"); + $node2->waitUntilSucceeds("ping -c 1 $ip1"); + ''; +}) diff --git a/nixpkgs/nixos/tests/flatpak.nix b/nixpkgs/nixos/tests/flatpak.nix new file mode 100644 index 000000000000..b0c61830d05a --- /dev/null +++ b/nixpkgs/nixos/tests/flatpak.nix @@ -0,0 +1,26 @@ +# run installed tests +import ./make-test.nix ({ pkgs, ... }: + +{ + name = "flatpak"; + meta = { + maintainers = pkgs.flatpak.meta.maintainers; + }; + + machine = { pkgs, ... }: { + imports = [ ./common/x11.nix ]; + 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; + environment.gnome3.excludePackages = pkgs.gnome3.optionalPackages; + services.flatpak.enable = true; + environment.systemPackages = with pkgs; [ gnupg gnome-desktop-testing ostree python2 ]; + virtualisation.memorySize = 2047; + virtualisation.diskSize = 1024; + }; + + testScript = '' + $machine->waitForX(); + $machine->succeed("gnome-desktop-testing-runner -d '${pkgs.flatpak.installedTests}/share' --timeout 3600"); + ''; +}) diff --git a/nixpkgs/nixos/tests/fsck.nix b/nixpkgs/nixos/tests/fsck.nix new file mode 100644 index 000000000000..f943bb7f2350 --- /dev/null +++ b/nixpkgs/nixos/tests/fsck.nix @@ -0,0 +1,29 @@ +import ./make-test.nix { + name = "fsck"; + + machine = { lib, ... }: { + virtualisation.emptyDiskImages = [ 1 ]; + + fileSystems = lib.mkVMOverride { + "/mnt" = { + device = "/dev/vdb"; + fsType = "ext4"; + autoFormat = true; + }; + }; + }; + + testScript = '' + $machine->waitForUnit('default.target'); + + subtest "root fs is fsckd", sub { + $machine->succeed('journalctl -b | grep "fsck.ext4.*/dev/vda"'); + }; + + subtest "mnt fs is fsckd", sub { + $machine->succeed('journalctl -b | grep "fsck.*/dev/vdb.*clean"'); + $machine->succeed('grep "Requires=systemd-fsck@dev-vdb.service" /run/systemd/generator/mnt.mount'); + $machine->succeed('grep "After=systemd-fsck@dev-vdb.service" /run/systemd/generator/mnt.mount'); + }; + ''; +} diff --git a/nixpkgs/nixos/tests/fwupd.nix b/nixpkgs/nixos/tests/fwupd.nix new file mode 100644 index 000000000000..88dac8ccbcdb --- /dev/null +++ b/nixpkgs/nixos/tests/fwupd.nix @@ -0,0 +1,21 @@ +# run installed tests +import ./make-test.nix ({ pkgs, ... }: { + name = "fwupd"; + + meta = { + maintainers = pkgs.fwupd.meta.maintainers; + }; + + machine = { pkgs, ... }: { + services.fwupd.enable = true; + services.fwupd.blacklistPlugins = []; # don't blacklist test plugin + services.fwupd.enableTestRemote = true; + environment.systemPackages = with pkgs; [ gnome-desktop-testing ]; + environment.variables.XDG_DATA_DIRS = [ "${pkgs.fwupd.installedTests}/share" ]; + virtualisation.memorySize = 768; + }; + + testScript = '' + $machine->succeed("gnome-desktop-testing-runner"); + ''; +}) diff --git a/nixpkgs/nixos/tests/gdk-pixbuf.nix b/nixpkgs/nixos/tests/gdk-pixbuf.nix new file mode 100644 index 000000000000..005c5111da2b --- /dev/null +++ b/nixpkgs/nixos/tests/gdk-pixbuf.nix @@ -0,0 +1,21 @@ +# run installed tests +import ./make-test.nix ({ pkgs, ... }: { + name = "gdk-pixbuf"; + + meta = { + maintainers = pkgs.gdk_pixbuf.meta.maintainers; + }; + + machine = { pkgs, ... }: { + environment.systemPackages = with pkgs; [ gnome-desktop-testing ]; + environment.variables.XDG_DATA_DIRS = [ "${pkgs.gdk_pixbuf.installedTests}/share" ]; + + # Tests allocate a lot of memory trying to exploit a CVE + # but qemu-system-i386 has a 2047M memory limit + virtualisation.memorySize = if pkgs.stdenv.isi686 then 2047 else 4096; + }; + + testScript = '' + $machine->succeed("gnome-desktop-testing-runner -t 1800"); # increase timeout to 1800s + ''; +}) diff --git a/nixpkgs/nixos/tests/gitea.nix b/nixpkgs/nixos/tests/gitea.nix new file mode 100644 index 000000000000..28e6479e9cbe --- /dev/null +++ b/nixpkgs/nixos/tests/gitea.nix @@ -0,0 +1,79 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +{ + mysql = makeTest { + name = "gitea-mysql"; + meta.maintainers = [ maintainers.aanderse ]; + + machine = + { config, pkgs, ... }: + { services.mysql.enable = true; + services.mysql.package = pkgs.mariadb; + services.mysql.ensureDatabases = [ "gitea" ]; + services.mysql.ensureUsers = [ + { name = "gitea"; + ensurePermissions = { "gitea.*" = "ALL PRIVILEGES"; }; + } + ]; + + services.gitea.enable = true; + services.gitea.database.type = "mysql"; + services.gitea.database.socket = "/run/mysqld/mysqld.sock"; + }; + + testScript = '' + startAll; + + $machine->waitForUnit('gitea.service'); + $machine->waitForOpenPort('3000'); + $machine->succeed("curl --fail http://localhost:3000/"); + ''; + }; + + postgres = makeTest { + name = "gitea-postgres"; + meta.maintainers = [ maintainers.aanderse ]; + + machine = + { config, pkgs, ... }: + { + services.gitea.enable = true; + services.gitea.database.type = "postgres"; + services.gitea.database.password = "secret"; + }; + + testScript = '' + startAll; + + $machine->waitForUnit('gitea.service'); + $machine->waitForOpenPort('3000'); + $machine->succeed("curl --fail http://localhost:3000/"); + ''; + }; + + sqlite = makeTest { + name = "gitea-sqlite"; + meta.maintainers = [ maintainers.aanderse ]; + + machine = + { config, pkgs, ... }: + { services.gitea.enable = true; + services.gitea.disableRegistration = true; + }; + + testScript = '' + startAll; + + $machine->waitForUnit('gitea.service'); + $machine->waitForOpenPort('3000'); + $machine->succeed("curl --fail http://localhost:3000/"); + $machine->succeed("curl --fail http://localhost:3000/user/sign_up | grep 'Registration is disabled. Please contact your site administrator.'"); + ''; + }; +} diff --git a/nixpkgs/nixos/tests/gitlab.nix b/nixpkgs/nixos/tests/gitlab.nix new file mode 100644 index 000000000000..16e0dd723ecf --- /dev/null +++ b/nixpkgs/nixos/tests/gitlab.nix @@ -0,0 +1,83 @@ +# This test runs gitlab and checks if it works + +import ./make-test.nix ({ pkgs, lib, ...} : with lib; { + name = "gitlab"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ globin ]; + }; + + nodes = { + gitlab = { ... }: { + virtualisation.memorySize = if pkgs.stdenv.is64bit then 4096 else 2047; + systemd.services.gitlab.serviceConfig.Restart = mkForce "no"; + systemd.services.gitlab-workhorse.serviceConfig.Restart = mkForce "no"; + systemd.services.gitaly.serviceConfig.Restart = mkForce "no"; + systemd.services.gitlab-sidekiq.serviceConfig.Restart = mkForce "no"; + + services.nginx = { + enable = true; + recommendedProxySettings = true; + virtualHosts = { + "localhost" = { + locations."/".proxyPass = "http://unix:/run/gitlab/gitlab-workhorse.socket"; + }; + }; + }; + + services.gitlab = { + enable = true; + databasePassword = "dbPassword"; + initialRootPassword = "notproduction"; + smtp.enable = true; + secrets = { + secret = "secret"; + otp = "otpsecret"; + db = "dbsecret"; + + # nix-shell -p openssl --run "openssl genrsa 2048" + jws = '' + -----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEA13/qEio76OWUtWO0WIz9lWnsTWOU8Esv4sQHDq9PCEFsLt21 + PAXrlWhLjjWcxGfsrDwnh7YErGHYL62BMSxMdFJolaknlQK/O/V8UETDe45VoHM+ + Znk270RfUcfYFgiihnXUZXVmL0om9TsQSk646wCcjCY9LxtxUyKNhvT7KjgYw2aX + z34aw7M+Js3T2p1TjZPSC82GtmtKkJEKFMi5EjprLTDE7EdcUzr9Xuw+kQ+gRm9k + 7FE+JQqSoprwE3Q0v2OAn3UhLMgg0gNFRnsc5l6IAshDzV+H22RPqKKlJjVjjfPY + 0TQSvYLVApigHbDPH0BoCXfjFfQazbbP3OUHrwIDAQABAoIBAQCMU+tkcMQaYIV5 + qLdjgkwO467QpivyXcOM8wF1eosIYTHFQvIlZ+WEoSmyLQ8shlADyBgls01Pw1c3 + lNAv6RzQEmmwKzpvOh61OKH+0whIiOMRXHoh2IUBQZCgfHYlwvGyhUAN4WjtGmhM + AG4XNTQNM5S9Xpkw97nP3Qwz+YskbbkrfqtCEVy9ro+4nhbjqPsuO3adbnkva4zR + cyurRhrHgHU6LPjn5NHnHH4qw2faY2oAsL8pmpkTbO5IqWDvOcbjNfjVPgVoq26O + bbaa1qs4nmc80qQgMjRPJef535xyf3eLsSlDvpf6O8sPrJzVR1zaqEqixpQCZDac + +kRiSBrhAoGBAOwHiq0PuyJh6VzBu7ybqX6+gF/wA4Jkwzx6mbfaBgurvU1aospp + kisIonAkxSbxllZMnjbkShZEdATYKeT9o5NEhnU4YnHfc5bJZbiWOZAzYGLcY7g8 + vDQ31pBItyY4pFgPbSpNlbUvUsoPVJ45RasRADDTNCzMzdjFQQXst2V9AoGBAOm7 + sSpzYfFPLEAhieAkuhtbsX58Boo46djiKVfzGftfp6F9aHTOfzGORU5jrZ16mSbS + qkkC6BEFrATX2051dzzXC89fWoJYALrsffE5I3KlKXsCAWSnCP1MMxOfH+Ls61Mr + 7pK/LKfvJt53mUH4jIdbmmFUDwbg18oBEH+x9PmbAoGAS/+JqXu9N67rIxDGUE6W + 3tacI0f2+U9Uhe67/DTZaXyc8YFTlXU0uWKIWy+bw5RaYeM9tlL/f/f+m2i25KK+ + vrZ7zNag7CWU5GJovGyykDnauTpZaYM03mN0VPT08/uc/zXIYqyknbhlIeaZynCK + fDB3LUF0NVCknz20WCIGU0kCgYEAkxY0ZXx61Dp4pFr2wwEZxQGs7uXpz64FKyEX + 12r6nMATY4Lh6y/Px0W6w5vis8lk+5Ny6cNUevHQ0LNuJS+yu6ywl+1vrbrnqroM + f3LvpcPeGLSoX8jl1VDQi7aFgG6LoKly1xJLbdsH4NPutB9PgBbbTghx9GgmI88L + rPA2M6UCgYBOmkYJocNgxg6B1/n4Tb9fN1Q/XuJrFDE6NxVUoke+IIyMPRH7FC3m + VMYzu+b7zTVJjaBb1cmJemxl/xajziWDofJYPefhdbOVU7HXtmJFY0IG3pVxU1zW + 3bmDj5QAtCUDpuuNa6GEIT0YR4+D/V7o3DmlZ0tVIwKJmVJoQ2f5dw== + -----END RSA PRIVATE KEY----- + ''; + }; + }; + }; + }; + + testScript = '' + $gitlab->start(); + $gitlab->waitForUnit("gitaly.service"); + $gitlab->waitForUnit("gitlab-workhorse.service"); + $gitlab->waitForUnit("gitlab.service"); + $gitlab->waitForUnit("gitlab-sidekiq.service"); + $gitlab->waitForFile("/var/gitlab/state/tmp/sockets/gitlab.socket"); + $gitlab->waitUntilSucceeds("curl -sSf http://gitlab/users/sign_in"); + $gitlab->succeed("curl -isSf http://gitlab | grep -i location | grep -q http://gitlab/users/sign_in"); + $gitlab->succeed("${pkgs.sudo}/bin/sudo -u gitlab -H gitlab-rake gitlab:check 1>&2") + ''; +}) diff --git a/nixpkgs/nixos/tests/gitolite.nix b/nixpkgs/nixos/tests/gitolite.nix new file mode 100644 index 000000000000..690e456ed7c8 --- /dev/null +++ b/nixpkgs/nixos/tests/gitolite.nix @@ -0,0 +1,139 @@ +import ./make-test.nix ({ pkgs, ...}: + +let + adminPrivateKey = pkgs.writeText "id_ed25519" '' + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACDu7qxYQAPdAU6RrhB3llk2N1v4PTwcVzcX1oX265uC3gAAAJBJiYxDSYmM + QwAAAAtzc2gtZWQyNTUxOQAAACDu7qxYQAPdAU6RrhB3llk2N1v4PTwcVzcX1oX265uC3g + AAAEDE1W6vMwSEUcF1r7Hyypm/+sCOoDmKZgPxi3WOa1mD2u7urFhAA90BTpGuEHeWWTY3 + W/g9PBxXNxfWhfbrm4LeAAAACGJmb0BtaW5pAQIDBAU= + -----END OPENSSH PRIVATE KEY----- + ''; + + adminPublicKey = '' + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO7urFhAA90BTpGuEHeWWTY3W/g9PBxXNxfWhfbrm4Le root@client + ''; + + alicePrivateKey = pkgs.writeText "id_ed25519" '' + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACBbeWvHh/AWGWI6EIc1xlSihyXtacNQ9KeztlW/VUy8wQAAAJAwVQ5VMFUO + VQAAAAtzc2gtZWQyNTUxOQAAACBbeWvHh/AWGWI6EIc1xlSihyXtacNQ9KeztlW/VUy8wQ + AAAEB7lbfkkdkJoE+4TKHPdPQWBKLSx+J54Eg8DaTr+3KoSlt5a8eH8BYZYjoQhzXGVKKH + Je1pw1D0p7O2Vb9VTLzBAAAACGJmb0BtaW5pAQIDBAU= + -----END OPENSSH PRIVATE KEY----- + ''; + + alicePublicKey = pkgs.writeText "id_ed25519.pub" '' + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFt5a8eH8BYZYjoQhzXGVKKHJe1pw1D0p7O2Vb9VTLzB alice@client + ''; + + bobPrivateKey = pkgs.writeText "id_ed25519" '' + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACCWTaJ1D9Xjxy6759FvQ9oXTes1lmWBciXPkEeqTikBMAAAAJDQBmNV0AZj + VQAAAAtzc2gtZWQyNTUxOQAAACCWTaJ1D9Xjxy6759FvQ9oXTes1lmWBciXPkEeqTikBMA + AAAEDM1IYYFUwk/IVxauha9kuR6bbRtT3gZ6ZA0GLb9txb/pZNonUP1ePHLrvn0W9D2hdN + 6zWWZYFyJc+QR6pOKQEwAAAACGJmb0BtaW5pAQIDBAU= + -----END OPENSSH PRIVATE KEY----- + ''; + + bobPublicKey = pkgs.writeText "id_ed25519.pub" '' + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJZNonUP1ePHLrvn0W9D2hdN6zWWZYFyJc+QR6pOKQEw bob@client + ''; + + gitoliteAdminConfSnippet = '' + repo alice-project + RW+ = alice + ''; +in +{ + name = "gitolite"; + + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ bjornfor ]; + }; + + nodes = { + + server = + { ... }: + { + services.gitolite = { + enable = true; + adminPubkey = adminPublicKey; + }; + services.openssh.enable = true; + }; + + client = + { pkgs, ... }: + { + environment.systemPackages = [ pkgs.git ]; + programs.ssh.extraConfig = '' + Host * + UserKnownHostsFile /dev/null + StrictHostKeyChecking no + # there's nobody around that can input password + PreferredAuthentications publickey + ''; + users.users.alice = { isNormalUser = true; }; + users.users.bob = { isNormalUser = true; }; + }; + + }; + + testScript = '' + startAll; + + subtest "can setup ssh keys on system", sub { + $client->mustSucceed("mkdir -p ~root/.ssh"); + $client->mustSucceed("cp ${adminPrivateKey} ~root/.ssh/id_ed25519"); + $client->mustSucceed("chmod 600 ~root/.ssh/id_ed25519"); + + $client->mustSucceed("sudo -u alice mkdir -p ~alice/.ssh"); + $client->mustSucceed("sudo -u alice cp ${alicePrivateKey} ~alice/.ssh/id_ed25519"); + $client->mustSucceed("sudo -u alice chmod 600 ~alice/.ssh/id_ed25519"); + + $client->mustSucceed("sudo -u bob mkdir -p ~bob/.ssh"); + $client->mustSucceed("sudo -u bob cp ${bobPrivateKey} ~bob/.ssh/id_ed25519"); + $client->mustSucceed("sudo -u bob chmod 600 ~bob/.ssh/id_ed25519"); + }; + + subtest "gitolite server starts", sub { + $server->waitForUnit("gitolite-init.service"); + $server->waitForUnit("sshd.service"); + $client->mustSucceed('ssh gitolite@server info'); + }; + + subtest "admin can clone and configure gitolite-admin.git", sub { + $client->mustSucceed('git clone gitolite@server:gitolite-admin.git'); + $client->mustSucceed("git config --global user.name 'System Administrator'"); + $client->mustSucceed("git config --global user.email root\@domain.example"); + $client->mustSucceed("cp ${alicePublicKey} gitolite-admin/keydir/alice.pub"); + $client->mustSucceed("cp ${bobPublicKey} gitolite-admin/keydir/bob.pub"); + $client->mustSucceed('(cd gitolite-admin && git add . && git commit -m "Add keys for alice, bob" && git push)'); + $client->mustSucceed("printf '${gitoliteAdminConfSnippet}' >> gitolite-admin/conf/gitolite.conf"); + $client->mustSucceed('(cd gitolite-admin && git add . && git commit -m "Add repo for alice" && git push)'); + }; + + subtest "non-admins cannot clone gitolite-admin.git", sub { + $client->mustFail('sudo -i -u alice git clone gitolite@server:gitolite-admin.git'); + $client->mustFail('sudo -i -u bob git clone gitolite@server:gitolite-admin.git'); + }; + + subtest "non-admins can clone testing.git", sub { + $client->mustSucceed('sudo -i -u alice git clone gitolite@server:testing.git'); + $client->mustSucceed('sudo -i -u bob git clone gitolite@server:testing.git'); + }; + + subtest "alice can clone alice-project.git", sub { + $client->mustSucceed('sudo -i -u alice git clone gitolite@server:alice-project.git'); + }; + + subtest "bob cannot clone alice-project.git", sub { + $client->mustFail('sudo -i -u bob git clone gitolite@server:alice-project.git'); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/gjs.nix b/nixpkgs/nixos/tests/gjs.nix new file mode 100644 index 000000000000..e6002ef98dd0 --- /dev/null +++ b/nixpkgs/nixos/tests/gjs.nix @@ -0,0 +1,19 @@ +# run installed tests +import ./make-test.nix ({ pkgs, ... }: { + name = "gjs"; + + meta = { + maintainers = pkgs.gnome3.gjs.meta.maintainers; + }; + + machine = { pkgs, ... }: { + imports = [ ./common/x11.nix ]; + environment.systemPackages = with pkgs; [ gnome-desktop-testing ]; + environment.variables.XDG_DATA_DIRS = [ "${pkgs.gnome3.gjs.installedTests}/share" ]; + }; + + testScript = '' + $machine->waitForX; + $machine->succeed("gnome-desktop-testing-runner"); + ''; +}) diff --git a/nixpkgs/nixos/tests/gnome3-gdm.nix b/nixpkgs/nixos/tests/gnome3-gdm.nix new file mode 100644 index 000000000000..c2808d87d99d --- /dev/null +++ b/nixpkgs/nixos/tests/gnome3-gdm.nix @@ -0,0 +1,63 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "gnome3-gdm"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ lethalman ]; + }; + + machine = + { ... }: + + { imports = [ ./common/user-account.nix ]; + + services.xserver.enable = true; + + services.xserver.displayManager.gdm = { + enable = true; + autoLogin = { + enable = true; + user = "alice"; + }; + }; + services.xserver.desktopManager.gnome3.enable = true; + + virtualisation.memorySize = 1024; + }; + + testScript = let + # Keep line widths somewhat managable + bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus"; + gdbus = "${bus} gdbus"; + # Call javascript in gnome shell, returns a tuple (success, output), where + # `success` is true if the dbus call was successful and output is what the + # javascript evaluates to. + eval = "call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval"; + # False when startup is done + startingUp = "${gdbus} ${eval} Main.layoutManager._startingUp"; + # Hopefully gnome-terminal's wm class + wmClass = "${gdbus} ${eval} global.display.focus_window.wm_class"; + in '' + # wait for gdm to start + $machine->waitForUnit("display-manager.service"); + + # wait for alice to be logged in + $machine->waitForUnit("default.target","alice"); + + # Check that logging in has given the user ownership of devices. + $machine->succeed("getfacl /dev/snd/timer | grep -q alice"); + + # Wait for the wayland server + $machine->waitForFile("/run/user/1000/wayland-0"); + + # Wait for gnome shell, correct output should be "(true, 'false')" + $machine->waitUntilSucceeds("su - alice -c '${startingUp} | grep -q true,..false'"); + + # open a terminal + $machine->succeed("su - alice -c '${bus} gnome-terminal'"); + # and check it's there + $machine->waitUntilSucceeds("su - alice -c '${wmClass} | grep -q gnome-terminal-server'"); + + # wait to get a nice screenshot + $machine->sleep(20); + $machine->screenshot("screen"); + ''; +}) diff --git a/nixpkgs/nixos/tests/gnome3.nix b/nixpkgs/nixos/tests/gnome3.nix new file mode 100644 index 000000000000..95694ea4828d --- /dev/null +++ b/nixpkgs/nixos/tests/gnome3.nix @@ -0,0 +1,41 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "gnome3"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ domenkozar eelco chaoflow lethalman ]; + }; + + machine = + { ... }: + + { imports = [ ./common/user-account.nix ]; + + services.xserver.enable = true; + + services.xserver.displayManager.gdm.enable = false; + services.xserver.displayManager.lightdm.enable = true; + services.xserver.displayManager.lightdm.autoLogin.enable = true; + services.xserver.displayManager.lightdm.autoLogin.user = "alice"; + services.xserver.desktopManager.gnome3.enable = true; + services.xserver.desktopManager.default = "gnome-xorg"; + + virtualisation.memorySize = 1024; + }; + + testScript = + '' + $machine->waitForX; + + # wait for alice to be logged in + $machine->waitForUnit("default.target","alice"); + + # Check that logging in has given the user ownership of devices. + $machine->succeed("getfacl /dev/snd/timer | grep -q alice"); + + $machine->succeed("su - alice -c 'DISPLAY=:0.0 gnome-terminal &'"); + $machine->succeed("xauth merge ~alice/.Xauthority"); + $machine->waitForWindow(qr/alice.*machine/); + $machine->succeed("timeout 900 bash -c 'while read msg; do if [[ \$msg =~ \"GNOME Shell started\" ]]; then break; fi; done < <(journalctl -f)'"); + $machine->sleep(10); + $machine->screenshot("screen"); + ''; +}) diff --git a/nixpkgs/nixos/tests/gocd-agent.nix b/nixpkgs/nixos/tests/gocd-agent.nix new file mode 100644 index 000000000000..50470379576b --- /dev/null +++ b/nixpkgs/nixos/tests/gocd-agent.nix @@ -0,0 +1,40 @@ +# verifies: +# 1. GoCD agent starts +# 2. GoCD agent responds +# 3. GoCD agent is available on GoCD server using GoCD API +# 3.1. https://api.go.cd/current/#get-all-agents + +let + serverUrl = "localhost:8153/go/api/agents"; + header = "Accept: application/vnd.go.cd.v2+json"; +in + +import ./make-test.nix ({ pkgs, ...} : { + name = "gocd-agent"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ grahamc swarren83 ]; + }; + + nodes = { + gocd_agent = + { ... }: + { + virtualisation.memorySize = 2046; + services.gocd-agent = { + enable = true; + }; + services.gocd-server = { + enable = true; + }; + }; + }; + + testScript = '' + startAll; + $gocd_agent->waitForUnit("gocd-server"); + $gocd_agent->waitForOpenPort("8153"); + $gocd_agent->waitForUnit("gocd-agent"); + $gocd_agent->waitUntilSucceeds("curl ${serverUrl} -H '${header}' | ${pkgs.jq}/bin/jq -e ._embedded.agents[0].uuid"); + $gocd_agent->succeed("curl ${serverUrl} -H '${header}' | ${pkgs.jq}/bin/jq -e ._embedded.agents[0].agent_state | grep -q Idle"); + ''; +}) diff --git a/nixpkgs/nixos/tests/gocd-server.nix b/nixpkgs/nixos/tests/gocd-server.nix new file mode 100644 index 000000000000..80cf04ed6404 --- /dev/null +++ b/nixpkgs/nixos/tests/gocd-server.nix @@ -0,0 +1,28 @@ +# verifies: +# 1. GoCD server starts +# 2. GoCD server responds + +import ./make-test.nix ({ pkgs, ...} : + +{ + name = "gocd-server"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ swarren83 ]; + }; + +nodes = { + gocd_server = + { ... }: + { + virtualisation.memorySize = 2046; + services.gocd-server.enable = true; + }; +}; + + testScript = '' + $gocd_server->start; + $gocd_server->waitForUnit("gocd-server"); + $gocd_server->waitForOpenPort("8153"); + $gocd_server->waitUntilSucceeds("curl -s -f localhost:8153/go"); + ''; +}) diff --git a/nixpkgs/nixos/tests/google-oslogin/default.nix b/nixpkgs/nixos/tests/google-oslogin/default.nix new file mode 100644 index 000000000000..3b84bba3f985 --- /dev/null +++ b/nixpkgs/nixos/tests/google-oslogin/default.nix @@ -0,0 +1,52 @@ +import ../make-test.nix ({ pkgs, ... } : +let + inherit (import ./../ssh-keys.nix pkgs) + snakeOilPrivateKey snakeOilPublicKey; +in { + name = "google-oslogin"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ adisbladis flokli ]; + }; + + nodes = { + # the server provides both the the mocked google metadata server and the ssh server + server = (import ./server.nix pkgs); + + client = { ... }: {}; + }; + testScript = '' + startAll; + + $server->waitForUnit("mock-google-metadata.service"); + $server->waitForOpenPort(80); + + # mockserver should return a non-expired ssh key for both mockuser and mockadmin + $server->succeed('${pkgs.google-compute-engine-oslogin}/bin/google_authorized_keys mockuser | grep -q "${snakeOilPublicKey}"'); + $server->succeed('${pkgs.google-compute-engine-oslogin}/bin/google_authorized_keys mockadmin | grep -q "${snakeOilPublicKey}"'); + + # install snakeoil ssh key on the client + $client->succeed("mkdir -p ~/.ssh"); + $client->succeed("cat ${snakeOilPrivateKey} > ~/.ssh/id_snakeoil"); + $client->succeed("chmod 600 ~/.ssh/id_snakeoil"); + + $client->waitForUnit("network.target"); + $server->waitForUnit("sshd.service"); + + # we should not be able to connect as non-existing user + $client->fail("ssh -o User=ghost -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server -i ~/.ssh/id_snakeoil 'true'"); + + # we should be able to connect as mockuser + $client->succeed("ssh -o User=mockuser -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server -i ~/.ssh/id_snakeoil 'true'"); + # but we shouldn't be able to sudo + $client->fail("ssh -o User=mockuser -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server -i ~/.ssh/id_snakeoil '/run/wrappers/bin/sudo /run/current-system/sw/bin/id' | grep -q 'root'"); + + # we should also be able to log in as mockadmin + $client->succeed("ssh -o User=mockadmin -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server -i ~/.ssh/id_snakeoil 'true'"); + # pam_oslogin_admin.so should now have generated a sudoers file + $server->succeed("find /run/google-sudoers.d | grep -q '/run/google-sudoers.d/mockadmin'"); + + # and we should be able to sudo + $client->succeed("ssh -o User=mockadmin -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server -i ~/.ssh/id_snakeoil '/run/wrappers/bin/sudo /run/current-system/sw/bin/id' | grep -q 'root'"); + ''; + }) + diff --git a/nixpkgs/nixos/tests/google-oslogin/server.nix b/nixpkgs/nixos/tests/google-oslogin/server.nix new file mode 100644 index 000000000000..fdb7141da317 --- /dev/null +++ b/nixpkgs/nixos/tests/google-oslogin/server.nix @@ -0,0 +1,29 @@ +{ pkgs, ... }: +let + inherit (import ./../ssh-keys.nix pkgs) + snakeOilPrivateKey snakeOilPublicKey; +in { + networking.firewall.allowedTCPPorts = [ 80 ]; + + systemd.services.mock-google-metadata = { + description = "Mock Google metadata service"; + serviceConfig.Type = "simple"; + serviceConfig.ExecStart = "${pkgs.python3}/bin/python ${./server.py}"; + environment = { + SNAKEOIL_PUBLIC_KEY = snakeOilPublicKey; + }; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + }; + + services.openssh.enable = true; + services.openssh.challengeResponseAuthentication = false; + services.openssh.passwordAuthentication = false; + + security.googleOsLogin.enable = true; + + # Mock google service + networking.extraHosts = '' + 127.0.0.1 metadata.google.internal + ''; +} diff --git a/nixpkgs/nixos/tests/google-oslogin/server.py b/nixpkgs/nixos/tests/google-oslogin/server.py new file mode 100644 index 000000000000..bfc527cb97d3 --- /dev/null +++ b/nixpkgs/nixos/tests/google-oslogin/server.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +import json +import sys +import time +import os +import hashlib +import base64 + +from http.server import BaseHTTPRequestHandler, HTTPServer +from typing import Dict + +SNAKEOIL_PUBLIC_KEY = os.environ['SNAKEOIL_PUBLIC_KEY'] + + +def w(msg): + sys.stderr.write(f"{msg}\n") + sys.stderr.flush() + + +def gen_fingerprint(pubkey): + decoded_key = base64.b64decode(pubkey.encode("ascii").split()[1]) + return hashlib.sha256(decoded_key).hexdigest() + +def gen_email(username): + """username seems to be a 21 characters long number string, so mimic that in a reproducible way""" + return str(int(hashlib.sha256(username.encode()).hexdigest(), 16))[0:21] + +def gen_mockuser(username: str, uid: str, gid: str, home_directory: str, snakeoil_pubkey: str) -> Dict: + snakeoil_pubkey_fingerprint = gen_fingerprint(snakeoil_pubkey) + # seems to be a 21 characters long numberstring, so mimic that in a reproducible way + email = gen_email(username) + return { + "loginProfiles": [ + { + "name": email, + "posixAccounts": [ + { + "primary": True, + "username": username, + "uid": uid, + "gid": gid, + "homeDirectory": home_directory, + "operatingSystemType": "LINUX" + } + ], + "sshPublicKeys": { + snakeoil_pubkey_fingerprint: { + "key": snakeoil_pubkey, + "expirationTimeUsec": str((time.time() + 600) * 1000000), # 10 minutes in the future + "fingerprint": snakeoil_pubkey_fingerprint + } + } + } + ] + } + + +class ReqHandler(BaseHTTPRequestHandler): + def _send_json_ok(self, data): + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + out = json.dumps(data).encode() + w(out) + self.wfile.write(out) + + def do_GET(self): + p = str(self.path) + # mockuser and mockadmin are allowed to login, both use the same snakeoil public key + if p == '/computeMetadata/v1/oslogin/users?username=mockuser' \ + or p == '/computeMetadata/v1/oslogin/users?uid=1009719690': + self._send_json_ok(gen_mockuser(username='mockuser', uid='1009719690', gid='1009719690', + home_directory='/home/mockuser', snakeoil_pubkey=SNAKEOIL_PUBLIC_KEY)) + elif p == '/computeMetadata/v1/oslogin/users?username=mockadmin' \ + or p == '/computeMetadata/v1/oslogin/users?uid=1009719691': + self._send_json_ok(gen_mockuser(username='mockadmin', uid='1009719691', gid='1009719691', + home_directory='/home/mockadmin', snakeoil_pubkey=SNAKEOIL_PUBLIC_KEY)) + + # mockuser is allowed to login + elif p == f"/computeMetadata/v1/oslogin/authorize?email={gen_email('mockuser')}&policy=login": + self._send_json_ok({'success': True}) + + # mockadmin may also become root + elif p == f"/computeMetadata/v1/oslogin/authorize?email={gen_email('mockadmin')}&policy=login" or p == f"/computeMetadata/v1/oslogin/authorize?email={gen_email('mockadmin')}&policy=adminLogin": + self._send_json_ok({'success': True}) + else: + sys.stderr.write(f"Unhandled path: {p}\n") + sys.stderr.flush() + self.send_response(501) + self.end_headers() + self.wfile.write(b'') + + +if __name__ == '__main__': + s = HTTPServer(('0.0.0.0', 80), ReqHandler) + s.serve_forever() diff --git a/nixpkgs/nixos/tests/grafana.nix b/nixpkgs/nixos/tests/grafana.nix new file mode 100644 index 000000000000..9dc765a879bc --- /dev/null +++ b/nixpkgs/nixos/tests/grafana.nix @@ -0,0 +1,25 @@ +import ./make-test.nix ({ lib, ... }: +{ + name = "grafana"; + + meta = with lib.maintainers; { + maintainers = [ willibutz ]; + }; + + machine = { ... }: { + services.grafana = { + enable = true; + addr = "localhost"; + analytics.reporting.enable = false; + domain = "localhost"; + security.adminUser = "testusername"; + }; + }; + + testScript = '' + $machine->start; + $machine->waitForUnit("grafana.service"); + $machine->waitForOpenPort(3000); + $machine->succeed("curl -sSfL http://127.0.0.1:3000/"); + ''; +}) diff --git a/nixpkgs/nixos/tests/graphite.nix b/nixpkgs/nixos/tests/graphite.nix new file mode 100644 index 000000000000..27a87bdbb9f2 --- /dev/null +++ b/nixpkgs/nixos/tests/graphite.nix @@ -0,0 +1,41 @@ +import ./make-test.nix ({ pkgs, ... } : +{ + name = "graphite"; + nodes = { + one = + { ... }: { + virtualisation.memorySize = 1024; + time.timeZone = "UTC"; + services.graphite = { + web.enable = true; + api = { + enable = true; + port = 8082; + finders = [ pkgs.python27Packages.influxgraph ]; + }; + carbon.enableCache = true; + seyren.enable = true; + pager.enable = true; + beacon.enable = true; + }; + }; + }; + + testScript = '' + startAll; + $one->waitForUnit("default.target"); + $one->waitForUnit("graphiteWeb.service"); + $one->waitForUnit("graphiteApi.service"); + $one->waitForUnit("graphitePager.service"); + $one->waitForUnit("graphite-beacon.service"); + $one->waitForUnit("carbonCache.service"); + $one->waitForUnit("seyren.service"); + # The services above are of type "simple". systemd considers them active immediately + # 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->waitForOpenPort(8080); + $one->waitForOpenPort(2003); + $one->succeed("echo \"foo 1 `date +%s`\" | nc -N localhost 2003"); + $one->waitUntilSucceeds("curl 'http://localhost:8080/metrics/find/?query=foo&format=treejson' --silent | grep foo >&2"); + ''; +}) diff --git a/nixpkgs/nixos/tests/hadoop/hdfs.nix b/nixpkgs/nixos/tests/hadoop/hdfs.nix new file mode 100644 index 000000000000..e7d72a56e1e7 --- /dev/null +++ b/nixpkgs/nixos/tests/hadoop/hdfs.nix @@ -0,0 +1,54 @@ +import ../make-test.nix ({...}: { + nodes = { + namenode = {pkgs, ...}: { + services.hadoop = { + package = pkgs.hadoop_3_1; + hdfs.namenode.enabled = true; + coreSite = { + "fs.defaultFS" = "hdfs://namenode:8020"; + }; + hdfsSite = { + "dfs.replication" = 1; + "dfs.namenode.rpc-bind-host" = "0.0.0.0"; + "dfs.namenode.http-bind-host" = "0.0.0.0"; + }; + }; + networking.firewall.allowedTCPPorts = [ + 9870 # namenode.http-address + 8020 # namenode.rpc-address + ]; + }; + datanode = {pkgs, ...}: { + services.hadoop = { + package = pkgs.hadoop_3_1; + hdfs.datanode.enabled = true; + coreSite = { + "fs.defaultFS" = "hdfs://namenode:8020"; + }; + }; + networking.firewall.allowedTCPPorts = [ + 9864 # datanode.http.address + 9866 # datanode.address + 9867 # datanode.ipc.address + ]; + }; + }; + + testScript = '' + startAll + + $namenode->waitForUnit("hdfs-namenode"); + $namenode->waitForUnit("network.target"); + $namenode->waitForOpenPort(8020); + $namenode->waitForOpenPort(9870); + + $datanode->waitForUnit("hdfs-datanode"); + $datanode->waitForUnit("network.target"); + $datanode->waitForOpenPort(9864); + $datanode->waitForOpenPort(9866); + $datanode->waitForOpenPort(9867); + + $namenode->succeed("curl http://namenode:9870"); + $datanode->succeed("curl http://datanode:9864"); + ''; +}) diff --git a/nixpkgs/nixos/tests/hadoop/yarn.nix b/nixpkgs/nixos/tests/hadoop/yarn.nix new file mode 100644 index 000000000000..031592301f17 --- /dev/null +++ b/nixpkgs/nixos/tests/hadoop/yarn.nix @@ -0,0 +1,46 @@ +import ../make-test.nix ({...}: { + nodes = { + resourcemanager = {pkgs, ...}: { + services.hadoop.package = pkgs.hadoop_3_1; + services.hadoop.yarn.resourcemanager.enabled = true; + services.hadoop.yarnSite = { + "yarn.resourcemanager.scheduler.class" = "org.apache.hadoop.yarn.server.resourcemanager.scheduler.fifo.FifoScheduler"; + }; + networking.firewall.allowedTCPPorts = [ + 8088 # resourcemanager.webapp.address + 8031 # resourcemanager.resource-tracker.address + ]; + }; + nodemanager = {pkgs, ...}: { + services.hadoop.package = pkgs.hadoop_3_1; + services.hadoop.yarn.nodemanager.enabled = true; + services.hadoop.yarnSite = { + "yarn.resourcemanager.hostname" = "resourcemanager"; + "yarn.nodemanager.log-dirs" = "/tmp/userlogs"; + "yarn.nodemanager.address" = "0.0.0.0:8041"; + }; + networking.firewall.allowedTCPPorts = [ + 8042 # nodemanager.webapp.address + 8041 # nodemanager.address + ]; + }; + + }; + + testScript = '' + startAll; + + $resourcemanager->waitForUnit("yarn-resourcemanager"); + $resourcemanager->waitForUnit("network.target"); + $resourcemanager->waitForOpenPort(8031); + $resourcemanager->waitForOpenPort(8088); + + $nodemanager->waitForUnit("yarn-nodemanager"); + $nodemanager->waitForUnit("network.target"); + $nodemanager->waitForOpenPort(8042); + $nodemanager->waitForOpenPort(8041); + + $resourcemanager->succeed("curl http://localhost:8088"); + $nodemanager->succeed("curl http://localhost:8042"); + ''; +}) diff --git a/nixpkgs/nixos/tests/haka.nix b/nixpkgs/nixos/tests/haka.nix new file mode 100644 index 000000000000..6277ebb4933f --- /dev/null +++ b/nixpkgs/nixos/tests/haka.nix @@ -0,0 +1,24 @@ +# This test runs haka and probes it with hakactl + +import ./make-test.nix ({ pkgs, ...} : { + name = "haka"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ tvestelind ]; + }; + + nodes = { + haka = + { ... }: + { + services.haka.enable = true; + }; + }; + + testScript = '' + startAll; + + $haka->waitForUnit("haka.service"); + $haka->succeed("hakactl status"); + $haka->succeed("hakactl stop"); + ''; +}) diff --git a/nixpkgs/nixos/tests/handbrake.nix b/nixpkgs/nixos/tests/handbrake.nix new file mode 100644 index 000000000000..ae87e1f69a7d --- /dev/null +++ b/nixpkgs/nixos/tests/handbrake.nix @@ -0,0 +1,25 @@ +import ./make-test.nix ({ pkgs, ... }: +let + # Download Big Buck Bunny example, licensed under CC Attribution 3.0. + testMkv = pkgs.fetchurl { + url = "https://github.com/Matroska-Org/matroska-test-files/blob/cf0792be144ac470c4b8052cfe19bb691993e3a2/test_files/test1.mkv?raw=true"; + sha256 = "1hfxbbgxwfkzv85pvpvx55a72qsd0hxjbm9hkl5r3590zw4s75h9"; + }; +in { + name = "handbrake"; + + meta = { + maintainers = with pkgs.stdenv.lib.maintainers; [ danieldk ]; + }; + + machine = { pkgs, ... }: { + environment.systemPackages = with pkgs; [ handbrake ]; + }; + + testScript = '' + # Test MP4 and MKV transcoding. Since this is a short clip, transcoding typically + # only takes a few seconds. + $machine->succeed("HandBrakeCLI -i ${testMkv} -o test.mp4 -e x264 -q 20 -B 160"); + $machine->succeed("HandBrakeCLI -i ${testMkv} -o test.mkv -e x264 -q 20 -B 160"); + ''; +}) diff --git a/nixpkgs/nixos/tests/haproxy.nix b/nixpkgs/nixos/tests/haproxy.nix new file mode 100644 index 000000000000..22a83e9d1eab --- /dev/null +++ b/nixpkgs/nixos/tests/haproxy.nix @@ -0,0 +1,41 @@ +import ./make-test.nix ({ pkgs, ...}: { + name = "haproxy"; + nodes = { + machine = { ... }: { + imports = [ ../modules/profiles/minimal.nix ]; + services.haproxy = { + enable = true; + config = '' + defaults + timeout connect 10s + + backend http_server + mode http + server httpd [::1]:8000 + + frontend http + bind *:80 + mode http + use_backend http_server + ''; + }; + services.httpd = { + enable = true; + documentRoot = pkgs.writeTextDir "index.txt" "We are all good!"; + adminAddr = "notme@yourhost.local"; + listen = [{ + ip = "::1"; + port = 8000; + }]; + }; + }; + }; + testScript = '' + startAll; + $machine->waitForUnit('multi-user.target'); + $machine->waitForUnit('haproxy.service'); + $machine->waitForUnit('httpd.service'); + $machine->succeed('curl -k http://localhost:80/index.txt | grep "We are all good!"'); + + ''; +}) diff --git a/nixpkgs/nixos/tests/hardened.nix b/nixpkgs/nixos/tests/hardened.nix new file mode 100644 index 000000000000..07bd10963bab --- /dev/null +++ b/nixpkgs/nixos/tests/hardened.nix @@ -0,0 +1,87 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "hardened"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ joachifm ]; + }; + + machine = + { lib, pkgs, config, ... }: + with lib; + { users.users.alice = { isNormalUser = true; extraGroups = [ "proc" ]; }; + users.users.sybil = { isNormalUser = true; group = "wheel"; }; + imports = [ ../modules/profiles/hardened.nix ]; + nix.useSandbox = false; + virtualisation.emptyDiskImages = [ 4096 ]; + boot.initrd.postDeviceCommands = '' + ${pkgs.dosfstools}/bin/mkfs.vfat -n EFISYS /dev/vdb + ''; + fileSystems = lib.mkVMOverride { + "/efi" = { + device = "/dev/disk/by-label/EFISYS"; + fsType = "vfat"; + options = [ "noauto" ]; + }; + }; + boot.extraModulePackages = [ config.boot.kernelPackages.wireguard ]; + boot.kernelModules = [ "wireguard" ]; + }; + + testScript = + '' + $machine->waitForUnit("multi-user.target"); + + # Test loading out-of-tree modules + subtest "extra-module-packages", sub { + $machine->succeed("grep -Fq wireguard /proc/modules"); + }; + + # Test hidepid + subtest "hidepid", sub { + $machine->succeed("grep -Fq hidepid=2 /proc/mounts"); + # cannot use pgrep -u here, it segfaults when access to process info is denied + $machine->succeed("[ `su - sybil -c 'ps --no-headers --user root | wc -l'` = 0 ]"); + $machine->succeed("[ `su - alice -c 'ps --no-headers --user root | wc -l'` != 0 ]"); + }; + + # Test kernel module hardening + subtest "lock-modules", sub { + # note: this better a be module we normally wouldn't load ... + $machine->fail("modprobe dccp"); + }; + + # Test userns + subtest "userns", sub { + $machine->fail("unshare --user"); + }; + + # Test dmesg restriction + subtest "dmesg", sub { + $machine->fail("su -l alice -c dmesg"); + }; + + # Test access to kcore + subtest "kcore", sub { + $machine->fail("cat /proc/kcore"); + }; + + # Test deferred mount + subtest "mount", sub { + $machine->fail("mountpoint -q /efi"); # was deferred + $machine->execute("mkdir -p /efi"); + $machine->succeed("mount /dev/disk/by-label/EFISYS /efi"); + $machine->succeed("mountpoint -q /efi"); # now mounted + }; + + # Test Nix dæmon usage + subtest "nix-daemon", sub { + $machine->fail("su -l nobody -s /bin/sh -c 'nix ping-store'"); + $machine->succeed("su -l alice -c 'nix ping-store'") =~ "OK"; + }; + + # Test kernel image protection + subtest "kernelimage", sub { + $machine->fail("systemctl hibernate"); + $machine->fail("systemctl kexec"); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/hibernate.nix b/nixpkgs/nixos/tests/hibernate.nix new file mode 100644 index 000000000000..274aa7becc82 --- /dev/null +++ b/nixpkgs/nixos/tests/hibernate.nix @@ -0,0 +1,43 @@ +# Test whether hibernation from partition works. + +import ./make-test.nix (pkgs: { + name = "hibernate"; + + nodes = { + machine = { config, lib, pkgs, ... }: with lib; { + virtualisation.emptyDiskImages = [ config.virtualisation.memorySize ]; + + systemd.services.backdoor.conflicts = [ "sleep.target" ]; + + swapDevices = mkOverride 0 [ { device = "/dev/vdb"; } ]; + + networking.firewall.allowedTCPPorts = [ 4444 ]; + + systemd.services.listener.serviceConfig.ExecStart = "${pkgs.netcat}/bin/nc -l 4444 -k"; + }; + + probe = { pkgs, ...}: { + environment.systemPackages = [ pkgs.netcat ]; + }; + }; + + # 9P doesn't support reconnection to virtio transport after a hibernation. + # Therefore, machine just hangs on any Nix store access. + # To work around it we run a daemon which listens to a TCP connection and + # try to connect to it as a test. + + testScript = + '' + $machine->waitForUnit("multi-user.target"); + $machine->succeed("mkswap /dev/vdb"); + $machine->succeed("swapon -a"); + $machine->startJob("listener"); + $machine->waitForOpenPort(4444); + $machine->succeed("systemctl hibernate &"); + $machine->waitForShutdown; + $probe->waitForUnit("multi-user.target"); + $machine->start; + $probe->waitUntilSucceeds("echo test | nc machine 4444 -N"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/hitch/default.nix b/nixpkgs/nixos/tests/hitch/default.nix new file mode 100644 index 000000000000..cb24c4dcffc2 --- /dev/null +++ b/nixpkgs/nixos/tests/hitch/default.nix @@ -0,0 +1,33 @@ +import ../make-test.nix ({ pkgs, ... }: +{ + name = "hitch"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ jflanglois ]; + }; + machine = { pkgs, ... }: { + environment.systemPackages = [ pkgs.curl ]; + services.hitch = { + enable = true; + backend = "[127.0.0.1]:80"; + pem-files = [ + ./example.pem + ]; + }; + + services.httpd = { + enable = true; + documentRoot = ./example; + adminAddr = "noone@testing.nowhere"; + }; + }; + + testScript = + '' + startAll; + + $machine->waitForUnit('multi-user.target'); + $machine->waitForUnit('hitch.service'); + $machine->waitForOpenPort(443); + $machine->succeed('curl -k https://localhost:443/index.txt | grep "We are all good!"'); + ''; +}) diff --git a/nixpkgs/nixos/tests/hitch/example.pem b/nixpkgs/nixos/tests/hitch/example.pem new file mode 100644 index 000000000000..fde6f3cbd19a --- /dev/null +++ b/nixpkgs/nixos/tests/hitch/example.pem @@ -0,0 +1,53 @@ +-----BEGIN CERTIFICATE----- +MIIEKTCCAxGgAwIBAgIJAIFAWQXSZ7lIMA0GCSqGSIb3DQEBCwUAMIGqMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMUmVkd29vZCBD +aXR5MRkwFwYDVQQKDBBUZXN0aW5nIDEyMyBJbmMuMRQwEgYDVQQLDAtJVCBTZXJ2 +aWNlczEYMBYGA1UEAwwPdGVzdGluZy5ub3doZXJlMSQwIgYJKoZIhvcNAQkBFhVu +b29uZUB0ZXN0aW5nLm5vd2hlcmUwHhcNMTgwNDIzMDcxMTI5WhcNMTkwNDIzMDcx +MTI5WjCBqjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNV +BAcMDFJlZHdvb2QgQ2l0eTEZMBcGA1UECgwQVGVzdGluZyAxMjMgSW5jLjEUMBIG +A1UECwwLSVQgU2VydmljZXMxGDAWBgNVBAMMD3Rlc3Rpbmcubm93aGVyZTEkMCIG +CSqGSIb3DQEJARYVbm9vbmVAdGVzdGluZy5ub3doZXJlMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAxQq6AA9o/QErMbQwfgDF4mqXcvglRTwPr2zPE6Rv +1g0ncRBSMM8iKbPapHM6qHNfg2e1fU2SFqzD6HkyZqHHLCgLzkdzswEcEjsMqiUP +OR++5g4CWoQrdTi31itzYzCjnQ45BrAMrLEhBQgDTNwrEE+Tit0gpOGggtj/ktLk +OD8BKa640lkmWEUGF18fd3rYTUC4hwM5qhAVXTe21vj9ZWsgprpQKdN61v0dCUap +C5eAgvZ8Re+Cd0Id674hK4cJ4SekqfHKv/jLyIg3Vsdc9nkhmiC4O6KH5f1Zzq2i +E4Kd5mnJDFxfSzIErKWmbhriLWsj3KEJ983AGLJ9hxQTAwIDAQABo1AwTjAdBgNV +HQ4EFgQU76Mm6DP/BePJRQUNrJ9z038zjocwHwYDVR0jBBgwFoAU76Mm6DP/BePJ +RQUNrJ9z038zjocwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAZzt +VdPaUqrvDAh5rMYqzYMJ3tj6daNYoX6CbTFoevK5J5D4FESM0D/FMKgpNiVz39kB +8Cjaw5rPHMHY61rHz7JRDK1sWXsonwzCF21BK7Tx0G1CIfLpYHWYb/FfdWGROx+O +hPgKuoMRWQB+txozkZp5BqWJmk5MOyFCDEXhMOmrfsJq0IYU6QaH3Lsf1oJRy4yU +afFrT9o3DLOyYLG/j/HXijCu8DVjZVa4aboum79ecYzPjjGF1posrFUnvQiuAeYy +t7cuHNUB8gW9lWR5J7tP8fzFWtIcyT2oRL8u3H+fXf0i4bW73wtOBOoeULBzBNE7 +6rphcSrQunSZQIc+hg== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDFCroAD2j9ASsx +tDB+AMXiapdy+CVFPA+vbM8TpG/WDSdxEFIwzyIps9qkczqoc1+DZ7V9TZIWrMPo +eTJmoccsKAvOR3OzARwSOwyqJQ85H77mDgJahCt1OLfWK3NjMKOdDjkGsAyssSEF +CANM3CsQT5OK3SCk4aCC2P+S0uQ4PwEprrjSWSZYRQYXXx93ethNQLiHAzmqEBVd +N7bW+P1layCmulAp03rW/R0JRqkLl4CC9nxF74J3Qh3rviErhwnhJ6Sp8cq/+MvI +iDdWx1z2eSGaILg7oofl/VnOraITgp3mackMXF9LMgSspaZuGuItayPcoQn3zcAY +sn2HFBMDAgMBAAECggEAcaR8HijFHpab+PC5vxJnDuz3KEHiDQpU6ZJR5DxEnCm+ +A8GsBaaRR4gJpCspO5o/DiS0Ue55QUanPt8XqIXJv7fhBznCiw0qyYDxDviMzR94 +FGskBFySS+tIa+dnh1+4HY7kaO0Egl0udB5o+N1KoP+kUsSyXSYcUxsgW+fx5FW9 +22Ya3HNWnWxMCSfSGGlTFXGj2whf25SkL25dM9iblO4ZOx4MX8kaXij7TaYy8hMM +Vf6/OMnXqtPKho+ctZZVKZkE9PxdS4f/pnp5EsdoOZwNBtfQ1WqVLWd3DlGWhnsH +7L8ZSP2HkoI4Pd1wtkpOKZc+yM2bFXWa8WY4TcmpUQKBgQD33HxGdtmtZehrexSA +/ZwWJlMslUsNz4Ivv6s7J4WCRhdh94+r9TWQP/yHdT9Ry5bvn84I5ZLUdp+aA962 +mvjz+GIglkCGpA7HU/hqurB1O63pj2cIDB8qhV21zjVIoqXcQ7IBJ+tqD79nF8vm +h3KfuHUhuu1rayGepbtIyNhLdwKBgQDLgw4TJBg/QB8RzYECk78QnfZpCExsQA/z +YJpc+dF2/nsid5R2u9jWzfmgHM2Jjo2/+ofRUaTqcFYU0K57CqmQkOLIzsbNQoYt +e2NOANNVHiZLuzTZC2r3BrrkNbo3YvQzhAesUA5lS6LfrxBLUKiwo2LU9NlmJs3b +UPVFYI0/1QKBgCswxIcS1sOcam+wNtZzWuuRKhUuvrFdY3YmlBPuwxj8Vb7AgMya +IgdM3xhLmgkKzPZchm6OcpOLSCxyWDDBuHfq5E6BYCUWGW0qeLNAbNdA2wFD99Qz +KIskSjwP/sD1dql3MmF5L1CABf5U6zb0i0jBv8ds50o8lNMsVgJM3UPpAoGBAL1+ +nzllb4pdi1CJWKnspoizfQCZsIdPM0r71V/jYY36MO+MBtpz2NlSWzAiAaQm74gl +oBdgfT2qMg0Zro11BSRONEykdOolGkj5TiMQk7b65s+3VeMPRZ8UTis2d9kgs5/Q +PVDODkl1nwfGu1ZVmW04BUujXVZHpYCkJm1eFMetAoGAImE7gWj+qRMhpbtCCGCg +z06gDKvMrF6S+GJsvUoSyM8oUtfdPodI6gWAC65NfYkIiqbpCaEVNzfui73f5Lnz +p5X1IbzhuH5UZs/k5A3OR2PPDbPs3lqEw7YJdBdLVRmO1o824uaXaJJwkL/1C+lq +8dh1wV3CnynNmZApkz4vpzQ= +-----END PRIVATE KEY----- diff --git a/nixpkgs/nixos/tests/hitch/example/index.txt b/nixpkgs/nixos/tests/hitch/example/index.txt new file mode 100644 index 000000000000..0478b1c26351 --- /dev/null +++ b/nixpkgs/nixos/tests/hitch/example/index.txt @@ -0,0 +1 @@ +We are all good! diff --git a/nixpkgs/nixos/tests/hocker-fetchdocker/default.nix b/nixpkgs/nixos/tests/hocker-fetchdocker/default.nix new file mode 100644 index 000000000000..4f30f01e4032 --- /dev/null +++ b/nixpkgs/nixos/tests/hocker-fetchdocker/default.nix @@ -0,0 +1,15 @@ +import ../make-test.nix ({ pkgs, ...} : { + name = "test-hocker-fetchdocker"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ixmatus ]; + }; + + machine = import ./machine.nix; + + testScript = '' + startAll; + + $machine->waitForUnit("sockets.target"); + $machine->waitUntilSucceeds("docker run registry-1.docker.io/v2/library/hello-world:latest"); + ''; +}) diff --git a/nixpkgs/nixos/tests/hocker-fetchdocker/hello-world-container.nix b/nixpkgs/nixos/tests/hocker-fetchdocker/hello-world-container.nix new file mode 100644 index 000000000000..a127875264e9 --- /dev/null +++ b/nixpkgs/nixos/tests/hocker-fetchdocker/hello-world-container.nix @@ -0,0 +1,19 @@ +{ fetchDockerConfig, fetchDockerLayer, fetchdocker }: +fetchdocker rec { + name = "hello-world"; + registry = "https://registry-1.docker.io/v2/"; + repository = "library"; + imageName = "hello-world"; + tag = "latest"; + imageConfig = fetchDockerConfig { + inherit tag registry repository imageName; + sha256 = "1ivbd23hyindkahzfw4kahgzi6ibzz2ablmgsz6340vc6qr1gagj"; + }; + imageLayers = let + layer0 = fetchDockerLayer { + inherit registry repository imageName; + layerDigest = "ca4f61b1923c10e9eb81228bd46bee1dfba02b9c7dac1844527a734752688ede"; + sha256 = "1plfd194fwvsa921ib3xkhms1yqxxrmx92r2h7myj41wjaqn2kya"; + }; + in [ layer0 ]; + } diff --git a/nixpkgs/nixos/tests/hocker-fetchdocker/machine.nix b/nixpkgs/nixos/tests/hocker-fetchdocker/machine.nix new file mode 100644 index 000000000000..78343f0e02f0 --- /dev/null +++ b/nixpkgs/nixos/tests/hocker-fetchdocker/machine.nix @@ -0,0 +1,26 @@ +{ pkgs, ... }: +{ nixpkgs.config.packageOverrides = pkgs': { + hello-world-container = pkgs'.callPackage ./hello-world-container.nix { }; + }; + + virtualisation.docker = { + enable = true; + package = pkgs.docker; + }; + + systemd.services.docker-load-fetchdocker-image = { + description = "Docker load hello-world-container"; + wantedBy = [ "multi-user.target" ]; + wants = [ "docker.service" "local-fs.target" ]; + after = [ "docker.service" "local-fs.target" ]; + + script = '' + ${pkgs.hello-world-container}/compositeImage.sh | ${pkgs.docker}/bin/docker load + ''; + + serviceConfig = { + Type = "oneshot"; + }; + }; +} + diff --git a/nixpkgs/nixos/tests/home-assistant.nix b/nixpkgs/nixos/tests/home-assistant.nix new file mode 100644 index 000000000000..a93360b252f6 --- /dev/null +++ b/nixpkgs/nixos/tests/home-assistant.nix @@ -0,0 +1,102 @@ +import ./make-test.nix ({ pkgs, ... }: + +let + configDir = "/var/lib/foobar"; + apiPassword = "some_secret"; + mqttPassword = "another_secret"; + hassCli = "hass-cli --server http://hass:8123 --password '${apiPassword}'"; + +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"; } + ]; + }; + frontend = { }; + http.api_password = apiPassword; + 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; + }; + }; + }; + + testScript = '' + startAll; + $hass->waitForUnit("home-assistant.service"); + + # The config is specified using a Nix attribute set, + # converted from JSON to YAML, and linked to the config dir + $hass->succeed("test -L ${configDir}/configuration.yaml"); + # The lovelace config is copied because lovelaceConfigWritable = true + $hass->succeed("test -f ${configDir}/ui-lovelace.yaml"); + + # Check that Home Assistant's web interface and API can be reached + $hass->waitForOpenPort(8123); + $hass->succeed("curl --fail http://localhost:8123/states"); + $hass->succeed("curl --fail -H 'x-ha-access: ${apiPassword}' http://localhost:8123/api/ | grep -qF 'API running'"); + + # Toggle a binary sensor using MQTT + $hass->succeed("curl http://localhost:8123/api/states/binary_sensor.mqtt_binary_sensor -H 'x-ha-access: ${apiPassword}' | grep -qF '\"state\": \"off\"'"); + $hass->waitUntilSucceeds("mosquitto_pub -V mqttv311 -t home-assistant/test -u homeassistant -P '${mqttPassword}' -m let_there_be_light"); + $hass->succeed("curl http://localhost:8123/api/states/binary_sensor.mqtt_binary_sensor -H 'x-ha-access: ${apiPassword}' | grep -qF '\"state\": \"on\"'"); + + # Toggle a binary sensor using hass-cli + $hass->succeed("${hassCli} --output json entity get binary_sensor.mqtt_binary_sensor | grep -qF '\"state\": \"on\"'"); + $hass->succeed("${hassCli} entity edit binary_sensor.mqtt_binary_sensor --json='{\"state\": \"off\"}'"); + $hass->succeed("curl http://localhost:8123/api/states/binary_sensor.mqtt_binary_sensor -H 'x-ha-access: ${apiPassword}' | grep -qF '\"state\": \"off\"'"); + + # Print log to ease debugging + my $log = $hass->succeed("cat ${configDir}/home-assistant.log"); + print "\n### home-assistant.log ###\n"; + print "$log\n"; + + # Check that no errors were logged + $hass->fail("cat ${configDir}/home-assistant.log | grep -qF ERROR"); + ''; +}) diff --git a/nixpkgs/nixos/tests/hound.nix b/nixpkgs/nixos/tests/hound.nix new file mode 100644 index 000000000000..cb8e25332c07 --- /dev/null +++ b/nixpkgs/nixos/tests/hound.nix @@ -0,0 +1,58 @@ +# Test whether `houndd` indexes nixpkgs +import ./make-test.nix ({ pkgs, ... } : { + name = "hound"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ grahamc ]; + }; + machine = { pkgs, ... }: { + services.hound = { + enable = true; + config = '' + { + "max-concurrent-indexers": 1, + "dbpath": "/var/lib/hound/data", + "repos": { + "nix": { + "url": "file:///var/lib/hound/my-git" + } + } + } + ''; + }; + + systemd.services.houndseed = { + description = "seed hound with a git repo"; + requiredBy = [ "hound.service" ]; + before = [ "hound.service" ]; + + serviceConfig = { + User = "hound"; + Group = "hound"; + WorkingDirectory = "/var/lib/hound"; + }; + path = [ pkgs.git ]; + script = '' + git config --global user.email "you@example.com" + git config --global user.name "Your Name" + git init my-git --bare + git init my-git-clone + cd my-git-clone + echo 'hi nix!' > hello + git add hello + git commit -m "hello there :)" + git remote add origin /var/lib/hound/my-git + git push origin master + ''; + }; + }; + + testScript = + '' startAll; + + $machine->waitForUnit("network.target"); + $machine->waitForUnit("hound.service"); + $machine->waitForOpenPort(6080); + $machine->waitUntilSucceeds('curl http://127.0.0.1:6080/api/v1/search\?stats\=fosho\&repos\=\*\&rng=%3A20\&q\=hi\&files\=\&i=nope | grep "Filename" | grep "hello"'); + + ''; +}) diff --git a/nixpkgs/nixos/tests/hydra/create-trivial-project.sh b/nixpkgs/nixos/tests/hydra/create-trivial-project.sh new file mode 100755 index 000000000000..39122c9b473a --- /dev/null +++ b/nixpkgs/nixos/tests/hydra/create-trivial-project.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# +# This script creates a project, a jobset with an input of type local +# path. This local path is a directory that contains a Nix expression +# to define a job. +# The EXPR-PATH environment variable must be set with the local path. + +set -e + +URL=http://localhost:3000 +USERNAME="admin" +PASSWORD="admin" +PROJECT_NAME="trivial" +JOBSET_NAME="trivial" +EXPR_PATH=${EXPR_PATH:-} + +if [ -z $EXPR_PATH ]; then + echo "Environment variable EXPR_PATH must be set" + exit 1 +fi + +mycurl() { + curl --referer $URL -H "Accept: application/json" -H "Content-Type: application/json" $@ +} + +cat >data.json <<EOF +{ "username": "$USERNAME", "password": "$PASSWORD" } +EOF +mycurl -X POST -d '@data.json' $URL/login -c hydra-cookie.txt + +cat >data.json <<EOF +{ + "displayname":"Trivial", + "enabled":"1", + "visible":"1" +} +EOF +mycurl --silent -X PUT $URL/project/$PROJECT_NAME -d @data.json -b hydra-cookie.txt + +cat >data.json <<EOF +{ + "description": "Trivial", + "checkinterval": "60", + "enabled": "1", + "visible": "1", + "keepnr": "1", + "nixexprinput": "trivial", + "nixexprpath": "trivial.nix", + "inputs": { + "trivial": { + "value": "$EXPR_PATH", + "type": "path" + } + } +} +EOF +mycurl --silent -X PUT $URL/jobset/$PROJECT_NAME/$JOBSET_NAME -d @data.json -b hydra-cookie.txt diff --git a/nixpkgs/nixos/tests/hydra/default.nix b/nixpkgs/nixos/tests/hydra/default.nix new file mode 100644 index 000000000000..db4e97e0039b --- /dev/null +++ b/nixpkgs/nixos/tests/hydra/default.nix @@ -0,0 +1,77 @@ +import ../make-test.nix ({ pkgs, ...} : + +let + trivialJob = pkgs.writeTextDir "trivial.nix" '' + { trivial = builtins.derivation { + name = "trivial"; + system = "x86_64-linux"; + builder = "/bin/sh"; + args = ["-c" "echo success > $out; exit 0"]; + }; + } + ''; + + createTrivialProject = pkgs.stdenv.mkDerivation { + name = "create-trivial-project"; + unpackPhase = ":"; + buildInputs = [ pkgs.makeWrapper ]; + installPhase = "install -m755 -D ${./create-trivial-project.sh} $out/bin/create-trivial-project.sh"; + postFixup = '' + wrapProgram "$out/bin/create-trivial-project.sh" --prefix PATH ":" ${pkgs.stdenv.lib.makeBinPath [ pkgs.curl ]} --set EXPR_PATH ${trivialJob} + ''; + }; + +in { + name = "hydra-init-localdb"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ pstn lewo ma27 ]; + }; + + machine = + { pkgs, ... }: + + { + virtualisation.memorySize = 1024; + time.timeZone = "UTC"; + + environment.systemPackages = [ createTrivialProject pkgs.jq ]; + services.hydra = { + enable = true; + + #Hydra needs those settings to start up, so we add something not harmfull. + hydraURL = "example.com"; + notificationSender = "example@example.com"; + }; + nix = { + buildMachines = [{ + hostName = "localhost"; + systems = [ "x86_64-linux" ]; + }]; + + binaryCaches = []; + }; + }; + + testScript = + '' + # let the system boot up + $machine->waitForUnit("multi-user.target"); + # test whether the database is running + $machine->succeed("systemctl status postgresql.service"); + # test whether the actual hydra daemons are running + $machine->succeed("systemctl status hydra-queue-runner.service"); + $machine->succeed("systemctl status hydra-init.service"); + $machine->succeed("systemctl status hydra-evaluator.service"); + $machine->succeed("systemctl status hydra-send-stats.service"); + + $machine->succeed("hydra-create-user admin --role admin --password admin"); + + # create a project with a trivial job + $machine->waitForOpenPort(3000); + + # make sure the build as been successfully built + $machine->succeed("create-trivial-project.sh"); + + $machine->waitUntilSucceeds('curl -L -s http://localhost:3000/build/1 -H "Accept: application/json" | jq .buildstatus | xargs test 0 -eq'); + ''; +}) diff --git a/nixpkgs/nixos/tests/i3wm.nix b/nixpkgs/nixos/tests/i3wm.nix new file mode 100644 index 000000000000..d309f19a0b4a --- /dev/null +++ b/nixpkgs/nixos/tests/i3wm.nix @@ -0,0 +1,35 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "i3wm"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aszlig ]; + }; + + machine = { lib, ... }: { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + services.xserver.displayManager.auto.user = "alice"; + services.xserver.windowManager.default = lib.mkForce "i3"; + services.xserver.windowManager.i3.enable = true; + }; + + testScript = { ... }: '' + $machine->waitForX; + $machine->waitForFile("/home/alice/.Xauthority"); + $machine->succeed("xauth merge ~alice/.Xauthority"); + $machine->waitForWindow(qr/first configuration/); + $machine->sleep(2); + $machine->screenshot("started"); + $machine->sendKeys("ret"); + $machine->sleep(2); + $machine->sendKeys("alt"); + $machine->sleep(2); + $machine->screenshot("configured"); + $machine->sendKeys("ret"); + # make sure the config file is created before we continue + $machine->waitForFile("/home/alice/.config/i3/config"); + $machine->sleep(2); + $machine->sendKeys("alt-ret"); + $machine->waitForWindow(qr/alice.*machine/); + $machine->sleep(2); + $machine->screenshot("terminal"); + ''; +}) diff --git a/nixpkgs/nixos/tests/iftop.nix b/nixpkgs/nixos/tests/iftop.nix new file mode 100644 index 000000000000..a4f524ceb27b --- /dev/null +++ b/nixpkgs/nixos/tests/iftop.nix @@ -0,0 +1,34 @@ +import ./make-test.nix ({ pkgs, lib, ... }: + +with lib; + +{ + name = "iftop"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ ma27 ]; + + nodes = { + withIftop = { + imports = [ ./common/user-account.nix ]; + programs.iftop.enable = true; + }; + withoutIftop = { + imports = [ ./common/user-account.nix ]; + environment.systemPackages = [ pkgs.iftop ]; + }; + }; + + testScript = '' + subtest "machine with iftop enabled", sub { + $withIftop->waitForUnit("default.target"); + # limit to eth1 (eth0 is the test driver's control interface) + # and don't try name lookups + $withIftop->succeed("su -l alice -c 'iftop -t -s 1 -n -i eth1'"); + }; + subtest "machine without iftop", sub { + $withoutIftop->waitForUnit("default.target"); + # check that iftop is there but user alice lacks capabilities + $withoutIftop->succeed("iftop -t -s 1 -n -i eth1"); + $withoutIftop->fail("su -l alice -c 'iftop -t -s 1 -n -i eth1'"); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/incron.nix b/nixpkgs/nixos/tests/incron.nix new file mode 100644 index 000000000000..e39bbb5f096b --- /dev/null +++ b/nixpkgs/nixos/tests/incron.nix @@ -0,0 +1,52 @@ +import ./make-test.nix ({ pkgs, lib, ... }: + +{ + name = "incron"; + meta.maintainers = [ lib.maintainers.aanderse ]; + + machine = + { ... }: + { services.incron.enable = true; + services.incron.extraPackages = [ pkgs.coreutils ]; + services.incron.systab = '' + /test IN_CREATE,IN_MODIFY,IN_CLOSE_WRITE,IN_MOVED_FROM,IN_MOVED_TO echo "$@/$# $%" >> /root/incron.log + ''; + + # ensure the directory to be monitored exists before incron is started + system.activationScripts.incronTest = '' + mkdir /test + ''; + }; + + testScript = '' + startAll; + + $machine->waitForUnit("multi-user.target"); + $machine->waitForUnit("incron.service"); + + $machine->succeed("test -d /test"); + # create some activity for incron to monitor + $machine->succeed("touch /test/file"); + $machine->succeed("echo foo >> /test/file"); + $machine->succeed("mv /test/file /root"); + $machine->succeed("mv /root/file /test"); + + $machine->sleep(1); + + # touch /test/file + $machine->succeed("grep '/test/file IN_CREATE' /root/incron.log"); + + # echo foo >> /test/file + $machine->succeed("grep '/test/file IN_MODIFY' /root/incron.log"); + $machine->succeed("grep '/test/file IN_CLOSE_WRITE' /root/incron.log"); + + # mv /test/file /root + $machine->succeed("grep '/test/file IN_MOVED_FROM' /root/incron.log"); + + # mv /root/file /test + $machine->succeed("grep '/test/file IN_MOVED_TO' /root/incron.log"); + + # ensure something unexpected is not present + $machine->fail("grep 'IN_OPEN' /root/incron.log"); + ''; +}) diff --git a/nixpkgs/nixos/tests/influxdb.nix b/nixpkgs/nixos/tests/influxdb.nix new file mode 100644 index 000000000000..440049d95111 --- /dev/null +++ b/nixpkgs/nixos/tests/influxdb.nix @@ -0,0 +1,33 @@ +# This test runs influxdb and checks if influxdb is up and running + +import ./make-test.nix ({ pkgs, ...} : { + name = "influxdb"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ chaoflow offline ]; + }; + + nodes = { + one = { ... }: { + services.influxdb.enable = true; + }; + }; + + testScript = '' + startAll; + + $one->waitForUnit("influxdb.service"); + + # create database + $one->succeed(q~ + curl -XPOST http://localhost:8086/query --data-urlencode "q=CREATE DATABASE test" + ~); + + # write some points and run simple query + $one->succeed(q~ + curl -XPOST 'http://localhost:8086/write?db=test' --data-binary 'cpu_load_short,host=server01,region=us-west value=0.64 1434055562000000000' + ~); + $one->succeed(q~ + curl -GET 'http://localhost:8086/query' --data-urlencode "db=test" --data-urlencode "q=SELECT \"value\" FROM \"cpu_load_short\" WHERE \"region\"='us-west'" | grep "0\.64" + ~); + ''; +}) diff --git a/nixpkgs/nixos/tests/initrd-network-ssh/default.nix b/nixpkgs/nixos/tests/initrd-network-ssh/default.nix new file mode 100644 index 000000000000..b2209f297a4f --- /dev/null +++ b/nixpkgs/nixos/tests/initrd-network-ssh/default.nix @@ -0,0 +1,59 @@ +import ../make-test.nix ({ lib, ... }: + +{ + name = "initrd-network-ssh"; + meta = with lib.maintainers; { + maintainers = [ willibutz ]; + }; + + nodes = with lib; rec { + server = + { config, ... }: + { + boot.kernelParams = [ + "ip=${config.networking.primaryIPAddress}:::255.255.255.0::eth1:none" + ]; + boot.initrd.network = { + enable = true; + ssh = { + enable = true; + authorizedKeys = [ "${readFile ./openssh.pub}" ]; + port = 22; + hostRSAKey = ./dropbear.priv; + }; + }; + boot.initrd.preLVMCommands = '' + while true; do + if [ -f fnord ]; then + poweroff + fi + sleep 1 + done + ''; + }; + + client = + { config, ... }: + { + environment.etc.knownHosts = { + text = concatStrings [ + "server," + "${toString (head (splitString " " ( + toString (elemAt (splitString "\n" config.networking.extraHosts) 2) + )))} " + "${readFile ./dropbear.pub}" + ]; + }; + }; + }; + + testScript = '' + startAll; + $client->waitForUnit("network.target"); + $client->copyFileFromHost("${./openssh.priv}","/etc/sshKey"); + $client->succeed("chmod 0600 /etc/sshKey"); + $client->waitUntilSucceeds("ping -c 1 server"); + $client->succeed("ssh -i /etc/sshKey -o UserKnownHostsFile=/etc/knownHosts server 'touch /fnord'"); + $client->shutdown; + ''; +}) diff --git a/nixpkgs/nixos/tests/initrd-network-ssh/dropbear.priv b/nixpkgs/nixos/tests/initrd-network-ssh/dropbear.priv new file mode 100644 index 000000000000..af340535f0a3 --- /dev/null +++ b/nixpkgs/nixos/tests/initrd-network-ssh/dropbear.priv Binary files differdiff --git a/nixpkgs/nixos/tests/initrd-network-ssh/dropbear.pub b/nixpkgs/nixos/tests/initrd-network-ssh/dropbear.pub new file mode 100644 index 000000000000..385c625522aa --- /dev/null +++ b/nixpkgs/nixos/tests/initrd-network-ssh/dropbear.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCzJ0OniLB91MpPC86I1m3wwJeAc+Gme7bAuaLIU/cSfPwxT5NO7MfCp0Pu94gYDKtDXMs/wXg0bTAVDeAFFkdIj6kBBumEmQLCTL48q2UxDIXVLT/E/AAgj6q7WwgCg7fwm4Vjn4z7aUyBx8EfRy+5/SQyeYla3D/lFYgMi5x4D6J+yeR+JPAptDE/IR5IizNV7mY0ZcoXYyHrrehI1tTYEEqjX13ZqS4OCBFWwHe1QHhRNM+jHhcATbgikjAj8FyFPtLvc+dSVtkuhQktQl36Bi8zMUQcV6+mM7Ln6DBcDlM9urHKLYPTWmUAyhxM955iglOn5z0RaAIcyNMT6hz0rHaNf0BIlmbXoTC0XGjHh/OnoOEC/zg0JqgQTnPiU45K4TnRSSXp2GfiDfiQAK0+HaXACkjuFR68u7WCZpB1Bse1OgKNClFqtRhIr5DilUb2/e5DCCmFkddMUcjmYqzZdbXNt7fo8CFULe+mbiCp8+tMg4aRTaDZ/Hk93nCvGE5TP2ypEMbfL6nRVKvXOjhdvSQQgKwx+O003FDEHCSG0Bpageh7yVpna+SPrbGklce7MjTpbx3iIwmvKpQ6asnK1L3KkahpY1S3NhQ+/S3Gs8KWQ5LAU+d3xiPX3jfIVHsCIIyxHDbwcJvxM4MFBFQpqRMD6E+LoM9RHjl4C9k2iQ== tmtynkky@duuni diff --git a/nixpkgs/nixos/tests/initrd-network-ssh/generate-keys.nix b/nixpkgs/nixos/tests/initrd-network-ssh/generate-keys.nix new file mode 100644 index 000000000000..0183e12d7a88 --- /dev/null +++ b/nixpkgs/nixos/tests/initrd-network-ssh/generate-keys.nix @@ -0,0 +1,12 @@ +with import ../../.. {}; + +runCommand "gen-keys" { + buildInputs = [ dropbear openssh ]; + } + '' + mkdir $out + dropbearkey -t rsa -f $out/dropbear.priv -s 4096 | sed -n 2p > $out/dropbear.pub + ssh-keygen -q -t rsa -b 4096 -N "" -f client + mv client $out/openssh.priv + mv client.pub $out/openssh.pub + '' diff --git a/nixpkgs/nixos/tests/initrd-network-ssh/openssh.priv b/nixpkgs/nixos/tests/initrd-network-ssh/openssh.priv new file mode 100644 index 000000000000..816d65435fd7 --- /dev/null +++ b/nixpkgs/nixos/tests/initrd-network-ssh/openssh.priv @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEA7+9A2PCPOTAlFmrablrUWA+VZdAuLfM6JXeHsOF7ZbC2F6lv +WmvDM925DQqhiAjcgWnt5WHWS5Y+b7lGnuzT7fyKegXd80nCRmqlpSG3srX0/lxR +aQAJLzfoDjcsF+ceswQo6GSsYnCHVxMNs007gbbVY3f7o+sWZtLdxJPD2iHvl5Zr +LK0d1RLMmU6cfIhIABlL0S8EWiv29RROepsCQnS0dnK2b+von1SCYoggvAMe2ToA +IAJ8+uqaYfGAyn9q8fjZiRHxLmKDq90tKoCUL5r/2dmEIE+t8T/3PfHoq1QzZts9 +W9idhBdT21dEXBtGyoMtckp5njk5m82LQDYiOXkuSoIUhSOteh5g7fBv1BtVSERx +Jg3UeJjPeGKFwdnzapmAKC2w/6V8xcIINNA+fhZA7B9fD1RAi2TECZ+gyMYDc4T+ +USlMSm9cfvSOrf2+5ngtFb84nHjqvClxCMLu+bCWK8HamqUzhE/a5LbR+48E7PyG +s3KV+sWFN9KOnakTjj/6iQhXZRhgeAK39F2XTk5Ms5Y+BRSStnMoMZA2grIV+jHi +1zbWokVqXPI5YRo5isR/PgtKAV6FfNWumcYoFJ9F40pMHQ6hJVEmtrCBx7EApSl3 +mSGbQJUmilLC51qNhwQRbD//ZtpIrN82HTMKzZ6kj7kDCdsff+wsnkIXmmMCAwEA +AQKCAgA4tMINw6UF7hQF3VEsnbjr6xrzCiWv5HlMm5htPI1OdlpC81+G7ksfOfrf +UzDkFrwOtftsqBfem268Nvyy2OQprfMIbdSMCFWrEM9/XJ2u1gRGDYmMGF8TUtI8 +cduw9oWx53zHl+uKBHBoKu+k/c7flFeQf63wisIroRCawhWau0SF/h3sXCndzuie +Hw8q+4aQx2m80bDkotlmCNuXbIU3MZ/pEql9gDLlXTLHmMaryM0EqAmZhx0ErGe6 +WDqJIV4kPB0loSDwRoY6GzbugZ8ENUzcruTkQhCpIOYNNNw5idfwKkaxK1vm+SBv +iYt1fVjYyfH2vhVKSNoNsaGEloa1u4Dymt/FpFztEpRzHXcw93N8BdLxJ4OUhzm2 +iAbpiyjniTIeAVVi7BUwLXh5WAx8nT0eeb1zKoZg1p1ciK5cYl1Uel7j8xRycsSW +3YgmtuPqY4Agbc9v3eXbQZNDk48JFMEqpIxk97FAkRYpzfxg5Qq14WJCp60CkdRt +T60hXy8lT/BcI8OWLfGJuBbsVLNRiC7PpwqRKQAinXSv134FpP7jrhpkMybs2oIS +5obRG7J5OfOTp925erG5mrpwqa3BPkgqx347Wj9z8quOZyuhi+XaPvqmPtvs5JOl +4RCqjt6RQlHm7xos9ZZGI4jDAIFaFWgyVZrYplOgwxWma4DTgQKCAQEA9+tizQRU +lF0lxNcEPvsFnYJo80Y+MQK9VdtlhR19YuSfwP1NCaMG1MhQ+PVBVmepOwJMRJR7 +9PLfOouNMfixKBGP12dtStMuh7jowq/BxhRI6JWp3RhTZ1yJ9ouzHze7IDrEBa6w +p0hUu9H0Sbt51LXbC3JmTyhbdhfry559DfyGW1Ma/bv/pihL9B5Y7sNf1thNp1gi +GbQ9B+o2Yyw8ZD8zY+sl+aYDSWyCtcBV/KXEF74Bkfs/a5ExJ00X0jYj/TAp2ray +T4PY0FR8wN/O10bFLP9j+Xa/ywbcPhoj8nvVRIg9VfWT/QaEd+KR0EZVxdjCCqne +enbSQksTpAZNwQKCAQEA98E+BMmS+yHUVUhNZABtQ5avwuV4+DoSN8KTp3xwQ0CH +m9fWxSDs12FdyMhDxrJPeywvHtZ18/7cl3dr8wnFVE0s4ongnRDXsNk5xN6J3AaO +KqW4HF9cbwZqzLILy8TrO+EK/EQV9FypbrxqvxAlP1kezIA2CJNzVRAgimSuV/H7 +05HTnp5W06fjtEf8U1CUrdNetoSROUo1j/IMGPYGlsBFYAGrj5y/BlKd+3T3kjRp +Xje7HpiykjrZHn0WDp04Ln+u9nveEewXmHKch313emt7HpW0xspp8JM8OZtEKozk +D5PfYdBfMJJOUlqovCCzTTJ6kNOahknKXFeO/qs5IwKCAQEAjF0/zhWikXF/fcfD +Bql2z2vTYdEmSvdjHSYff1Nn90K71DdVk5wytOxJM/sfp/z+yoMNjVKIL/IGQw5Z +va4xFx+CUhGjxlZ0pLEjT37U9gHsGYsK5jvslLvG/MixfH5AOwoqi5ERQVTpbIF9 +jvVPEAh6YSu/ExglWGJIxTsRUIblxvTxdjEnl/p+rlM0RNJnA6vpo1J51BXA7CdF +7bZQ5u0Feo/bK1I70ClYg/DGfkmYEV0pZG5cxNkqfDbgwsqWa7YGLGd94xkh+ymq +jETqxeWyozxhbQ83nYpfzeVc7t//qlJ8b5uf0wUKoRmtNr9rtp13lzP/21REzPXW +w+oxwQKCAQAoAf2Y2lAw25KlPuq4ZlU+n9u8FkBFnWMJvBMJ7c9XHNmJMf6NkLaO +RTvWy3geYvbwxf7J9QnRH+vRTciR05cY+Olxn6A03N5nwXxRrToH3MsiWeZ0NnX/ +u8KNUYcUHbV60ulqOThuYHQ/3I9EUUAijaqqjV2sXts19ke68W0x6HKpBJhuudT9 +ktPzbdhyP8Xyl/pocNnerXwexZBsi3Ye6+eIDFz+8OnsBHVcgNPluS72tvsxgqj7 +ciNTiBGCxKKo55eCWBhRPpXE2WUrf/hGPYsBMl2h6FfZMH1+M/N7B4tgdJmS+woU +Ftws8lTjJEiwA6HFN1ZxrwLNjJobx9yPAoIBAE0igsBuWWn6rXeOPylYg4264XOq +8gb94pte2n9amDgCzyCn8m6AL3snLC/AoCD19DK+gyK0ukoesXPa3iX6w2xv69ZC +urDx36Jhd4zrJb4QsFPoeKfDP+UvNVZaS41vipRRzY/y11em15prUZ4U8FA/UT1Y +FzkBo9r6iUZRnyBLppMuEfWASDtuRNmeIHynoT1AcQOH3l9vR210iEpmAuJr0CYA +bvTuz3UzzGGEAuIUvuaiRtkfKY52jBmiEr7SSPCr1HvLj3Ccz8bgjgR2kiXmcU50 +1zLnaPAD44LZ/0Fjqj+PimQGT6K7CNXPllmYh7MvoU52g3SVPf6rHlIR0Nc= +-----END RSA PRIVATE KEY----- diff --git a/nixpkgs/nixos/tests/initrd-network-ssh/openssh.pub b/nixpkgs/nixos/tests/initrd-network-ssh/openssh.pub new file mode 100644 index 000000000000..5b72b8085f27 --- /dev/null +++ b/nixpkgs/nixos/tests/initrd-network-ssh/openssh.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDv70DY8I85MCUWatpuWtRYD5Vl0C4t8zold4ew4XtlsLYXqW9aa8Mz3bkNCqGICNyBae3lYdZLlj5vuUae7NPt/Ip6Bd3zScJGaqWlIbeytfT+XFFpAAkvN+gONywX5x6zBCjoZKxicIdXEw2zTTuBttVjd/uj6xZm0t3Ek8PaIe+XlmssrR3VEsyZTpx8iEgAGUvRLwRaK/b1FE56mwJCdLR2crZv6+ifVIJiiCC8Ax7ZOgAgAnz66pph8YDKf2rx+NmJEfEuYoOr3S0qgJQvmv/Z2YQgT63xP/c98eirVDNm2z1b2J2EF1PbV0RcG0bKgy1ySnmeOTmbzYtANiI5eS5KghSFI616HmDt8G/UG1VIRHEmDdR4mM94YoXB2fNqmYAoLbD/pXzFwgg00D5+FkDsH18PVECLZMQJn6DIxgNzhP5RKUxKb1x+9I6t/b7meC0VvziceOq8KXEIwu75sJYrwdqapTOET9rkttH7jwTs/IazcpX6xYU30o6dqROOP/qJCFdlGGB4Arf0XZdOTkyzlj4FFJK2cygxkDaCshX6MeLXNtaiRWpc8jlhGjmKxH8+C0oBXoV81a6ZxigUn0XjSkwdDqElUSa2sIHHsQClKXeZIZtAlSaKUsLnWo2HBBFsP/9m2kis3zYdMwrNnqSPuQMJ2x9/7CyeQheaYw== tmtynkky@duuni diff --git a/nixpkgs/nixos/tests/initrd-network.nix b/nixpkgs/nixos/tests/initrd-network.nix new file mode 100644 index 000000000000..ed9b82e2da77 --- /dev/null +++ b/nixpkgs/nixos/tests/initrd-network.nix @@ -0,0 +1,22 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "initrd-network"; + + meta.maintainers = [ pkgs.stdenv.lib.maintainers.eelco ]; + + machine = { ... }: { + imports = [ ../modules/profiles/minimal.nix ]; + boot.initrd.network.enable = true; + boot.initrd.network.postCommands = + '' + ip addr | grep 10.0.2.15 || exit 1 + ping -c1 10.0.2.2 || exit 1 + ''; + }; + + testScript = + '' + startAll; + $machine->waitForUnit("multi-user.target"); + $machine->succeed("ip link >&2"); + ''; +}) diff --git a/nixpkgs/nixos/tests/installer.nix b/nixpkgs/nixos/tests/installer.nix new file mode 100644 index 000000000000..2553a0d116ae --- /dev/null +++ b/nixpkgs/nixos/tests/installer.nix @@ -0,0 +1,677 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + + # The configuration to install. + makeConfig = { bootLoader, grubVersion, grubDevice, grubIdentifier, grubUseEfi + , extraConfig, forceGrubReinstallCount ? 0 + }: + pkgs.writeText "configuration.nix" '' + { config, lib, pkgs, modulesPath, ... }: + + { imports = + [ ./hardware-configuration.nix + <nixpkgs/nixos/modules/testing/test-instrumentation.nix> + ]; + + # To ensure that we can rebuild the grub configuration on the nixos-rebuild + system.extraDependencies = with pkgs; [ stdenvNoCC ]; + + ${optionalString (bootLoader == "grub") '' + boot.loader.grub.version = ${toString grubVersion}; + ${optionalString (grubVersion == 1) '' + boot.loader.grub.splashImage = null; + ''} + + boot.loader.grub.extraConfig = "serial; terminal_output.serial"; + ${if grubUseEfi then '' + boot.loader.grub.device = "nodev"; + boot.loader.grub.efiSupport = true; + boot.loader.grub.efiInstallAsRemovable = true; # XXX: needed for OVMF? + '' else '' + boot.loader.grub.device = "${grubDevice}"; + boot.loader.grub.fsIdentifier = "${grubIdentifier}"; + ''} + + boot.loader.grub.configurationLimit = 100 + ${toString forceGrubReinstallCount}; + ''} + + ${optionalString (bootLoader == "systemd-boot") '' + boot.loader.systemd-boot.enable = true; + ''} + + users.users.alice = { + isNormalUser = true; + home = "/home/alice"; + description = "Alice Foobar"; + }; + + hardware.enableAllFirmware = lib.mkForce false; + + services.udisks2.enable = lib.mkDefault false; + + ${replaceChars ["\n"] ["\n "] extraConfig} + } + ''; + + + # The test script boots a NixOS VM, installs NixOS on an empty hard + # disk, and then reboot from the hard disk. It's parameterized with + # a test script fragment `createPartitions', which must create + # partitions and filesystems. + testScriptFun = { bootLoader, createPartitions, grubVersion, grubDevice, grubUseEfi + , grubIdentifier, preBootCommands, extraConfig + }: + let + iface = if grubVersion == 1 then "ide" else "virtio"; + isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi); + + # FIXME don't duplicate the -enable-kvm etc. flags here yet again! + qemuFlags = + (if system == "x86_64-linux" then "-m 768 " else "-m 512 ") + + (optionalString (system == "x86_64-linux") "-cpu kvm64 ") + + (optionalString (system == "aarch64-linux") "-enable-kvm -machine virt,gic-version=host -cpu host "); + + hdFlags = ''hda => "vm-state-machine/machine.qcow2", hdaInterface => "${iface}", '' + + optionalString isEfi (if pkgs.stdenv.isAarch64 + then ''bios => "${pkgs.OVMF.fd}/FV/QEMU_EFI.fd", '' + else ''bios => "${pkgs.OVMF.fd}/FV/OVMF.fd", ''); + in if !isEfi && !(pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) then + throw "Non-EFI boot methods are only supported on i686 / x86_64" + else '' + $machine->start; + + # Make sure that we get a login prompt etc. + $machine->succeed("echo hello"); + #$machine->waitForUnit('getty@tty2'); + #$machine->waitForUnit("rogue"); + $machine->waitForUnit("nixos-manual"); + + # Wait for hard disks to appear in /dev + $machine->succeed("udevadm settle"); + + # Partition the disk. + ${createPartitions} + + # Create the NixOS configuration. + $machine->succeed("nixos-generate-config --root /mnt"); + + $machine->succeed("cat /mnt/etc/nixos/hardware-configuration.nix >&2"); + + $machine->copyFileFromHost( + "${ makeConfig { inherit bootLoader grubVersion grubDevice grubIdentifier grubUseEfi extraConfig; } }", + "/mnt/etc/nixos/configuration.nix"); + + # Perform the installation. + $machine->succeed("nixos-install < /dev/null >&2"); + + # Do it again to make sure it's idempotent. + $machine->succeed("nixos-install < /dev/null >&2"); + + $machine->succeed("umount /mnt/boot || true"); + $machine->succeed("umount /mnt"); + $machine->succeed("sync"); + + $machine->shutdown; + + # Now see if we can boot the installation. + $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}", name => "boot-after-install" }); + + # For example to enter LUKS passphrase. + ${preBootCommands} + + # Did /boot get mounted? + $machine->waitForUnit("local-fs.target"); + + ${if bootLoader == "grub" then + ''$machine->succeed("test -e /boot/grub");'' + else + ''$machine->succeed("test -e /boot/loader/loader.conf");'' + } + + # Check whether /root has correct permissions. + $machine->succeed("stat -c '%a' /root") =~ /700/ or die; + + # Did the swap device get activated? + # uncomment once https://bugs.freedesktop.org/show_bug.cgi?id=86930 is resolved + $machine->waitForUnit("swap.target"); + $machine->succeed("cat /proc/swaps | grep -q /dev"); + + # Check that the store is in good shape + $machine->succeed("nix-store --verify --check-contents >&2"); + + # Check whether the channel works. + $machine->succeed("nix-env -iA nixos.procps >&2"); + $machine->succeed("type -tP ps | tee /dev/stderr") =~ /.nix-profile/ + or die "nix-env failed"; + + # Check that the daemon works, and that non-root users can run builds (this will build a new profile generation through the daemon) + $machine->succeed("su alice -l -c 'nix-env -iA nixos.procps' >&2"); + + # We need a writable Nix store on next boot. + $machine->copyFileFromHost( + "${ makeConfig { inherit bootLoader grubVersion grubDevice grubIdentifier grubUseEfi extraConfig; forceGrubReinstallCount = 1; } }", + "/etc/nixos/configuration.nix"); + + # Check whether nixos-rebuild works. + $machine->succeed("nixos-rebuild switch >&2"); + + # Test nixos-option. + $machine->succeed("nixos-option boot.initrd.kernelModules | grep virtio_console"); + $machine->succeed("nixos-option boot.initrd.kernelModules | grep 'List of modules'"); + $machine->succeed("nixos-option boot.initrd.kernelModules | grep qemu-guest.nix"); + + $machine->shutdown; + + # Check whether a writable store build works + $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}", name => "rebuild-switch" }); + ${preBootCommands} + $machine->waitForUnit("multi-user.target"); + $machine->copyFileFromHost( + "${ makeConfig { inherit bootLoader grubVersion grubDevice grubIdentifier grubUseEfi extraConfig; forceGrubReinstallCount = 2; } }", + "/etc/nixos/configuration.nix"); + $machine->succeed("nixos-rebuild boot >&2"); + $machine->shutdown; + + # And just to be sure, check that the machine still boots after + # "nixos-rebuild switch". + $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}", "boot-after-rebuild-switch" }); + ${preBootCommands} + $machine->waitForUnit("network.target"); + $machine->shutdown; + ''; + + + makeInstallerTest = name: + { createPartitions, preBootCommands ? "", extraConfig ? "" + , extraInstallerConfig ? {} + , bootLoader ? "grub" # either "grub" or "systemd-boot" + , grubVersion ? 2, grubDevice ? "/dev/vda", grubIdentifier ? "uuid", grubUseEfi ? false + , enableOCR ? false, meta ? {} + }: + makeTest { + inherit enableOCR; + name = "installer-" + name; + meta = with pkgs.stdenv.lib.maintainers; { + # put global maintainers here, individuals go into makeInstallerTest fkt call + maintainers = (meta.maintainers or []); + }; + nodes = { + + # The configuration of the machine used to run "nixos-install". + machine = + { pkgs, ... }: + + { imports = + [ ../modules/profiles/installation-device.nix + ../modules/profiles/base.nix + extraInstallerConfig + ]; + + virtualisation.diskSize = 8 * 1024; + virtualisation.memorySize = 1024; + + # Use a small /dev/vdb as the root disk for the + # installer. This ensures the target disk (/dev/vda) is + # the same during and after installation. + virtualisation.emptyDiskImages = [ 512 ]; + virtualisation.bootDevice = + if grubVersion == 1 then "/dev/sdb" else "/dev/vdb"; + virtualisation.qemu.diskInterface = + if grubVersion == 1 then "scsi" else "virtio"; + + boot.loader.systemd-boot.enable = mkIf (bootLoader == "systemd-boot") true; + + hardware.enableAllFirmware = mkForce false; + + # The test cannot access the network, so any packages we + # need must be included in the VM. + system.extraDependencies = with pkgs; + [ sudo + libxml2.bin + libxslt.bin + desktop-file-utils + docbook5 + docbook_xsl_ns + unionfs-fuse + ntp + nixos-artwork.wallpapers.simple-dark-gray-bottom + perlPackages.XMLLibXML + perlPackages.ListCompare + shared-mime-info + texinfo + xorg.lndir + + # add curl so that rather than seeing the test attempt to download + # curl's tarball, we see what it's trying to download + curl + ] + ++ optional (bootLoader == "grub" && grubVersion == 1) pkgs.grub + ++ optionals (bootLoader == "grub" && grubVersion == 2) [ pkgs.grub2 pkgs.grub2_efi ]; + + services.udisks2.enable = mkDefault false; + + nix.binaryCaches = mkForce [ ]; + nix.extraOptions = + '' + hashed-mirrors = + connect-timeout = 1 + ''; + }; + + }; + + testScript = testScriptFun { + inherit bootLoader createPartitions preBootCommands + grubVersion grubDevice grubIdentifier grubUseEfi extraConfig; + }; + }; + + +in { + + # !!! `parted mkpart' seems to silently create overlapping partitions. + + + # The (almost) simplest partitioning scheme: a swap partition and + # one big filesystem partition. + simple = makeInstallerTest "simple" + { createPartitions = + '' + $machine->succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + . " mkpart primary linux-swap 1M 1024M" + . " mkpart primary ext2 1024M -1s", + "udevadm settle", + "mkswap /dev/vda1 -L swap", + "swapon -L swap", + "mkfs.ext3 -L nixos /dev/vda2", + "mount LABEL=nixos /mnt", + ); + ''; + }; + + # Simple GPT/UEFI configuration using systemd-boot with 3 partitions: ESP, swap & root filesystem + simpleUefiSystemdBoot = makeInstallerTest "simpleUefiSystemdBoot" + { createPartitions = + '' + $machine->succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel gpt" + . " mkpart ESP fat32 1M 50MiB" # /boot + . " set 1 boot on" + . " mkpart primary linux-swap 50MiB 1024MiB" + . " mkpart primary ext2 1024MiB -1MiB", # / + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.ext3 -L nixos /dev/vda3", + "mount LABEL=nixos /mnt", + "mkfs.vfat -n BOOT /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=BOOT /mnt/boot", + ); + ''; + bootLoader = "systemd-boot"; + }; + + simpleUefiGrub = makeInstallerTest "simpleUefiGrub" + { createPartitions = + '' + $machine->succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel gpt" + . " mkpart ESP fat32 1M 50MiB" # /boot + . " set 1 boot on" + . " mkpart primary linux-swap 50MiB 1024MiB" + . " mkpart primary ext2 1024MiB -1MiB", # / + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.ext3 -L nixos /dev/vda3", + "mount LABEL=nixos /mnt", + "mkfs.vfat -n BOOT /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=BOOT /mnt/boot", + ); + ''; + bootLoader = "grub"; + grubUseEfi = true; + }; + + # Same as the previous, but now with a separate /boot partition. + separateBoot = makeInstallerTest "separateBoot" + { createPartitions = + '' + $machine->succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + . " mkpart primary ext2 1M 50MB" # /boot + . " mkpart primary linux-swap 50MB 1024M" + . " mkpart primary ext2 1024M -1s", # / + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.ext3 -L nixos /dev/vda3", + "mount LABEL=nixos /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=boot /mnt/boot", + ); + ''; + }; + + # Same as the previous, but with fat32 /boot. + separateBootFat = makeInstallerTest "separateBootFat" + { createPartitions = + '' + $machine->succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + . " mkpart primary ext2 1M 50MB" # /boot + . " mkpart primary linux-swap 50MB 1024M" + . " mkpart primary ext2 1024M -1s", # / + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.ext3 -L nixos /dev/vda3", + "mount LABEL=nixos /mnt", + "mkfs.vfat -n BOOT /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=BOOT /mnt/boot", + ); + ''; + }; + + # zfs on / with swap + zfsroot = makeInstallerTest "zfs-root" + { + extraInstallerConfig = { + boot.supportedFilesystems = [ "zfs" ]; + }; + + extraConfig = '' + boot.supportedFilesystems = [ "zfs" ]; + + # Using by-uuid overrides the default of by-id, and is unique + # to the qemu disks, as they don't produce by-id paths for + # some reason. + boot.zfs.devNodes = "/dev/disk/by-uuid/"; + networking.hostId = "00000000"; + ''; + + createPartitions = + '' + $machine->succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + . " mkpart primary linux-swap 1M 1024M" + . " mkpart primary 1024M -1s", + "udevadm settle", + + "mkswap /dev/vda1 -L swap", + "swapon -L swap", + + "zpool create rpool /dev/vda2", + "zfs create -o mountpoint=legacy rpool/root", + "mount -t zfs rpool/root /mnt", + + "udevadm settle" + ); + ''; + }; + + # Create two physical LVM partitions combined into one volume group + # that contains the logical swap and root partitions. + lvm = makeInstallerTest "lvm" + { createPartitions = + '' + $machine->succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + . " mkpart primary 1M 2048M" # PV1 + . " set 1 lvm on" + . " mkpart primary 2048M -1s" # PV2 + . " set 2 lvm on", + "udevadm settle", + "pvcreate /dev/vda1 /dev/vda2", + "vgcreate MyVolGroup /dev/vda1 /dev/vda2", + "lvcreate --size 1G --name swap MyVolGroup", + "lvcreate --size 2G --name nixos MyVolGroup", + "mkswap -f /dev/MyVolGroup/swap -L swap", + "swapon -L swap", + "mkfs.xfs -L nixos /dev/MyVolGroup/nixos", + "mount LABEL=nixos /mnt", + ); + ''; + }; + + # Boot off an encrypted root partition + luksroot = makeInstallerTest "luksroot" + { createPartitions = '' + $machine->succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + . " mkpart primary ext2 1M 50MB" # /boot + . " mkpart primary linux-swap 50M 1024M" + . " mkpart primary 1024M -1s", # LUKS + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "modprobe dm_mod dm_crypt", + "echo -n supersecret | cryptsetup luksFormat -q /dev/vda3 -", + "echo -n supersecret | cryptsetup luksOpen --key-file - /dev/vda3 cryptroot", + "mkfs.ext3 -L nixos /dev/mapper/cryptroot", + "mount LABEL=nixos /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=boot /mnt/boot", + ); + ''; + extraConfig = '' + boot.kernelParams = lib.mkAfter [ "console=tty0" ]; + ''; + enableOCR = true; + preBootCommands = '' + $machine->start; + $machine->waitForText(qr/Passphrase for/); + $machine->sendChars("supersecret\n"); + ''; + }; + + # Test whether opening encrypted filesystem with keyfile + # Checks for regression of missing cryptsetup, when no luks device without + # keyfile is configured + encryptedFSWithKeyfile = makeInstallerTest "encryptedFSWithKeyfile" + { createPartitions = '' + $machine->succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + . " mkpart primary ext2 1M 50MB" # /boot + . " mkpart primary linux-swap 50M 1024M" + . " mkpart primary 1024M 1280M" # LUKS with keyfile + . " mkpart primary 1280M -1s", + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.ext3 -L nixos /dev/vda4", + "mount LABEL=nixos /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=boot /mnt/boot", + "modprobe dm_mod dm_crypt", + "echo -n supersecret > /mnt/keyfile", + "cryptsetup luksFormat -q /dev/vda3 --key-file /mnt/keyfile", + "cryptsetup luksOpen --key-file /mnt/keyfile /dev/vda3 crypt", + "mkfs.ext3 -L test /dev/mapper/crypt", + "cryptsetup luksClose crypt", + "mkdir -p /mnt/test" + ); + ''; + extraConfig = '' + fileSystems."/test" = + { device = "/dev/disk/by-label/test"; + fsType = "ext3"; + encrypted.enable = true; + encrypted.blkDev = "/dev/vda3"; + encrypted.label = "crypt"; + encrypted.keyFile = "/mnt-root/keyfile"; + }; + ''; + }; + + + swraid = makeInstallerTest "swraid" + { createPartitions = + '' + $machine->succeed( + "flock /dev/vda parted --script /dev/vda --" + . " mklabel msdos" + . " mkpart primary ext2 1M 100MB" # /boot + . " mkpart extended 100M -1s" + . " mkpart logical 102M 2102M" # md0 (root), first device + . " mkpart logical 2103M 4103M" # md0 (root), second device + . " mkpart logical 4104M 4360M" # md1 (swap), first device + . " mkpart logical 4361M 4617M", # md1 (swap), second device + "udevadm settle", + "ls -l /dev/vda* >&2", + "cat /proc/partitions >&2", + "udevadm control --stop-exec-queue", + "mdadm --create --force /dev/md0 --metadata 1.2 --level=raid1 --raid-devices=2 /dev/vda5 /dev/vda6", + "mdadm --create --force /dev/md1 --metadata 1.2 --level=raid1 --raid-devices=2 /dev/vda7 /dev/vda8", + "udevadm control --start-exec-queue", + "udevadm settle", + "mkswap -f /dev/md1 -L swap", + "swapon -L swap", + "mkfs.ext3 -L nixos /dev/md0", + "mount LABEL=nixos /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir /mnt/boot", + "mount LABEL=boot /mnt/boot", + "udevadm settle", + ); + ''; + preBootCommands = '' + $machine->start; + $machine->fail("dmesg | grep 'immediate safe mode'"); + ''; + }; + + # Test a basic install using GRUB 1. + grub1 = makeInstallerTest "grub1" + { createPartitions = + '' + $machine->succeed( + "flock /dev/sda parted --script /dev/sda -- mklabel msdos" + . " mkpart primary linux-swap 1M 1024M" + . " mkpart primary ext2 1024M -1s", + "udevadm settle", + "mkswap /dev/sda1 -L swap", + "swapon -L swap", + "mkfs.ext3 -L nixos /dev/sda2", + "mount LABEL=nixos /mnt", + "mkdir -p /mnt/tmp", + ); + ''; + grubVersion = 1; + grubDevice = "/dev/sda"; + }; + + # Test using labels to identify volumes in grub + simpleLabels = makeInstallerTest "simpleLabels" { + createPartitions = '' + $machine->succeed( + "sgdisk -Z /dev/vda", + "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.ext4 -L root /dev/vda3", + "mount LABEL=root /mnt", + ); + ''; + grubIdentifier = "label"; + }; + + # Test using the provided disk name within grub + # TODO: Fix udev so the symlinks are unneeded in /dev/disks + simpleProvided = makeInstallerTest "simpleProvided" { + createPartitions = '' + my $UUID = "\$(blkid -s UUID -o value /dev/vda2)"; + $machine->succeed( + "sgdisk -Z /dev/vda", + "sgdisk -n 1:0:+1M -n 2:0:+100M -n 3:0:+1G -N 4 -t 1:ef02 -t 2:8300 -t 3:8200 -t 4:8300 -c 2:boot -c 4:root /dev/vda", + "mkswap /dev/vda3 -L swap", + "swapon -L swap", + "mkfs.ext4 -L boot /dev/vda2", + "mkfs.ext4 -L root /dev/vda4", + ); + $machine->execute("ln -s ../../vda2 /dev/disk/by-uuid/$UUID"); + $machine->execute("ln -s ../../vda4 /dev/disk/by-label/root"); + $machine->succeed( + "mount /dev/disk/by-label/root /mnt", + "mkdir /mnt/boot", + "mount /dev/disk/by-uuid/$UUID /mnt/boot" + ); + ''; + grubIdentifier = "provided"; + }; + + # Simple btrfs grub testing + btrfsSimple = makeInstallerTest "btrfsSimple" { + createPartitions = '' + $machine->succeed( + "sgdisk -Z /dev/vda", + "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.btrfs -L root /dev/vda3", + "mount LABEL=root /mnt", + ); + ''; + }; + + # Test to see if we can detect /boot and /nix on subvolumes + btrfsSubvols = makeInstallerTest "btrfsSubvols" { + createPartitions = '' + $machine->succeed( + "sgdisk -Z /dev/vda", + "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.btrfs -L root /dev/vda3", + "btrfs device scan", + "mount LABEL=root /mnt", + "btrfs subvol create /mnt/boot", + "btrfs subvol create /mnt/nixos", + "btrfs subvol create /mnt/nixos/default", + "umount /mnt", + "mount -o defaults,subvol=nixos/default LABEL=root /mnt", + "mkdir /mnt/boot", + "mount -o defaults,subvol=boot LABEL=root /mnt/boot", + ); + ''; + }; + + # Test to see if we can detect default and aux subvolumes correctly + btrfsSubvolDefault = makeInstallerTest "btrfsSubvolDefault" { + createPartitions = '' + $machine->succeed( + "sgdisk -Z /dev/vda", + "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.btrfs -L root /dev/vda3", + "btrfs device scan", + "mount LABEL=root /mnt", + "btrfs subvol create /mnt/badpath", + "btrfs subvol create /mnt/badpath/boot", + "btrfs subvol create /mnt/nixos", + "btrfs subvol set-default \$(btrfs subvol list /mnt | grep 'nixos' | awk '{print \$2}') /mnt", + "umount /mnt", + "mount -o defaults LABEL=root /mnt", + "mkdir -p /mnt/badpath/boot", # Help ensure the detection mechanism is actually looking up subvolumes + "mkdir /mnt/boot", + "mount -o defaults,subvol=badpath/boot LABEL=root /mnt/boot", + ); + ''; + }; + +} diff --git a/nixpkgs/nixos/tests/ipfs.nix b/nixpkgs/nixos/tests/ipfs.nix new file mode 100644 index 000000000000..3cff7e99ff88 --- /dev/null +++ b/nixpkgs/nixos/tests/ipfs.nix @@ -0,0 +1,55 @@ + +import ./make-test.nix ({ pkgs, ...} : { + name = "ipfs"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ mguentner ]; + }; + + nodes = { + adder = + { ... }: + { + services.ipfs = { + enable = true; + defaultMode = "norouting"; + gatewayAddress = "/ip4/127.0.0.1/tcp/2323"; + apiAddress = "/ip4/127.0.0.1/tcp/2324"; + }; + networking.firewall.allowedTCPPorts = [ 4001 ]; + }; + getter = + { ... }: + { + services.ipfs = { + enable = true; + defaultMode = "norouting"; + autoMount = true; + }; + networking.firewall.allowedTCPPorts = [ 4001 ]; + }; + }; + + testScript = '' + startAll; + $adder->waitForUnit("ipfs-norouting"); + $getter->waitForUnit("ipfs-norouting"); + + # wait until api is available + $adder->waitUntilSucceeds("ipfs --api /ip4/127.0.0.1/tcp/2324 id"); + my $addrId = $adder->succeed("ipfs --api /ip4/127.0.0.1/tcp/2324 id -f=\"<id>\""); + my $addrIp = (split /[ \/]+/, $adder->succeed("ip -o -4 addr show dev eth1"))[3]; + + $adder->mustSucceed("[ -n \"\$(ipfs --api /ip4/127.0.0.1/tcp/2324 config Addresses.Gateway | grep /ip4/127.0.0.1/tcp/2323)\" ]"); + + # wait until api is available + $getter->waitUntilSucceeds("ipfs --api /ip4/127.0.0.1/tcp/5001 id"); + my $ipfsHash = $adder->mustSucceed("echo fnord | ipfs --api /ip4/127.0.0.1/tcp/2324 add | cut -d' ' -f2"); + chomp($ipfsHash); + + $adder->mustSucceed("[ -n \"\$(echo fnord | ipfs --api /ip4/127.0.0.1/tcp/2324 add | grep added)\" ]"); + + $getter->mustSucceed("ipfs --api /ip4/127.0.0.1/tcp/5001 swarm connect /ip4/$addrIp/tcp/4001/ipfs/$addrId"); + $getter->mustSucceed("[ -n \"\$(ipfs --api /ip4/127.0.0.1/tcp/5001 cat /ipfs/$ipfsHash | grep fnord)\" ]"); + $getter->mustSucceed("[ -n \"$(cat /ipfs/$ipfsHash | grep fnord)\" ]"); + ''; +}) diff --git a/nixpkgs/nixos/tests/ipv6.nix b/nixpkgs/nixos/tests/ipv6.nix new file mode 100644 index 000000000000..97f348a9beeb --- /dev/null +++ b/nixpkgs/nixos/tests/ipv6.nix @@ -0,0 +1,79 @@ +# Test of IPv6 functionality in NixOS, including whether router +# solicication/advertisement using radvd works. + +import ./make-test.nix ({ pkgs, ...} : { + name = "ipv6"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + nodes = + { client = { ... }: { }; + + server = + { ... }: + { services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + + router = + { ... }: + { services.radvd.enable = true; + services.radvd.config = + '' + interface eth1 { + AdvSendAdvert on; + # ULA prefix (RFC 4193). + prefix fd60:cc69:b537:1::/64 { }; + }; + ''; + }; + }; + + testScript = + '' + # Start the router first so that it respond to router solicitations. + $router->waitForUnit("radvd"); + + startAll; + + $client->waitForUnit("network.target"); + $server->waitForUnit("network.target"); + $server->waitForUnit("httpd.service"); + + # Wait until the given interface has a non-tentative address of + # the desired scope (i.e. has completed Duplicate Address + # Detection). + sub waitForAddress { + my ($machine, $iface, $scope) = @_; + $machine->waitUntilSucceeds("[ `ip -o -6 addr show dev $iface scope $scope | grep -v tentative | wc -l` -ge 1 ]"); + my $ip = (split /[ \/]+/, $machine->succeed("ip -o -6 addr show dev $iface scope $scope"))[3]; + $machine->log("$scope address on $iface is $ip"); + return $ip; + } + + subtest "loopback address", sub { + $client->succeed("ping -c 1 ::1 >&2"); + $client->fail("ping -c 1 ::2 >&2"); + }; + + subtest "local link addressing", sub { + my $clientIp = waitForAddress $client, "eth1", "link"; + my $serverIp = waitForAddress $server, "eth1", "link"; + $client->succeed("ping -c 1 $clientIp%eth1 >&2"); + $client->succeed("ping -c 1 $serverIp%eth1 >&2"); + }; + + subtest "global addressing", sub { + my $clientIp = waitForAddress $client, "eth1", "global"; + my $serverIp = waitForAddress $server, "eth1", "global"; + $client->succeed("ping -c 1 $clientIp >&2"); + $client->succeed("ping -c 1 $serverIp >&2"); + $client->succeed("curl --fail -g http://[$serverIp]"); + $client->fail("curl --fail -g http://[$clientIp]"); + }; + + # TODO: test reachability of a machine on another network. + ''; +}) diff --git a/nixpkgs/nixos/tests/jackett.nix b/nixpkgs/nixos/tests/jackett.nix new file mode 100644 index 000000000000..399a0c272327 --- /dev/null +++ b/nixpkgs/nixos/tests/jackett.nix @@ -0,0 +1,18 @@ +import ./make-test.nix ({ lib, ... }: + +with lib; + +rec { + name = "jackett"; + meta.maintainers = with maintainers; [ etu ]; + + nodes.machine = + { pkgs, ... }: + { services.jackett.enable = true; }; + + testScript = '' + $machine->waitForUnit('jackett.service'); + $machine->waitForOpenPort('9117'); + $machine->succeed("curl --fail http://localhost:9117/"); + ''; +}) diff --git a/nixpkgs/nixos/tests/jenkins.nix b/nixpkgs/nixos/tests/jenkins.nix new file mode 100644 index 000000000000..4f2d2085cd1a --- /dev/null +++ b/nixpkgs/nixos/tests/jenkins.nix @@ -0,0 +1,50 @@ +# verifies: +# 1. jenkins service starts on master node +# 2. jenkins user can be extended on both master and slave +# 3. jenkins service not started on slave node + +import ./make-test.nix ({ pkgs, ...} : { + name = "jenkins"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ bjornfor coconnor domenkozar eelco chaoflow ]; + }; + + nodes = { + + master = + { ... }: + { services.jenkins.enable = true; + + # should have no effect + services.jenkinsSlave.enable = true; + + users.users.jenkins.extraGroups = [ "users" ]; + + systemd.services.jenkins.serviceConfig.TimeoutStartSec = "6min"; + }; + + slave = + { ... }: + { services.jenkinsSlave.enable = true; + + users.users.jenkins.extraGroups = [ "users" ]; + }; + + }; + + testScript = '' + startAll; + + $master->waitForUnit("jenkins"); + + $master->mustSucceed("curl http://localhost:8080 | grep 'Authentication required'"); + + print $master->execute("sudo -u jenkins groups"); + $master->mustSucceed("sudo -u jenkins groups | grep jenkins | grep users"); + + print $slave->execute("sudo -u jenkins groups"); + $slave->mustSucceed("sudo -u jenkins groups | grep jenkins | grep users"); + + $slave->mustFail("systemctl is-enabled jenkins.service"); + ''; +}) diff --git a/nixpkgs/nixos/tests/kafka.nix b/nixpkgs/nixos/tests/kafka.nix new file mode 100644 index 000000000000..72f91f6428a5 --- /dev/null +++ b/nixpkgs/nixos/tests/kafka.nix @@ -0,0 +1,75 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + makeKafkaTest = name: kafkaPackage: (makeTest { + inherit name; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ nequissimus ]; + }; + + nodes = { + zookeeper1 = { ... }: { + services.zookeeper = { + enable = true; + }; + + networking.firewall.allowedTCPPorts = [ 2181 ]; + virtualisation.memorySize = 1024; + }; + kafka = { ... }: { + services.apache-kafka = { + enable = true; + extraProperties = '' + offsets.topic.replication.factor = 1 + zookeeper.session.timeout.ms = 600000 + ''; + package = kafkaPackage; + zookeeper = "zookeeper1:2181"; + # These are the default options, but UseCompressedOops doesn't work with 32bit JVM + jvmOptions = [ + "-server" "-Xmx1G" "-Xms1G" "-XX:+UseParNewGC" "-XX:+UseConcMarkSweepGC" "-XX:+CMSClassUnloadingEnabled" + "-XX:+CMSScavengeBeforeRemark" "-XX:+DisableExplicitGC" "-Djava.awt.headless=true" "-Djava.net.preferIPv4Stack=true" + ] ++ optionals (! pkgs.stdenv.isi686 ) [ "-XX:+UseCompressedOops" ]; + }; + + networking.firewall.allowedTCPPorts = [ 9092 ]; + # i686 tests: qemu-system-i386 can simulate max 2047MB RAM (not 2048) + virtualisation.memorySize = 2047; + }; + }; + + testScript = '' + startAll; + + $zookeeper1->waitForUnit("default.target"); + $zookeeper1->waitForUnit("zookeeper.service"); + $zookeeper1->waitForOpenPort(2181); + + $kafka->waitForUnit("default.target"); + $kafka->waitForUnit("apache-kafka.service"); + $kafka->waitForOpenPort(9092); + + $kafka->waitUntilSucceeds("${kafkaPackage}/bin/kafka-topics.sh --create --zookeeper zookeeper1:2181 --partitions 1 --replication-factor 1 --topic testtopic"); + $kafka->mustSucceed("echo 'test 1' | ${kafkaPackage}/bin/kafka-console-producer.sh --broker-list localhost:9092 --topic testtopic"); + '' + (if name == "kafka_0_9" then '' + $kafka->mustSucceed("${kafkaPackage}/bin/kafka-console-consumer.sh --zookeeper zookeeper1:2181 --topic testtopic --from-beginning --max-messages 1 | grep 'test 1'"); + '' else '' + $kafka->mustSucceed("${kafkaPackage}/bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic testtopic --from-beginning --max-messages 1 | grep 'test 1'"); + ''); + }); + +in with pkgs; { + kafka_0_9 = makeKafkaTest "kafka_0_9" apacheKafka_0_9; + kafka_0_10 = makeKafkaTest "kafka_0_10" apacheKafka_0_10; + kafka_0_11 = makeKafkaTest "kafka_0_11" apacheKafka_0_11; + kafka_1_0 = makeKafkaTest "kafka_1_0" apacheKafka_1_0; + kafka_1_1 = makeKafkaTest "kafka_1_1" apacheKafka_1_1; + kafka_2_0 = makeKafkaTest "kafka_2_0" apacheKafka_2_0; + kafka_2_1 = makeKafkaTest "kafka_2_1" apacheKafka_2_1; +} diff --git a/nixpkgs/nixos/tests/kerberos/default.nix b/nixpkgs/nixos/tests/kerberos/default.nix new file mode 100644 index 000000000000..f2f1a438918c --- /dev/null +++ b/nixpkgs/nixos/tests/kerberos/default.nix @@ -0,0 +1,7 @@ +{ system ? builtins.currentSystem +, pkgs ? import ../../.. { inherit system; } +}: +{ + mit = import ./mit.nix { inherit system pkgs; }; + heimdal = import ./heimdal.nix { inherit system pkgs; }; +} diff --git a/nixpkgs/nixos/tests/kerberos/heimdal.nix b/nixpkgs/nixos/tests/kerberos/heimdal.nix new file mode 100644 index 000000000000..a0551b131e91 --- /dev/null +++ b/nixpkgs/nixos/tests/kerberos/heimdal.nix @@ -0,0 +1,53 @@ +import ../make-test.nix ({pkgs, ...}: { + name = "kerberos_server-heimdal"; + machine = { config, libs, pkgs, ...}: + { services.kerberos_server = + { enable = true; + realms = { + "FOO.BAR".acl = [{principal = "admin"; access = ["add" "cpw"];}]; + }; + }; + krb5 = { + enable = true; + kerberos = pkgs.heimdalFull; + libdefaults = { + default_realm = "FOO.BAR"; + }; + realms = { + "FOO.BAR" = { + admin_server = "machine"; + kdc = "machine"; + }; + }; + }; + }; + + testScript = '' + $machine->start; + + $machine->succeed( + "kadmin -l init --realm-max-ticket-life='8 day' \\ + --realm-max-renewable-life='10 day' FOO.BAR" + ); + + $machine->succeed("systemctl restart kadmind.service kdc.service"); + $machine->waitForUnit("kadmind.service"); + $machine->waitForUnit("kdc.service"); + $machine->waitForUnit("kpasswdd.service"); + + $machine->succeed( + "kadmin -l add --password=admin_pw --use-defaults admin" + ); + $machine->succeed( + "kadmin -l ext_keytab --keytab=admin.keytab admin" + ); + $machine->succeed( + "kadmin -p admin -K admin.keytab add --password=alice_pw --use-defaults \\ + alice" + ); + $machine->succeed( + "kadmin -l ext_keytab --keytab=alice.keytab alice" + ); + $machine->succeed("kinit -kt alice.keytab alice"); + ''; +}) diff --git a/nixpkgs/nixos/tests/kerberos/mit.nix b/nixpkgs/nixos/tests/kerberos/mit.nix new file mode 100644 index 000000000000..6da3a384aa99 --- /dev/null +++ b/nixpkgs/nixos/tests/kerberos/mit.nix @@ -0,0 +1,45 @@ +import ../make-test.nix ({pkgs, ...}: { + name = "kerberos_server-mit"; + machine = { config, libs, pkgs, ...}: + { services.kerberos_server = + { enable = true; + realms = { + "FOO.BAR".acl = [{principal = "admin"; access = ["add" "cpw"];}]; + }; + }; + krb5 = { + enable = true; + kerberos = pkgs.krb5Full; + libdefaults = { + default_realm = "FOO.BAR"; + }; + realms = { + "FOO.BAR" = { + admin_server = "machine"; + kdc = "machine"; + }; + }; + }; + users.extraUsers.alice = { isNormalUser = true; }; + }; + + testScript = '' + $machine->start; + + $machine->succeed( + "kdb5_util create -s -r FOO.BAR -P master_key" + ); + + $machine->succeed("systemctl restart kadmind.service kdc.service"); + $machine->waitForUnit("kadmind.service"); + $machine->waitForUnit("kdc.service"); + + $machine->succeed( + "kadmin.local add_principal -pw admin_pw admin" + ); + $machine->succeed( + "kadmin -p admin -w admin_pw addprinc -pw alice_pw alice" + ); + $machine->succeed("echo alice_pw | sudo -u alice kinit"); + ''; +}) diff --git a/nixpkgs/nixos/tests/kernel-latest.nix b/nixpkgs/nixos/tests/kernel-latest.nix new file mode 100644 index 000000000000..f30bd2e2e760 --- /dev/null +++ b/nixpkgs/nixos/tests/kernel-latest.nix @@ -0,0 +1,17 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "kernel-latest"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ nequissimus ]; + }; + + machine = { pkgs, ... }: + { + boot.kernelPackages = pkgs.linuxPackages_latest; + }; + + testScript = + '' + $machine->succeed("uname -s | grep 'Linux'"); + $machine->succeed("uname -a | grep '${pkgs.linuxPackages_latest.kernel.version}'"); + ''; +}) diff --git a/nixpkgs/nixos/tests/kernel-lts.nix b/nixpkgs/nixos/tests/kernel-lts.nix new file mode 100644 index 000000000000..28717fa6a844 --- /dev/null +++ b/nixpkgs/nixos/tests/kernel-lts.nix @@ -0,0 +1,17 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "kernel-lts"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ nequissimus ]; + }; + + machine = { pkgs, ... }: + { + boot.kernelPackages = pkgs.linuxPackages; + }; + + testScript = + '' + $machine->succeed("uname -s | grep 'Linux'"); + $machine->succeed("uname -a | grep '${pkgs.linuxPackages.kernel.version}'"); + ''; +}) diff --git a/nixpkgs/nixos/tests/kexec.nix b/nixpkgs/nixos/tests/kexec.nix new file mode 100644 index 000000000000..db596189d46d --- /dev/null +++ b/nixpkgs/nixos/tests/kexec.nix @@ -0,0 +1,19 @@ +# Test whether fast reboots via kexec work. + +import ./make-test.nix ({ pkgs, ...} : { + name = "kexec"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + machine = { ... }: + { virtualisation.vlans = [ ]; }; + + testScript = + '' + $machine->waitForUnit("multi-user.target"); + $machine->execute("systemctl kexec &"); + $machine->{connected} = 0; + $machine->waitForUnit("multi-user.target"); + ''; +}) diff --git a/nixpkgs/nixos/tests/keymap.nix b/nixpkgs/nixos/tests/keymap.nix new file mode 100644 index 000000000000..2b4c1ab7b052 --- /dev/null +++ b/nixpkgs/nixos/tests/keymap.nix @@ -0,0 +1,157 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; + +let + readyFile = "/tmp/readerReady"; + resultFile = "/tmp/readerResult"; + + testReader = pkgs.writeScript "test-input-reader" '' + #!${pkgs.stdenv.shell} + rm -f ${resultFile} ${resultFile}.tmp + logger "testReader: START: Waiting for $1 characters, expecting '$2'." + touch ${readyFile} + read -r -N $1 chars + rm -f ${readyFile} + + if [ "$chars" == "$2" ]; then + logger -s "testReader: PASS: Got '$2' as expected." 2>${resultFile}.tmp + else + logger -s "testReader: FAIL: Expected '$2' but got '$chars'." 2>${resultFile}.tmp + fi + # rename after the file is written to prevent a race condition + mv ${resultFile}.tmp ${resultFile} + ''; + + + mkKeyboardTest = layout: { extraConfig ? {}, tests }: with pkgs.lib; let + combinedTests = foldAttrs (acc: val: acc ++ val) [] (builtins.attrValues tests); + perlStr = val: "'${escape ["'" "\\"] val}'"; + lq = length combinedTests.qwerty; + le = length combinedTests.expect; + msg = "length mismatch between qwerty (${toString lq}) and expect (${toString le}) lists!"; + send = concatMapStringsSep ", " perlStr combinedTests.qwerty; + expect = if (lq == le) then concatStrings combinedTests.expect else throw msg; + + in makeTest { + name = "keymap-${layout}"; + + machine.services.xserver.desktopManager.xterm.enable = false; + machine.i18n.consoleKeyMap = mkOverride 900 layout; + machine.services.xserver.layout = mkOverride 900 layout; + machine.imports = [ ./common/x11.nix extraConfig ]; + + testScript = '' + + sub mkTest ($$) { + my ($desc, $cmd) = @_; + + subtest $desc, sub { + # prepare and start testReader + $machine->execute("rm -f ${readyFile} ${resultFile}"); + $machine->succeed("$cmd ${testReader} ${toString le} ".q(${escapeShellArg expect} & )); + + if ($desc eq "Xorg keymap") { + # make sure the xterm window is open and has focus + $machine->waitForWindow(qr/testterm/); + $machine->waitUntilSucceeds("${pkgs.xdotool}/bin/xdotool search --sync --onlyvisible --class testterm windowfocus --sync"); + } + + # wait for reader to be ready + $machine->waitForFile("${readyFile}"); + $machine->sleep(1); + + # send all keys + foreach ((${send})) { $machine->sendKeys($_); }; + + # wait for result and check + $machine->waitForFile("${resultFile}"); + $machine->succeed("grep -q 'PASS:' ${resultFile}"); + }; + }; + + $machine->waitForX; + + mkTest "VT keymap", "openvt -sw --"; + mkTest "Xorg keymap", "DISPLAY=:0 xterm -title testterm -class testterm -fullscreen -e"; + ''; + }; + +in pkgs.lib.mapAttrs mkKeyboardTest { + azerty = { + tests = { + azqw.qwerty = [ "q" "w" ]; + azqw.expect = [ "a" "z" ]; + altgr.qwerty = [ "alt_r-2" "alt_r-3" "alt_r-4" "alt_r-5" "alt_r-6" ]; + altgr.expect = [ "~" "#" "{" "[" "|" ]; + }; + + extraConfig.i18n.consoleKeyMap = "azerty/fr"; + extraConfig.services.xserver.layout = "fr"; + }; + + colemak = { + tests = { + homerow.qwerty = [ "a" "s" "d" "f" "j" "k" "l" "semicolon" ]; + homerow.expect = [ "a" "r" "s" "t" "n" "e" "i" "o" ]; + }; + + extraConfig.i18n.consoleKeyMap = "colemak/colemak"; + extraConfig.services.xserver.layout = "us"; + extraConfig.services.xserver.xkbVariant = "colemak"; + }; + + dvorak = { + tests = { + homerow.qwerty = [ "a" "s" "d" "f" "j" "k" "l" "semicolon" ]; + homerow.expect = [ "a" "o" "e" "u" "h" "t" "n" "s" ]; + symbols.qwerty = [ "q" "w" "e" "minus" "equal" ]; + symbols.expect = [ "'" "," "." "[" "]" ]; + }; + }; + + dvp = { + tests = { + homerow.qwerty = [ "a" "s" "d" "f" "j" "k" "l" "semicolon" ]; + homerow.expect = [ "a" "o" "e" "u" "h" "t" "n" "s" ]; + numbers.qwerty = map (x: "shift-${x}") + [ "1" "2" "3" "4" "5" "6" "7" "8" "9" "0" "minus" ]; + numbers.expect = [ "%" "7" "5" "3" "1" "9" "0" "2" "4" "6" "8" ]; + symbols.qwerty = [ "1" "2" "3" "4" "5" "6" "7" "8" "9" "0" "minus" ]; + symbols.expect = [ "&" "[" "{" "}" "(" "=" "*" ")" "+" "]" "!" ]; + }; + + extraConfig.services.xserver.layout = "us"; + extraConfig.services.xserver.xkbVariant = "dvp"; + }; + + neo = { + tests = { + layer1.qwerty = [ "f" "j" ]; + layer1.expect = [ "e" "n" ]; + layer2.qwerty = [ "shift-f" "shift-j" "shift-6" ]; + layer2.expect = [ "E" "N" "$" ]; + layer3.qwerty = [ "caps_lock-d" "caps_lock-f" ]; + layer3.expect = [ "{" "}" ]; + }; + + extraConfig.services.xserver.layout = "de"; + extraConfig.services.xserver.xkbVariant = "neo"; + }; + + qwertz = { + tests = { + zy.qwerty = [ "z" "y" ]; + zy.expect = [ "y" "z" ]; + altgr.qwerty = map (x: "alt_r-${x}") + [ "q" "less" "7" "8" "9" "0" ]; + altgr.expect = [ "@" "|" "{" "[" "]" "}" ]; + }; + + extraConfig.i18n.consoleKeyMap = "de"; + extraConfig.services.xserver.layout = "de"; + }; +} diff --git a/nixpkgs/nixos/tests/krb5/default.nix b/nixpkgs/nixos/tests/krb5/default.nix new file mode 100644 index 000000000000..dd5b2f37202e --- /dev/null +++ b/nixpkgs/nixos/tests/krb5/default.nix @@ -0,0 +1,5 @@ +{ system ? builtins.currentSystem }: +{ + example-config = import ./example-config.nix { inherit system; }; + deprecated-config = import ./deprecated-config.nix { inherit system; }; +} diff --git a/nixpkgs/nixos/tests/krb5/deprecated-config.nix b/nixpkgs/nixos/tests/krb5/deprecated-config.nix new file mode 100644 index 000000000000..7d7926309c95 --- /dev/null +++ b/nixpkgs/nixos/tests/krb5/deprecated-config.nix @@ -0,0 +1,48 @@ +# Verifies that the configuration suggested in deprecated example values +# will result in the expected output. + +import ../make-test.nix ({ pkgs, ...} : { + name = "krb5-with-deprecated-config"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eqyiel ]; + }; + + machine = + { ... }: { + krb5 = { + enable = true; + defaultRealm = "ATHENA.MIT.EDU"; + domainRealm = "athena.mit.edu"; + kdc = "kerberos.mit.edu"; + kerberosAdminServer = "kerberos.mit.edu"; + }; + }; + + testScript = + let snapshot = pkgs.writeText "krb5-with-deprecated-config.conf" '' + [libdefaults] + default_realm = ATHENA.MIT.EDU + + [realms] + ATHENA.MIT.EDU = { + admin_server = kerberos.mit.edu + kdc = kerberos.mit.edu + } + + [domain_realm] + .athena.mit.edu = ATHENA.MIT.EDU + athena.mit.edu = ATHENA.MIT.EDU + + [capaths] + + + [appdefaults] + + + [plugins] + + ''; + in '' + $machine->succeed("diff /etc/krb5.conf ${snapshot}"); + ''; +}) diff --git a/nixpkgs/nixos/tests/krb5/example-config.nix b/nixpkgs/nixos/tests/krb5/example-config.nix new file mode 100644 index 000000000000..f01cf6988eef --- /dev/null +++ b/nixpkgs/nixos/tests/krb5/example-config.nix @@ -0,0 +1,106 @@ +# Verifies that the configuration suggested in (non-deprecated) example values +# will result in the expected output. + +import ../make-test.nix ({ pkgs, ...} : { + name = "krb5-with-example-config"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eqyiel ]; + }; + + machine = + { pkgs, ... }: { + krb5 = { + enable = true; + kerberos = pkgs.krb5Full; + libdefaults = { + default_realm = "ATHENA.MIT.EDU"; + }; + realms = { + "ATHENA.MIT.EDU" = { + admin_server = "athena.mit.edu"; + kdc = "athena.mit.edu"; + }; + }; + domain_realm = { + "example.com" = "EXAMPLE.COM"; + ".example.com" = "EXAMPLE.COM"; + }; + capaths = { + "ATHENA.MIT.EDU" = { + "EXAMPLE.COM" = "."; + }; + "EXAMPLE.COM" = { + "ATHENA.MIT.EDU" = "."; + }; + }; + appdefaults = { + pam = { + debug = false; + ticket_lifetime = 36000; + renew_lifetime = 36000; + max_timeout = 30; + timeout_shift = 2; + initial_timeout = 1; + }; + }; + plugins = { + ccselect = { + disable = "k5identity"; + }; + }; + extraConfig = '' + [logging] + kdc = SYSLOG:NOTICE + admin_server = SYSLOG:NOTICE + default = SYSLOG:NOTICE + ''; + }; + }; + + testScript = + let snapshot = pkgs.writeText "krb5-with-example-config.conf" '' + [libdefaults] + default_realm = ATHENA.MIT.EDU + + [realms] + ATHENA.MIT.EDU = { + admin_server = athena.mit.edu + kdc = athena.mit.edu + } + + [domain_realm] + .example.com = EXAMPLE.COM + example.com = EXAMPLE.COM + + [capaths] + ATHENA.MIT.EDU = { + EXAMPLE.COM = . + } + EXAMPLE.COM = { + ATHENA.MIT.EDU = . + } + + [appdefaults] + pam = { + debug = false + initial_timeout = 1 + max_timeout = 30 + renew_lifetime = 36000 + ticket_lifetime = 36000 + timeout_shift = 2 + } + + [plugins] + ccselect = { + disable = k5identity + } + + [logging] + kdc = SYSLOG:NOTICE + admin_server = SYSLOG:NOTICE + default = SYSLOG:NOTICE + ''; + in '' + $machine->succeed("diff /etc/krb5.conf ${snapshot}"); + ''; +}) diff --git a/nixpkgs/nixos/tests/kubernetes/base.nix b/nixpkgs/nixos/tests/kubernetes/base.nix new file mode 100644 index 000000000000..9d77be131751 --- /dev/null +++ b/nixpkgs/nixos/tests/kubernetes/base.nix @@ -0,0 +1,115 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../../.. { inherit system config; } +}: + +with import ../../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + mkKubernetesBaseTest = + { name, domain ? "my.zyx", test, machines + , pkgs ? import <nixpkgs> { inherit system; } + , certs ? import ./certs.nix { inherit pkgs; externalDomain = domain; kubelets = attrNames machines; } + , extraConfiguration ? null }: + let + masterName = head (filter (machineName: any (role: role == "master") machines.${machineName}.roles) (attrNames machines)); + master = machines.${masterName}; + extraHosts = '' + ${master.ip} etcd.${domain} + ${master.ip} api.${domain} + ${concatMapStringsSep "\n" (machineName: "${machines.${machineName}.ip} ${machineName}.${domain}") (attrNames machines)} + ''; + in makeTest { + inherit name; + + nodes = mapAttrs (machineName: machine: + { config, pkgs, lib, nodes, ... }: + mkMerge [ + { + virtualisation.memorySize = mkDefault 1536; + virtualisation.diskSize = mkDefault 4096; + networking = { + inherit domain extraHosts; + primaryIPAddress = mkForce machine.ip; + + firewall = { + allowedTCPPorts = [ + 10250 # kubelet + ]; + trustedInterfaces = ["docker0"]; + + extraCommands = concatMapStrings (node: '' + iptables -A INPUT -s ${node.config.networking.primaryIPAddress} -j ACCEPT + '') (attrValues nodes); + }; + }; + programs.bash.enableCompletion = true; + environment.variables = { + ETCDCTL_CERT_FILE = "${certs.worker}/etcd-client.pem"; + ETCDCTL_KEY_FILE = "${certs.worker}/etcd-client-key.pem"; + ETCDCTL_CA_FILE = "${certs.worker}/ca.pem"; + ETCDCTL_PEERS = "https://etcd.${domain}:2379"; + }; + services.flannel.iface = "eth1"; + services.kubernetes.apiserver.advertiseAddress = master.ip; + } + (optionalAttrs (any (role: role == "master") machine.roles) { + networking.firewall.allowedTCPPorts = [ + 2379 2380 # etcd + 443 # kubernetes apiserver + ]; + services.etcd = { + enable = true; + certFile = "${certs.master}/etcd.pem"; + keyFile = "${certs.master}/etcd-key.pem"; + trustedCaFile = "${certs.master}/ca.pem"; + peerClientCertAuth = true; + listenClientUrls = ["https://0.0.0.0:2379"]; + listenPeerUrls = ["https://0.0.0.0:2380"]; + advertiseClientUrls = ["https://etcd.${config.networking.domain}:2379"]; + initialCluster = ["${masterName}=https://etcd.${config.networking.domain}:2380"]; + initialAdvertisePeerUrls = ["https://etcd.${config.networking.domain}:2380"]; + }; + }) + (import ./kubernetes-common.nix { inherit (machine) roles; inherit pkgs config certs; }) + (optionalAttrs (machine ? "extraConfiguration") (machine.extraConfiguration { inherit config pkgs lib nodes; })) + (optionalAttrs (extraConfiguration != null) (extraConfiguration { inherit config pkgs lib nodes; })) + ] + ) machines; + + testScript = '' + startAll; + + ${test} + ''; + }; + + mkKubernetesMultiNodeTest = attrs: mkKubernetesBaseTest ({ + machines = { + machine1 = { + roles = ["master"]; + ip = "192.168.1.1"; + }; + machine2 = { + roles = ["node"]; + ip = "192.168.1.2"; + }; + }; + } // attrs // { + name = "kubernetes-${attrs.name}-multinode"; + }); + + mkKubernetesSingleNodeTest = attrs: mkKubernetesBaseTest ({ + machines = { + machine1 = { + roles = ["master" "node"]; + ip = "192.168.1.1"; + }; + }; + } // attrs // { + name = "kubernetes-${attrs.name}-singlenode"; + }); +in { + inherit mkKubernetesBaseTest mkKubernetesSingleNodeTest mkKubernetesMultiNodeTest; +} diff --git a/nixpkgs/nixos/tests/kubernetes/certs.nix b/nixpkgs/nixos/tests/kubernetes/certs.nix new file mode 100644 index 000000000000..85e92f6330c9 --- /dev/null +++ b/nixpkgs/nixos/tests/kubernetes/certs.nix @@ -0,0 +1,219 @@ +{ + pkgs ? import <nixpkgs> {}, + externalDomain ? "myawesomecluster.cluster.yourdomain.net", + serviceClusterIp ? "10.0.0.1", + kubelets, + ... +}: +let + runWithCFSSL = name: cmd: + let secrets = pkgs.runCommand "${name}-cfss.json" { + buildInputs = [ pkgs.cfssl pkgs.jq ]; + outputs = [ "out" "cert" "key" "csr" ]; + } + '' + ( + echo "${cmd}" + cfssl ${cmd} > tmp + cat tmp | jq -r .key > $key + cat tmp | jq -r .cert > $cert + cat tmp | jq -r .csr > $csr + + touch $out + ) 2>&1 | fold -w 80 -s + ''; + in { + key = secrets.key; + cert = secrets.cert; + csr = secrets.csr; + }; + + writeCFSSL = content: + pkgs.runCommand content.name { + buildInputs = [ pkgs.cfssl pkgs.jq ]; + } '' + mkdir -p $out + cd $out + + json=${pkgs.lib.escapeShellArg (builtins.toJSON content)} + + # for a given $field in the $json, treat the associated value as a + # file path and substitute the contents thereof into the $json + # object. + expandFileField() { + local field=$1 + if jq -e --arg field "$field" 'has($field)'; then + local path="$(echo "$json" | jq -r ".$field")" + json="$(echo "$json" | jq --arg val "$(cat "$path")" ".$field = \$val")" + fi + } + + expandFileField key + expandFileField ca + expandFileField cert + + echo "$json" | cfssljson -bare ${content.name} + ''; + + noCSR = content: pkgs.lib.filterAttrs (n: v: n != "csr") content; + noKey = content: pkgs.lib.filterAttrs (n: v: n != "key") content; + + writeFile = content: + if pkgs.lib.isDerivation content + then content + else pkgs.writeText "content" (builtins.toJSON content); + + createServingCertKey = { ca, cn, hosts? [], size ? 2048, name ? cn }: + noCSR ( + (runWithCFSSL name "gencert -ca=${writeFile ca.cert} -ca-key=${writeFile ca.key} -profile=server -config=${writeFile ca.config} ${writeFile { + CN = cn; + hosts = hosts; + key = { algo = "rsa"; inherit size; }; + }}") // { inherit name; } + ); + + createClientCertKey = { ca, cn, groups ? [], size ? 2048, name ? cn }: + noCSR ( + (runWithCFSSL name "gencert -ca=${writeFile ca.cert} -ca-key=${writeFile ca.key} -profile=client -config=${writeFile ca.config} ${writeFile { + CN = cn; + names = map (group: {O = group;}) groups; + hosts = [""]; + key = { algo = "rsa"; inherit size; }; + }}") // { inherit name; } + ); + + createSigningCertKey = { C ? "xx", ST ? "x", L ? "x", O ? "x", OU ? "x", CN ? "ca", emailAddress ? "x", expiry ? "43800h", size ? 2048, name ? CN }: + (noCSR (runWithCFSSL CN "genkey -initca ${writeFile { + key = { algo = "rsa"; inherit size; }; + names = [{ inherit C ST L O OU CN emailAddress; }]; + }}")) // { + inherit name; + config.signing = { + default.expiry = expiry; + profiles = { + server = { + inherit expiry; + usages = [ + "signing" + "key encipherment" + "server auth" + ]; + }; + client = { + inherit expiry; + usages = [ + "signing" + "key encipherment" + "client auth" + ]; + }; + peer = { + inherit expiry; + usages = [ + "signing" + "key encipherment" + "server auth" + "client auth" + ]; + }; + }; + }; + }; + + ca = createSigningCertKey {}; + + kube-apiserver = createServingCertKey { + inherit ca; + cn = "kube-apiserver"; + hosts = ["kubernetes.default" "kubernetes.default.svc" "localhost" "api.${externalDomain}" serviceClusterIp]; + }; + + kubelet = createServingCertKey { + inherit ca; + cn = "kubelet"; + hosts = ["*.${externalDomain}"]; + }; + + service-accounts = createServingCertKey { + inherit ca; + cn = "kube-service-accounts"; + }; + + etcd = createServingCertKey { + inherit ca; + cn = "etcd"; + hosts = ["etcd.${externalDomain}"]; + }; + + etcd-client = createClientCertKey { + inherit ca; + cn = "etcd-client"; + }; + + kubelet-client = createClientCertKey { + inherit ca; + cn = "kubelet-client"; + groups = ["system:masters"]; + }; + + apiserver-client = { + kubelet = hostname: createClientCertKey { + inherit ca; + name = "apiserver-client-kubelet-${hostname}"; + cn = "system:node:${hostname}.${externalDomain}"; + groups = ["system:nodes"]; + }; + + kube-proxy = createClientCertKey { + inherit ca; + name = "apiserver-client-kube-proxy"; + cn = "system:kube-proxy"; + groups = ["system:kube-proxy" "system:nodes"]; + }; + + kube-controller-manager = createClientCertKey { + inherit ca; + name = "apiserver-client-kube-controller-manager"; + cn = "system:kube-controller-manager"; + groups = ["system:masters"]; + }; + + kube-scheduler = createClientCertKey { + inherit ca; + name = "apiserver-client-kube-scheduler"; + cn = "system:kube-scheduler"; + groups = ["system:kube-scheduler"]; + }; + + admin = createClientCertKey { + inherit ca; + cn = "admin"; + groups = ["system:masters"]; + }; + }; +in { + master = pkgs.buildEnv { + name = "master-keys"; + paths = [ + (writeCFSSL (noKey ca)) + (writeCFSSL kube-apiserver) + (writeCFSSL kubelet-client) + (writeCFSSL apiserver-client.kube-controller-manager) + (writeCFSSL apiserver-client.kube-scheduler) + (writeCFSSL service-accounts) + (writeCFSSL etcd) + ]; + }; + + worker = pkgs.buildEnv { + name = "worker-keys"; + paths = [ + (writeCFSSL (noKey ca)) + (writeCFSSL kubelet) + (writeCFSSL apiserver-client.kube-proxy) + (writeCFSSL etcd-client) + ] ++ map (hostname: writeCFSSL (apiserver-client.kubelet hostname)) kubelets; + }; + + admin = writeCFSSL apiserver-client.admin; +} diff --git a/nixpkgs/nixos/tests/kubernetes/default.nix b/nixpkgs/nixos/tests/kubernetes/default.nix new file mode 100644 index 000000000000..a801759bf582 --- /dev/null +++ b/nixpkgs/nixos/tests/kubernetes/default.nix @@ -0,0 +1,7 @@ +{ system ? builtins.currentSystem }: +{ + dns = import ./dns.nix { inherit system; }; + # e2e = import ./e2e.nix { inherit system; }; # TODO: make it pass + # the following test(s) can be removed when e2e is working: + rbac = import ./rbac.nix { inherit system; }; +} diff --git a/nixpkgs/nixos/tests/kubernetes/dns.nix b/nixpkgs/nixos/tests/kubernetes/dns.nix new file mode 100644 index 000000000000..f25ea5b9ed84 --- /dev/null +++ b/nixpkgs/nixos/tests/kubernetes/dns.nix @@ -0,0 +1,127 @@ +{ system ? builtins.currentSystem, pkgs ? import <nixpkgs> { inherit system; } }: +with import ./base.nix { inherit system; }; +let + domain = "my.zyx"; + + certs = import ./certs.nix { externalDomain = domain; kubelets = [ "machine1" "machine2" ]; }; + + redisPod = pkgs.writeText "redis-pod.json" (builtins.toJSON { + kind = "Pod"; + apiVersion = "v1"; + metadata.name = "redis"; + metadata.labels.name = "redis"; + spec.containers = [{ + name = "redis"; + image = "redis"; + args = ["--bind" "0.0.0.0"]; + imagePullPolicy = "Never"; + ports = [{ + name = "redis-server"; + containerPort = 6379; + }]; + }]; + }); + + redisService = pkgs.writeText "redis-service.json" (builtins.toJSON { + kind = "Service"; + apiVersion = "v1"; + metadata.name = "redis"; + spec = { + ports = [{port = 6379; targetPort = 6379;}]; + selector = {name = "redis";}; + }; + }); + + redisImage = pkgs.dockerTools.buildImage { + name = "redis"; + tag = "latest"; + contents = [ pkgs.redis pkgs.bind.host ]; + config.Entrypoint = "/bin/redis-server"; + }; + + probePod = pkgs.writeText "probe-pod.json" (builtins.toJSON { + kind = "Pod"; + apiVersion = "v1"; + metadata.name = "probe"; + metadata.labels.name = "probe"; + spec.containers = [{ + name = "probe"; + image = "probe"; + args = [ "-f" ]; + tty = true; + imagePullPolicy = "Never"; + }]; + }); + + probeImage = pkgs.dockerTools.buildImage { + name = "probe"; + tag = "latest"; + contents = [ pkgs.bind.host pkgs.busybox ]; + config.Entrypoint = "/bin/tail"; + }; + + extraConfiguration = { config, pkgs, ... }: { + environment.systemPackages = [ pkgs.bind.host ]; + # virtualisation.docker.extraOptions = "--dns=${config.services.kubernetes.addons.dns.clusterIp}"; + services.dnsmasq.enable = true; + services.dnsmasq.servers = [ + "/cluster.local/${config.services.kubernetes.addons.dns.clusterIp}#53" + ]; + }; + + base = { + name = "dns"; + inherit domain certs extraConfiguration; + }; + + singleNodeTest = { + test = '' + # prepare machine1 for test + $machine1->waitUntilSucceeds("kubectl get node machine1.${domain} | grep -w Ready"); + $machine1->execute("docker load < ${redisImage}"); + $machine1->waitUntilSucceeds("kubectl create -f ${redisPod}"); + $machine1->waitUntilSucceeds("kubectl create -f ${redisService}"); + $machine1->execute("docker load < ${probeImage}"); + $machine1->waitUntilSucceeds("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'"); + + # check dns on host (dnsmasq) + $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"); + ''; + }; + + multiNodeTest = { + test = '' + # prepare machines for test + $machine1->waitUntilSucceeds("kubectl get node machine1.${domain} | grep -w Ready"); + $machine1->waitUntilSucceeds("kubectl get node machine2.${domain} | grep -w Ready"); + $machine2->execute("docker load < ${redisImage}"); + $machine1->waitUntilSucceeds("kubectl create -f ${redisPod}"); + $machine1->waitUntilSucceeds("kubectl create -f ${redisService}"); + $machine2->execute("docker load < ${probeImage}"); + $machine1->waitUntilSucceeds("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'"); + + # check dns on hosts (dnsmasq) + $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"); + ''; + }; +in { + singlenode = mkKubernetesSingleNodeTest (base // singleNodeTest); + multinode = mkKubernetesMultiNodeTest (base // multiNodeTest); +} diff --git a/nixpkgs/nixos/tests/kubernetes/e2e.nix b/nixpkgs/nixos/tests/kubernetes/e2e.nix new file mode 100644 index 000000000000..175d8413045e --- /dev/null +++ b/nixpkgs/nixos/tests/kubernetes/e2e.nix @@ -0,0 +1,40 @@ +{ system ? builtins.currentSystem, pkgs ? import <nixpkgs> { inherit system; } }: +with import ./base.nix { inherit system; }; +let + domain = "my.zyx"; + certs = import ./certs.nix { externalDomain = domain; kubelets = ["machine1" "machine2"]; }; + kubeconfig = pkgs.writeText "kubeconfig.json" (builtins.toJSON { + apiVersion = "v1"; + kind = "Config"; + clusters = [{ + name = "local"; + cluster.certificate-authority = "${certs.master}/ca.pem"; + cluster.server = "https://api.${domain}"; + }]; + users = [{ + name = "kubelet"; + user = { + client-certificate = "${certs.admin}/admin.pem"; + client-key = "${certs.admin}/admin-key.pem"; + }; + }]; + contexts = [{ + context = { + cluster = "local"; + user = "kubelet"; + }; + current-context = "kubelet-context"; + }]; + }); + + base = { + name = "e2e"; + inherit domain certs; + test = '' + $machine1->succeed("e2e.test -kubeconfig ${kubeconfig} -provider local -ginkgo.focus '\\[Conformance\\]' -ginkgo.skip '\\[Flaky\\]|\\[Serial\\]'"); + ''; + }; +in { + singlenode = mkKubernetesSingleNodeTest base; + multinode = mkKubernetesMultiNodeTest base; +} diff --git a/nixpkgs/nixos/tests/kubernetes/kubernetes-common.nix b/nixpkgs/nixos/tests/kubernetes/kubernetes-common.nix new file mode 100644 index 000000000000..87c65b883659 --- /dev/null +++ b/nixpkgs/nixos/tests/kubernetes/kubernetes-common.nix @@ -0,0 +1,57 @@ +{ roles, config, pkgs, certs }: +with pkgs.lib; +let + base = { + inherit roles; + flannel.enable = true; + addons.dashboard.enable = true; + + caFile = "${certs.master}/ca.pem"; + apiserver = { + tlsCertFile = "${certs.master}/kube-apiserver.pem"; + tlsKeyFile = "${certs.master}/kube-apiserver-key.pem"; + kubeletClientCertFile = "${certs.master}/kubelet-client.pem"; + kubeletClientKeyFile = "${certs.master}/kubelet-client-key.pem"; + serviceAccountKeyFile = "${certs.master}/kube-service-accounts.pem"; + }; + etcd = { + servers = ["https://etcd.${config.networking.domain}:2379"]; + certFile = "${certs.worker}/etcd-client.pem"; + keyFile = "${certs.worker}/etcd-client-key.pem"; + }; + kubeconfig = { + server = "https://api.${config.networking.domain}"; + }; + kubelet = { + tlsCertFile = "${certs.worker}/kubelet.pem"; + tlsKeyFile = "${certs.worker}/kubelet-key.pem"; + hostname = "${config.networking.hostName}.${config.networking.domain}"; + kubeconfig = { + certFile = "${certs.worker}/apiserver-client-kubelet-${config.networking.hostName}.pem"; + keyFile = "${certs.worker}/apiserver-client-kubelet-${config.networking.hostName}-key.pem"; + }; + }; + controllerManager = { + serviceAccountKeyFile = "${certs.master}/kube-service-accounts-key.pem"; + kubeconfig = { + certFile = "${certs.master}/apiserver-client-kube-controller-manager.pem"; + keyFile = "${certs.master}/apiserver-client-kube-controller-manager-key.pem"; + }; + }; + scheduler = { + kubeconfig = { + certFile = "${certs.master}/apiserver-client-kube-scheduler.pem"; + keyFile = "${certs.master}/apiserver-client-kube-scheduler-key.pem"; + }; + }; + proxy = { + kubeconfig = { + certFile = "${certs.worker}/apiserver-client-kube-proxy.pem"; + keyFile = "${certs.worker}//apiserver-client-kube-proxy-key.pem"; + }; + }; + }; + +in { + services.kubernetes = base; +} diff --git a/nixpkgs/nixos/tests/kubernetes/rbac.nix b/nixpkgs/nixos/tests/kubernetes/rbac.nix new file mode 100644 index 000000000000..226808c4b263 --- /dev/null +++ b/nixpkgs/nixos/tests/kubernetes/rbac.nix @@ -0,0 +1,137 @@ +{ system ? builtins.currentSystem, pkgs ? import <nixpkgs> { inherit system; } }: +with import ./base.nix { inherit system; }; +let + + roServiceAccount = pkgs.writeText "ro-service-account.json" (builtins.toJSON { + kind = "ServiceAccount"; + apiVersion = "v1"; + metadata = { + name = "read-only"; + namespace = "default"; + }; + }); + + roRoleBinding = pkgs.writeText "ro-role-binding.json" (builtins.toJSON { + apiVersion = "rbac.authorization.k8s.io/v1"; + kind = "RoleBinding"; + metadata = { + name = "read-pods"; + namespace = "default"; + }; + roleRef = { + apiGroup = "rbac.authorization.k8s.io"; + kind = "Role"; + name = "pod-reader"; + }; + subjects = [{ + kind = "ServiceAccount"; + name = "read-only"; + namespace = "default"; + }]; + }); + + roRole = pkgs.writeText "ro-role.json" (builtins.toJSON { + apiVersion = "rbac.authorization.k8s.io/v1"; + kind = "Role"; + metadata = { + name = "pod-reader"; + namespace = "default"; + }; + rules = [{ + apiGroups = [""]; + resources = ["pods"]; + verbs = ["get" "list" "watch"]; + }]; + }); + + kubectlPod = pkgs.writeText "kubectl-pod.json" (builtins.toJSON { + kind = "Pod"; + apiVersion = "v1"; + metadata.name = "kubectl"; + metadata.namespace = "default"; + metadata.labels.name = "kubectl"; + spec.serviceAccountName = "read-only"; + spec.containers = [{ + name = "kubectl"; + image = "kubectl:latest"; + command = ["/bin/tail" "-f"]; + imagePullPolicy = "Never"; + tty = true; + }]; + }); + + kubectlPod2 = pkgs.writeTextDir "kubectl-pod-2.json" (builtins.toJSON { + kind = "Pod"; + apiVersion = "v1"; + metadata.name = "kubectl-2"; + metadata.namespace = "default"; + metadata.labels.name = "kubectl-2"; + spec.serviceAccountName = "read-only"; + spec.containers = [{ + name = "kubectl-2"; + image = "kubectl:latest"; + command = ["/bin/tail" "-f"]; + imagePullPolicy = "Never"; + tty = true; + }]; + }); + + kubectl = pkgs.runCommand "copy-kubectl" { buildInputs = [ pkgs.kubernetes ]; } '' + mkdir -p $out/bin + cp ${pkgs.kubernetes}/bin/kubectl $out/bin/kubectl + ''; + + kubectlImage = pkgs.dockerTools.buildImage { + name = "kubectl"; + tag = "latest"; + contents = [ kubectl pkgs.busybox kubectlPod2 ]; + config.Entrypoint = "/bin/sh"; + }; + + base = { + name = "rbac"; + }; + + singlenode = base // { + test = '' + $machine1->waitUntilSucceeds("kubectl get node machine1.my.zyx | grep -w Ready"); + + $machine1->execute("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->succeed("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 = '' + $machine1->waitUntilSucceeds("kubectl get node machine1.my.zyx | grep -w Ready"); + $machine1->waitUntilSucceeds("kubectl get node machine2.my.zyx | grep -w Ready"); + + $machine2->execute("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->succeed("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"); + ''; + }; + +in { + singlenode = mkKubernetesSingleNodeTest singlenode; + multinode = mkKubernetesMultiNodeTest multinode; +} diff --git a/nixpkgs/nixos/tests/ldap.nix b/nixpkgs/nixos/tests/ldap.nix new file mode 100644 index 000000000000..b3fd42e75886 --- /dev/null +++ b/nixpkgs/nixos/tests/ldap.nix @@ -0,0 +1,396 @@ +import ./make-test.nix ({ pkgs, lib, ...} : + +let + unlines = lib.concatStringsSep "\n"; + unlinesAttrs = f: as: unlines (lib.mapAttrsToList f as); + + dbDomain = "example.com"; + dbSuffix = "dc=example,dc=com"; + dbAdminDn = "cn=admin,${dbSuffix}"; + dbAdminPwd = "admin-password"; + # NOTE: slappasswd -h "{SSHA}" -s '${dbAdminPwd}' + dbAdminPwdHash = "{SSHA}i7FopSzkFQMrHzDMB1vrtkI0rBnwouP8"; + ldapUser = "test-ldap-user"; + ldapUserId = 10000; + ldapUserPwd = "user-password"; + # NOTE: slappasswd -h "{SSHA}" -s '${ldapUserPwd}' + ldapUserPwdHash = "{SSHA}v12XICMZNGT6r2KJ26rIkN8Vvvp4QX6i"; + ldapGroup = "test-ldap-group"; + ldapGroupId = 10000; + + mkClient = useDaemon: + { lib, ... }: + { + virtualisation.memorySize = 256; + virtualisation.vlans = [ 1 ]; + security.pam.services.su.rootOK = lib.mkForce false; + users.ldap.enable = true; + users.ldap.daemon = { + enable = useDaemon; + rootpwmoddn = "cn=admin,${dbSuffix}"; + rootpwmodpw = "/etc/nslcd.rootpwmodpw"; + }; + # NOTE: password stored in clear in Nix's store, but this is a test. + environment.etc."nslcd.rootpwmodpw".source = pkgs.writeText "rootpwmodpw" dbAdminPwd; + users.ldap.loginPam = true; + users.ldap.nsswitch = true; + users.ldap.server = "ldap://server"; + users.ldap.base = "ou=posix,${dbSuffix}"; + users.ldap.bind = { + distinguishedName = "cn=admin,${dbSuffix}"; + password = "/etc/ldap/bind.password"; + }; + # NOTE: password stored in clear in Nix's store, but this is a test. + environment.etc."ldap/bind.password".source = pkgs.writeText "password" dbAdminPwd; + }; +in + +{ + name = "ldap"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ montag451 ]; + }; + + nodes = { + + server = + { pkgs, config, ... }: + let + inherit (config.services) openldap; + + slapdConfig = pkgs.writeText "cn=config.ldif" ('' + dn: cn=config + objectClass: olcGlobal + #olcPidFile: /run/slapd/slapd.pid + # List of arguments that were passed to the server + #olcArgsFile: /run/slapd/slapd.args + # Read slapd-config(5) for possible values + olcLogLevel: none + # The tool-threads parameter sets the actual amount of CPU's + # that is used for indexing. + olcToolThreads: 1 + + dn: olcDatabase={-1}frontend,cn=config + objectClass: olcDatabaseConfig + objectClass: olcFrontendConfig + # The maximum number of entries that is returned for a search operation + olcSizeLimit: 500 + # Allow unlimited access to local connection from the local root user + olcAccess: to * + by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage + by * break + # Allow unauthenticated read access for schema and base DN autodiscovery + olcAccess: to dn.exact="" + by * read + olcAccess: to dn.base="cn=Subschema" + by * read + + dn: olcDatabase=config,cn=config + objectClass: olcDatabaseConfig + olcRootDN: cn=admin,cn=config + #olcRootPW: + # NOTE: access to cn=config, system root can be manager + # with SASL mechanism (-Y EXTERNAL) over unix socket (-H ldapi://) + olcAccess: to * + by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage + by * break + + dn: cn=schema,cn=config + objectClass: olcSchemaConfig + + include: file://${pkgs.openldap}/etc/schema/core.ldif + include: file://${pkgs.openldap}/etc/schema/cosine.ldif + include: file://${pkgs.openldap}/etc/schema/nis.ldif + include: file://${pkgs.openldap}/etc/schema/inetorgperson.ldif + + dn: cn=module{0},cn=config + objectClass: olcModuleList + # Where the dynamically loaded modules are stored + #olcModulePath: /usr/lib/ldap + olcModuleLoad: back_mdb + + '' + + unlinesAttrs (olcSuffix: {conf, ...}: + "include: file://" + pkgs.writeText "config.ldif" conf + ) slapdDatabases + ); + + slapdDatabases = { + "${dbSuffix}" = { + conf = '' + dn: olcBackend={1}mdb,cn=config + objectClass: olcBackendConfig + + dn: olcDatabase={1}mdb,cn=config + olcSuffix: ${dbSuffix} + olcDbDirectory: ${openldap.dataDir}/${dbSuffix} + objectClass: olcDatabaseConfig + objectClass: olcMdbConfig + # NOTE: checkpoint the database periodically in case of system failure + # and to speed up slapd shutdown. + olcDbCheckpoint: 512 30 + # Database max size is 1G + olcDbMaxSize: 1073741824 + olcLastMod: TRUE + # NOTE: database superuser. Needed for syncrepl, + # and used to auth as admin through a TCP connection. + olcRootDN: cn=admin,${dbSuffix} + olcRootPW: ${dbAdminPwdHash} + # + olcDbIndex: objectClass eq + olcDbIndex: cn,uid eq + olcDbIndex: uidNumber,gidNumber eq + olcDbIndex: member,memberUid eq + # + olcAccess: to attrs=userPassword + by self write + by anonymous auth + by dn="cn=admin,${dbSuffix}" write + by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write + by * none + olcAccess: to attrs=shadowLastChange + by self write + by dn="cn=admin,${dbSuffix}" write + by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write + by * none + olcAccess: to dn.sub="ou=posix,${dbSuffix}" + by self read + by dn="cn=admin,${dbSuffix}" read + by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read + olcAccess: to * + by self read + by * none + ''; + data = '' + dn: ${dbSuffix} + objectClass: top + objectClass: dcObject + objectClass: organization + o: ${dbDomain} + + dn: cn=admin,${dbSuffix} + objectClass: simpleSecurityObject + objectClass: organizationalRole + description: ${dbDomain} LDAP administrator + roleOccupant: ${dbSuffix} + userPassword: ${ldapUserPwdHash} + + dn: ou=posix,${dbSuffix} + objectClass: top + objectClass: organizationalUnit + + dn: ou=accounts,ou=posix,${dbSuffix} + objectClass: top + objectClass: organizationalUnit + + dn: ou=groups,ou=posix,${dbSuffix} + objectClass: top + objectClass: organizationalUnit + '' + + lib.concatMapStrings posixAccount [ + { uid=ldapUser; uidNumber=ldapUserId; gidNumber=ldapGroupId; userPassword=ldapUserPwdHash; } + ] + + lib.concatMapStrings posixGroup [ + { gid=ldapGroup; gidNumber=ldapGroupId; members=[]; } + ]; + }; + }; + + # NOTE: create a user account using the posixAccount objectClass. + posixAccount = + { uid + , uidNumber ? null + , gidNumber ? null + , cn ? "" + , sn ? "" + , userPassword ? "" + , loginShell ? "/bin/sh" + }: '' + + dn: uid=${uid},ou=accounts,ou=posix,${dbSuffix} + objectClass: person + objectClass: posixAccount + objectClass: shadowAccount + cn: ${cn} + gecos: + ${if gidNumber == null then "#" else "gidNumber: ${toString gidNumber}"} + homeDirectory: /home/${uid} + loginShell: ${loginShell} + sn: ${sn} + ${if uidNumber == null then "#" else "uidNumber: ${toString uidNumber}"} + ${if userPassword == "" then "#" else "userPassword: ${userPassword}"} + ''; + + # NOTE: create a group using the posixGroup objectClass. + posixGroup = + { gid + , gidNumber + , members + }: '' + + dn: cn=${gid},ou=groups,ou=posix,${dbSuffix} + objectClass: top + objectClass: posixGroup + gidNumber: ${toString gidNumber} + ${lib.concatMapStrings (member: "memberUid: ${member}\n") members} + ''; + in + { + virtualisation.memorySize = 256; + virtualisation.vlans = [ 1 ]; + networking.firewall.allowedTCPPorts = [ 389 ]; + services.openldap.enable = true; + services.openldap.dataDir = "/var/db/openldap"; + services.openldap.configDir = "/var/db/slapd"; + services.openldap.urlList = [ + "ldap:///" + "ldapi:///" + ]; + systemd.services.openldap = { + preStart = '' + set -e + # NOTE: slapd's config is always re-initialized. + rm -rf "${openldap.configDir}"/cn=config \ + "${openldap.configDir}"/cn=config.ldif + install -D -d -m 0700 -o "${openldap.user}" -g "${openldap.group}" "${openldap.configDir}" + # NOTE: olcDbDirectory must be created before adding the config. + '' + + unlinesAttrs (olcSuffix: {data, ...}: '' + # NOTE: database is always re-initialized. + rm -rf "${openldap.dataDir}/${olcSuffix}" + install -D -d -m 0700 -o "${openldap.user}" -g "${openldap.group}" \ + "${openldap.dataDir}/${olcSuffix}" + '') slapdDatabases + + '' + # NOTE: slapd is supposed to be stopped while in preStart, + # hence slap* commands can safely be used. + umask 0077 + ${pkgs.openldap}/bin/slapadd -n 0 \ + -F "${openldap.configDir}" \ + -l ${slapdConfig} + chown -R "${openldap.user}:${openldap.group}" "${openldap.configDir}" + # NOTE: slapadd(8): To populate the config database slapd-config(5), + # use -n 0 as it is always the first database. + # It must physically exist on the filesystem prior to this, however. + '' + + unlinesAttrs (olcSuffix: {data, ...}: '' + # NOTE: load database ${olcSuffix} + # (as root to avoid depending on sudo or chpst) + ${pkgs.openldap}/bin/slapadd \ + -F "${openldap.configDir}" \ + -l ${pkgs.writeText "data.ldif" data} + '' + '' + # NOTE: redundant with default openldap's preStart, but do not harm. + chown -R "${openldap.user}:${openldap.group}" \ + "${openldap.dataDir}/${olcSuffix}" + '') slapdDatabases; + }; + }; + + client1 = mkClient true; # use nss_pam_ldapd + client2 = mkClient false; # use nss_ldap and pam_ldap + + }; + + testScript = '' + $server->start; + $server->waitForUnit("default.target"); + + subtest "slapd", sub { + subtest "auth as database admin with SASL and check a POSIX account", sub { + $server->succeed(join ' ', 'test', + '"$(ldapsearch -LLL -H ldapi:// -Y EXTERNAL', + '-b \'uid=${ldapUser},ou=accounts,ou=posix,${dbSuffix}\' ', + '-s base uidNumber |', + 'sed -ne \'s/^uidNumber: \\(.*\\)/\\1/p\' ', + ')" -eq ${toString ldapUserId}'); + }; + subtest "auth as database admin with password and check a POSIX account", sub { + $server->succeed(join ' ', 'test', + '"$(ldapsearch -LLL -H ldap://server', + '-D \'cn=admin,${dbSuffix}\' -w \'${dbAdminPwd}\' ', + '-b \'uid=${ldapUser},ou=accounts,ou=posix,${dbSuffix}\' ', + '-s base uidNumber |', + 'sed -ne \'s/^uidNumber: \\(.*\\)/\\1/p\' ', + ')" -eq ${toString ldapUserId}'); + }; + }; + + $client1->start; + $client1->waitForUnit("default.target"); + + subtest "password", sub { + subtest "su with password to a POSIX account", sub { + $client1->succeed("${pkgs.expect}/bin/expect -c '" . join ';', + 'spawn su "${ldapUser}"', + 'expect "Password:"', + 'send "${ldapUserPwd}\n"', + 'expect "*"', + 'send "whoami\n"', + 'expect -ex "${ldapUser}" {exit}', + 'exit 1' . "'"); + }; + subtest "change password of a POSIX account as root", sub { + $client1->succeed("chpasswd <<<'${ldapUser}:new-password'"); + $client1->succeed("${pkgs.expect}/bin/expect -c '" . join ';', + 'spawn su "${ldapUser}"', + 'expect "Password:"', + 'send "new-password\n"', + 'expect "*"', + 'send "whoami\n"', + 'expect -ex "${ldapUser}" {exit}', + 'exit 1' . "'"); + $client1->succeed('chpasswd <<<\'${ldapUser}:${ldapUserPwd}\' '); + }; + subtest "change password of a POSIX account from itself", sub { + $client1->succeed('chpasswd <<<\'${ldapUser}:${ldapUserPwd}\' '); + $client1->succeed("${pkgs.expect}/bin/expect -c '" . join ';', + 'spawn su --login ${ldapUser} -c passwd', + 'expect "Password: "', + 'send "${ldapUserPwd}\n"', + 'expect "(current) UNIX password: "', + 'send "${ldapUserPwd}\n"', + 'expect "New password: "', + 'send "new-password\n"', + 'expect "Retype new password: "', + 'send "new-password\n"', + 'expect "passwd: password updated successfully" {exit}', + 'exit 1' . "'"); + $client1->succeed("${pkgs.expect}/bin/expect -c '" . join ';', + 'spawn su "${ldapUser}"', + 'expect "Password:"', + 'send "${ldapUserPwd}\n"', + 'expect "su: Authentication failure" {exit}', + 'exit 1' . "'"); + $client1->succeed("${pkgs.expect}/bin/expect -c '" . join ';', + 'spawn su "${ldapUser}"', + 'expect "Password:"', + 'send "new-password\n"', + 'expect "*"', + 'send "whoami\n"', + 'expect -ex "${ldapUser}" {exit}', + 'exit 1' . "'"); + $client1->succeed('chpasswd <<<\'${ldapUser}:${ldapUserPwd}\' '); + }; + }; + + $client2->start; + $client2->waitForUnit("default.target"); + + subtest "NSS", sub { + $client1->succeed("test \"\$(id -u '${ldapUser}')\" -eq ${toString ldapUserId}"); + $client1->succeed("test \"\$(id -u -n '${ldapUser}')\" = '${ldapUser}'"); + $client1->succeed("test \"\$(id -g '${ldapUser}')\" -eq ${toString ldapGroupId}"); + $client1->succeed("test \"\$(id -g -n '${ldapUser}')\" = '${ldapGroup}'"); + $client2->succeed("test \"\$(id -u '${ldapUser}')\" -eq ${toString ldapUserId}"); + $client2->succeed("test \"\$(id -u -n '${ldapUser}')\" = '${ldapUser}'"); + $client2->succeed("test \"\$(id -g '${ldapUser}')\" -eq ${toString ldapGroupId}"); + $client2->succeed("test \"\$(id -g -n '${ldapUser}')\" = '${ldapGroup}'"); + }; + + subtest "PAM", sub { + $client1->succeed("echo ${ldapUserPwd} | su -l '${ldapUser}' -c true"); + $client2->succeed("echo ${ldapUserPwd} | su -l '${ldapUser}' -c true"); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/leaps.nix b/nixpkgs/nixos/tests/leaps.nix new file mode 100644 index 000000000000..6163fed56b6f --- /dev/null +++ b/nixpkgs/nixos/tests/leaps.nix @@ -0,0 +1,30 @@ +import ./make-test.nix ({ pkgs, ... }: + +{ + name = "leaps"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ qknight ]; + }; + + nodes = + { + client = { }; + + server = + { services.leaps = { + enable = true; + port = 6666; + path = "/leaps/"; + }; + networking.firewall.enable = false; + }; + }; + + testScript = + '' + startAll; + $server->waitForOpenPort(6666); + $client->waitForUnit("network.target"); + $client->succeed("${pkgs.curl}/bin/curl http://server:6666/leaps/ | grep -i 'leaps'"); + ''; +}) diff --git a/nixpkgs/nixos/tests/lidarr.nix b/nixpkgs/nixos/tests/lidarr.nix new file mode 100644 index 000000000000..58bf82503f8c --- /dev/null +++ b/nixpkgs/nixos/tests/lidarr.nix @@ -0,0 +1,18 @@ +import ./make-test.nix ({ lib, ... }: + +with lib; + +rec { + name = "lidarr"; + meta.maintainers = with maintainers; [ etu ]; + + nodes.machine = + { pkgs, ... }: + { services.lidarr.enable = true; }; + + testScript = '' + $machine->waitForUnit('lidarr.service'); + $machine->waitForOpenPort('8686'); + $machine->succeed("curl --fail http://localhost:8686/"); + ''; +}) diff --git a/nixpkgs/nixos/tests/lightdm.nix b/nixpkgs/nixos/tests/lightdm.nix new file mode 100644 index 000000000000..8a9a7408d292 --- /dev/null +++ b/nixpkgs/nixos/tests/lightdm.nix @@ -0,0 +1,29 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "lightdm"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aszlig ]; + }; + + machine = { ... }: { + imports = [ ./common/user-account.nix ]; + services.xserver.enable = true; + services.xserver.displayManager.lightdm.enable = true; + services.xserver.windowManager.default = "icewm"; + services.xserver.windowManager.icewm.enable = true; + services.xserver.desktopManager.default = "none"; + }; + + enableOCR = true; + + testScript = { nodes, ... }: let + user = nodes.machine.config.users.users.alice; + in '' + startAll; + $machine->waitForText(qr/${user.description}/); + $machine->screenshot("lightdm"); + $machine->sendChars("${user.password}\n"); + $machine->waitForFile("/home/alice/.Xauthority"); + $machine->succeed("xauth merge ~alice/.Xauthority"); + $machine->waitForWindow("^IceWM "); + ''; +}) diff --git a/nixpkgs/nixos/tests/login.nix b/nixpkgs/nixos/tests/login.nix new file mode 100644 index 000000000000..3dbb494b6895 --- /dev/null +++ b/nixpkgs/nixos/tests/login.nix @@ -0,0 +1,72 @@ +import ./make-test.nix ({ pkgs, latestKernel ? false, ... }: + +{ + name = "login"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + machine = + { pkgs, lib, ... }: + { boot.kernelPackages = lib.mkIf latestKernel pkgs.linuxPackages_latest; + }; + + testScript = + '' + $machine->waitForUnit('multi-user.target'); + $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty1'"); + $machine->screenshot("postboot"); + + subtest "create user", sub { + $machine->succeed("useradd -m alice"); + $machine->succeed("(echo foobar; echo foobar) | passwd alice"); + }; + + # Check whether switching VTs works. + subtest "virtual console switching", sub { + $machine->fail("pgrep -f 'agetty.*tty2'"); + $machine->sendKeys("alt-f2"); + $machine->waitUntilSucceeds("[ \$(fgconsole) = 2 ]"); + $machine->waitForUnit('getty@tty2.service'); + $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty2'"); + }; + + # Log in as alice on a virtual console. + subtest "virtual console login", sub { + $machine->waitUntilTTYMatches(2, "login: "); + $machine->sendChars("alice\n"); + $machine->waitUntilTTYMatches(2, "login: alice"); + $machine->waitUntilSucceeds("pgrep login"); + $machine->waitUntilTTYMatches(2, "Password: "); + $machine->sendChars("foobar\n"); + $machine->waitUntilSucceeds("pgrep -u alice bash"); + $machine->sendChars("touch done\n"); + $machine->waitForFile("/home/alice/done"); + }; + + # Check whether systemd gives and removes device ownership as + # needed. + subtest "device permissions", sub { + $machine->succeed("getfacl /dev/snd/timer | grep -q alice"); + $machine->sendKeys("alt-f1"); + $machine->waitUntilSucceeds("[ \$(fgconsole) = 1 ]"); + $machine->fail("getfacl /dev/snd/timer | grep -q alice"); + $machine->succeed("chvt 2"); + $machine->waitUntilSucceeds("getfacl /dev/snd/timer | grep -q alice"); + }; + + # Log out. + subtest "virtual console logout", sub { + $machine->sendChars("exit\n"); + $machine->waitUntilFails("pgrep -u alice bash"); + $machine->screenshot("mingetty"); + }; + + # Check whether ctrl-alt-delete works. + subtest "ctrl-alt-delete", sub { + $machine->sendKeys("ctrl-alt-delete"); + $machine->waitForShutdown; + }; + ''; + +}) diff --git a/nixpkgs/nixos/tests/make-test.nix b/nixpkgs/nixos/tests/make-test.nix new file mode 100644 index 000000000000..cee5da93454a --- /dev/null +++ b/nixpkgs/nixos/tests/make-test.nix @@ -0,0 +1,9 @@ +f: { + system ? builtins.currentSystem, + pkgs ? import ../.. { inherit system; config = {}; }, + ... +} @ args: + +with import ../lib/testing.nix { inherit system pkgs; }; + +makeTest (if pkgs.lib.isFunction f then f (args // { inherit pkgs; inherit (pkgs) lib; }) else f) diff --git a/nixpkgs/nixos/tests/mathics.nix b/nixpkgs/nixos/tests/mathics.nix new file mode 100644 index 000000000000..fcbeeb18a727 --- /dev/null +++ b/nixpkgs/nixos/tests/mathics.nix @@ -0,0 +1,20 @@ +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/nixpkgs/nixos/tests/matrix-synapse.nix b/nixpkgs/nixos/tests/matrix-synapse.nix new file mode 100644 index 000000000000..8504a7c0d057 --- /dev/null +++ b/nixpkgs/nixos/tests/matrix-synapse.nix @@ -0,0 +1,31 @@ +import ./make-test.nix ({ pkgs, ... } : { + + name = "matrix-synapse"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ corngood ]; + }; + + nodes = { + # Since 0.33.0, matrix-synapse doesn't allow underscores in server names + serverpostgres = args: { + services.matrix-synapse.enable = true; + services.matrix-synapse.database_type = "psycopg2"; + }; + + serversqlite = args: { + services.matrix-synapse.enable = true; + services.matrix-synapse.database_type = "sqlite3"; + }; + }; + + testScript = '' + startAll; + $serverpostgres->waitForUnit("matrix-synapse.service"); + $serverpostgres->waitUntilSucceeds("curl -Lk https://localhost:8448/"); + $serverpostgres->requireActiveUnit("postgresql.service"); + $serversqlite->waitForUnit("matrix-synapse.service"); + $serversqlite->waitUntilSucceeds("curl -Lk https://localhost:8448/"); + $serversqlite->mustSucceed("[ -e /var/lib/matrix-synapse/homeserver.db ]"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/memcached.nix b/nixpkgs/nixos/tests/memcached.nix new file mode 100644 index 000000000000..b120599c51dd --- /dev/null +++ b/nixpkgs/nixos/tests/memcached.nix @@ -0,0 +1,28 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "memcached"; + + nodes = { + machine = + { ... }: + { + imports = [ ../modules/profiles/minimal.nix ]; + services.memcached.enable = true; + }; + }; + + testScript = let + testScript = pkgs.writeScript "testScript.py" '' + #!${pkgs.python3.withPackages (p: [p.memcached])}/bin/python + + import memcache + c = memcache.Client(['localhost:11211']) + c.set('key', 'value') + assert 'value' == c.get('key') + ''; + in '' + startAll; + $machine->waitForUnit("memcached.service"); + $machine->waitForOpenPort("11211"); + $machine->succeed("${testScript}"); + ''; +}) diff --git a/nixpkgs/nixos/tests/mesos.nix b/nixpkgs/nixos/tests/mesos.nix new file mode 100644 index 000000000000..2e6dc0eda063 --- /dev/null +++ b/nixpkgs/nixos/tests/mesos.nix @@ -0,0 +1,92 @@ +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/nixpkgs/nixos/tests/mesos_test.py b/nixpkgs/nixos/tests/mesos_test.py new file mode 100644 index 000000000000..be8bb32e49a7 --- /dev/null +++ b/nixpkgs/nixos/tests/mesos_test.py @@ -0,0 +1,72 @@ +#!/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/nixpkgs/nixos/tests/minio.nix b/nixpkgs/nixos/tests/minio.nix new file mode 100644 index 000000000000..40a599546650 --- /dev/null +++ b/nixpkgs/nixos/tests/minio.nix @@ -0,0 +1,34 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "minio"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ bachp ]; + }; + + nodes = { + machine = { pkgs, ... }: { + services.minio = { + enable = true; + accessKey = "BKIKJAA5BMMU2RHO6IBB"; + secretKey = "V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12"; + }; + environment.systemPackages = [ pkgs.minio-client ]; + + # Minio requires at least 1GiB of free disk space to run. + virtualisation.diskSize = 4 * 1024; + }; + }; + + testScript = + '' + startAll; + $machine->waitForUnit("minio.service"); + $machine->waitForOpenPort(9000); + + # Create a test bucket on the server + $machine->succeed("mc config host add minio http://localhost:9000 BKIKJAA5BMMU2RHO6IBB V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 S3v4"); + $machine->succeed("mc mb minio/test-bucket"); + $machine->succeed("mc ls minio") =~ /test-bucket/ or die; + $machine->shutdown; + + ''; +}) diff --git a/nixpkgs/nixos/tests/misc.nix b/nixpkgs/nixos/tests/misc.nix new file mode 100644 index 000000000000..3ad55651b112 --- /dev/null +++ b/nixpkgs/nixos/tests/misc.nix @@ -0,0 +1,142 @@ +# Miscellaneous small tests that don't warrant their own VM run. + +import ./make-test.nix ({ pkgs, ...} : rec { + name = "misc"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + foo = pkgs.writeText "foo" "Hello World"; + + machine = + { lib, ... }: + with lib; + { swapDevices = mkOverride 0 + [ { device = "/root/swapfile"; size = 128; } ]; + environment.variables.EDITOR = mkOverride 0 "emacs"; + documentation.nixos.enable = mkOverride 0 true; + systemd.tmpfiles.rules = [ "d /tmp 1777 root root 10d" ]; + fileSystems = mkVMOverride { "/tmp2" = + { fsType = "tmpfs"; + options = [ "mode=1777" "noauto" ]; + }; + }; + systemd.automounts = singleton + { wantedBy = [ "multi-user.target" ]; + where = "/tmp2"; + }; + users.users.sybil = { isNormalUser = true; group = "wheel"; }; + security.sudo = { enable = true; wheelNeedsPassword = false; }; + boot.kernel.sysctl."vm.swappiness" = 1; + boot.kernelParams = [ "vsyscall=emulate" ]; + system.extraDependencies = [ foo ]; + }; + + testScript = + '' + subtest "nix-db", sub { + my $json = $machine->succeed("nix path-info --json ${foo}"); + $json =~ /"narHash":"sha256:0afw0d9j1hvwiz066z93jiddc33nxg6i6qyp26vnqyglpyfivlq5"/ or die "narHash not set"; + $json =~ /"narSize":128/ or die "narSize not set"; + }; + + subtest "nixos-version", sub { + $machine->succeed("[ `nixos-version | wc -w` = 2 ]"); + }; + + subtest "nixos-rebuild", sub { + $machine->succeed("nixos-rebuild --help | grep 'NixOS module' "); + }; + + # Sanity check for uid/gid assignment. + subtest "users-groups", sub { + $machine->succeed("[ `id -u messagebus` = 4 ]"); + $machine->succeed("[ `id -g messagebus` = 4 ]"); + $machine->succeed("[ `getent group users` = 'users:x:100:' ]"); + }; + + # Regression test for GMP aborts on QEMU. + subtest "gmp", sub { + $machine->succeed("expr 1 + 2"); + }; + + # Test that the swap file got created. + subtest "swapfile", sub { + $machine->waitForUnit("root-swapfile.swap"); + $machine->succeed("ls -l /root/swapfile | grep 134217728"); + }; + + # Test whether kernel.poweroff_cmd is set. + subtest "poweroff_cmd", sub { + $machine->succeed("[ -x \"\$(cat /proc/sys/kernel/poweroff_cmd)\" ]") + }; + + # Test whether the blkio controller is properly enabled. + subtest "blkio-cgroup", sub { + $machine->succeed("[ -n \"\$(cat /sys/fs/cgroup/blkio/blkio.sectors)\" ]") + }; + + # Test whether we have a reboot record in wtmp. + subtest "reboot-wtmp", sub { + $machine->shutdown; + $machine->waitForUnit('multi-user.target'); + $machine->succeed("last | grep reboot >&2"); + }; + + # Test whether we can override environment variables. + subtest "override-env-var", sub { + $machine->succeed('[ "$EDITOR" = emacs ]'); + }; + + # Test whether hostname (and by extension nss_myhostname) works. + subtest "hostname", sub { + $machine->succeed('[ "`hostname`" = machine ]'); + #$machine->succeed('[ "`hostname -s`" = machine ]'); + }; + + # Test whether systemd-udevd automatically loads modules for our hardware. + $machine->succeed("systemctl start systemd-udev-settle.service"); + subtest "udev-auto-load", sub { + $machine->waitForUnit('systemd-udev-settle.service'); + $machine->succeed('lsmod | grep mousedev'); + }; + + # Test whether systemd-tmpfiles-clean works. + subtest "tmpfiles", sub { + $machine->succeed('touch /tmp/foo'); + $machine->succeed('systemctl start systemd-tmpfiles-clean'); + $machine->succeed('[ -e /tmp/foo ]'); + $machine->succeed('date -s "@$(($(date +%s) + 1000000))"'); # move into the future + $machine->succeed('systemctl start systemd-tmpfiles-clean'); + $machine->fail('[ -e /tmp/foo ]'); + }; + + # Test whether automounting works. + subtest "automount", sub { + $machine->fail("grep '/tmp2 tmpfs' /proc/mounts"); + $machine->succeed("touch /tmp2/x"); + $machine->succeed("grep '/tmp2 tmpfs' /proc/mounts"); + }; + + subtest "shell-vars", sub { + $machine->succeed('[ -n "$NIX_PATH" ]'); + }; + + subtest "nix-db", sub { + $machine->succeed("nix-store -qR /run/current-system | grep nixos-"); + }; + + # Test sysctl + subtest "sysctl", sub { + $machine->waitForUnit("systemd-sysctl.service"); + $machine->succeed('[ `sysctl -ne vm.swappiness` = 1 ]'); + $machine->execute('sysctl vm.swappiness=60'); + $machine->succeed('[ `sysctl -ne vm.swappiness` = 60 ]'); + }; + + # Test boot parameters + subtest "bootparam", sub { + $machine->succeed('grep -Fq vsyscall=emulate /proc/cmdline'); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/mongodb.nix b/nixpkgs/nixos/tests/mongodb.nix new file mode 100644 index 000000000000..c9439b65292d --- /dev/null +++ b/nixpkgs/nixos/tests/mongodb.nix @@ -0,0 +1,34 @@ +# This test start mongodb, runs a query using mongo shell + +import ./make-test.nix ({ pkgs, ...} : let + testQuery = pkgs.writeScript "nixtest.js" '' + db.greetings.insert({ "greeting": "hello" }); + print(db.greetings.findOne().greeting); + ''; +in { + name = "mongodb"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ bluescreen303 offline cstrahan rvl ]; + }; + + nodes = { + one = + { ... }: + { + services = { + mongodb.enable = true; + mongodb.extraConfig = '' + # Allow starting engine with only a small virtual disk + storage.journal.enabled: false + storage.mmapv1.smallFiles: true + ''; + }; + }; + }; + + testScript = '' + startAll; + $one->waitForUnit("mongodb.service"); + $one->succeed("mongo nixtest ${testQuery}") =~ /hello/ or die; + ''; +}) diff --git a/nixpkgs/nixos/tests/morty.nix b/nixpkgs/nixos/tests/morty.nix new file mode 100644 index 000000000000..eab123bd50f8 --- /dev/null +++ b/nixpkgs/nixos/tests/morty.nix @@ -0,0 +1,32 @@ +import ./make-test.nix ({ pkgs, ... }: + +{ + name = "morty"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ leenaars ]; + }; + + nodes = + { mortyProxyWithKey = + + { ... }: + { services.morty = { + enable = true; + key = "78a9cd0cfee20c672f78427efb2a2a96036027f0"; + port = 3001; + }; + }; + + }; + + testScript = + { ... }: + '' + $mortyProxyWithKey->waitForUnit("default.target"); + + $mortyProxyWithKey->waitForOpenPort(3001); + $mortyProxyWithKey->succeed("curl -L 127.0.0.1:3001 | grep MortyProxy"); + + ''; + +}) diff --git a/nixpkgs/nixos/tests/mpd.nix b/nixpkgs/nixos/tests/mpd.nix new file mode 100644 index 000000000000..ac2b810defe3 --- /dev/null +++ b/nixpkgs/nixos/tests/mpd.nix @@ -0,0 +1,119 @@ +import ./make-test.nix ({ pkgs, ... }: + let + track = pkgs.fetchurl { + # Sourced from http://freemusicarchive.org/music/Blue_Wave_Theory/Surf_Music_Month_Challenge/Skyhawk_Beach_fade_in + # License: http://creativecommons.org/licenses/by-sa/4.0/ + + name = "Blue_Wave_Theory-Skyhawk_Beach.mp3"; + url = https://freemusicarchive.org/file/music/ccCommunity/Blue_Wave_Theory/Surf_Music_Month_Challenge/Blue_Wave_Theory_-_04_-_Skyhawk_Beach.mp3; + sha256 = "0xw417bxkx4gqqy139bb21yldi37xx8xjfxrwaqa0gyw19dl6mgp"; + }; + + defaultCfg = rec { + user = "mpd"; + group = "mpd"; + dataDir = "/var/lib/mpd"; + musicDirectory = "${dataDir}/music"; + }; + + defaultMpdCfg = with defaultCfg; { + inherit dataDir musicDirectory user group; + enable = true; + }; + + musicService = { user, group, musicDirectory }: { + description = "Sets up the music file(s) for MPD to use."; + requires = [ "mpd.service" ]; + after = [ "mpd.service" ]; + wantedBy = [ "default.target" ]; + script = '' + mkdir -p ${musicDirectory} && chown -R ${user}:${group} ${musicDirectory} + cp ${track} ${musicDirectory} + chown ${user}:${group} ${musicDirectory}/$(basename ${track}) + ''; + }; + + mkServer = { mpd, musicService, }: + { boot.kernelModules = [ "snd-dummy" ]; + sound.enable = true; + services.mpd = mpd; + systemd.services.musicService = musicService; + }; + in { + name = "mpd"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ emmanuelrosa ]; + }; + + nodes = + { client = + { ... }: { }; + + serverALSA = + { ... }: (mkServer { + mpd = defaultMpdCfg // { + network.listenAddress = "any"; + extraConfig = '' + audio_output { + type "alsa" + name "ALSA" + mixer_type "null" + } + ''; + }; + + musicService = with defaultMpdCfg; musicService { inherit user group musicDirectory; }; + }) // { networking.firewall.allowedTCPPorts = [ 6600 ]; }; + + serverPulseAudio = + { ... }: (mkServer { + mpd = defaultMpdCfg // { + extraConfig = '' + audio_output { + type "pulse" + name "The Pulse" + } + ''; + }; + + musicService = with defaultCfg; musicService { inherit user group musicDirectory; }; + }) // { hardware.pulseaudio.enable = true; }; + }; + + testScript = '' + my $mpc = "${pkgs.mpc_cli}/bin/mpc --wait"; + + # Connects to the given server and attempts to play a tune. + sub play_some_music { + my $server = $_[0]; + + $server->waitForUnit("mpd.service"); + $server->succeed("$mpc update"); + my @tracks = $server->execute("$mpc ls"); + + for my $track (split(/\n/, $tracks[1])) { + $server->succeed("$mpc add $track"); + }; + + my @added_tracks = $server->execute("$mpc listall"); + (length $added_tracks[1]) > 0 or die "Failed to add audio tracks to the playlist."; + + $server->succeed("$mpc play"); + + my @status = $server->execute("$mpc status"); + my @output = split(/\n/, $status[1]); + $output[1] =~ /.*playing.*/ or die "Audio track is not playing, as expected."; + + $server->succeed("$mpc stop"); + }; + + play_some_music($serverALSA); + play_some_music($serverPulseAudio); + + $client->succeed("$mpc -h serverALSA status"); + + # The PulseAudio-based server is configured not to accept external client connections + # to perform the following test: + $client->fail("$mpc -h serverPulseAudio status"); + ''; +}) diff --git a/nixpkgs/nixos/tests/mpich-example.c b/nixpkgs/nixos/tests/mpich-example.c new file mode 100644 index 000000000000..c48e3c45b72e --- /dev/null +++ b/nixpkgs/nixos/tests/mpich-example.c @@ -0,0 +1,21 @@ +#include <stdio.h> +#include <stdlib.h> +#include <mpi.h> + +int +main (int argc, char *argv[]) +{ + int rank, size, length; + char name[BUFSIZ]; + + MPI_Init (&argc, &argv); + MPI_Comm_rank (MPI_COMM_WORLD, &rank); + MPI_Comm_size (MPI_COMM_WORLD, &size); + MPI_Get_processor_name (name, &length); + + printf ("%s: hello world from process %d of %d\n", name, rank, size); + + MPI_Finalize (); + + return EXIT_SUCCESS; +} diff --git a/nixpkgs/nixos/tests/mumble.nix b/nixpkgs/nixos/tests/mumble.nix new file mode 100644 index 000000000000..8146453bfd55 --- /dev/null +++ b/nixpkgs/nixos/tests/mumble.nix @@ -0,0 +1,73 @@ +import ./make-test.nix ({ pkgs, ...} : + +let + client = { pkgs, ... }: { + imports = [ ./common/x11.nix ]; + environment.systemPackages = [ pkgs.mumble ]; + }; +in +{ + name = "mumble"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ thoughtpolice eelco chaoflow ]; + }; + + nodes = { + server = { config, ... }: { + services.murmur.enable = true; + services.murmur.registerName = "NixOS tests"; + networking.firewall.allowedTCPPorts = [ config.services.murmur.port ]; + }; + + client1 = client; + client2 = client; + }; + + testScript = '' + startAll; + + $server->waitForUnit("murmur.service"); + $client1->waitForX; + $client2->waitForX; + + $client1->execute("mumble mumble://client1\@server/test &"); + $client2->execute("mumble mumble://client2\@server/test &"); + + # cancel client audio configuration + $client1->waitForWindow(qr/Audio Tuning Wizard/); + $client2->waitForWindow(qr/Audio Tuning Wizard/); + $server->sleep(5); # wait because mumble is slow to register event handlers + $client1->sendKeys("esc"); + $client2->sendKeys("esc"); + + # cancel client cert configuration + $client1->waitForWindow(qr/Certificate Management/); + $client2->waitForWindow(qr/Certificate Management/); + $server->sleep(5); # wait because mumble is slow to register event handlers + $client1->sendKeys("esc"); + $client2->sendKeys("esc"); + + # accept server certificate + $client1->waitForWindow(qr/^Mumble$/); + $client2->waitForWindow(qr/^Mumble$/); + $server->sleep(5); # wait because mumble is slow to register event handlers + $client1->sendChars("y"); + $client2->sendChars("y"); + $server->sleep(5); # wait because mumble is slow to register event handlers + + # sometimes the wrong of the 2 windows is focused, we switch focus and try pressing "y" again + $client1->sendKeys("alt-tab"); + $client2->sendKeys("alt-tab"); + $server->sleep(5); # wait because mumble is slow to register event handlers + $client1->sendChars("y"); + $client2->sendChars("y"); + + # Find clients in logs + $server->waitUntilSucceeds("grep -q 'client1' /var/log/murmur/murmurd.log"); + $server->waitUntilSucceeds("grep -q 'client2' /var/log/murmur/murmurd.log"); + + $server->sleep(5); # wait to get screenshot + $client1->screenshot("screen1"); + $client2->screenshot("screen2"); + ''; +}) diff --git a/nixpkgs/nixos/tests/munin.nix b/nixpkgs/nixos/tests/munin.nix new file mode 100644 index 000000000000..95cecf17b8cc --- /dev/null +++ b/nixpkgs/nixos/tests/munin.nix @@ -0,0 +1,44 @@ +# This test runs basic munin setup with node and cron job running on the same +# machine. + +import ./make-test.nix ({ pkgs, ...} : { + name = "munin"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ domenkozar eelco chaoflow ]; + }; + + nodes = { + one = + { config, ... }: + { + services = { + munin-node = { + enable = true; + # disable a failing plugin to prevent irrelevant error message, see #23049 + disabledPlugins = [ "apc_nis" ]; + }; + munin-cron = { + enable = true; + hosts = '' + [${config.networking.hostName}] + address localhost + ''; + }; + }; + # long timeout to prevent hydra failure on high load + systemd.services.munin-node.serviceConfig.TimeoutStartSec = "10min"; + }; + }; + + testScript = '' + startAll; + + $one->waitForUnit("munin-node.service"); + # make sure the node is actually listening + $one->waitForOpenPort(4949); + $one->succeed('systemctl start munin-cron'); + # wait for munin-cron output + $one->waitForFile("/var/lib/munin/one/one-uptime-uptime-g.rrd"); + $one->waitForFile("/var/www/munin/one/index.html"); + ''; +}) diff --git a/nixpkgs/nixos/tests/mutable-users.nix b/nixpkgs/nixos/tests/mutable-users.nix new file mode 100644 index 000000000000..e590703ab2f4 --- /dev/null +++ b/nixpkgs/nixos/tests/mutable-users.nix @@ -0,0 +1,39 @@ +# Mutable users tests. + +import ./make-test.nix ({ pkgs, ...} : { + name = "mutable-users"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ gleber ]; + }; + + nodes = { + machine = { ... }: { + users.mutableUsers = false; + }; + mutable = { ... }: { + users.mutableUsers = true; + }; + }; + + testScript = {nodes, ...}: let + immutableSystem = nodes.machine.config.system.build.toplevel; + mutableSystem = nodes.mutable.config.system.build.toplevel; + in '' + $machine->start(); + $machine->waitForUnit("default.target"); + + # Machine starts in immutable mode. Add a user and test if reactivating + # configuration removes the user. + $machine->fail("cat /etc/passwd | grep ^foobar:"); + $machine->succeed("sudo useradd foobar"); + $machine->succeed("cat /etc/passwd | grep ^foobar:"); + $machine->succeed("${immutableSystem}/bin/switch-to-configuration test"); + $machine->fail("cat /etc/passwd | grep ^foobar:"); + + # In immutable mode passwd is not wrapped, while in mutable mode it is + # wrapped. + $machine->succeed('which passwd | grep /run/current-system/'); + $machine->succeed("${mutableSystem}/bin/switch-to-configuration test"); + $machine->succeed('which passwd | grep /run/wrappers/'); + ''; +}) diff --git a/nixpkgs/nixos/tests/mxisd.nix b/nixpkgs/nixos/tests/mxisd.nix new file mode 100644 index 000000000000..3d03a5a53e38 --- /dev/null +++ b/nixpkgs/nixos/tests/mxisd.nix @@ -0,0 +1,21 @@ +import ./make-test.nix ({ pkgs, ... } : { + + name = "mxisd"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ mguentner ]; + }; + + nodes = { + server_mxisd = args : { + services.mxisd.enable = true; + services.mxisd.matrix.domain = "example.org"; + }; + }; + + testScript = '' + startAll; + $server_mxisd->waitForUnit("mxisd.service"); + $server_mxisd->waitForOpenPort(8090); + $server_mxisd->succeed("curl -Ssf \"http://127.0.0.1:8090/_matrix/identity/api/v1\"") + ''; +}) diff --git a/nixpkgs/nixos/tests/mysql-backup.nix b/nixpkgs/nixos/tests/mysql-backup.nix new file mode 100644 index 000000000000..81482dfef7e5 --- /dev/null +++ b/nixpkgs/nixos/tests/mysql-backup.nix @@ -0,0 +1,50 @@ +# Test whether mysqlBackup option works +import ./make-test.nix ({ pkgs, ... } : { + name = "mysql-backup"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ rvl ]; + }; + + nodes = { + master = { pkgs, ... }: { + services.mysql = { + enable = true; + initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ]; + package = pkgs.mysql; + }; + + services.mysqlBackup = { + enable = true; + databases = [ "doesnotexist" "testdb" ]; + }; + }; + }; + + testScript = + '' startAll; + + # Delete backup file that may be left over from a previous test run. + # This is not needed on Hydra but useful for repeated local test runs. + $master->execute("rm -f /var/backup/mysql/testdb.gz"); + + # Need to have mysql started so that it can be populated with data. + $master->waitForUnit("mysql.service"); + + # Wait for testdb to be fully populated (5 rows). + $master->waitUntilSucceeds("mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"); + + # Do a backup and wait for it to start + $master->startJob("mysql-backup.service"); + $master->waitForJob("mysql-backup.service"); + + # wait for backup to fail, because of database 'doesnotexist' + $master->waitUntilFails("systemctl is-active -q mysql-backup.service"); + + # wait for backup file and check that data appears in backup + $master->waitForFile("/var/backup/mysql/testdb.gz"); + $master->succeed("${pkgs.gzip}/bin/zcat /var/backup/mysql/testdb.gz | grep hello"); + + # Check that a failed backup is logged + $master->succeed("journalctl -u mysql-backup.service | grep 'fail.*doesnotexist' > /dev/null"); + ''; +}) diff --git a/nixpkgs/nixos/tests/mysql-replication.nix b/nixpkgs/nixos/tests/mysql-replication.nix new file mode 100644 index 000000000000..84d70cf35246 --- /dev/null +++ b/nixpkgs/nixos/tests/mysql-replication.nix @@ -0,0 +1,81 @@ +import ./make-test.nix ({ pkgs, ...} : + +let + replicateUser = "replicate"; + replicatePassword = "secret"; +in + +{ + name = "mysql-replication"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow shlevy ]; + }; + + nodes = { + master = + { pkgs, ... }: + + { + services.mysql.enable = true; + services.mysql.package = pkgs.mysql; + services.mysql.replication.role = "master"; + services.mysql.replication.slaveHost = "%"; + services.mysql.replication.masterUser = replicateUser; + services.mysql.replication.masterPassword = replicatePassword; + services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ]; + networking.firewall.allowedTCPPorts = [ 3306 ]; + }; + + slave1 = + { pkgs, nodes, ... }: + + { + services.mysql.enable = true; + services.mysql.package = pkgs.mysql; + services.mysql.replication.role = "slave"; + services.mysql.replication.serverId = 2; + services.mysql.replication.masterHost = nodes.master.config.networking.hostName; + services.mysql.replication.masterUser = replicateUser; + services.mysql.replication.masterPassword = replicatePassword; + }; + + slave2 = + { pkgs, nodes, ... }: + + { + services.mysql.enable = true; + services.mysql.package = pkgs.mysql; + services.mysql.replication.role = "slave"; + services.mysql.replication.serverId = 3; + services.mysql.replication.masterHost = nodes.master.config.networking.hostName; + services.mysql.replication.masterUser = replicateUser; + services.mysql.replication.masterPassword = replicatePassword; + }; + }; + + testScript = '' + $master->start; + $master->waitForUnit("mysql"); + $master->waitForOpenPort(3306); + # Wait for testdb to be fully populated (5 rows). + $master->waitUntilSucceeds("mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"); + + $slave1->start; + $slave2->start; + $slave1->waitForUnit("mysql"); + $slave1->waitForOpenPort(3306); + $slave2->waitForUnit("mysql"); + $slave2->waitForOpenPort(3306); + + # wait for replications to finish + $slave1->waitUntilSucceeds("mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"); + $slave2->waitUntilSucceeds("mysql -u root -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"); + $slave2->succeed("systemctl start mysql"); + $slave2->waitForUnit("mysql"); + $slave2->waitForOpenPort(3306); + $slave2->waitUntilSucceeds("echo 'select * from testdb.tests where Id = 123;' | mysql -u root -N | grep 456"); + ''; +}) diff --git a/nixpkgs/nixos/tests/mysql.nix b/nixpkgs/nixos/tests/mysql.nix new file mode 100644 index 000000000000..7251c4a86499 --- /dev/null +++ b/nixpkgs/nixos/tests/mysql.nix @@ -0,0 +1,24 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "mysql"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow shlevy ]; + }; + + nodes = { + master = + { pkgs, ... }: + + { + services.mysql.enable = true; + services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ]; + services.mysql.package = pkgs.mysql; + }; + }; + + testScript = '' + startAll; + + $master->waitForUnit("mysql"); + $master->succeed("echo 'use testdb; select * from tests' | mysql -u root -N | grep 4"); + ''; +}) diff --git a/nixpkgs/nixos/tests/nat.nix b/nixpkgs/nixos/tests/nat.nix new file mode 100644 index 000000000000..34229e913110 --- /dev/null +++ b/nixpkgs/nixos/tests/nat.nix @@ -0,0 +1,117 @@ +# This is a simple distributed test involving a topology with two +# separate virtual networks - the "inside" and the "outside" - with a +# client on the inside network, a server on the outside network, and a +# router connected to both that performs Network Address Translation +# for the client. +import ./make-test.nix ({ pkgs, lib, withFirewall, withConntrackHelpers ? false, ... }: + let + unit = if withFirewall then "firewall" else "nat"; + + routerBase = + lib.mkMerge [ + { virtualisation.vlans = [ 2 1 ]; + networking.firewall.enable = withFirewall; + networking.nat.internalIPs = [ "192.168.1.0/24" ]; + networking.nat.externalInterface = "eth1"; + } + (lib.optionalAttrs withConntrackHelpers { + networking.firewall.connectionTrackingModules = [ "ftp" ]; + networking.firewall.autoLoadConntrackHelpers = true; + }) + ]; + in + { + name = "nat" + (if withFirewall then "WithFirewall" else "Standalone") + + (lib.optionalString withConntrackHelpers "withConntrackHelpers"); + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow rob ]; + }; + + nodes = + { client = + { pkgs, nodes, ... }: + lib.mkMerge [ + { virtualisation.vlans = [ 1 ]; + networking.defaultGateway = + (pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ipv4.addresses).address; + } + (lib.optionalAttrs withConntrackHelpers { + networking.firewall.connectionTrackingModules = [ "ftp" ]; + networking.firewall.autoLoadConntrackHelpers = true; + }) + ]; + + router = + { ... }: lib.mkMerge [ + routerBase + { networking.nat.enable = true; } + ]; + + routerDummyNoNat = + { ... }: lib.mkMerge [ + routerBase + { networking.nat.enable = false; } + ]; + + server = + { ... }: + { virtualisation.vlans = [ 2 ]; + networking.firewall.enable = false; + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + services.vsftpd.enable = true; + services.vsftpd.anonymousUser = true; + }; + }; + + testScript = + { nodes, ... }: let + routerDummyNoNatClosure = nodes.routerDummyNoNat.config.system.build.toplevel; + routerClosure = nodes.router.config.system.build.toplevel; + in '' + $client->start; + $router->start; + $server->start; + + # The router should have access to the server. + $server->waitForUnit("network.target"); + $server->waitForUnit("httpd"); + $router->waitForUnit("network.target"); + $router->succeed("curl --fail http://server/ >&2"); + + # The client should be also able to connect via the NAT router. + $router->waitForUnit("${unit}"); + $client->waitForUnit("network.target"); + $client->succeed("curl --fail http://server/ >&2"); + $client->succeed("ping -c 1 server >&2"); + + # Test whether passive FTP works. + $server->waitForUnit("vsftpd"); + $server->succeed("echo Hello World > /home/ftp/foo.txt"); + $client->succeed("curl -v ftp://server/foo.txt >&2"); + + # Test whether active FTP works. + $client->${if withConntrackHelpers then "succeed" else "fail"}( + "curl -v -P - ftp://server/foo.txt >&2"); + + # Test ICMP. + $client->succeed("ping -c 1 router >&2"); + $router->succeed("ping -c 1 client >&2"); + + # If we turn off NAT, the client shouldn't be able to reach the server. + $router->succeed("${routerDummyNoNatClosure}/bin/switch-to-configuration test 2>&1"); + $client->fail("curl --fail --connect-timeout 5 http://server/ >&2"); + $client->fail("ping -c 1 server >&2"); + + # And make sure that reloading the NAT job works. + $router->succeed("${routerClosure}/bin/switch-to-configuration test 2>&1"); + # FIXME: this should not be necessary, but nat.service is not started because + # network.target is not triggered + # (https://github.com/NixOS/nixpkgs/issues/16230#issuecomment-226408359) + ${lib.optionalString (!withFirewall) '' + $router->succeed("systemctl start nat.service"); + ''} + $client->succeed("curl --fail http://server/ >&2"); + $client->succeed("ping -c 1 server >&2"); + ''; + }) diff --git a/nixpkgs/nixos/tests/ndppd.nix b/nixpkgs/nixos/tests/ndppd.nix new file mode 100644 index 000000000000..9f24eb6d9d45 --- /dev/null +++ b/nixpkgs/nixos/tests/ndppd.nix @@ -0,0 +1,61 @@ +import ./make-test.nix ({ pkgs, lib, ...} : { + name = "ndppd"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ fpletz ]; + }; + + nodes = { + upstream = { pkgs, ... }: { + environment.systemPackages = [ pkgs.tcpdump ]; + networking.useDHCP = false; + networking.interfaces = { + eth1 = { + ipv6.addresses = [ + { address = "fd23::1"; prefixLength = 112; } + ]; + ipv6.routes = [ + { address = "fd42::"; + prefixLength = 112; + } + ]; + }; + }; + }; + server = { pkgs, ... }: { + boot.kernel.sysctl = { + "net.ipv6.conf.all.forwarding" = "1"; + "net.ipv6.conf.default.forwarding" = "1"; + }; + environment.systemPackages = [ pkgs.tcpdump ]; + networking.useDHCP = false; + networking.interfaces = { + eth1 = { + ipv6.addresses = [ + { address = "fd23::2"; prefixLength = 112; } + ]; + }; + }; + services.ndppd = { + enable = true; + interface = "eth1"; + network = "fd42::/112"; + }; + containers.client = { + autoStart = true; + privateNetwork = true; + hostAddress = "192.168.255.1"; + localAddress = "192.168.255.2"; + hostAddress6 = "fd42::1"; + localAddress6 = "fd42::2"; + config = {}; + }; + }; + }; + + testScript = '' + startAll; + $server->waitForUnit("multi-user.target"); + $upstream->waitForUnit("multi-user.target"); + $upstream->waitUntilSucceeds("ping -c5 fd42::2"); + ''; +}) diff --git a/nixpkgs/nixos/tests/neo4j.nix b/nixpkgs/nixos/tests/neo4j.nix new file mode 100644 index 000000000000..86ed8970517c --- /dev/null +++ b/nixpkgs/nixos/tests/neo4j.nix @@ -0,0 +1,20 @@ +import ./make-test.nix { + name = "neo4j"; + + nodes = { + master = + { ... }: + + { + services.neo4j.enable = true; + }; + }; + + testScript = '' + startAll; + + $master->waitForUnit("neo4j"); + $master->sleep(20); # Hopefully this is long enough!! + $master->succeed("curl http://localhost:7474/"); + ''; +} diff --git a/nixpkgs/nixos/tests/netdata.nix b/nixpkgs/nixos/tests/netdata.nix new file mode 100644 index 000000000000..9bd147968e4b --- /dev/null +++ b/nixpkgs/nixos/tests/netdata.nix @@ -0,0 +1,38 @@ +# This test runs netdata and checks for data via apps.plugin + +import ./make-test.nix ({ pkgs, ...} : { + name = "netdata"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ cransom ]; + }; + + nodes = { + netdata = + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ curl jq ]; + services.netdata.enable = true; + }; + }; + + testScript = '' + startAll; + + $netdata->waitForUnit("netdata.service"); + + # wait for the service to listen before sending a request + $netdata->waitForOpenPort(19999); + + # check if the netdata main page loads. + $netdata->succeed("curl --fail http://localhost:19999/"); + + # check if netdata can read disk ops for root owned processes. + # if > 0, successful. verifies both netdata working and + # apps.plugin has elevated capabilities. + my $cmd = <<'CMD'; + curl -s http://localhost:19999/api/v1/data\?chart=users.pwrites | \ + jq -e '[.data[range(10)][.labels | indices("root")[0]]] | add | . > 0' + CMD + $netdata->waitUntilSucceeds($cmd); + ''; +}) diff --git a/nixpkgs/nixos/tests/networking-proxy.nix b/nixpkgs/nixos/tests/networking-proxy.nix new file mode 100644 index 000000000000..ab908c96e5ee --- /dev/null +++ b/nixpkgs/nixos/tests/networking-proxy.nix @@ -0,0 +1,111 @@ +# Test whether `networking.proxy' work as expected. + +# TODO: use a real proxy node and put this test into networking.nix +# TODO: test whether nix tools work as expected behind a proxy + +let default-config = { + imports = [ ./common/user-account.nix ]; + + services.xserver.enable = false; + + virtualisation.memorySize = 128; + }; +in import ./make-test.nix ({ pkgs, ...} : { + name = "networking-proxy"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ]; + }; + + nodes = { + # no proxy + machine = + { ... }: + + default-config; + + # proxy default + machine2 = + { ... }: + + default-config // { + networking.proxy.default = "http://user:pass@host:port"; + }; + + # specific proxy options + machine3 = + { ... }: + + default-config // + { + networking.proxy = { + # useless because overriden by the next options + default = "http://user:pass@host:port"; + # advanced proxy setup + httpProxy = "123-http://user:pass@http-host:port"; + httpsProxy = "456-http://user:pass@https-host:port"; + rsyncProxy = "789-http://user:pass@rsync-host:port"; + ftpProxy = "101112-http://user:pass@ftp-host:port"; + noProxy = "131415-127.0.0.1,localhost,.localdomain"; + }; + }; + + # mix default + proxy options + machine4 = + { ... }: + + default-config // { + networking.proxy = { + # open for all *_proxy env var + default = "000-http://user:pass@default-host:port"; + # except for those 2 + rsyncProxy = "123-http://user:pass@http-host:port"; + noProxy = "131415-127.0.0.1,localhost,.localdomain"; + }; + }; + }; + + testScript = + '' + startAll; + + # no proxy at all + print $machine->execute("env | grep -i proxy"); + print $machine->execute("su - alice -c 'env | grep -i proxy'"); + $machine->mustFail("env | grep -i proxy"); + $machine->mustFail("su - alice -c 'env | grep -i proxy'"); + + # Use a default proxy option + print $machine2->execute("env | grep -i proxy"); + print $machine2->execute("su - alice -c 'env | grep -i proxy'"); + $machine2->mustSucceed("env | grep -i proxy"); + $machine2->mustSucceed("su - alice -c 'env | grep -i proxy'"); + + # explicitly set each proxy option + print $machine3->execute("env | grep -i proxy"); + print $machine3->execute("su - alice -c 'env | grep -i proxy'"); + $machine3->mustSucceed("env | grep -i http_proxy | grep 123"); + $machine3->mustSucceed("env | grep -i https_proxy | grep 456"); + $machine3->mustSucceed("env | grep -i rsync_proxy | grep 789"); + $machine3->mustSucceed("env | grep -i ftp_proxy | grep 101112"); + $machine3->mustSucceed("env | grep -i no_proxy | grep 131415"); + $machine3->mustSucceed("su - alice -c 'env | grep -i http_proxy | grep 123'"); + $machine3->mustSucceed("su - alice -c 'env | grep -i https_proxy | grep 456'"); + $machine3->mustSucceed("su - alice -c 'env | grep -i rsync_proxy | grep 789'"); + $machine3->mustSucceed("su - alice -c 'env | grep -i ftp_proxy | grep 101112'"); + $machine3->mustSucceed("su - alice -c 'env | grep -i no_proxy | grep 131415'"); + + # set default proxy option + some other specifics + print $machine4->execute("env | grep -i proxy"); + print $machine4->execute("su - alice -c 'env | grep -i proxy'"); + $machine4->mustSucceed("env | grep -i http_proxy | grep 000"); + $machine4->mustSucceed("env | grep -i https_proxy | grep 000"); + $machine4->mustSucceed("env | grep -i rsync_proxy | grep 123"); + $machine4->mustSucceed("env | grep -i ftp_proxy | grep 000"); + $machine4->mustSucceed("env | grep -i no_proxy | grep 131415"); + $machine4->mustSucceed("su - alice -c 'env | grep -i http_proxy | grep 000'"); + $machine4->mustSucceed("su - alice -c 'env | grep -i https_proxy | grep 000'"); + $machine4->mustSucceed("su - alice -c 'env | grep -i rsync_proxy | grep 123'"); + $machine4->mustSucceed("su - alice -c 'env | grep -i ftp_proxy | grep 000'"); + $machine4->mustSucceed("su - alice -c 'env | grep -i no_proxy | grep 131415'"); + ''; +}) diff --git a/nixpkgs/nixos/tests/networking.nix b/nixpkgs/nixos/tests/networking.nix new file mode 100644 index 000000000000..ed9f287d5582 --- /dev/null +++ b/nixpkgs/nixos/tests/networking.nix @@ -0,0 +1,609 @@ +{ system ? builtins.currentSystem +, config ? {} +, pkgs ? import ../.. { inherit system config; } +# bool: whether to use networkd in the tests +, networkd }: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + router = { config, pkgs, ... }: + with pkgs.lib; + let + vlanIfs = range 1 (length config.virtualisation.vlans); + in { + environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules + virtualisation.vlans = [ 1 2 3 ]; + boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true; + networking = { + useDHCP = false; + useNetworkd = networkd; + firewall.checkReversePath = true; + firewall.allowedUDPPorts = [ 547 ]; + interfaces = mkOverride 0 (listToAttrs (flip map vlanIfs (n: + nameValuePair "eth${toString n}" { + ipv4.addresses = [ { address = "192.168.${toString n}.1"; prefixLength = 24; } ]; + ipv6.addresses = [ { address = "fd00:1234:5678:${toString n}::1"; prefixLength = 64; } ]; + }))); + }; + services.dhcpd4 = { + enable = true; + interfaces = map (n: "eth${toString n}") vlanIfs; + extraConfig = '' + authoritative; + '' + 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; + } + ''); + }; + services.radvd = { + enable = true; + config = flip concatMapStrings vlanIfs (n: '' + interface eth${toString n} { + AdvSendAdvert on; + AdvManagedFlag on; + AdvOtherConfigFlag on; + + prefix fd00:1234:5678:${toString n}::/64 { + AdvAutonomous off; + }; + }; + ''); + }; + services.dhcpd6 = { + enable = true; + interfaces = map (n: "eth${toString n}") vlanIfs; + extraConfig = '' + authoritative; + '' + flip concatMapStrings vlanIfs (n: '' + subnet6 fd00:1234:5678:${toString n}::/64 { + range6 fd00:1234:5678:${toString n}::2 fd00:1234:5678:${toString n}::2; + } + ''); + }; + }; + + testCases = { + loopback = { + name = "Loopback"; + machine.networking.useNetworkd = networkd; + testScript = '' + startAll; + $machine->waitForUnit("network.target"); + $machine->succeed("ip addr show lo | grep -q 'inet 127.0.0.1/8 '"); + $machine->succeed("ip addr show lo | grep -q 'inet6 ::1/128 '"); + ''; + }; + static = { + name = "Static"; + nodes.router = router; + nodes.client = { pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 2 ]; + networking = { + useNetworkd = networkd; + useDHCP = false; + defaultGateway = "192.168.1.1"; + interfaces.eth1.ipv4.addresses = mkOverride 0 [ + { address = "192.168.1.2"; prefixLength = 24; } + { address = "192.168.1.3"; prefixLength = 32; } + { address = "192.168.1.10"; prefixLength = 32; } + ]; + interfaces.eth2.ipv4.addresses = mkOverride 0 [ + { address = "192.168.2.2"; prefixLength = 24; } + ]; + }; + }; + testScript = { ... }: + '' + startAll; + + $client->waitForUnit("network.target"); + $router->waitForUnit("network-online.target"); + + # Make sure dhcpcd is not started + $client->fail("systemctl status dhcpcd.service"); + + # Test vlan 1 + $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.3"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.10"); + + $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.10"); + + # Test vlan 2 + $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.2.2"); + + $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.2.2"); + + # Test default gateway + $router->waitUntilSucceeds("ping -c 1 192.168.3.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.3.1"); + ''; + }; + dhcpSimple = { + name = "SimpleDHCP"; + nodes.router = router; + nodes.client = { pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 2 ]; + networking = { + useNetworkd = networkd; + useDHCP = true; + interfaces.eth1 = { + ipv4.addresses = mkOverride 0 [ ]; + ipv6.addresses = mkOverride 0 [ ]; + }; + interfaces.eth2 = { + ipv4.addresses = mkOverride 0 [ ]; + ipv6.addresses = mkOverride 0 [ ]; + }; + }; + }; + testScript = { ... }: + '' + startAll; + + $client->waitForUnit("network.target"); + $router->waitForUnit("network-online.target"); + + # Wait until we have an ip address on each interface + $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); + $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'"); + $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q '192.168.2'"); + $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q 'fd00:1234:5678:2:'"); + + # Test vlan 1 + $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1"); + $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::2"); + + $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1"); + $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::2"); + + # Test vlan 2 + $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.2.2"); + $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::1"); + $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::2"); + + $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.2.2"); + $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::1"); + $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::2"); + ''; + }; + dhcpOneIf = { + name = "OneInterfaceDHCP"; + nodes.router = router; + nodes.client = { pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 2 ]; + networking = { + useNetworkd = networkd; + useDHCP = false; + interfaces.eth1 = { + ipv4.addresses = mkOverride 0 [ ]; + useDHCP = true; + }; + interfaces.eth2.ipv4.addresses = mkOverride 0 [ ]; + }; + }; + testScript = { ... }: + '' + startAll; + + # Wait for networking to come up + $client->waitForUnit("network.target"); + $router->waitForUnit("network.target"); + + # Wait until we have an ip address on each interface + $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); + + # Test vlan 1 + $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); + + $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + + # Test vlan 2 + $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $client->fail("ping -c 1 192.168.2.2"); + + $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $router->fail("ping -c 1 192.168.2.2"); + ''; + }; + bond = let + node = address: { pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 2 ]; + networking = { + useNetworkd = networkd; + useDHCP = false; + bonds.bond = { + interfaces = [ "eth1" "eth2" ]; + driverOptions.mode = "balance-rr"; + }; + interfaces.eth1.ipv4.addresses = mkOverride 0 [ ]; + interfaces.eth2.ipv4.addresses = mkOverride 0 [ ]; + interfaces.bond.ipv4.addresses = mkOverride 0 + [ { inherit address; prefixLength = 30; } ]; + }; + }; + in { + name = "Bond"; + nodes.client1 = node "192.168.1.1"; + nodes.client2 = node "192.168.1.2"; + testScript = { ... }: + '' + startAll; + + # Wait for networking to come up + $client1->waitForUnit("network.target"); + $client2->waitForUnit("network.target"); + + # Test bonding + $client1->waitUntilSucceeds("ping -c 2 192.168.1.1"); + $client1->waitUntilSucceeds("ping -c 2 192.168.1.2"); + + $client2->waitUntilSucceeds("ping -c 2 192.168.1.1"); + $client2->waitUntilSucceeds("ping -c 2 192.168.1.2"); + ''; + }; + bridge = let + node = { address, vlan }: { pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ vlan ]; + networking = { + useNetworkd = networkd; + useDHCP = false; + interfaces.eth1.ipv4.addresses = mkOverride 0 + [ { inherit address; prefixLength = 24; } ]; + }; + }; + in { + name = "Bridge"; + nodes.client1 = node { address = "192.168.1.2"; vlan = 1; }; + nodes.client2 = node { address = "192.168.1.3"; vlan = 2; }; + nodes.router = { pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 2 ]; + networking = { + useNetworkd = networkd; + useDHCP = false; + bridges.bridge.interfaces = [ "eth1" "eth2" ]; + interfaces.eth1.ipv4.addresses = mkOverride 0 [ ]; + interfaces.eth2.ipv4.addresses = mkOverride 0 [ ]; + interfaces.bridge.ipv4.addresses = mkOverride 0 + [ { address = "192.168.1.1"; prefixLength = 24; } ]; + }; + }; + testScript = { ... }: + '' + startAll; + + # Wait for networking to come up + $client1->waitForUnit("network.target"); + $client2->waitForUnit("network.target"); + $router->waitForUnit("network.target"); + + # Test bridging + $client1->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client1->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $client1->waitUntilSucceeds("ping -c 1 192.168.1.3"); + + $client2->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client2->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $client2->waitUntilSucceeds("ping -c 1 192.168.1.3"); + + $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); + ''; + }; + macvlan = { + name = "MACVLAN"; + nodes.router = router; + nodes.client = { pkgs, ... }: with pkgs.lib; { + environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules + virtualisation.vlans = [ 1 ]; + networking = { + useNetworkd = networkd; + firewall.logReversePathDrops = true; # to debug firewall rules + # reverse path filtering rules for the macvlan interface seem + # to be incorrect, causing the test to fail. Disable temporarily. + firewall.checkReversePath = false; + useDHCP = true; + macvlans.macvlan.interface = "eth1"; + interfaces.eth1.ipv4.addresses = mkOverride 0 [ ]; + }; + }; + testScript = { ... }: + '' + startAll; + + # Wait for networking to come up + $client->waitForUnit("network.target"); + $router->waitForUnit("network.target"); + + # Wait until we have an ip address on each interface + $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); + $client->waitUntilSucceeds("ip addr show dev macvlan | grep -q '192.168.1'"); + + # Print lots of diagnostic information + $router->log('**********************************************'); + $router->succeed("ip addr >&2"); + $router->succeed("ip route >&2"); + $router->execute("iptables-save >&2"); + $client->log('=============================================='); + $client->succeed("ip addr >&2"); + $client->succeed("ip route >&2"); + $client->execute("iptables-save >&2"); + $client->log('##############################################'); + + # Test macvlan creates routable ips + $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.3"); + + $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); + ''; + }; + sit = let + node = { address4, remote, address6 }: { pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 ]; + networking = { + useNetworkd = networkd; + firewall.enable = false; + useDHCP = false; + sits.sit = { + inherit remote; + local = address4; + dev = "eth1"; + }; + interfaces.eth1.ipv4.addresses = mkOverride 0 + [ { address = address4; prefixLength = 24; } ]; + interfaces.sit.ipv6.addresses = mkOverride 0 + [ { address = address6; prefixLength = 64; } ]; + }; + }; + in { + name = "Sit"; + nodes.client1 = node { address4 = "192.168.1.1"; remote = "192.168.1.2"; address6 = "fc00::1"; }; + nodes.client2 = node { address4 = "192.168.1.2"; remote = "192.168.1.1"; address6 = "fc00::2"; }; + testScript = { ... }: + '' + startAll; + + # Wait for networking to be configured + $client1->waitForUnit("network.target"); + $client2->waitForUnit("network.target"); + + # Print diagnostic information + $client1->succeed("ip addr >&2"); + $client2->succeed("ip addr >&2"); + + # Test ipv6 + $client1->waitUntilSucceeds("ping -c 1 fc00::1"); + $client1->waitUntilSucceeds("ping -c 1 fc00::2"); + + $client2->waitUntilSucceeds("ping -c 1 fc00::1"); + $client2->waitUntilSucceeds("ping -c 1 fc00::2"); + ''; + }; + vlan = let + node = address: { pkgs, ... }: with pkgs.lib; { + #virtualisation.vlans = [ 1 ]; + networking = { + useNetworkd = networkd; + useDHCP = false; + vlans.vlan = { + id = 1; + interface = "eth0"; + }; + interfaces.eth0.ipv4.addresses = mkOverride 0 [ ]; + interfaces.eth1.ipv4.addresses = mkOverride 0 [ ]; + interfaces.vlan.ipv4.addresses = mkOverride 0 + [ { inherit address; prefixLength = 24; } ]; + }; + }; + in { + name = "vlan"; + nodes.client1 = node "192.168.1.1"; + nodes.client2 = node "192.168.1.2"; + testScript = { ... }: + '' + startAll; + + # Wait for networking to be configured + $client1->waitForUnit("network.target"); + $client2->waitForUnit("network.target"); + + # Test vlan is setup + $client1->succeed("ip addr show dev vlan >&2"); + $client2->succeed("ip addr show dev vlan >&2"); + ''; + }; + virtual = { + name = "Virtual"; + machine = { + networking.interfaces."tap0" = { + ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ]; + ipv6.addresses = [ { address = "2001:1470:fffd:2096::"; prefixLength = 64; } ]; + virtual = true; + }; + networking.interfaces."tun0" = { + ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ]; + ipv6.addresses = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ]; + virtual = true; + }; + }; + + testScript = '' + my $targetList = <<'END'; + tap0: tap persist user 0 + tun0: tun persist user 0 + END + + # Wait for networking to come up + $machine->start; + $machine->waitForUnit("network-online.target"); + + # Test interfaces set up + my $list = $machine->succeed("ip tuntap list | sort"); + "$list" eq "$targetList" or die( + "The list of virtual interfaces does not match the expected one:\n", + "Result:\n", "$list\n", + "Expected:\n", "$targetList\n" + ); + + # Test interfaces clean up + $machine->succeed("systemctl stop network-addresses-tap0"); + $machine->sleep(10); + $machine->succeed("systemctl stop network-addresses-tun0"); + $machine->sleep(10); + my $residue = $machine->succeed("ip tuntap list"); + $residue eq "" or die( + "Some virtual interface has not been properly cleaned:\n", + "$residue\n" + ); + ''; + }; + privacy = { + name = "Privacy"; + nodes.router = { ... }: { + virtualisation.vlans = [ 1 ]; + boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true; + networking = { + useNetworkd = networkd; + interfaces.eth1.ipv6.addresses = singleton { + address = "fd00:1234:5678:1::1"; + prefixLength = 64; + }; + }; + services.radvd = { + enable = true; + config = '' + interface eth1 { + AdvSendAdvert on; + AdvManagedFlag on; + AdvOtherConfigFlag on; + + prefix fd00:1234:5678:1::/64 { + AdvAutonomous on; + AdvOnLink on; + }; + }; + ''; + }; + }; + nodes.client = { pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 ]; + networking = { + useNetworkd = networkd; + useDHCP = true; + interfaces.eth1 = { + preferTempAddress = true; + ipv4.addresses = mkOverride 0 [ ]; + ipv6.addresses = mkOverride 0 [ ]; + }; + }; + }; + testScript = { ... }: + '' + startAll; + + $client->waitForUnit("network.target"); + $router->waitForUnit("network-online.target"); + + # Wait until we have an ip address + $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'"); + + # Test vlan 1 + $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1"); + + # Test address used is temporary + $client->waitUntilSucceeds("! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'"); + ''; + }; + routes = { + name = "routes"; + machine = { + networking.useDHCP = false; + networking.interfaces."eth0" = { + ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ]; + ipv6.addresses = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ]; + ipv6.routes = [ + { address = "fdfd:b3f0::"; prefixLength = 48; } + { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; } + ]; + ipv4.routes = [ + { address = "10.0.0.0"; prefixLength = 16; options = { mtu = "1500"; }; } + { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; } + ]; + }; + virtualisation.vlans = [ ]; + }; + + testScript = '' + my $targetIPv4Table = <<'END'; + 10.0.0.0/16 proto static scope link mtu 1500 + 192.168.1.0/24 proto kernel scope link src 192.168.1.2 + 192.168.2.0/24 via 192.168.1.1 proto static + END + + my $targetIPv6Table = <<'END'; + 2001:1470:fffd:2097::/64 proto kernel metric 256 pref medium + 2001:1470:fffd:2098::/64 via fdfd:b3f0::1 proto static metric 1024 pref medium + fdfd:b3f0::/48 proto static metric 1024 pref medium + END + + $machine->start; + $machine->waitForUnit("network.target"); + + # test routing tables + my $ipv4Table = $machine->succeed("ip -4 route list dev eth0 | head -n3"); + my $ipv6Table = $machine->succeed("ip -6 route list dev eth0 | head -n3"); + "$ipv4Table" eq "$targetIPv4Table" or die( + "The IPv4 routing table does not match the expected one:\n", + "Result:\n", "$ipv4Table\n", + "Expected:\n", "$targetIPv4Table\n" + ); + "$ipv6Table" eq "$targetIPv6Table" or die( + "The IPv6 routing table does not match the expected one:\n", + "Result:\n", "$ipv6Table\n", + "Expected:\n", "$targetIPv6Table\n" + ); + + # test clean-up of the tables + $machine->succeed("systemctl stop network-addresses-eth0"); + my $ipv4Residue = $machine->succeed("ip -4 route list dev eth0 | head -n-3"); + my $ipv6Residue = $machine->succeed("ip -6 route list dev eth0 | head -n-3"); + $ipv4Residue eq "" or die( + "The IPv4 routing table has not been properly cleaned:\n", + "$ipv4Residue\n" + ); + $ipv6Residue eq "" or die( + "The IPv6 routing table has not been properly cleaned:\n", + "$ipv6Residue\n" + ); + ''; + }; + }; + +in mapAttrs (const (attrs: makeTest (attrs // { + name = "${attrs.name}-Networking-${if networkd then "Networkd" else "Scripted"}"; +}))) testCases diff --git a/nixpkgs/nixos/tests/nextcloud/basic.nix b/nixpkgs/nixos/tests/nextcloud/basic.nix new file mode 100644 index 000000000000..c3b710f0f904 --- /dev/null +++ b/nixpkgs/nixos/tests/nextcloud/basic.nix @@ -0,0 +1,56 @@ +import ../make-test.nix ({ pkgs, ...}: let + adminpass = "notproduction"; + adminuser = "root"; +in { + name = "nextcloud-basic"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ globin eqyiel ]; + }; + + nodes = { + # The only thing the client needs to do is download a file. + client = { ... }: {}; + + nextcloud = { config, pkgs, ... }: { + 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 + inherit adminpass; + }; + }; + }; + }; + + testScript = let + withRcloneEnv = pkgs.writeScript "with-rclone-env" '' + #!${pkgs.stdenv.shell} + export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav + export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/" + export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud" + export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}" + export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})" + "''${@}" + ''; + copySharedFile = pkgs.writeScript "copy-shared-file" '' + #!${pkgs.stdenv.shell} + echo 'hi' | ${withRcloneEnv} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file + ''; + + diffSharedFile = pkgs.writeScript "diff-shared-file" '' + #!${pkgs.stdenv.shell} + diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file) + ''; + in '' + startAll(); + $nextcloud->waitForUnit("multi-user.target"); + $nextcloud->succeed("curl -sSf http://nextcloud/login"); + $nextcloud->succeed("${withRcloneEnv} ${copySharedFile}"); + $client->waitForUnit("multi-user.target"); + $client->succeed("${withRcloneEnv} ${diffSharedFile}"); + ''; +}) diff --git a/nixpkgs/nixos/tests/nextcloud/default.nix b/nixpkgs/nixos/tests/nextcloud/default.nix new file mode 100644 index 000000000000..e4c7a70606cf --- /dev/null +++ b/nixpkgs/nixos/tests/nextcloud/default.nix @@ -0,0 +1,9 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../../.. { inherit system config; } +}: +{ + basic = import ./basic.nix { inherit system pkgs; }; + with-postgresql-and-redis = import ./with-postgresql-and-redis.nix { inherit system pkgs; }; + with-mysql-and-memcached = import ./with-mysql-and-memcached.nix { inherit system pkgs; }; +} diff --git a/nixpkgs/nixos/tests/nextcloud/with-mysql-and-memcached.nix b/nixpkgs/nixos/tests/nextcloud/with-mysql-and-memcached.nix new file mode 100644 index 000000000000..c0d347238b47 --- /dev/null +++ b/nixpkgs/nixos/tests/nextcloud/with-mysql-and-memcached.nix @@ -0,0 +1,97 @@ +import ../make-test.nix ({ pkgs, ...}: let + adminpass = "hunter2"; + adminuser = "root"; +in { + name = "nextcloud-with-mysql-and-memcached"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eqyiel ]; + }; + + nodes = { + # The only thing the client needs to do is download a file. + client = { ... }: {}; + + nextcloud = { config, pkgs, ... }: { + networking.firewall.allowedTCPPorts = [ 80 ]; + + services.nextcloud = { + enable = true; + hostName = "nextcloud"; + nginx.enable = true; + https = true; + caching = { + apcu = true; + redis = false; + memcached = true; + }; + config = { + dbtype = "mysql"; + dbname = "nextcloud"; + dbuser = "nextcloud"; + dbhost = "127.0.0.1"; + dbport = 3306; + dbpass = "hunter2"; + # Don't inherit adminuser since "root" is supposed to be the default + inherit adminpass; + }; + }; + + services.mysql = { + enable = true; + bind = "127.0.0.1"; + package = pkgs.mariadb; + initialScript = pkgs.writeText "mysql-init" '' + CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'hunter2'; + CREATE DATABASE IF NOT EXISTS nextcloud; + GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, + CREATE TEMPORARY TABLES ON nextcloud.* TO 'nextcloud'@'localhost' + IDENTIFIED BY 'hunter2'; + FLUSH privileges; + ''; + }; + + systemd.services."nextcloud-setup"= { + requires = ["mysql.service"]; + after = ["mysql.service"]; + }; + + services.memcached.enable = true; + }; + }; + + testScript = let + configureMemcached = pkgs.writeScript "configure-memcached" '' + #!${pkgs.stdenv.shell} + nextcloud-occ config:system:set memcached_servers 0 0 --value 127.0.0.1 --type string + nextcloud-occ config:system:set memcached_servers 0 1 --value 11211 --type integer + nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\APCu' --type string + nextcloud-occ config:system:set memcache.distributed --value '\OC\Memcache\Memcached' --type string + ''; + withRcloneEnv = pkgs.writeScript "with-rclone-env" '' + #!${pkgs.stdenv.shell} + export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav + export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/" + export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud" + export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}" + export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})" + ''; + copySharedFile = pkgs.writeScript "copy-shared-file" '' + #!${pkgs.stdenv.shell} + echo 'hi' | ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file + ''; + + diffSharedFile = pkgs.writeScript "diff-shared-file" '' + #!${pkgs.stdenv.shell} + diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file) + ''; + in '' + startAll(); + $nextcloud->waitForUnit("multi-user.target"); + $nextcloud->succeed("${configureMemcached}"); + $nextcloud->succeed("curl -sSf http://nextcloud/login"); + $nextcloud->succeed("${withRcloneEnv} ${copySharedFile}"); + $client->waitForUnit("multi-user.target"); + $client->succeed("${withRcloneEnv} ${diffSharedFile}"); + + ''; +}) diff --git a/nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix b/nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix new file mode 100644 index 000000000000..0351d4db69ac --- /dev/null +++ b/nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix @@ -0,0 +1,130 @@ +import ../make-test.nix ({ pkgs, ...}: let + adminpass = "hunter2"; + adminuser = "custom-admin-username"; +in { + name = "nextcloud-with-postgresql-and-redis"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eqyiel ]; + }; + + nodes = { + # The only thing the client needs to do is download a file. + client = { ... }: {}; + + nextcloud = { config, pkgs, ... }: { + networking.firewall.allowedTCPPorts = [ 80 ]; + + services.nextcloud = { + enable = true; + hostName = "nextcloud"; + nginx.enable = true; + caching = { + apcu = false; + redis = true; + memcached = false; + }; + config = { + dbtype = "pgsql"; + dbname = "nextcloud"; + dbuser = "nextcloud"; + dbhost = "localhost"; + dbpassFile = toString (pkgs.writeText "db-pass-file" '' + hunter2 + ''); + inherit adminuser; + adminpassFile = toString (pkgs.writeText "admin-pass-file" '' + ${adminpass} + ''); + }; + }; + + services.redis = { + unixSocket = "/var/run/redis/redis.sock"; + enable = true; + extraConfig = '' + unixsocketperm 770 + ''; + }; + + systemd.services.redis = { + preStart = '' + mkdir -p /var/run/redis + chown ${config.services.redis.user}:${config.services.nginx.group} /var/run/redis + ''; + serviceConfig.PermissionsStartOnly = true; + }; + + systemd.services."nextcloud-setup"= { + requires = ["postgresql.service"]; + after = [ + "postgresql.service" + "chown-redis-socket.service" + ]; + }; + + # At the time of writing, redis creates its socket with the "nobody" + # group. I figure this is slightly less bad than making the socket world + # readable. + systemd.services."chown-redis-socket" = { + enable = true; + script = '' + until ${pkgs.redis}/bin/redis-cli ping; do + echo "waiting for redis..." + sleep 1 + done + chown ${config.services.redis.user}:${config.services.nginx.group} /var/run/redis/redis.sock + ''; + after = [ "redis.service" ]; + requires = [ "redis.service" ]; + wantedBy = [ "redis.service" ]; + serviceConfig = { + Type = "oneshot"; + }; + }; + + services.postgresql = { + enable = true; + initialScript = pkgs.writeText "psql-init" '' + create role nextcloud with login password 'hunter2'; + create database nextcloud with owner nextcloud; + ''; + }; + }; + }; + + testScript = let + configureRedis = pkgs.writeScript "configure-redis" '' + #!${pkgs.stdenv.shell} + nextcloud-occ config:system:set redis 'host' --value '/var/run/redis/redis.sock' --type string + nextcloud-occ config:system:set redis 'port' --value 0 --type integer + nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\Redis' --type string + nextcloud-occ config:system:set memcache.locking --value '\OC\Memcache\Redis' --type string + ''; + withRcloneEnv = pkgs.writeScript "with-rclone-env" '' + #!${pkgs.stdenv.shell} + export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav + export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/" + export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud" + export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}" + export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})" + "''${@}" + ''; + copySharedFile = pkgs.writeScript "copy-shared-file" '' + #!${pkgs.stdenv.shell} + echo 'hi' | ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file + ''; + + diffSharedFile = pkgs.writeScript "diff-shared-file" '' + #!${pkgs.stdenv.shell} + diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file) + ''; + in '' + startAll(); + $nextcloud->waitForUnit("multi-user.target"); + $nextcloud->succeed("${configureRedis}"); + $nextcloud->succeed("curl -sSf http://nextcloud/login"); + $nextcloud->succeed("${withRcloneEnv} ${copySharedFile}"); + $client->waitForUnit("multi-user.target"); + $client->succeed("${withRcloneEnv} ${diffSharedFile}"); + ''; +}) diff --git a/nixpkgs/nixos/tests/nexus.nix b/nixpkgs/nixos/tests/nexus.nix new file mode 100644 index 000000000000..783c9f5c019f --- /dev/null +++ b/nixpkgs/nixos/tests/nexus.nix @@ -0,0 +1,32 @@ +# verifies: +# 1. nexus service starts on server +# 2. nexus service can startup on server (creating database and all other initial stuff) +# 3. the web application is reachable via HTTP + +import ./make-test.nix ({ pkgs, ...} : { + name = "nexus"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ironpinguin ma27 ]; + }; + + nodes = { + + server = + { ... }: + { virtualisation.memorySize = 2047; # qemu-system-i386 has a 2047M limit + virtualisation.diskSize = 8192; + + services.nexus.enable = true; + }; + + }; + + testScript = '' + startAll; + + $server->waitForUnit("nexus"); + $server->waitForOpenPort(8081); + + $server->succeed("curl -f 127.0.0.1:8081"); + ''; +}) diff --git a/nixpkgs/nixos/tests/nfs.nix b/nixpkgs/nixos/tests/nfs.nix new file mode 100644 index 000000000000..ce1717018933 --- /dev/null +++ b/nixpkgs/nixos/tests/nfs.nix @@ -0,0 +1,90 @@ +import ./make-test.nix ({ pkgs, version ? 4, ... }: + +let + + client = + { pkgs, ... }: + { fileSystems = pkgs.lib.mkVMOverride + [ { mountPoint = "/data"; + # nfs4 exports the export with fsid=0 as a virtual root directory + device = if (version == 4) then "server:/" else "server:/data"; + fsType = "nfs"; + options = [ "vers=${toString version}" ]; + } + ]; + networking.firewall.enable = false; # FIXME: only open statd + }; + +in + +{ + name = "nfs"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + nodes = + { client1 = client; + client2 = client; + + server = + { ... }: + { services.nfs.server.enable = true; + services.nfs.server.exports = + '' + /data 192.168.1.0/255.255.255.0(rw,no_root_squash,no_subtree_check,fsid=0) + ''; + services.nfs.server.createMountPoints = true; + networking.firewall.enable = false; # FIXME: figure out what ports need to be allowed + }; + }; + + testScript = + '' + $server->waitForUnit("nfs-server"); + $server->succeed("systemctl start network-online.target"); + $server->waitForUnit("network-online.target"); + + startAll; + + $client1->waitForUnit("data.mount"); + $client1->succeed("echo bla > /data/foo"); + $server->succeed("test -e /data/foo"); + + $client2->waitForUnit("data.mount"); + $client2->succeed("echo bla > /data/bar"); + $server->succeed("test -e /data/bar"); + + # Test whether restarting ‘nfs-server’ works correctly. + $server->succeed("systemctl restart nfs-server"); + $client2->succeed("echo bla >> /data/bar"); # will take 90 seconds due to the NFS grace period + + # Test whether we can get a lock. + $client2->succeed("time flock -n -s /data/lock true"); + + # Test locking: client 1 acquires an exclusive lock, so client 2 + # should then fail to acquire a shared lock. + $client1->succeed("flock -x /data/lock -c 'touch locked; sleep 100000' &"); + $client1->waitForFile("locked"); + $client2->fail("flock -n -s /data/lock true"); + + # Test whether client 2 obtains the lock if we reset client 1. + $client2->succeed("flock -x /data/lock -c 'echo acquired; touch locked; sleep 100000' >&2 &"); + $client1->crash; + $client1->start; + $client2->waitForFile("locked"); + + # Test whether locks survive a reboot of the server. + $client1->waitForUnit("data.mount"); + $server->shutdown; + $server->start; + $client1->succeed("touch /data/xyzzy"); + $client1->fail("time flock -n -s /data/lock true"); + + # Test whether unmounting during shutdown happens quickly. + my $t1 = time; + $client1->shutdown; + my $duration = time - $t1; + die "shutdown took too long ($duration seconds)" if $duration > 30; + ''; +}) diff --git a/nixpkgs/nixos/tests/nghttpx.nix b/nixpkgs/nixos/tests/nghttpx.nix new file mode 100644 index 000000000000..433562b97191 --- /dev/null +++ b/nixpkgs/nixos/tests/nghttpx.nix @@ -0,0 +1,61 @@ +let + nginxRoot = "/var/run/nginx"; +in + import ./make-test.nix ({...}: { + name = "nghttpx"; + nodes = { + webserver = { + networking.firewall.allowedTCPPorts = [ 80 ]; + systemd.services.nginx = { + preStart = '' + mkdir -p ${nginxRoot} + echo "Hello world!" > ${nginxRoot}/hello-world.txt + ''; + }; + + services.nginx = { + enable = true; + virtualHosts."server" = { + locations."/".root = nginxRoot; + }; + }; + }; + + proxy = { + networking.firewall.allowedTCPPorts = [ 80 ]; + services.nghttpx = { + enable = true; + frontends = [ + { server = { + host = "*"; + port = 80; + }; + + params = { + tls = "no-tls"; + }; + } + ]; + backends = [ + { server = { + host = "webserver"; + port = 80; + }; + patterns = [ "/" ]; + params.proto = "http/1.1"; + } + ]; + }; + }; + + client = {}; + }; + + testScript = '' + startAll; + + $webserver->waitForOpenPort("80"); + $proxy->waitForOpenPort("80"); + $client->waitUntilSucceeds("curl -s --fail http://proxy/hello-world.txt"); + ''; + }) diff --git a/nixpkgs/nixos/tests/nginx-sso.nix b/nixpkgs/nixos/tests/nginx-sso.nix new file mode 100644 index 000000000000..e19992cb6bf7 --- /dev/null +++ b/nixpkgs/nixos/tests/nginx-sso.nix @@ -0,0 +1,44 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "nginx-sso"; + meta = { + maintainers = with pkgs.stdenv.lib.maintainers; [ delroth ]; + }; + + machine = { + services.nginx.sso = { + enable = true; + configuration = { + listen = { addr = "127.0.0.1"; port = 8080; }; + + providers.token.tokens = { + myuser = "MyToken"; + }; + + acl = { + rule_sets = [ + { + rules = [ { field = "x-application"; equals = "MyApp"; } ]; + allow = [ "myuser" ]; + } + ]; + }; + }; + }; + }; + + testScript = '' + startAll; + + $machine->waitForUnit("nginx-sso.service"); + $machine->waitForOpenPort(8080); + + # No valid user -> 401. + $machine->fail("curl -sSf http://localhost:8080/auth"); + + # Valid user but no matching ACL -> 403. + $machine->fail("curl -sSf -H 'Authorization: Token MyToken' http://localhost:8080/auth"); + + # Valid user and matching ACL -> 200. + $machine->succeed("curl -sSf -H 'Authorization: Token MyToken' -H 'X-Application: MyApp' http://localhost:8080/auth"); + ''; +}) diff --git a/nixpkgs/nixos/tests/nginx.nix b/nixpkgs/nixos/tests/nginx.nix new file mode 100644 index 000000000000..32b113649237 --- /dev/null +++ b/nixpkgs/nixos/tests/nginx.nix @@ -0,0 +1,42 @@ +# verifies: +# 1. nginx generates config file with shared http context definitions above +# generated virtual hosts config. + +import ./make-test.nix ({ pkgs, ...} : { + name = "nginx"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ mbbx6spp ]; + }; + + nodes = { + webserver = + { ... }: + { services.nginx.enable = true; + services.nginx.commonHttpConfig = '' + log_format ceeformat '@cee: {"status":"$status",' + '"request_time":$request_time,' + '"upstream_response_time":$upstream_response_time,' + '"pipe":"$pipe","bytes_sent":$bytes_sent,' + '"connection":"$connection",' + '"remote_addr":"$remote_addr",' + '"host":"$host",' + '"timestamp":"$time_iso8601",' + '"request":"$request",' + '"http_referer":"$http_referer",' + '"upstream_addr":"$upstream_addr"}'; + ''; + services.nginx.virtualHosts."0.my.test" = { + extraConfig = '' + access_log syslog:server=unix:/dev/log,facility=user,tag=mytag,severity=info ceeformat; + ''; + }; + }; + }; + + testScript = '' + startAll; + + $webserver->waitForUnit("nginx"); + $webserver->waitForOpenPort("80"); + ''; +}) diff --git a/nixpkgs/nixos/tests/nix-ssh-serve.nix b/nixpkgs/nixos/tests/nix-ssh-serve.nix new file mode 100644 index 000000000000..494d55121eb1 --- /dev/null +++ b/nixpkgs/nixos/tests/nix-ssh-serve.nix @@ -0,0 +1,39 @@ +import ./make-test.nix ({ pkgs, lib, ... }: +let inherit (import ./ssh-keys.nix pkgs) + snakeOilPrivateKey snakeOilPublicKey; + ssh-config = builtins.toFile "ssh.conf" '' + UserKnownHostsFile=/dev/null + StrictHostKeyChecking=no + ''; +in + { name = "nix-ssh-serve"; + meta.maintainers = [ lib.maintainers.shlevy ]; + nodes = + { server.nix.sshServe = + { enable = true; + keys = [ snakeOilPublicKey ]; + protocol = "ssh-ng"; + }; + server.nix.package = pkgs.nix; + client.nix.package = pkgs.nix; + }; + testScript = '' + startAll; + + $client->succeed("mkdir -m 700 /root/.ssh"); + $client->copyFileFromHost("${ssh-config}", "/root/.ssh/config"); + $client->succeed("cat ${snakeOilPrivateKey} > /root/.ssh/id_ecdsa"); + $client->succeed("chmod 600 /root/.ssh/id_ecdsa"); + + $client->succeed("nix-store --add /etc/machine-id > mach-id-path"); + + $server->waitForUnit("sshd"); + + $client->fail("diff /root/other-store\$(cat mach-id-path) /etc/machine-id"); + # Currently due to shared store this is a noop :( + $client->succeed("nix copy --to ssh-ng://nix-ssh\@server \$(cat mach-id-path)"); + $client->succeed("nix-store --realise \$(cat mach-id-path) --store /root/other-store --substituters ssh-ng://nix-ssh\@server"); + $client->succeed("diff /root/other-store\$(cat mach-id-path) /etc/machine-id"); + ''; + } +) diff --git a/nixpkgs/nixos/tests/novacomd.nix b/nixpkgs/nixos/tests/novacomd.nix new file mode 100644 index 000000000000..4eb60c0feb5c --- /dev/null +++ b/nixpkgs/nixos/tests/novacomd.nix @@ -0,0 +1,34 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "novacomd"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ dtzWill ]; + }; + + machine = { ... }: { + services.novacomd.enable = true; + }; + + testScript = '' + $machine->waitForUnit("multi-user.target"); + + # multi-user.target wants novacomd.service, but let's make sure + $machine->waitForUnit("novacomd.service"); + + # Check status and try connecting with novacom + $machine->succeed("systemctl status novacomd.service >&2"); + # to prevent non-deterministic failure, + # make sure the daemon is really listening + $machine->waitForOpenPort(6968); + $machine->succeed("novacom -l"); + + # Stop the daemon, double-check novacom fails if daemon isn't working + $machine->stopJob("novacomd"); + $machine->fail("novacom -l"); + + # And back again for good measure + $machine->startJob("novacomd"); + # make sure the daemon is really listening + $machine->waitForOpenPort(6968); + $machine->succeed("novacom -l"); + ''; +}) diff --git a/nixpkgs/nixos/tests/nsd.nix b/nixpkgs/nixos/tests/nsd.nix new file mode 100644 index 000000000000..c3c91e71b5ca --- /dev/null +++ b/nixpkgs/nixos/tests/nsd.nix @@ -0,0 +1,101 @@ +let + common = { pkgs, ... }: { + networking.firewall.enable = false; + networking.useDHCP = false; + # for a host utility with IPv6 support + environment.systemPackages = [ pkgs.bind ]; + }; +in import ./make-test.nix ({ pkgs, ...} : { + name = "nsd"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aszlig ]; + }; + + nodes = { + clientv4 = { lib, nodes, ... }: { + imports = [ common ]; + networking.nameservers = lib.mkForce [ + (lib.head nodes.server.config.networking.interfaces.eth1.ipv4.addresses).address + ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = "192.168.0.2"; prefixLength = 24; } + ]; + }; + + clientv6 = { lib, nodes, ... }: { + imports = [ common ]; + networking.nameservers = lib.mkForce [ + (lib.head nodes.server.config.networking.interfaces.eth1.ipv6.addresses).address + ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = "dead:beef::2"; prefixLength = 24; } + ]; + }; + + server = { lib, ... }: { + imports = [ common ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = "192.168.0.1"; prefixLength = 24; } + ]; + networking.interfaces.eth1.ipv6.addresses = [ + { address = "dead:beef::1"; prefixLength = 64; } + ]; + services.nsd.enable = true; + services.nsd.rootServer = true; + services.nsd.interfaces = lib.mkForce []; + services.nsd.zones."example.com.".data = '' + @ SOA ns.example.com noc.example.com 666 7200 3600 1209600 3600 + ipv4 A 1.2.3.4 + ipv6 AAAA abcd::eeff + deleg NS ns.example.com + ns A 192.168.0.1 + ns AAAA dead:beef::1 + ''; + services.nsd.zones."deleg.example.com.".data = '' + @ SOA ns.example.com noc.example.com 666 7200 3600 1209600 3600 + @ A 9.8.7.6 + @ AAAA fedc::bbaa + ''; + services.nsd.zones.".".data = '' + @ SOA ns.example.com noc.example.com 666 7200 3600 1209600 3600 + root A 1.8.7.4 + root AAAA acbd::4 + ''; + }; + }; + + testScript = '' + startAll; + + $clientv4->waitForUnit("network.target"); + $clientv6->waitForUnit("network.target"); + $server->waitForUnit("nsd.service"); + + sub assertHost { + my ($type, $rr, $query, $expected) = @_; + my $self = $type eq 4 ? $clientv4 : $clientv6; + my $out = $self->succeed("host -$type -t $rr $query"); + $self->log("output: $out"); + chomp $out; + die "DNS IPv$type query on $query gave '$out' instead of '$expected'" + if ($out !~ $expected); + } + + foreach (4, 6) { + subtest "ipv$_", sub { + assertHost($_, "a", "example.com", qr/has no [^ ]+ record/); + assertHost($_, "aaaa", "example.com", qr/has no [^ ]+ record/); + + assertHost($_, "soa", "example.com", qr/SOA.*?noc\.example\.com/); + assertHost($_, "a", "ipv4.example.com", qr/address 1.2.3.4$/); + assertHost($_, "aaaa", "ipv6.example.com", qr/address abcd::eeff$/); + + assertHost($_, "a", "deleg.example.com", qr/address 9.8.7.6$/); + assertHost($_, "aaaa", "deleg.example.com", qr/address fedc::bbaa$/); + + assertHost($_, "a", "root", qr/address 1.8.7.4$/); + assertHost($_, "aaaa", "root", qr/address acbd::4$/); + }; + } + ''; +}) diff --git a/nixpkgs/nixos/tests/openldap.nix b/nixpkgs/nixos/tests/openldap.nix new file mode 100644 index 000000000000..1eaf87a8eaa8 --- /dev/null +++ b/nixpkgs/nixos/tests/openldap.nix @@ -0,0 +1,35 @@ +import ./make-test.nix { + name = "openldap"; + + machine = { pkgs, ... }: { + services.openldap = { + enable = true; + extraConfig = '' + include ${pkgs.openldap}/etc/schema/core.schema + include ${pkgs.openldap}/etc/schema/cosine.schema + include ${pkgs.openldap}/etc/schema/inetorgperson.schema + include ${pkgs.openldap}/etc/schema/nis.schema + database bdb + suffix dc=example + directory /var/db/openldap + rootdn cn=root,dc=example + rootpw notapassword + ''; + declarativeContents = '' + dn: dc=example + objectClass: domain + dc: example + + dn: ou=users,dc=example + objectClass: organizationalUnit + ou: users + ''; + }; + }; + + testScript = '' + $machine->waitForUnit('openldap.service'); + $machine->succeed('systemctl status openldap.service'); + $machine->succeed('ldapsearch -LLL -D "cn=root,dc=example" -w notapassword -b "dc=example"'); + ''; +} diff --git a/nixpkgs/nixos/tests/opensmtpd.nix b/nixpkgs/nixos/tests/opensmtpd.nix new file mode 100644 index 000000000000..883ad7604941 --- /dev/null +++ b/nixpkgs/nixos/tests/opensmtpd.nix @@ -0,0 +1,125 @@ +import ./make-test.nix { + name = "opensmtpd"; + + nodes = { + smtp1 = { pkgs, ... }: { + imports = [ common/user-account.nix ]; + networking = { + firewall.allowedTCPPorts = [ 25 ]; + useDHCP = false; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [ + { address = "192.168.1.1"; prefixLength = 24; } + ]; + }; + environment.systemPackages = [ pkgs.opensmtpd ]; + services.opensmtpd = { + enable = true; + extraServerArgs = [ "-v" ]; + serverConfiguration = '' + listen on 0.0.0.0 + action do_relay relay + # DO NOT DO THIS IN PRODUCTION! + # Setting up authentication requires a certificate which is painful in + # a test environment, but THIS WOULD BE DANGEROUS OUTSIDE OF A + # WELL-CONTROLLED ENVIRONMENT! + match from any for any action do_relay + ''; + }; + }; + + smtp2 = { pkgs, ... }: { + imports = [ common/user-account.nix ]; + networking = { + firewall.allowedTCPPorts = [ 25 143 ]; + useDHCP = false; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [ + { address = "192.168.1.2"; prefixLength = 24; } + ]; + }; + environment.systemPackages = [ pkgs.opensmtpd ]; + services.opensmtpd = { + enable = true; + extraServerArgs = [ "-v" ]; + serverConfiguration = '' + listen on 0.0.0.0 + action dovecot_deliver mda \ + "${pkgs.dovecot}/libexec/dovecot/deliver -d %{user.username}" + match from any for local action dovecot_deliver + ''; + }; + services.dovecot2 = { + enable = true; + enableImap = true; + mailLocation = "maildir:~/mail"; + protocols = [ "imap" ]; + }; + }; + + client = { pkgs, ... }: { + networking = { + useDHCP = false; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [ + { address = "192.168.1.3"; prefixLength = 24; } + ]; + }; + environment.systemPackages = let + sendTestMail = pkgs.writeScriptBin "send-a-test-mail" '' + #!${pkgs.python3.interpreter} + import smtplib, sys + + with smtplib.SMTP('192.168.1.1') as smtp: + smtp.sendmail('alice@[192.168.1.1]', 'bob@[192.168.1.2]', """ + From: alice@smtp1 + To: bob@smtp2 + Subject: Test + + Hello World + """) + ''; + + checkMailLanded = pkgs.writeScriptBin "check-mail-landed" '' + #!${pkgs.python3.interpreter} + import imaplib + + with imaplib.IMAP4('192.168.1.2', 143) as imap: + imap.login('bob', 'foobar') + imap.select() + status, refs = imap.search(None, 'ALL') + assert status == 'OK' + assert len(refs) == 1 + status, msg = imap.fetch(refs[0], 'BODY[TEXT]') + assert status == 'OK' + content = msg[0][1] + print("===> content:", content) + split = content.split(b'\r\n') + print("===> split:", split) + lastline = split[-3] + print("===> lastline:", lastline) + assert lastline.strip() == b'Hello World' + ''; + in [ sendTestMail checkMailLanded ]; + }; + }; + + testScript = '' + startAll; + + $client->waitForUnit("network-online.target"); + $smtp1->waitForUnit('opensmtpd'); + $smtp2->waitForUnit('opensmtpd'); + $smtp2->waitForUnit('dovecot2'); + + # To prevent sporadic failures during daemon startup, make sure + # services are listening on their ports before sending requests + $smtp1->waitForOpenPort(25); + $smtp2->waitForOpenPort(25); + $smtp2->waitForOpenPort(143); + + $client->succeed('send-a-test-mail'); + $smtp1->waitUntilFails('smtpctl show queue | egrep .'); + $smtp2->waitUntilFails('smtpctl show queue | egrep .'); + $client->succeed('check-mail-landed >&2'); + ''; + + meta.timeout = 30; +} diff --git a/nixpkgs/nixos/tests/openssh.nix b/nixpkgs/nixos/tests/openssh.nix new file mode 100644 index 000000000000..c66b90b802d5 --- /dev/null +++ b/nixpkgs/nixos/tests/openssh.nix @@ -0,0 +1,81 @@ +import ./make-test.nix ({ pkgs, ... }: + +let inherit (import ./ssh-keys.nix pkgs) + snakeOilPrivateKey snakeOilPublicKey; +in { + name = "openssh"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aszlig eelco chaoflow ]; + }; + + nodes = { + + server = + { ... }: + + { + services.openssh.enable = true; + security.pam.services.sshd.limits = + [ { domain = "*"; item = "memlock"; type = "-"; value = 1024; } ]; + users.users.root.openssh.authorizedKeys.keys = [ + snakeOilPublicKey + ]; + }; + + server_lazy = + { ... }: + + { + services.openssh = { enable = true; startWhenNeeded = true; }; + security.pam.services.sshd.limits = + [ { domain = "*"; item = "memlock"; type = "-"; value = 1024; } ]; + users.users.root.openssh.authorizedKeys.keys = [ + snakeOilPublicKey + ]; + }; + + client = + { ... }: { }; + + }; + + testScript = '' + startAll; + + my $key=`${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f key -N ""`; + + $server->waitForUnit("sshd"); + + subtest "manual-authkey", sub { + $server->succeed("mkdir -m 700 /root/.ssh"); + $server->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys"); + $server_lazy->succeed("mkdir -m 700 /root/.ssh"); + $server_lazy->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys"); + + $client->succeed("mkdir -m 700 /root/.ssh"); + $client->copyFileFromHost("key", "/root/.ssh/id_ed25519"); + $client->succeed("chmod 600 /root/.ssh/id_ed25519"); + + $client->waitForUnit("network.target"); + $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'echo hello world' >&2"); + $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'ulimit -l' | grep 1024"); + + $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'echo hello world' >&2"); + $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'ulimit -l' | grep 1024"); + + }; + + subtest "configured-authkey", sub { + $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" . + " server true"); + + $client->succeed("ssh -o UserKnownHostsFile=/dev/null" . + " -o StrictHostKeyChecking=no -i privkey.snakeoil" . + " server_lazy true"); + + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/osquery.nix b/nixpkgs/nixos/tests/osquery.nix new file mode 100644 index 000000000000..281dbcff6643 --- /dev/null +++ b/nixpkgs/nixos/tests/osquery.nix @@ -0,0 +1,28 @@ +import ./make-test.nix ({ pkgs, lib, ... }: + +with lib; + +{ + name = "osquery"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ma27 ]; + }; + + machine = { + services.osquery.enable = true; + services.osquery.loggerPath = "/var/log/osquery/logs"; + services.osquery.pidfile = "/var/run/osqueryd.pid"; + }; + + testScript = '' + $machine->start; + $machine->waitForUnit("osqueryd.service"); + + $machine->succeed("echo 'SELECT address FROM etc_hosts LIMIT 1;' | osqueryi | grep '127.0.0.1'"); + $machine->succeed( + "echo 'SELECT value FROM osquery_flags WHERE name = \"logger_path\";' | osqueryi | grep /var/log/osquery/logs" + ); + + $machine->succeed("echo 'SELECT value FROM osquery_flags WHERE name = \"pidfile\";' | osqueryi | grep /var/run/osqueryd.pid"); + ''; +}) diff --git a/nixpkgs/nixos/tests/ostree.nix b/nixpkgs/nixos/tests/ostree.nix new file mode 100644 index 000000000000..8b19004874e7 --- /dev/null +++ b/nixpkgs/nixos/tests/ostree.nix @@ -0,0 +1,21 @@ +# run installed tests +import ./make-test.nix ({ pkgs, lib, ... }: { + name = "ostree"; + + meta = { + maintainers = pkgs.ostree.meta.maintainers; + }; + + # TODO: Wrap/patch the tests directly in the package + machine = { pkgs, ... }: { + environment.systemPackages = with pkgs; [ + gnome-desktop-testing ostree gnupg (python3.withPackages (p: with p; [ pyyaml ])) + ]; + + environment.variables.GI_TYPELIB_PATH = lib.makeSearchPath "lib/girepository-1.0" (with pkgs; [ gtk3 pango.out ostree gdk_pixbuf atk ]); # for GJS tests + }; + + testScript = '' + $machine->succeed("gnome-desktop-testing-runner -d ${pkgs.ostree.installedTests}/share"); + ''; +}) diff --git a/nixpkgs/nixos/tests/pam-oath-login.nix b/nixpkgs/nixos/tests/pam-oath-login.nix new file mode 100644 index 000000000000..b9d489950e72 --- /dev/null +++ b/nixpkgs/nixos/tests/pam-oath-login.nix @@ -0,0 +1,124 @@ +import ./make-test.nix ({ ... }: + +let + oathSnakeoilSecret = "cdd4083ef8ff1fa9178c6d46bfb1a3"; + + # With HOTP mode the password is calculated based on a counter of + # how many passwords have been made. In this env, we'll always be on + # the 0th counter, so the password is static. + # + # Generated in nix-shell -p oathToolkit + # via: oathtool -v -d6 -w10 cdd4083ef8ff1fa9178c6d46bfb1a3 + # and picking a the first 4: + oathSnakeOilPassword1 = "143349"; + oathSnakeOilPassword2 = "801753"; + + alicePassword = "foobar"; + # Generated via: mkpasswd -m sha-512 and passing in "foobar" + hashedAlicePassword = "$6$MsMrE1q.1HrCgTS$Vq2e/uILzYjSN836TobAyN9xh9oi7EmCmucnZID25qgPoibkw8qTCugiAPnn4eCGvn1A.7oEBFJaaGUaJsQQY."; + +in +{ + name = "pam-oath-login"; + + machine = + { ... }: + { + security.pam.oath = { + enable = true; + }; + + users.users.alice = { + isNormalUser = true; + name = "alice"; + uid = 1000; + hashedPassword = hashedAlicePassword; + extraGroups = [ "wheel" ]; + createHome = true; + home = "/home/alice"; + }; + + + systemd.services.setupOathSnakeoilFile = { + wantedBy = [ "default.target" ]; + before = [ "default.target" ]; + unitConfig = { + type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + touch /etc/users.oath + chmod 600 /etc/users.oath + chown root /etc/users.oath + echo "HOTP/E/6 alice - ${oathSnakeoilSecret}" > /etc/users.oath + ''; + }; + }; + + testScript = + '' + $machine->waitForUnit('multi-user.target'); + $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty1'"); + $machine->screenshot("postboot"); + + + subtest "Invalid password", sub { + $machine->fail("pgrep -f 'agetty.*tty2'"); + $machine->sendKeys("alt-f2"); + $machine->waitUntilSucceeds("[ \$(fgconsole) = 2 ]"); + $machine->waitForUnit('getty@tty2.service'); + $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty2'"); + + $machine->waitUntilTTYMatches(2, "login: "); + $machine->sendChars("alice\n"); + $machine->waitUntilTTYMatches(2, "login: alice"); + $machine->waitUntilSucceeds("pgrep login"); + + $machine->waitUntilTTYMatches(2, "One-time password"); + $machine->sendChars("${oathSnakeOilPassword1}\n"); + $machine->waitUntilTTYMatches(2, "Password: "); + $machine->sendChars("blorg\n"); + $machine->waitUntilTTYMatches(2, "Login incorrect"); + }; + + subtest "Invalid oath token", sub { + $machine->fail("pgrep -f 'agetty.*tty3'"); + $machine->sendKeys("alt-f3"); + $machine->waitUntilSucceeds("[ \$(fgconsole) = 3 ]"); + $machine->waitForUnit('getty@tty3.service'); + $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty3'"); + + $machine->waitUntilTTYMatches(3, "login: "); + $machine->sendChars("alice\n"); + $machine->waitUntilTTYMatches(3, "login: alice"); + $machine->waitUntilSucceeds("pgrep login"); + $machine->waitUntilTTYMatches(3, "One-time password"); + $machine->sendChars("000000\n"); + $machine->waitUntilTTYMatches(3, "Login incorrect"); + $machine->waitUntilTTYMatches(3, "login:"); + }; + + subtest "Happy path (both passwords are mandatory to get us in)", sub { + $machine->fail("pgrep -f 'agetty.*tty4'"); + $machine->sendKeys("alt-f4"); + $machine->waitUntilSucceeds("[ \$(fgconsole) = 4 ]"); + $machine->waitForUnit('getty@tty4.service'); + $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty4'"); + + $machine->waitUntilTTYMatches(4, "login: "); + $machine->sendChars("alice\n"); + $machine->waitUntilTTYMatches(4, "login: alice"); + $machine->waitUntilSucceeds("pgrep login"); + $machine->waitUntilTTYMatches(4, "One-time password"); + $machine->sendChars("${oathSnakeOilPassword2}\n"); + $machine->waitUntilTTYMatches(4, "Password: "); + $machine->sendChars("${alicePassword}\n"); + + $machine->waitUntilSucceeds("pgrep -u alice bash"); + $machine->sendChars("touch done4\n"); + $machine->waitForFile("/home/alice/done4"); + }; + + ''; + +}) diff --git a/nixpkgs/nixos/tests/pam-u2f.nix b/nixpkgs/nixos/tests/pam-u2f.nix new file mode 100644 index 000000000000..1052a2f3b916 --- /dev/null +++ b/nixpkgs/nixos/tests/pam-u2f.nix @@ -0,0 +1,23 @@ +import ./make-test.nix ({ ... }: + +{ + name = "pam-u2f"; + + machine = + { ... }: + { + security.pam.u2f = { + control = "required"; + cue = true; + debug = true; + enable = true; + interactive = true; + }; + }; + + testScript = + '' + $machine->waitForUnit('multi-user.target'); + $machine->succeed('egrep "auth required .*/lib/security/pam_u2f.so.*debug.*interactive.*cue" /etc/pam.d/ -R'); + ''; +}) diff --git a/nixpkgs/nixos/tests/partition.nix b/nixpkgs/nixos/tests/partition.nix new file mode 100644 index 000000000000..01a08995950f --- /dev/null +++ b/nixpkgs/nixos/tests/partition.nix @@ -0,0 +1,247 @@ +import ./make-test.nix ({ pkgs, ... }: + +with pkgs.lib; + +let + ksExt = pkgs.writeText "ks-ext4" '' + clearpart --all --initlabel --drives=vdb + + part /boot --recommended --label=boot --fstype=ext2 --ondisk=vdb + part swap --recommended --label=swap --fstype=swap --ondisk=vdb + part /nix --size=500 --label=nix --fstype=ext3 --ondisk=vdb + part / --recommended --label=root --fstype=ext4 --ondisk=vdb + ''; + + ksBtrfs = pkgs.writeText "ks-btrfs" '' + clearpart --all --initlabel --drives=vdb,vdc + + part swap1 --recommended --label=swap1 --fstype=swap --ondisk=vdb + part swap2 --recommended --label=swap2 --fstype=swap --ondisk=vdc + + part btrfs.1 --grow --ondisk=vdb + part btrfs.2 --grow --ondisk=vdc + + btrfs / --data=0 --metadata=1 --label=root btrfs.1 btrfs.2 + ''; + + ksF2fs = pkgs.writeText "ks-f2fs" '' + clearpart --all --initlabel --drives=vdb + + part swap --recommended --label=swap --fstype=swap --ondisk=vdb + part /boot --recommended --label=boot --fstype=f2fs --ondisk=vdb + part / --recommended --label=root --fstype=f2fs --ondisk=vdb + ''; + + ksRaid = pkgs.writeText "ks-raid" '' + clearpart --all --initlabel --drives=vdb,vdc + + part raid.01 --size=200 --ondisk=vdb + part raid.02 --size=200 --ondisk=vdc + + part swap1 --size=500 --label=swap1 --fstype=swap --ondisk=vdb + part swap2 --size=500 --label=swap2 --fstype=swap --ondisk=vdc + + part raid.11 --grow --ondisk=vdb + part raid.12 --grow --ondisk=vdc + + raid /boot --level=1 --fstype=ext3 --device=md0 raid.01 raid.02 + raid / --level=1 --fstype=xfs --device=md1 raid.11 raid.12 + ''; + + ksRaidLvmCrypt = pkgs.writeText "ks-lvm-crypt" '' + clearpart --all --initlabel --drives=vdb,vdc + + part raid.1 --grow --ondisk=vdb + part raid.2 --grow --ondisk=vdc + + raid pv.0 --level=1 --encrypted --passphrase=x --device=md0 raid.1 raid.2 + + volgroup nixos pv.0 + + logvol /boot --size=200 --fstype=ext3 --name=boot --vgname=nixos + logvol swap --size=500 --fstype=swap --name=swap --vgname=nixos + logvol / --size=1000 --grow --fstype=ext4 --name=root --vgname=nixos + ''; +in { + name = "partitiion"; + + machine = { pkgs, ... }: { + environment.systemPackages = [ + pkgs.pythonPackages.nixpart0 + pkgs.file pkgs.btrfs-progs pkgs.xfsprogs pkgs.lvm2 + ]; + virtualisation.emptyDiskImages = [ 4096 4096 ]; + }; + + testScript = '' + my $diskStart; + my @mtab; + + sub getMtab { + my $mounts = $machine->succeed("cat /proc/mounts"); + chomp $mounts; + return map [split], split /\n/, $mounts; + } + + sub parttest { + my ($desc, $code) = @_; + $machine->start; + $machine->waitForUnit("default.target"); + + # Gather mounts and superblock + @mtab = getMtab; + $diskStart = $machine->succeed("dd if=/dev/vda bs=512 count=1"); + + subtest($desc, $code); + $machine->shutdown; + } + + sub ensureSanity { + # Check whether the filesystem in /dev/vda is still intact + my $newDiskStart = $machine->succeed("dd if=/dev/vda bs=512 count=1"); + if ($diskStart ne $newDiskStart) { + $machine->log("Something went wrong, the partitioner wrote " . + "something into the first 512 bytes of /dev/vda!"); + die; + } + + # Check whether nixpart has unmounted anything + my @currentMtab = getMtab; + for my $mount (@mtab) { + my $path = $mount->[1]; + unless (grep { $_->[1] eq $path } @currentMtab) { + $machine->log("The partitioner seems to have unmounted $path."); + die; + } + } + } + + sub checkMount { + my $mounts = $machine->succeed("cat /proc/mounts"); + + } + + sub kickstart { + $machine->copyFileFromHost($_[0], "/kickstart"); + $machine->succeed("nixpart -v /kickstart"); + ensureSanity; + } + + sub ensurePartition { + my ($name, $match) = @_; + my $path = $name =~ /^\// ? $name : "/dev/disk/by-label/$name"; + my $out = $machine->succeed("file -Ls $path"); + my @matches = grep(/^$path: .*$match/i, $out); + if (!@matches) { + $machine->log("Partition on $path was expected to have a " . + "file system that matches $match, but instead has: $out"); + die; + } + } + + sub ensureNoPartition { + $machine->succeed("test ! -e /dev/$_[0]"); + } + + sub ensureMountPoint { + $machine->succeed("mountpoint $_[0]"); + } + + sub remountAndCheck { + $machine->nest("Remounting partitions:", sub { + # XXX: "findmnt -ARunl -oTARGET /mnt" seems to NOT print all mounts! + my $getmounts_cmd = "cat /proc/mounts | cut -d' ' -f2 | grep '^/mnt'"; + # Insert canaries first + my $canaries = $machine->succeed($getmounts_cmd . " | while read p;" . + " do touch \"\$p/canary\";" . + " echo \"\$p/canary\"; done"); + # Now unmount manually + $machine->succeed($getmounts_cmd . " | tac | xargs -r umount"); + # /mnt should be empty or non-existing + my $found = $machine->succeed("find /mnt -mindepth 1"); + chomp $found; + if ($found) { + $machine->log("Cruft found in /mnt:\n$found"); + die; + } + # Try to remount with nixpart + $machine->succeed("nixpart -vm /kickstart"); + ensureMountPoint("/mnt"); + # Check if our beloved canaries are dead + chomp $canaries; + $machine->nest("Checking canaries:", sub { + for my $canary (split /\n/, $canaries) { + $machine->succeed("test -e '$canary'"); + } + }); + }); + } + + parttest "ext2, ext3 and ext4 filesystems", sub { + kickstart("${ksExt}"); + ensurePartition("boot", "ext2"); + ensurePartition("swap", "swap"); + ensurePartition("nix", "ext3"); + ensurePartition("root", "ext4"); + ensurePartition("/dev/vdb4", "boot sector"); + ensureNoPartition("vdb6"); + ensureNoPartition("vdc1"); + remountAndCheck; + ensureMountPoint("/mnt/boot"); + ensureMountPoint("/mnt/nix"); + }; + + parttest "btrfs filesystem", sub { + $machine->succeed("modprobe btrfs"); + kickstart("${ksBtrfs}"); + ensurePartition("swap1", "swap"); + ensurePartition("swap2", "swap"); + ensurePartition("/dev/vdb2", "btrfs"); + ensurePartition("/dev/vdc2", "btrfs"); + ensureNoPartition("vdb3"); + ensureNoPartition("vdc3"); + remountAndCheck; + }; + + parttest "f2fs filesystem", sub { + $machine->succeed("modprobe f2fs"); + kickstart("${ksF2fs}"); + ensurePartition("swap", "swap"); + ensurePartition("boot", "f2fs"); + ensurePartition("root", "f2fs"); + remountAndCheck; + ensureMountPoint("/mnt/boot", "f2fs"); + }; + + parttest "RAID1 with XFS", sub { + kickstart("${ksRaid}"); + ensurePartition("swap1", "swap"); + ensurePartition("swap2", "swap"); + ensurePartition("/dev/md0", "ext3"); + ensurePartition("/dev/md1", "xfs"); + ensureNoPartition("vdb4"); + ensureNoPartition("vdc4"); + ensureNoPartition("md2"); + remountAndCheck; + ensureMountPoint("/mnt/boot"); + }; + + parttest "RAID1 with LUKS and LVM", sub { + kickstart("${ksRaidLvmCrypt}"); + ensurePartition("/dev/vdb1", "data"); + ensureNoPartition("vdb2"); + ensurePartition("/dev/vdc1", "data"); + ensureNoPartition("vdc2"); + + ensurePartition("/dev/md0", "luks"); + ensureNoPartition("md1"); + + ensurePartition("/dev/nixos/boot", "ext3"); + ensurePartition("/dev/nixos/swap", "swap"); + ensurePartition("/dev/nixos/root", "ext4"); + + remountAndCheck; + ensureMountPoint("/mnt/boot"); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/peerflix.nix b/nixpkgs/nixos/tests/peerflix.nix new file mode 100644 index 000000000000..fae37fedaac7 --- /dev/null +++ b/nixpkgs/nixos/tests/peerflix.nix @@ -0,0 +1,23 @@ +# This test runs peerflix and checks if peerflix starts + +import ./make-test.nix ({ pkgs, ...} : { + name = "peerflix"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ offline ]; + }; + + nodes = { + peerflix = + { ... }: + { + services.peerflix.enable = true; + }; + }; + + testScript = '' + startAll; + + $peerflix->waitForUnit("peerflix.service"); + $peerflix->waitUntilSucceeds("curl localhost:9000"); + ''; +}) diff --git a/nixpkgs/nixos/tests/pgjwt.nix b/nixpkgs/nixos/tests/pgjwt.nix new file mode 100644 index 000000000000..a2d81288c812 --- /dev/null +++ b/nixpkgs/nixos/tests/pgjwt.nix @@ -0,0 +1,37 @@ +import ./make-test.nix ({ pkgs, lib, ...}: +let + test = with pkgs; runCommand "patch-test" { + nativeBuildInputs = [ pgjwt ]; + } + '' + sed -e '12 i CREATE EXTENSION pgcrypto;\nCREATE EXTENSION pgtap;\nSET search_path TO tap,public;' ${pgjwt.src}/test.sql > $out; + ''; +in +with pkgs; { + name = "pgjwt"; + meta = with lib.maintainers; { + maintainers = [ spinus willibutz ]; + }; + + nodes = { + master = { ... }: + { + services.postgresql = { + enable = true; + extraPlugins = [ pgjwt pgtap ]; + }; + }; + }; + + testScript = { nodes, ... }: + let + sqlSU = "${nodes.master.config.services.postgresql.superUser}"; + pgProve = "${pkgs.perlPackages.TAPParserSourceHandlerpgTAP}"; + in + '' + startAll; + $master->waitForUnit("postgresql"); + $master->copyFileFromHost("${test}","/tmp/test.sql"); + $master->succeed("${pkgs.sudo}/bin/sudo -u ${sqlSU} PGOPTIONS=--search_path=tap,public ${pgProve}/bin/pg_prove -d postgres -v -f /tmp/test.sql"); + ''; +}) diff --git a/nixpkgs/nixos/tests/pgmanage.nix b/nixpkgs/nixos/tests/pgmanage.nix new file mode 100644 index 000000000000..110cbd5c5b40 --- /dev/null +++ b/nixpkgs/nixos/tests/pgmanage.nix @@ -0,0 +1,39 @@ +import ./make-test.nix ({ pkgs, ... } : +let + role = "test"; + password = "secret"; + conn = "local"; +in +{ + name = "pgmanage"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ basvandijk ]; + }; + nodes = { + one = { config, pkgs, ... }: { + services = { + postgresql = { + enable = true; + initialScript = pkgs.writeText "pg-init-script" '' + CREATE ROLE ${role} SUPERUSER LOGIN PASSWORD '${password}'; + ''; + }; + pgmanage = { + enable = true; + connections = { + "${conn}" = "hostaddr=127.0.0.1 port=${toString config.services.postgresql.port} dbname=postgres"; + }; + }; + }; + }; + }; + + testScript = '' + startAll; + $one->waitForUnit("default.target"); + $one->requireActiveUnit("pgmanage.service"); + + # Test if we can log in. + $one->waitUntilSucceeds("curl 'http://localhost:8080/pgmanage/auth' --data 'action=login&connname=${conn}&username=${role}&password=${password}' --fail"); + ''; +}) diff --git a/nixpkgs/nixos/tests/phabricator.nix b/nixpkgs/nixos/tests/phabricator.nix new file mode 100644 index 000000000000..20b3b838aba8 --- /dev/null +++ b/nixpkgs/nixos/tests/phabricator.nix @@ -0,0 +1,77 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "phabricator"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ chaoflow ]; + }; + + nodes = { + storage = + { ... }: + { services.nfs.server.enable = true; + services.nfs.server.exports = '' + /repos 192.168.1.0/255.255.255.0(rw,no_root_squash) + ''; + services.nfs.server.createMountPoints = true; + }; + + webserver = + { pkgs, ... }: + { fileSystems = pkgs.lib.mkVMOverride + [ { mountPoint = "/repos"; + device = "storage:/repos"; + fsType = "nfs"; + } + ]; + networking.firewall.enable = false; + networking.useDHCP = false; + + services = { + httpd = { + enable = true; + adminAddr = "root@localhost"; + virtualHosts = [{ + hostName = "phabricator.local"; + extraSubservices = [{serviceType = "phabricator";}]; + }]; + }; + + phd = { + enable = true; + }; + + mysql = { + enable = true; + package = pkgs.mysql; + extraOptions = '' + sql_mode=STRICT_ALL_TABLES + ''; + }; + }; + + environment.systemPackages = [ pkgs.php ]; + }; + + client = + { ... }: + { imports = [ ./common/x11.nix ]; + services.xserver.desktopManager.plasma5.enable = true; + }; + }; + + testScript = + '' + startAll; + + $client->waitForX; + + $webserver->waitForUnit("mysql"); + $webserver->waitForUnit("httpd"); + $webserver->execute("cd /nix/store; less >/repos/log1"); + + $client->sleep(30); # loading takes a long time + $client->execute("konqueror http://webserver/ &"); + $client->sleep(90); # loading takes a long time + + $client->screenshot("screen"); + ''; +}) diff --git a/nixpkgs/nixos/tests/php-pcre.nix b/nixpkgs/nixos/tests/php-pcre.nix new file mode 100644 index 000000000000..19bde9babad5 --- /dev/null +++ b/nixpkgs/nixos/tests/php-pcre.nix @@ -0,0 +1,44 @@ + +let testString = "can-use-subgroups"; in + +import ./make-test.nix ({ ...}: { + name = "php-httpd-pcre-jit-test"; + machine = { lib, pkgs, ... }: { + time.timeZone = "UTC"; + services.httpd = { + enable = true; + adminAddr = "please@dont.contact"; + extraSubservices = lib.singleton { + function = f: { + enablePHP = true; + phpOptions = "pcre.jit = true"; + + extraConfig = + let + testRoot = pkgs.writeText "index.php" + '' + <?php + preg_match('/(${testString})/', '${testString}', $result); + var_dump($result); + ?> + ''; + in + '' + Alias / ${testRoot}/ + + <Directory ${testRoot}> + Require all granted + </Directory> + ''; + }; + }; + }; + }; + testScript = { ... }: + '' + $machine->waitForUnit('httpd.service'); + # Ensure php evaluation by matching on the var_dump syntax + $machine->succeed('curl -vvv -s http://127.0.0.1:80/index.php \ + | grep "string(${toString (builtins.stringLength testString)}) \"${testString}\""'); + ''; +}) diff --git a/nixpkgs/nixos/tests/plasma5.nix b/nixpkgs/nixos/tests/plasma5.nix new file mode 100644 index 000000000000..788c8719c8d2 --- /dev/null +++ b/nixpkgs/nixos/tests/plasma5.nix @@ -0,0 +1,64 @@ +import ./make-test.nix ({ pkgs, ...} : + +{ + name = "plasma5"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ttuegel ]; + }; + + machine = { ... }: + let + sddm_theme = pkgs.stdenv.mkDerivation { + name = "breeze-ocr-theme"; + phases = "buildPhase"; + buildCommand = '' + mkdir -p $out/share/sddm/themes/ + cp -r ${pkgs.plasma-workspace}/share/sddm/themes/breeze $out/share/sddm/themes/breeze-ocr-theme + chmod -R +w $out/share/sddm/themes/breeze-ocr-theme + printf "[General]\ntype=color\ncolor=#1d99f3\nbackground=\n" > $out/share/sddm/themes/breeze-ocr-theme/theme.conf + ''; + }; + in + { + imports = [ ./common/user-account.nix ]; + services.xserver.enable = true; + services.xserver.displayManager.sddm.enable = true; + services.xserver.displayManager.sddm.theme = "breeze-ocr-theme"; + services.xserver.desktopManager.plasma5.enable = true; + services.xserver.desktopManager.default = "plasma5"; + services.xserver.displayManager.sddm.autoLogin = { + enable = true; + user = "alice"; + }; + virtualisation.memorySize = 1024; + environment.systemPackages = [ sddm_theme ]; + }; + + testScript = { nodes, ... }: let + user = nodes.machine.config.users.users.alice; + xdo = "${pkgs.xdotool}/bin/xdotool"; + in '' + startAll; + # wait for log in + $machine->waitForFile("/home/alice/.Xauthority"); + $machine->succeed("xauth merge ~alice/.Xauthority"); + + $machine->waitUntilSucceeds("pgrep plasmashell"); + $machine->waitForWindow("^Desktop "); + + # Check that logging in has given the user ownership of devices. + $machine->succeed("getfacl /dev/snd/timer | grep -q alice"); + + $machine->execute("su - alice -c 'DISPLAY=:0.0 dolphin &'"); + $machine->waitForWindow(" Dolphin"); + + $machine->execute("su - alice -c 'DISPLAY=:0.0 konsole &'"); + $machine->waitForWindow("Konsole"); + + $machine->execute("su - alice -c 'DISPLAY=:0.0 systemsettings5 &'"); + $machine->waitForWindow("Settings"); + + $machine->execute("${xdo} key Alt+F1 sleep 10"); + $machine->screenshot("screen"); + ''; +}) diff --git a/nixpkgs/nixos/tests/plotinus.nix b/nixpkgs/nixos/tests/plotinus.nix new file mode 100644 index 000000000000..609afe7b2145 --- /dev/null +++ b/nixpkgs/nixos/tests/plotinus.nix @@ -0,0 +1,27 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "plotinus"; + meta = { + maintainers = pkgs.plotinus.meta.maintainers; + }; + + machine = + { pkgs, ... }: + + { imports = [ ./common/x11.nix ]; + programs.plotinus.enable = true; + environment.systemPackages = [ pkgs.gnome3.gnome-calculator pkgs.xdotool ]; + }; + + testScript = + '' + $machine->waitForX; + $machine->succeed("gnome-calculator &"); + $machine->waitForWindow(qr/gnome-calculator/); + $machine->succeed("xdotool search --sync --onlyvisible --class gnome-calculator windowfocus --sync key ctrl+shift+p"); + $machine->sleep(5); # wait for the popup + $machine->succeed("xdotool key --delay 100 p r e f e r e n c e s Return"); + $machine->waitForWindow(qr/Preferences/); + $machine->screenshot("screen"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/postgis.nix b/nixpkgs/nixos/tests/postgis.nix new file mode 100644 index 000000000000..294eb50b5fe5 --- /dev/null +++ b/nixpkgs/nixos/tests/postgis.nix @@ -0,0 +1,29 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "postgis"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ lsix ]; + }; + + nodes = { + master = + { pkgs, ... }: + + { + services.postgresql = let mypg = pkgs.postgresql_11; in { + enable = true; + package = mypg; + extraPlugins = with mypg.pkgs; [ + postgis + ]; + }; + }; + }; + + testScript = '' + startAll; + $master->waitForUnit("postgresql"); + $master->sleep(10); # Hopefully this is long enough!! + $master->succeed("sudo -u postgres psql -c 'CREATE EXTENSION postgis;'"); + $master->succeed("sudo -u postgres psql -c 'CREATE EXTENSION postgis_topology;'"); + ''; +}) diff --git a/nixpkgs/nixos/tests/postgresql.nix b/nixpkgs/nixos/tests/postgresql.nix new file mode 100644 index 000000000000..ae5d6d095ea2 --- /dev/null +++ b/nixpkgs/nixos/tests/postgresql.nix @@ -0,0 +1,73 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + postgresql-versions = import ../../pkgs/servers/sql/postgresql pkgs; + test-sql = pkgs.writeText "postgresql-test" '' + CREATE EXTENSION pgcrypto; -- just to check if lib loading works + CREATE TABLE sth ( + id int + ); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + CREATE TABLE xmltest ( doc xml ); + INSERT INTO xmltest (doc) VALUES ('<test>ok</test>'); -- check if libxml2 enabled + ''; + make-postgresql-test = postgresql-name: postgresql-package: backup-all: makeTest { + name = postgresql-name; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ zagy ]; + }; + + machine = {...}: + { + services.postgresql.enable = true; + services.postgresql.package = postgresql-package; + + services.postgresqlBackup.enable = true; + services.postgresqlBackup.databases = optional (!backup-all) "postgres"; + }; + + testScript = let + backupName = if backup-all then "all" else "postgres"; + backupService = if backup-all then "postgresqlBackup" else "postgresqlBackup-postgres"; + in '' + sub check_count { + my ($select, $nlines) = @_; + return 'test $(sudo -u postgres psql postgres -tAc "' . $select . '"|wc -l) -eq ' . $nlines; + } + + $machine->start; + $machine->waitForUnit("postgresql"); + # postgresql should be available just after unit start + $machine->succeed("cat ${test-sql} | sudo -u postgres psql"); + $machine->shutdown; # make sure that postgresql survive restart (bug #1735) + sleep(2); + $machine->start; + $machine->waitForUnit("postgresql"); + $machine->fail(check_count("SELECT * FROM sth;", 3)); + $machine->succeed(check_count("SELECT * FROM sth;", 5)); + $machine->fail(check_count("SELECT * FROM sth;", 4)); + $machine->succeed(check_count("SELECT xpath(\'/test/text()\', doc) FROM xmltest;", 1)); + + # Check backup service + $machine->succeed("systemctl start ${backupService}.service"); + $machine->succeed("zcat /var/backup/postgresql/${backupName}.sql.gz | grep '<test>ok</test>'"); + $machine->succeed("stat -c '%a' /var/backup/postgresql/${backupName}.sql.gz | grep 600"); + $machine->shutdown; + ''; + + }; +in + (mapAttrs' (name: package: { inherit name; value=make-postgresql-test name package false;}) postgresql-versions) // { + postgresql_11-backup-all = make-postgresql-test "postgresql_11-backup-all" postgresql-versions.postgresql_11 true; + } + diff --git a/nixpkgs/nixos/tests/powerdns.nix b/nixpkgs/nixos/tests/powerdns.nix new file mode 100644 index 000000000000..8addcc784012 --- /dev/null +++ b/nixpkgs/nixos/tests/powerdns.nix @@ -0,0 +1,12 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "powerdns"; + + nodes.server = { ... }: { + services.powerdns.enable = true; + }; + + testScript = '' + $server->waitForUnit("pdns"); + $server->succeed("${pkgs.dnsutils}/bin/dig version.bind txt chaos \@127.0.0.1"); + ''; +}) diff --git a/nixpkgs/nixos/tests/predictable-interface-names.nix b/nixpkgs/nixos/tests/predictable-interface-names.nix new file mode 100644 index 000000000000..8306abb8c42f --- /dev/null +++ b/nixpkgs/nixos/tests/predictable-interface-names.nix @@ -0,0 +1,27 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +let + inherit (import ../lib/testing.nix { inherit system pkgs; }) makeTest; +in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: { + name = pkgs.lib.optionalString (!predictable) "un" + "predictable" + + pkgs.lib.optionalString withNetworkd "Networkd"; + value = makeTest { + name = "${if predictable then "" else "un"}predictableInterfaceNames${if withNetworkd then "-with-networkd" else ""}"; + meta = {}; + + machine = { lib, ... }: { + networking.usePredictableInterfaceNames = lib.mkForce predictable; + networking.useNetworkd = withNetworkd; + networking.dhcpcd.enable = !withNetworkd; + }; + + testScript = '' + print $machine->succeed("ip link"); + $machine->succeed("ip link show ${if predictable then "ens3" else "eth0"}"); + $machine->fail("ip link show ${if predictable then "eth0" else "ens3"}"); + ''; + }; +}) [[true false] [true false]]) diff --git a/nixpkgs/nixos/tests/printing.nix b/nixpkgs/nixos/tests/printing.nix new file mode 100644 index 000000000000..d85abf3c105c --- /dev/null +++ b/nixpkgs/nixos/tests/printing.nix @@ -0,0 +1,101 @@ +# Test printing via CUPS. + +import ./make-test.nix ({pkgs, ... }: { + name = "printing"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ domenkozar eelco chaoflow jgeerds ]; + }; + + nodes = { + + server = + { ... }: + { services.printing.enable = true; + services.printing.listenAddresses = [ "*:631" ]; + services.printing.defaultShared = true; + services.printing.extraConf = + '' + <Location /> + Order allow,deny + Allow from all + </Location> + ''; + networking.firewall.allowedTCPPorts = [ 631 ]; + }; + + client = + { ... }: + { services.printing.enable = true; + }; + + }; + + testScript = + '' + startAll; + + # Make sure that cups is up on both sides. + $server->waitForUnit("cups.service"); + $client->waitForUnit("cups.service"); + $client->sleep(10); # wait until cups is fully initialized + $client->succeed("lpstat -r") =~ /scheduler is running/ or die; + # Test that UNIX socket is used for connections. + $client->succeed("lpstat -H") =~ "/var/run/cups/cups.sock" or die; + # Test that HTTP server is available too. + $client->succeed("curl --fail http://localhost:631/"); + $client->succeed("curl --fail http://server:631/"); + $server->fail("curl --fail --connect-timeout 2 http://client:631/"); + + # Add a HP Deskjet printer connected via USB to the server. + $server->succeed("lpadmin -p DeskjetLocal -E -v usb://foobar/printers/foobar"); + + # Add it to the client as well via IPP. + $client->succeed("lpadmin -p DeskjetRemote -E -v ipp://server/printers/DeskjetLocal"); + $client->succeed("lpadmin -d DeskjetRemote"); + + # Do some status checks. + $client->succeed("lpstat -a") =~ /DeskjetRemote accepting requests/ or die; + $client->succeed("lpstat -h server:631 -a") =~ /DeskjetLocal accepting requests/ or die; + $client->succeed("cupsdisable DeskjetRemote"); + $client->succeed("lpq") =~ /DeskjetRemote is not ready.*no entries/s or die; + $client->succeed("cupsenable DeskjetRemote"); + $client->succeed("lpq") =~ /DeskjetRemote is ready.*no entries/s or die; + + # Test printing various file types. + foreach my $file ("${pkgs.groff.doc}/share/doc/*/examples/mom/penguin.pdf", + "${pkgs.groff.doc}/share/doc/*/meref.ps", + "${pkgs.cups.out}/share/doc/cups/images/cups.png", + "${pkgs.pcre.doc}/share/doc/pcre/pcre.txt") + { + $file =~ /([^\/]*)$/; my $fn = $1; + + subtest "print $fn", sub { + + # Print the file on the client. + $client->succeed("lp $file"); + $client->sleep(10); + $client->succeed("lpq") =~ /active.*root.*$fn/ or die; + + # Ensure that a raw PCL file appeared in the server's queue + # (showing that the right filters have been applied). Of + # course, since there is no actual USB printer attached, the + # file will stay in the queue forever. + $server->waitForFile("/var/spool/cups/d*-001"); + $server->sleep(10); + $server->succeed("lpq -a") =~ /$fn/ or die; + + # Delete the job on the client. It should disappear on the + # server as well. + $client->succeed("lprm"); + $client->sleep(10); + $client->succeed("lpq -a") =~ /no entries/; + Machine::retry sub { + return 1 if $server->succeed("lpq -a") =~ /no entries/; + }; + # The queue is empty already, so this should be safe. + # Otherwise, pairs of "c*"-"d*-001" files might persist. + $server->execute("rm /var/spool/cups/*"); + }; + } + ''; +}) diff --git a/nixpkgs/nixos/tests/prometheus-exporters.nix b/nixpkgs/nixos/tests/prometheus-exporters.nix new file mode 100644 index 000000000000..140687a8182f --- /dev/null +++ b/nixpkgs/nixos/tests/prometheus-exporters.nix @@ -0,0 +1,335 @@ +import ./make-test.nix ({ lib, pkgs, ... }: +let + escape' = str: lib.replaceChars [''"'' "$" "\n"] [''\\\"'' "\\$" ""] str; + +/* + * The attrset `exporterTests` contains one attribute + * for each exporter test. Each of these attributes + * is expected to be an attrset containing: + * + * `exporterConfig`: + * this attribute set contains config for the exporter itself + * + * `exporterTest` + * this attribute set contains test instructions + * + * `metricProvider` (optional) + * this attribute contains additional machine config + * + * Example: + * exporterTests.<exporterName> = { + * exporterConfig = { + * enable = true; + * }; + * metricProvider = { + * services.<metricProvider>.enable = true; + * }; + * exporterTest = '' + * waitForUnit("prometheus-<exporterName>-exporter.service"); + * waitForOpenPort("1234"); + * succeed("curl -sSf 'localhost:1234/metrics'"); + * ''; + * }; + * + * # this would generate the following test config: + * + * nodes.<exporterName> = { + * services.prometheus.<exporterName> = { + * enable = true; + * }; + * services.<metricProvider>.enable = true; + * }; + * + * testScript = '' + * $<exporterName>->start(); + * $<exporterName>->waitForUnit("prometheus-<exporterName>-exporter.service"); + * $<exporterName>->waitForOpenPort("1234"); + * $<exporterName>->succeed("curl -sSf 'localhost:1234/metrics'"); + * $<exporterName>->shutdown(); + * ''; + */ + + exporterTests = { + + blackbox = { + exporterConfig = { + enable = true; + configFile = pkgs.writeText "config.yml" (builtins.toJSON { + modules.icmp_v6 = { + prober = "icmp"; + icmp.preferred_ip_protocol = "ip6"; + }; + }); + }; + exporterTest = '' + waitForUnit("prometheus-blackbox-exporter.service"); + waitForOpenPort(9115); + succeed("curl -sSf 'http://localhost:9115/probe?target=localhost&module=icmp_v6' | grep -q 'probe_success 1'"); + ''; + }; + + collectd = { + exporterConfig = { + enable = true; + extraFlags = [ "--web.collectd-push-path /collectd" ]; + }; + exporterTest =let postData = escape' '' + [{ + "values":[23], + "dstypes":["gauge"], + "type":"gauge", + "interval":1000, + "host":"testhost", + "plugin":"testplugin", + "time":$(date +%s) + }] + ''; in '' + waitForUnit("prometheus-collectd-exporter.service"); + waitForOpenPort(9103); + succeed("curl -sSfH 'Content-Type: application/json' -X POST --data \"${postData}\" localhost:9103/collectd"); + succeed("curl -sSf localhost:9103/metrics | grep -q 'collectd_testplugin_gauge{instance=\"testhost\"} 23'"); + ''; + }; + + dnsmasq = { + exporterConfig = { + enable = true; + leasesPath = "/var/lib/dnsmasq/dnsmasq.leases"; + }; + metricProvider = { + services.dnsmasq.enable = true; + }; + exporterTest = '' + waitForUnit("prometheus-dnsmasq-exporter.service"); + waitForOpenPort(9153); + succeed("curl -sSf http://localhost:9153/metrics | grep -q 'dnsmasq_leases 0'"); + ''; + }; + + bind = { + exporterConfig = { + enable = true; + }; + metricProvider = { + services.bind.enable = true; + services.bind.extraConfig = '' + statistics-channels { + inet 127.0.0.1 port 8053 allow { localhost; }; + }; + ''; + }; + exporterTest = '' + waitForUnit("prometheus-bind-exporter.service"); + waitForOpenPort(9119); + succeed("curl -sSf http://localhost:9119/metrics" | grep -q 'bind_query_recursions_total 0'); + ''; + }; + + dovecot = { + exporterConfig = { + enable = true; + scopes = [ "global" ]; + socketPath = "/var/run/dovecot2/old-stats"; + user = "root"; # <- don't use user root in production + }; + metricProvider = { + services.dovecot2.enable = true; + }; + exporterTest = '' + waitForUnit("prometheus-dovecot-exporter.service"); + waitForOpenPort(9166); + succeed("curl -sSf http://localhost:9166/metrics | grep -q 'dovecot_up{scope=\"global\"} 1'"); + ''; + }; + + fritzbox = { # TODO add proper test case + exporterConfig = { + enable = true; + }; + exporterTest = '' + waitForUnit("prometheus-fritzbox-exporter.service"); + waitForOpenPort(9133); + succeed("curl -sSf http://localhost:9133/metrics | grep -q 'fritzbox_exporter_collect_errors 0'"); + ''; + }; + + json = { + exporterConfig = { + enable = true; + url = "http://localhost"; + configFile = pkgs.writeText "json-exporter-conf.json" (builtins.toJSON [{ + name = "json_test_metric"; + path = "$.test"; + }]); + }; + metricProvider = { + systemd.services.prometheus-json-exporter.after = [ "nginx.service" ]; + services.nginx = { + enable = true; + virtualHosts.localhost.locations."/".extraConfig = '' + return 200 "{\"test\":1}"; + ''; + }; + }; + exporterTest = '' + waitForUnit("nginx.service"); + waitForOpenPort(80); + waitForUnit("prometheus-json-exporter.service"); + waitForOpenPort(7979); + succeed("curl -sSf localhost:7979/metrics | grep -q 'json_test_metric 1'"); + ''; + }; + + nginx = { + exporterConfig = { + enable = true; + }; + metricProvider = { + services.nginx = { + enable = true; + statusPage = true; + virtualHosts."/".extraConfig = "return 204;"; + }; + }; + exporterTest = '' + waitForUnit("nginx.service") + waitForUnit("prometheus-nginx-exporter.service") + waitForOpenPort(9113) + succeed("curl -sSf http://localhost:9113/metrics | grep -q 'nginx_up 1'") + ''; + }; + + node = { + exporterConfig = { + enable = true; + }; + exporterTest = '' + waitForUnit("prometheus-node-exporter.service"); + waitForOpenPort(9100); + succeed("curl -sSf http://localhost:9100/metrics | grep -q 'node_exporter_build_info{.\\+} 1'"); + ''; + }; + + postfix = { + exporterConfig = { + enable = true; + }; + metricProvider = { + services.postfix.enable = true; + }; + exporterTest = '' + waitForUnit("prometheus-postfix-exporter.service"); + waitForOpenPort(9154); + succeed("curl -sSf http://localhost:9154/metrics | grep -q 'postfix_smtpd_connects_total 0'"); + ''; + }; + + snmp = { + exporterConfig = { + enable = true; + configuration.default = { + version = 2; + auth.community = "public"; + }; + }; + exporterTest = '' + waitForUnit("prometheus-snmp-exporter.service"); + waitForOpenPort(9116); + succeed("curl -sSf localhost:9116/metrics | grep -q 'snmp_request_errors_total 0'"); + ''; + }; + + surfboard = { + exporterConfig = { + enable = true; + modemAddress = "localhost"; + }; + metricProvider = { + systemd.services.prometheus-surfboard-exporter.after = [ "nginx.service" ]; + services.nginx = { + enable = true; + virtualHosts.localhost.locations."/cgi-bin/status".extraConfig = '' + return 204; + ''; + }; + }; + exporterTest = '' + waitForUnit("nginx.service"); + waitForOpenPort(80); + waitForUnit("prometheus-surfboard-exporter.service"); + waitForOpenPort(9239); + succeed("curl -sSf localhost:9239/metrics | grep -q 'surfboard_up 1'"); + ''; + }; + + tor = { + exporterConfig = { + enable = true; + }; + metricProvider = { + # Note: this does not connect the test environment to the Tor network. + # Client, relay, bridge or exit connectivity are disabled by default. + services.tor.enable = true; + services.tor.controlPort = 9051; + }; + exporterTest = '' + waitForUnit("tor.service"); + waitForOpenPort(9051); + waitForUnit("prometheus-tor-exporter.service"); + waitForOpenPort(9130); + succeed("curl -sSf localhost:9130/metrics | grep -q 'tor_version{.\\+} 1'"); + ''; + }; + + varnish = { + exporterConfig = { + enable = true; + instance = "/var/spool/varnish/varnish"; + group = "varnish"; + }; + metricProvider = { + systemd.services.prometheus-varnish-exporter.after = [ + "varnish.service" + ]; + services.varnish = { + enable = true; + config = '' + vcl 4.0; + backend default { + .host = "127.0.0.1"; + .port = "80"; + } + ''; + }; + }; + exporterTest = '' + waitForUnit("prometheus-varnish-exporter.service"); + waitForOpenPort(9131); + succeed("curl -sSf http://localhost:9131/metrics | grep -q 'varnish_up 1'"); + ''; + }; + }; + + nodes = lib.mapAttrs (exporter: testConfig: lib.mkMerge [{ + services.prometheus.exporters.${exporter} = testConfig.exporterConfig; + } testConfig.metricProvider or {}]) exporterTests; + + testScript = lib.concatStrings (lib.mapAttrsToList (exporter: testConfig: ('' + subtest "${exporter}", sub { + ${"$"+exporter}->start(); + ${lib.concatStringsSep " " (map (line: '' + ${"$"+exporter}->${line}; + '') (lib.splitString "\n" (lib.removeSuffix "\n" testConfig.exporterTest)))} + ${"$"+exporter}->shutdown(); + }; + '')) exporterTests); +in +{ + name = "prometheus-exporters"; + + inherit nodes testScript; + + meta = with lib.maintainers; { + maintainers = [ willibutz ]; + }; +}) diff --git a/nixpkgs/nixos/tests/prometheus.nix b/nixpkgs/nixos/tests/prometheus.nix new file mode 100644 index 000000000000..f1b20a33d71e --- /dev/null +++ b/nixpkgs/nixos/tests/prometheus.nix @@ -0,0 +1,48 @@ +import ./make-test.nix { + name = "prometheus"; + + nodes = { + one = { ... }: { + services.prometheus = { + enable = true; + scrapeConfigs = [{ + job_name = "prometheus"; + static_configs = [{ + targets = [ "127.0.0.1:9090" ]; + labels = { instance = "localhost"; }; + }]; + }]; + rules = [ ''testrule = count(up{job="prometheus"})'' ]; + + # a very simple version of the alertmanager configuration just to see if + # configuration checks & service startup are working + alertmanager = { + enable = true; + listenAddress = "[::1]"; + port = 9093; + configuration = { + route.receiver = "webhook"; + receivers = [ + { + name = "webhook"; + webhook_configs = [ + { url = "http://localhost"; } + ]; + } + ]; + }; + }; + }; + }; + }; + + testScript = '' + startAll; + $one->waitForUnit("prometheus.service"); + $one->waitForOpenPort(9090); + $one->succeed("curl -s http://127.0.0.1:9090/metrics"); + $one->waitForUnit("alertmanager.service"); + $one->waitForOpenPort("9093"); + $one->succeed("curl -f -s http://localhost:9093/"); + ''; +} diff --git a/nixpkgs/nixos/tests/prosody.nix b/nixpkgs/nixos/tests/prosody.nix new file mode 100644 index 000000000000..61ae5bb38ed9 --- /dev/null +++ b/nixpkgs/nixos/tests/prosody.nix @@ -0,0 +1,78 @@ +import ./make-test.nix { + name = "prosody"; + + machine = { pkgs, ... }: { + services.prosody = { + enable = true; + # TODO: use a self-signed certificate + c2sRequireEncryption = false; + extraConfig = '' + storage = "sql" + ''; + }; + environment.systemPackages = let + sendMessage = pkgs.writeScriptBin "send-message" '' + #!/usr/bin/env python3 + # Based on the sleekxmpp send_client example, look there for more details: + # https://github.com/fritzy/SleekXMPP/blob/develop/examples/send_client.py + import sleekxmpp + + class SendMsgBot(sleekxmpp.ClientXMPP): + """ + A basic SleekXMPP bot that will log in, send a message, + and then log out. + """ + def __init__(self, jid, password, recipient, message): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + + self.recipient = recipient + self.msg = message + + self.add_event_handler("session_start", self.start, threaded=True) + + def start(self, event): + self.send_presence() + self.get_roster() + + self.send_message(mto=self.recipient, + mbody=self.msg, + mtype='chat') + + self.disconnect(wait=True) + + + if __name__ == '__main__': + xmpp = SendMsgBot("test1@localhost", "test1", "test2@localhost", "Hello World!") + xmpp.register_plugin('xep_0030') # Service Discovery + xmpp.register_plugin('xep_0199') # XMPP Ping + + # TODO: verify certificate + # If you want to verify the SSL certificates offered by a server: + # xmpp.ca_certs = "path/to/ca/cert" + + if xmpp.connect(('localhost', 5222)): + xmpp.process(block=True) + else: + print("Unable to connect.") + sys.exit(1) + ''; + in [ (pkgs.python3.withPackages (ps: [ ps.sleekxmpp ])) sendMessage ]; + }; + + testScript = '' + $machine->waitForUnit('prosody.service'); + $machine->succeed('prosodyctl status') =~ /Prosody is running/; + + # set password to 'test' (it's asked twice) + $machine->succeed('yes test1 | prosodyctl adduser test1@localhost'); + # set password to 'y' + $machine->succeed('yes | prosodyctl adduser test2@localhost'); + # correct password to 'test2' + $machine->succeed('yes test2 | prosodyctl passwd test2@localhost'); + + $machine->succeed("send-message"); + + $machine->succeed('prosodyctl deluser test1@localhost'); + $machine->succeed('prosodyctl deluser test2@localhost'); + ''; +} diff --git a/nixpkgs/nixos/tests/proxy.nix b/nixpkgs/nixos/tests/proxy.nix new file mode 100644 index 000000000000..181953120282 --- /dev/null +++ b/nixpkgs/nixos/tests/proxy.nix @@ -0,0 +1,96 @@ +import ./make-test.nix ({ pkgs, ...} : + +let + + backend = + { pkgs, ... }: + + { services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + services.httpd.documentRoot = "${pkgs.valgrind.doc}/share/doc/valgrind/html"; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + +in + +{ + name = "proxy"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + nodes = + { proxy = + { nodes, ... }: + + { services.httpd.enable = true; + services.httpd.adminAddr = "bar@example.org"; + services.httpd.extraModules = [ "proxy_balancer" "lbmethod_byrequests" ]; + + services.httpd.extraConfig = + '' + ExtendedStatus on + + <Location /server-status> + Require all granted + SetHandler server-status + </Location> + + <Proxy balancer://cluster> + Require all granted + BalancerMember http://${nodes.backend1.config.networking.hostName} retry=0 + BalancerMember http://${nodes.backend2.config.networking.hostName} retry=0 + </Proxy> + + ProxyStatus full + ProxyPass /server-status ! + ProxyPass / balancer://cluster/ + ProxyPassReverse / balancer://cluster/ + + # For testing; don't want to wait forever for dead backend servers. + ProxyTimeout 5 + ''; + + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + + backend1 = backend; + backend2 = backend; + + client = { ... }: { }; + }; + + testScript = + '' + startAll; + + $proxy->waitForUnit("httpd"); + $backend1->waitForUnit("httpd"); + $backend2->waitForUnit("httpd"); + $client->waitForUnit("network.target"); + + # With the back-ends up, the proxy should work. + $client->succeed("curl --fail http://proxy/"); + + $client->succeed("curl --fail http://proxy/server-status"); + + # Block the first back-end. + $backend1->block; + + # The proxy should still work. + $client->succeed("curl --fail http://proxy/"); + + $client->succeed("curl --fail http://proxy/"); + + # Block the second back-end. + $backend2->block; + + # Now the proxy should fail as well. + $client->fail("curl --fail http://proxy/"); + + # But if the second back-end comes back, the proxy should start + # working again. + $backend2->unblock; + $client->succeed("curl --fail http://proxy/"); + ''; +}) diff --git a/nixpkgs/nixos/tests/quagga.nix b/nixpkgs/nixos/tests/quagga.nix new file mode 100644 index 000000000000..6aee7ea57f03 --- /dev/null +++ b/nixpkgs/nixos/tests/quagga.nix @@ -0,0 +1,96 @@ +# This test runs Quagga and checks if OSPF routing works. +# +# Network topology: +# [ client ]--net1--[ router1 ]--net2--[ router2 ]--net3--[ server ] +# +# All interfaces are in OSPF Area 0. + +import ./make-test.nix ({ pkgs, ... }: + let + + ifAddr = node: iface: (pkgs.lib.head node.config.networking.interfaces.${iface}.ipv4.addresses).address; + + ospfConf = '' + interface eth2 + ip ospf hello-interval 1 + ip ospf dead-interval 5 + ! + router ospf + network 192.168.0.0/16 area 0 + ''; + + in + { + name = "quagga"; + + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ tavyc ]; + }; + + nodes = { + + client = + { nodes, ... }: + { + virtualisation.vlans = [ 1 ]; + networking.defaultGateway = ifAddr nodes.router1 "eth1"; + }; + + router1 = + { ... }: + { + virtualisation.vlans = [ 1 2 ]; + boot.kernel.sysctl."net.ipv4.ip_forward" = "1"; + networking.firewall.extraCommands = "iptables -A nixos-fw -i eth2 -p ospf -j ACCEPT"; + services.quagga.ospf = { + enable = true; + config = ospfConf; + }; + }; + + router2 = + { ... }: + { + virtualisation.vlans = [ 3 2 ]; + boot.kernel.sysctl."net.ipv4.ip_forward" = "1"; + networking.firewall.extraCommands = "iptables -A nixos-fw -i eth2 -p ospf -j ACCEPT"; + services.quagga.ospf = { + enable = true; + config = ospfConf; + }; + }; + + server = + { nodes, ... }: + { + virtualisation.vlans = [ 3 ]; + networking.defaultGateway = ifAddr nodes.router2 "eth1"; + networking.firewall.allowedTCPPorts = [ 80 ]; + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.com"; + }; + }; + + testScript = + { ... }: + '' + startAll; + + # Wait for the networking to start on all machines + $_->waitForUnit("network.target") foreach values %vms; + + # Wait for OSPF to form adjacencies + for my $gw ($router1, $router2) { + $gw->waitForUnit("ospfd"); + $gw->waitUntilSucceeds("vtysh -c 'show ip ospf neighbor' | grep Full"); + $gw->waitUntilSucceeds("vtysh -c 'show ip route' | grep '^O>'"); + } + + # Test ICMP. + $client->succeed("ping -c 3 server >&2"); + + # Test whether HTTP works. + $server->waitForUnit("httpd"); + $client->succeed("curl --fail http://server/ >&2"); + ''; + }) diff --git a/nixpkgs/nixos/tests/quake3.nix b/nixpkgs/nixos/tests/quake3.nix new file mode 100644 index 000000000000..75c82cca63f5 --- /dev/null +++ b/nixpkgs/nixos/tests/quake3.nix @@ -0,0 +1,95 @@ +import ./make-test.nix ({ pkgs, ...} : + +let + + # Build Quake with coverage instrumentation. + overrides = pkgs: + rec { + quake3game = pkgs.quake3game.override (args: { + stdenv = pkgs.stdenvAdapters.addCoverageInstrumentation args.stdenv; + }); + }; + + # Only allow the demo data to be used (only if it's unfreeRedistributable). + unfreePredicate = pkg: with pkgs.lib; let + allowDrvPredicates = [ "quake3-demo" "quake3-pointrelease" ]; + allowLicenses = [ pkgs.lib.licenses.unfreeRedistributable ]; + in any (flip hasPrefix pkg.name) allowDrvPredicates && + elem (pkg.meta.license or null) allowLicenses; + +in + +rec { + name = "quake3"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ domenkozar eelco chaoflow ]; + }; + + # TODO: lcov doesn't work atm + #makeCoverageReport = true; + + client = + { pkgs, ... }: + + { imports = [ ./common/x11.nix ]; + hardware.opengl.driSupport = true; + environment.systemPackages = [ pkgs.quake3demo ]; + nixpkgs.config.packageOverrides = overrides; + nixpkgs.config.allowUnfreePredicate = unfreePredicate; + }; + + nodes = + { server = + { pkgs, ... }: + + { systemd.services."quake3-server" = + { wantedBy = [ "multi-user.target" ]; + script = + "${pkgs.quake3demo}/bin/quake3-server +set g_gametype 0 " + + "+map q3dm7 +addbot grunt +addbot daemia 2> /tmp/log"; + }; + nixpkgs.config.packageOverrides = overrides; + nixpkgs.config.allowUnfreePredicate = unfreePredicate; + networking.firewall.allowedUDPPorts = [ 27960 ]; + }; + + client1 = client; + client2 = client; + }; + + testScript = + '' + startAll; + + $server->waitForUnit("quake3-server"); + $client1->waitForX; + $client2->waitForX; + + $client1->execute("quake3 +set r_fullscreen 0 +set name Foo +connect server &"); + $client2->execute("quake3 +set r_fullscreen 0 +set name Bar +connect server &"); + + $server->waitUntilSucceeds("grep -q 'Foo.*entered the game' /tmp/log"); + $server->waitUntilSucceeds("grep -q 'Bar.*entered the game' /tmp/log"); + + $server->sleep(10); # wait for a while to get a nice screenshot + + $client1->block(); + + $server->sleep(20); + + $client1->screenshot("screen1"); + $client2->screenshot("screen2"); + + $client1->unblock(); + + $server->sleep(10); + + $client1->screenshot("screen3"); + $client2->screenshot("screen4"); + + $client1->shutdown(); + $client2->shutdown(); + $server->stopJob("quake3-server"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/rabbitmq.nix b/nixpkgs/nixos/tests/rabbitmq.nix new file mode 100644 index 000000000000..34ab05787867 --- /dev/null +++ b/nixpkgs/nixos/tests/rabbitmq.nix @@ -0,0 +1,21 @@ +# This test runs rabbitmq and checks if rabbitmq is up and running. + +import ./make-test.nix ({ pkgs, ... }: { + name = "rabbitmq"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow offline ]; + }; + + nodes = { + one = { ... }: { + services.rabbitmq.enable = true; + }; + }; + + testScript = '' + startAll; + + $one->waitForUnit("rabbitmq.service"); + $one->waitUntilSucceeds("su -s ${pkgs.stdenv.shell} rabbitmq -c \"rabbitmqctl status\""); + ''; +}) diff --git a/nixpkgs/nixos/tests/radarr.nix b/nixpkgs/nixos/tests/radarr.nix new file mode 100644 index 000000000000..6b9a909e44b5 --- /dev/null +++ b/nixpkgs/nixos/tests/radarr.nix @@ -0,0 +1,18 @@ +import ./make-test.nix ({ lib, ... }: + +with lib; + +rec { + name = "radarr"; + meta.maintainers = with maintainers; [ etu ]; + + nodes.machine = + { pkgs, ... }: + { services.radarr.enable = true; }; + + testScript = '' + $machine->waitForUnit('radarr.service'); + $machine->waitForOpenPort('7878'); + $machine->succeed("curl --fail http://localhost:7878/"); + ''; +}) diff --git a/nixpkgs/nixos/tests/radicale.nix b/nixpkgs/nixos/tests/radicale.nix new file mode 100644 index 000000000000..bf22fc9291af --- /dev/null +++ b/nixpkgs/nixos/tests/radicale.nix @@ -0,0 +1,106 @@ +let + user = "someuser"; + password = "some_password"; + port = builtins.toString 5232; + + common = { pkgs, ... }: { + services.radicale = { + enable = true; + config = '' + [auth] + type = htpasswd + htpasswd_filename = /etc/radicale/htpasswd + htpasswd_encryption = bcrypt + + [storage] + filesystem_folder = /tmp/collections + + [logging] + debug = True + ''; + }; + # WARNING: DON'T DO THIS IN PRODUCTION! + # This puts unhashed secrets directly into the Nix store for ease of testing. + environment.etc."radicale/htpasswd".source = pkgs.runCommand "htpasswd" {} '' + ${pkgs.apacheHttpd}/bin/htpasswd -bcB "$out" ${user} ${password} + ''; + }; + +in + + import ./make-test.nix ({ lib, ... }@args: { + name = "radicale"; + meta.maintainers = with lib.maintainers; [ aneeshusa infinisil ]; + + nodes = rec { + radicale = radicale1; # Make the test script read more nicely + radicale1 = lib.recursiveUpdate (common args) { + nixpkgs.overlays = [ + (self: super: { + radicale1 = super.radicale1.overrideAttrs (oldAttrs: { + propagatedBuildInputs = with self.pythonPackages; + (oldAttrs.propagatedBuildInputs or []) ++ [ passlib ]; + }); + }) + ]; + system.stateVersion = "17.03"; + }; + radicale1_export = lib.recursiveUpdate radicale1 { + services.radicale.extraArgs = [ + "--export-storage" "/tmp/collections-new" + ]; + }; + radicale2_verify = lib.recursiveUpdate radicale2 { + services.radicale.extraArgs = [ "--verify-storage" ]; + }; + radicale2 = lib.recursiveUpdate (common args) { + system.stateVersion = "17.09"; + }; + }; + + # This tests whether the web interface is accessible to an authenticated user + testScript = { nodes }: let + switchToConfig = nodeName: let + newSystem = nodes.${nodeName}.config.system.build.toplevel; + in "${newSystem}/bin/switch-to-configuration test"; + in '' + # Check Radicale 1 functionality + $radicale->succeed('${switchToConfig "radicale1"} >&2'); + $radicale->waitForUnit('radicale.service'); + $radicale->waitForOpenPort(${port}); + $radicale->succeed('curl --fail http://${user}:${password}@localhost:${port}/someuser/calendar.ics/'); + + # Export data in Radicale 2 format + $radicale->succeed('systemctl stop radicale'); + $radicale->succeed('ls -al /tmp/collections'); + $radicale->fail('ls -al /tmp/collections-new'); + # Radicale exits immediately after exporting storage + $radicale->succeed('${switchToConfig "radicale1_export"} >&2'); + $radicale->waitUntilFails('systemctl status radicale'); + $radicale->succeed('ls -al /tmp/collections'); + $radicale->succeed('ls -al /tmp/collections-new'); + + # Verify data in Radicale 2 format + $radicale->succeed('rm -r /tmp/collections/${user}'); + $radicale->succeed('mv /tmp/collections-new/collection-root /tmp/collections'); + $radicale->succeed('${switchToConfig "radicale2_verify"} >&2'); + $radicale->waitUntilFails('systemctl status radicale'); + my ($retcode, $logs) = $radicale->execute('journalctl -u radicale -n 5'); + if ($retcode != 0 || index($logs, 'Verifying storage') == -1) { + die "Radicale 2 didn't verify storage" + } + if (index($logs, 'failed') != -1 || index($logs, 'exception') != -1) { + die "storage verification failed" + } + + # Check Radicale 2 functionality + $radicale->succeed('${switchToConfig "radicale2"} >&2'); + $radicale->waitForUnit('radicale.service'); + $radicale->waitForOpenPort(${port}); + my ($retcode, $output) = $radicale->execute('curl --fail http://${user}:${password}@localhost:${port}/someuser/calendar.ics/'); + if ($retcode != 0 || index($output, 'VCALENDAR') == -1) { + die "Could not read calendar from Radicale 2" + } + $radicale->succeed('curl --fail http://${user}:${password}@localhost:${port}/.web/'); + ''; +}) diff --git a/nixpkgs/nixos/tests/redmine.nix b/nixpkgs/nixos/tests/redmine.nix new file mode 100644 index 000000000000..ea72a0121d11 --- /dev/null +++ b/nixpkgs/nixos/tests/redmine.nix @@ -0,0 +1,58 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + redmineTest = package: makeTest { + machine = + { config, pkgs, ... }: + { services.mysql.enable = true; + services.mysql.package = pkgs.mariadb; + services.mysql.ensureDatabases = [ "redmine" ]; + services.mysql.ensureUsers = [ + { name = "redmine"; + ensurePermissions = { "redmine.*" = "ALL PRIVILEGES"; }; + } + ]; + + services.redmine.enable = true; + services.redmine.package = package; + services.redmine.database.socket = "/run/mysqld/mysqld.sock"; + services.redmine.plugins = { + redmine_env_auth = pkgs.fetchurl { + url = https://github.com/Intera/redmine_env_auth/archive/0.7.zip; + sha256 = "1xb8lyarc7mpi86yflnlgyllh9hfwb9z304f19dx409gqpia99sc"; + }; + }; + services.redmine.themes = { + dkuk-redmine_alex_skin = pkgs.fetchurl { + url = https://bitbucket.org/dkuk/redmine_alex_skin/get/1842ef675ef3.zip; + sha256 = "0hrin9lzyi50k4w2bd2b30vrf1i4fi1c0gyas5801wn8i7kpm9yl"; + }; + }; + }; + + testScript = '' + startAll; + + $machine->waitForUnit('redmine.service'); + $machine->waitForOpenPort('3000'); + $machine->succeed("curl --fail http://localhost:3000/"); + ''; + }; +in +{ + redmine_3 = redmineTest pkgs.redmine // { + name = "redmine_3"; + meta.maintainers = [ maintainers.aanderse ]; + }; + + redmine_4 = redmineTest pkgs.redmine_4 // { + name = "redmine_4"; + meta.maintainers = [ maintainers.aanderse ]; + }; +} diff --git a/nixpkgs/nixos/tests/riak.nix b/nixpkgs/nixos/tests/riak.nix new file mode 100644 index 000000000000..68a9b7315b35 --- /dev/null +++ b/nixpkgs/nixos/tests/riak.nix @@ -0,0 +1,21 @@ +import ./make-test.nix { + name = "riak"; + + nodes = { + master = + { pkgs, ... }: + + { + services.riak.enable = true; + services.riak.package = pkgs.riak; + }; + }; + + testScript = '' + startAll; + + $master->waitForUnit("riak"); + $master->sleep(20); # Hopefully this is long enough!! + $master->succeed("riak ping 2>&1"); + ''; +} diff --git a/nixpkgs/nixos/tests/roundcube.nix b/nixpkgs/nixos/tests/roundcube.nix new file mode 100644 index 000000000000..178134fd9b30 --- /dev/null +++ b/nixpkgs/nixos/tests/roundcube.nix @@ -0,0 +1,28 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "roundcube"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ globin ]; + }; + + nodes = { + roundcube = { config, pkgs, ... }: { + services.roundcube = { + enable = true; + hostName = "roundcube"; + database.password = "notproduction"; + }; + services.nginx.virtualHosts.roundcube = { + forceSSL = false; + enableACME = false; + }; + }; + }; + + testScript = '' + $roundcube->start; + $roundcube->waitForUnit("postgresql.service"); + $roundcube->waitForUnit("phpfpm-roundcube.service"); + $roundcube->waitForUnit("nginx.service"); + $roundcube->succeed("curl -sSfL http://roundcube/"); + ''; +}) diff --git a/nixpkgs/nixos/tests/rspamd.nix b/nixpkgs/nixos/tests/rspamd.nix new file mode 100644 index 000000000000..396cd5b67d81 --- /dev/null +++ b/nixpkgs/nixos/tests/rspamd.nix @@ -0,0 +1,255 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + initMachine = '' + startAll + $machine->waitForUnit("rspamd.service"); + $machine->succeed("id \"rspamd\" >/dev/null"); + ''; + checkSocket = socket: user: group: mode: '' + $machine->succeed("ls ${socket} >/dev/null"); + $machine->succeed("[[ \"\$(stat -c %U ${socket})\" == \"${user}\" ]]"); + $machine->succeed("[[ \"\$(stat -c %G ${socket})\" == \"${group}\" ]]"); + $machine->succeed("[[ \"\$(stat -c %a ${socket})\" == \"${mode}\" ]]"); + ''; + simple = name: enableIPv6: makeTest { + name = "rspamd-${name}"; + machine = { + services.rspamd.enable = true; + networking.enableIPv6 = enableIPv6; + }; + testScript = '' + startAll + $machine->waitForUnit("multi-user.target"); + $machine->waitForOpenPort(11334); + $machine->waitForUnit("rspamd.service"); + $machine->succeed("id \"rspamd\" >/dev/null"); + ${checkSocket "/run/rspamd/rspamd.sock" "rspamd" "rspamd" "660" } + sleep 10; + $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf")); + $machine->log($machine->succeed("grep 'CONFDIR/worker-controller.inc' /etc/rspamd/rspamd.conf")); + $machine->log($machine->succeed("grep 'CONFDIR/worker-normal.inc' /etc/rspamd/rspamd.conf")); + $machine->log($machine->succeed("systemctl cat rspamd.service")); + $machine->log($machine->succeed("curl http://localhost:11334/auth")); + $machine->log($machine->succeed("curl http://127.0.0.1:11334/auth")); + ${optionalString enableIPv6 '' + $machine->log($machine->succeed("curl http://[::1]:11334/auth")); + ''} + ''; + }; +in +{ + simple = simple "simple" true; + ipv4only = simple "ipv4only" false; + deprecated = makeTest { + name = "rspamd-deprecated"; + machine = { + services.rspamd = { + enable = true; + bindSocket = [ "/run/rspamd.sock mode=0600 user=root group=root" ]; + bindUISocket = [ "/run/rspamd-worker.sock mode=0666 user=root group=root" ]; + }; + }; + + testScript = '' + ${initMachine} + $machine->waitForFile("/run/rspamd.sock"); + ${checkSocket "/run/rspamd.sock" "root" "root" "600" } + ${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" } + $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf")); + $machine->log($machine->succeed("grep 'CONFDIR/worker-controller.inc' /etc/rspamd/rspamd.conf")); + $machine->log($machine->succeed("grep 'CONFDIR/worker-normal.inc' /etc/rspamd/rspamd.conf")); + $machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat")); + $machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping")); + ''; + }; + + bindports = makeTest { + name = "rspamd-bindports"; + machine = { + services.rspamd = { + enable = true; + workers.normal.bindSockets = [{ + socket = "/run/rspamd.sock"; + mode = "0600"; + owner = "root"; + group = "root"; + }]; + workers.controller.bindSockets = [{ + socket = "/run/rspamd-worker.sock"; + mode = "0666"; + owner = "root"; + group = "root"; + }]; + workers.controller2 = { + type = "controller"; + bindSockets = [ "0.0.0.0:11335" ]; + extraConfig = '' + static_dir = "''${WWWDIR}"; + secure_ip = null; + password = "verysecretpassword"; + ''; + }; + }; + }; + + testScript = '' + ${initMachine} + $machine->waitForFile("/run/rspamd.sock"); + ${checkSocket "/run/rspamd.sock" "root" "root" "600" } + ${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" } + $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf")); + $machine->log($machine->succeed("grep 'CONFDIR/worker-controller.inc' /etc/rspamd/rspamd.conf")); + $machine->log($machine->succeed("grep 'CONFDIR/worker-normal.inc' /etc/rspamd/rspamd.conf")); + $machine->log($machine->succeed("grep 'LOCAL_CONFDIR/override.d/worker-controller2.inc' /etc/rspamd/rspamd.conf")); + $machine->log($machine->succeed("grep 'verysecretpassword' /etc/rspamd/override.d/worker-controller2.inc")); + $machine->waitUntilSucceeds("journalctl -u rspamd | grep -i 'starting controller process' >&2"); + $machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat")); + $machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping")); + $machine->log($machine->succeed("curl http://localhost:11335/ping")); + ''; + }; + customLuaRules = makeTest { + name = "rspamd-custom-lua-rules"; + machine = { + environment.etc."tests/no-muh.eml".text = '' + From: Sheep1<bah@example.com> + To: Sheep2<mah@example.com> + Subject: Evil cows + + I find cows to be evil don't you? + ''; + environment.etc."tests/muh.eml".text = '' + From: Cow<cow@example.com> + To: Sheep2<mah@example.com> + Subject: Evil cows + + Cows are majestic creatures don't Muh agree? + ''; + services.rspamd = { + enable = true; + locals = { + "antivirus.conf" = mkIf false { text = '' + clamav { + action = "reject"; + symbol = "CLAM_VIRUS"; + type = "clamav"; + log_clean = true; + servers = "/run/clamav/clamd.ctl"; + } + '';}; + "redis.conf" = { + enable = false; + text = '' + servers = "127.0.0.1"; + ''; + }; + "groups.conf".text = '' + group "cows" { + symbol { + NO_MUH = { + weight = 1.0; + description = "Mails should not muh"; + } + } + } + ''; + }; + localLuaRules = pkgs.writeText "rspamd.local.lua" '' + local rspamd_logger = require "rspamd_logger" + rspamd_config.NO_MUH = { + callback = function (task) + local parts = task:get_text_parts() + if parts then + for _,part in ipairs(parts) do + local content = tostring(part:get_content()) + rspamd_logger.infox(rspamd_config, 'Found content %s', content) + local found = string.find(content, "Muh"); + rspamd_logger.infox(rspamd_config, 'Found muh %s', tostring(found)) + if found then + return true + end + end + end + return false + end, + score = 5.0, + description = 'Allow no cows', + group = "cows", + } + rspamd_logger.infox(rspamd_config, 'Work dammit!!!') + ''; + }; + }; + testScript = '' + ${initMachine} + $machine->waitForOpenPort(11334); + $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf")); + $machine->log($machine->succeed("cat /etc/rspamd/rspamd.local.lua")); + $machine->log($machine->succeed("cat /etc/rspamd/local.d/groups.conf")); + # Verify that redis.conf was not written + $machine->fail("cat /etc/rspamd/local.d/redis.conf >&2"); + # Verify that antivirus.conf was not written + $machine->fail("cat /etc/rspamd/local.d/antivirus.conf >&2"); + ${checkSocket "/run/rspamd/rspamd.sock" "rspamd" "rspamd" "660" } + $machine->log($machine->succeed("curl --unix-socket /run/rspamd/rspamd.sock http://localhost/ping")); + $machine->log($machine->succeed("rspamc -h 127.0.0.1:11334 stat")); + $machine->log($machine->succeed("cat /etc/tests/no-muh.eml | rspamc -h 127.0.0.1:11334")); + $machine->log($machine->succeed("cat /etc/tests/muh.eml | rspamc -h 127.0.0.1:11334 symbols")); + $machine->waitUntilSucceeds("journalctl -u rspamd | grep -i muh >&2"); + $machine->log($machine->fail("cat /etc/tests/no-muh.eml | rspamc -h 127.0.0.1:11334 symbols | grep NO_MUH")); + $machine->log($machine->succeed("cat /etc/tests/muh.eml | rspamc -h 127.0.0.1:11334 symbols | grep NO_MUH")); + ''; + }; + postfixIntegration = makeTest { + name = "rspamd-postfix-integration"; + machine = { + environment.systemPackages = with pkgs; [ msmtp ]; + environment.etc."tests/gtube.eml".text = '' + From: Sheep1<bah@example.com> + To: Sheep2<tester@example.com> + Subject: Evil cows + + I find cows to be evil don't you? + + XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X + ''; + environment.etc."tests/example.eml".text = '' + From: Sheep1<bah@example.com> + To: Sheep2<tester@example.com> + Subject: Evil cows + + I find cows to be evil don't you? + ''; + users.users.tester.password = "test"; + services.postfix = { + enable = true; + destination = ["example.com"]; + }; + services.rspamd = { + enable = true; + postfix.enable = true; + workers.rspamd_proxy.type = "proxy"; + }; + }; + testScript = '' + ${initMachine} + $machine->waitForOpenPort(11334); + $machine->waitForOpenPort(25); + ${checkSocket "/run/rspamd/rspamd-milter.sock" "rspamd" "postfix" "660" } + $machine->log($machine->succeed("rspamc -h 127.0.0.1:11334 stat")); + $machine->log($machine->succeed("msmtp --host=localhost -t --read-envelope-from < /etc/tests/example.eml")); + $machine->log($machine->fail("msmtp --host=localhost -t --read-envelope-from < /etc/tests/gtube.eml")); + + $machine->waitUntilFails('[ "$(postqueue -p)" != "Mail queue is empty" ]'); + $machine->fail("journalctl -u postfix | grep -i error >&2"); + $machine->fail("journalctl -u postfix | grep -i warning >&2"); + ''; + }; +} diff --git a/nixpkgs/nixos/tests/rss2email.nix b/nixpkgs/nixos/tests/rss2email.nix new file mode 100644 index 000000000000..492d47da9f56 --- /dev/null +++ b/nixpkgs/nixos/tests/rss2email.nix @@ -0,0 +1,66 @@ +import ./make-test.nix { + name = "opensmtpd"; + + nodes = { + server = { pkgs, ... }: { + imports = [ common/user-account.nix ]; + services.nginx = { + enable = true; + virtualHosts."127.0.0.1".root = ./common/webroot; + }; + services.rss2email = { + enable = true; + to = "alice@localhost"; + interval = "1"; + config.from = "test@example.org"; + feeds = { + nixos = { url = "http://127.0.0.1/news-rss.xml"; }; + }; + }; + services.opensmtpd = { + enable = true; + extraServerArgs = [ "-v" ]; + serverConfiguration = '' + listen on 127.0.0.1 + action dovecot_deliver mda \ + "${pkgs.dovecot}/libexec/dovecot/deliver -d %{user.username}" + match from any for local action dovecot_deliver + ''; + }; + services.dovecot2 = { + enable = true; + enableImap = true; + mailLocation = "maildir:~/mail"; + protocols = [ "imap" ]; + }; + environment.systemPackages = let + checkMailLanded = pkgs.writeScriptBin "check-mail-landed" '' + #!${pkgs.python3.interpreter} + import imaplib + + with imaplib.IMAP4('127.0.0.1', 143) as imap: + imap.login('alice', 'foobar') + imap.select() + status, refs = imap.search(None, 'ALL') + print("=====> Result of search for all:", status, refs) + assert status == 'OK' + assert len(refs) > 0 + status, msg = imap.fetch(refs[0], 'BODY[TEXT]') + assert status == 'OK' + ''; + in [ pkgs.opensmtpd checkMailLanded ]; + }; + }; + + testScript = '' + startAll; + + $server->waitForUnit("network-online.target"); + $server->waitForUnit("opensmtpd"); + $server->waitForUnit("dovecot2"); + $server->waitForUnit("nginx"); + $server->waitForUnit("rss2email"); + + $server->waitUntilSucceeds('check-mail-landed >&2'); + ''; +} diff --git a/nixpkgs/nixos/tests/rsyslogd.nix b/nixpkgs/nixos/tests/rsyslogd.nix new file mode 100644 index 000000000000..f17e61814c5e --- /dev/null +++ b/nixpkgs/nixos/tests/rsyslogd.nix @@ -0,0 +1,42 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +{ + test1 = makeTest { + name = "rsyslogd-test1"; + meta.maintainers = [ maintainers.aanderse ]; + + machine = + { config, pkgs, ... }: + { services.rsyslogd.enable = true; + services.journald.forwardToSyslog = false; + }; + + # ensure rsyslogd isn't receiving messages from journald if explicitly disabled + testScript = '' + $machine->waitForUnit("default.target"); + $machine->fail("test -f /var/log/messages"); + ''; + }; + + test2 = makeTest { + name = "rsyslogd-test2"; + meta.maintainers = [ maintainers.aanderse ]; + + machine = + { config, pkgs, ... }: + { services.rsyslogd.enable = true; + }; + + # ensure rsyslogd is receiving messages from journald + testScript = '' + $machine->waitForUnit("default.target"); + $machine->succeed("test -f /var/log/messages"); + ''; + }; +} diff --git a/nixpkgs/nixos/tests/run-in-machine.nix b/nixpkgs/nixos/tests/run-in-machine.nix new file mode 100644 index 000000000000..339a4b9a7404 --- /dev/null +++ b/nixpkgs/nixos/tests/run-in-machine.nix @@ -0,0 +1,23 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; + +let + output = runInMachine { + drv = pkgs.hello; + machine = { ... }: { /* services.sshd.enable = true; */ }; + }; + + test = pkgs.runCommand "verify-output" { inherit output; } '' + if [ ! -e "$output/bin/hello" ]; then + echo "Derivation built using runInMachine produced incorrect output:" >&2 + ls -laR "$output" >&2 + exit 1 + fi + "$output/bin/hello" > "$out" + ''; + +in test // { inherit test; } # To emulate behaviour of makeTest diff --git a/nixpkgs/nixos/tests/rxe.nix b/nixpkgs/nixos/tests/rxe.nix new file mode 100644 index 000000000000..d0b53db8eeb6 --- /dev/null +++ b/nixpkgs/nixos/tests/rxe.nix @@ -0,0 +1,53 @@ +import ./make-test.nix ({ ... } : + +let + node = { pkgs, ... } : { + networking = { + firewall = { + allowedUDPPorts = [ 4791 ]; # open RoCE port + allowedTCPPorts = [ 4800 ]; # port for test utils + }; + rxe = { + enable = true; + interfaces = [ "eth1" ]; + }; + }; + + environment.systemPackages = with pkgs; [ rdma-core screen ]; + }; + +in { + name = "rxe"; + + nodes = { + server = node; + client = node; + }; + + testScript = '' + # Test if rxe interface comes up + $server->waitForUnit("default.target"); + $server->succeed("systemctl status rxe.service"); + $server->succeed("ibv_devices | grep rxe0"); + + $client->waitForUnit("default.target"); + + # ping pong test + $server->succeed("screen -dmS rc_pingpong ibv_rc_pingpong -p 4800 -g0"); + $client->succeed("sleep 2; ibv_rc_pingpong -p 4800 -g0 server"); + + $server->succeed("screen -dmS uc_pingpong ibv_uc_pingpong -p 4800 -g0"); + $client->succeed("sleep 2; ibv_uc_pingpong -p 4800 -g0 server"); + + $server->succeed("screen -dmS ud_pingpong ibv_ud_pingpong -p 4800 -s 1024 -g0"); + $client->succeed("sleep 2; ibv_ud_pingpong -p 4800 -s 1024 -g0 server"); + + $server->succeed("screen -dmS srq_pingpong ibv_srq_pingpong -p 4800 -g0"); + $client->succeed("sleep 2; ibv_srq_pingpong -p 4800 -g0 server"); + + $server->succeed("screen -dmS rping rping -s -a server -C 10"); + $client->succeed("sleep 2; rping -c -a server -C 10"); + ''; +}) + + diff --git a/nixpkgs/nixos/tests/samba.nix b/nixpkgs/nixos/tests/samba.nix new file mode 100644 index 000000000000..2802e00a5b1a --- /dev/null +++ b/nixpkgs/nixos/tests/samba.nix @@ -0,0 +1,47 @@ +import ./make-test.nix ({ pkgs, ... }: + +{ + name = "samba"; + + meta.maintainers = [ pkgs.lib.maintainers.eelco ]; + + nodes = + { client = + { pkgs, ... }: + { fileSystems = pkgs.lib.mkVMOverride + { "/public" = { + fsType = "cifs"; + device = "//server/public"; + options = [ "guest" ]; + }; + }; + }; + + server = + { ... }: + { services.samba.enable = true; + services.samba.shares.public = + { path = "/public"; + "read only" = true; + browseable = "yes"; + "guest ok" = "yes"; + comment = "Public samba share."; + }; + networking.firewall.allowedTCPPorts = [ 139 445 ]; + networking.firewall.allowedUDPPorts = [ 137 138 ]; + }; + }; + + # client# [ 4.542997] mount[777]: sh: systemd-ask-password: command not found + + testScript = + '' + $server->start; + $server->waitForUnit("samba.target"); + $server->succeed("mkdir -p /public; echo bar > /public/foo"); + + $client->start; + $client->waitForUnit("remote-fs.target"); + $client->succeed("[[ \$(cat /public/foo) = bar ]]"); + ''; +}) diff --git a/nixpkgs/nixos/tests/sddm.nix b/nixpkgs/nixos/tests/sddm.nix new file mode 100644 index 000000000000..678bcbeab20a --- /dev/null +++ b/nixpkgs/nixos/tests/sddm.nix @@ -0,0 +1,69 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; + +let + inherit (pkgs) lib; + + tests = { + default = { + name = "sddm"; + + machine = { ... }: { + imports = [ ./common/user-account.nix ]; + services.xserver.enable = true; + services.xserver.displayManager.sddm.enable = true; + services.xserver.windowManager.default = "icewm"; + services.xserver.windowManager.icewm.enable = true; + services.xserver.desktopManager.default = "none"; + }; + + enableOCR = true; + + testScript = { nodes, ... }: let + user = nodes.machine.config.users.users.alice; + in '' + startAll; + $machine->waitForText(qr/select your user/i); + $machine->screenshot("sddm"); + $machine->sendChars("${user.password}\n"); + $machine->waitForFile("/home/alice/.Xauthority"); + $machine->succeed("xauth merge ~alice/.Xauthority"); + $machine->waitForWindow("^IceWM "); + ''; + }; + + autoLogin = { + name = "sddm-autologin"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ttuegel ]; + }; + + machine = { ... }: { + imports = [ ./common/user-account.nix ]; + services.xserver.enable = true; + services.xserver.displayManager.sddm = { + enable = true; + autoLogin = { + enable = true; + user = "alice"; + }; + }; + services.xserver.windowManager.default = "icewm"; + services.xserver.windowManager.icewm.enable = true; + services.xserver.desktopManager.default = "none"; + }; + + testScript = { ... }: '' + startAll; + $machine->waitForFile("/home/alice/.Xauthority"); + $machine->succeed("xauth merge ~alice/.Xauthority"); + $machine->waitForWindow("^IceWM "); + ''; + }; + }; +in + lib.mapAttrs (lib.const makeTest) tests diff --git a/nixpkgs/nixos/tests/simple.nix b/nixpkgs/nixos/tests/simple.nix new file mode 100644 index 000000000000..84c5621d962f --- /dev/null +++ b/nixpkgs/nixos/tests/simple.nix @@ -0,0 +1,17 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "simple"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco ]; + }; + + machine = { ... }: { + imports = [ ../modules/profiles/minimal.nix ]; + }; + + testScript = + '' + startAll; + $machine->waitForUnit("multi-user.target"); + $machine->shutdown; + ''; +}) diff --git a/nixpkgs/nixos/tests/slim.nix b/nixpkgs/nixos/tests/slim.nix new file mode 100644 index 000000000000..42c87dfa039d --- /dev/null +++ b/nixpkgs/nixos/tests/slim.nix @@ -0,0 +1,66 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "slim"; + + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aszlig ]; + }; + + machine = { pkgs, ... }: { + imports = [ ./common/user-account.nix ]; + services.xserver.enable = true; + services.xserver.windowManager.default = "icewm"; + services.xserver.windowManager.icewm.enable = true; + services.xserver.desktopManager.default = "none"; + services.xserver.displayManager.slim = { + enable = true; + + # Use a custom theme in order to get best OCR results + theme = pkgs.runCommand "slim-theme-ocr" { + nativeBuildInputs = [ pkgs.imagemagick ]; + } '' + mkdir "$out" + convert -size 1x1 xc:white "$out/background.jpg" + convert -size 200x100 xc:white "$out/panel.jpg" + cat > "$out/slim.theme" <<EOF + background_color #ffffff + background_style tile + + input_fgcolor #000000 + msg_color #000000 + + session_color #000000 + session_font Verdana:size=16:bold + + username_msg Username: + username_font Verdana:size=16:bold + username_color #000000 + username_x 50% + username_y 40% + + password_msg Password: + password_x 50% + password_y 40% + EOF + ''; + }; + }; + + enableOCR = true; + + testScript = { nodes, ... }: let + user = nodes.machine.config.users.users.alice; + in '' + startAll; + $machine->waitForText(qr/Username:/); + $machine->sendChars("${user.name}\n"); + $machine->waitForText(qr/Password:/); + $machine->sendChars("${user.password}\n"); + + $machine->waitForFile('${user.home}/.Xauthority'); + $machine->succeed('xauth merge ${user.home}/.Xauthority'); + $machine->waitForWindow('^IceWM '); + + # Make sure SLiM doesn't create a log file + $machine->fail('test -e /var/log/slim.log'); + ''; +}) diff --git a/nixpkgs/nixos/tests/slurm.nix b/nixpkgs/nixos/tests/slurm.nix new file mode 100644 index 000000000000..b4458d8d0954 --- /dev/null +++ b/nixpkgs/nixos/tests/slurm.nix @@ -0,0 +1,139 @@ +import ./make-test.nix ({ lib, ... }: +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 + ''; + }; +in { + name = "slurm"; + + meta.maintainers = [ lib.maintainers.markuskowa ]; + + nodes = + let + computeNode = + { ...}: + { + # 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; + services.slurm = { + server.enable = true; + } // slurmconfig; + }; + + submit = + { ...}: + { + networking.firewall.enable = false; + services.slurm = { + enableStools = true; + } // slurmconfig; + }; + + dbd = + { pkgs, ... } : + { + networking.firewall.enable = false; + services.slurm.dbdserver = { + enable = true; + }; + services.mysql = { + enable = true; + package = pkgs.mysql; + ensureDatabases = [ "slurm_acct_db" ]; + ensureUsers = [{ + ensurePermissions = { "slurm_acct_db.*" = "ALL PRIVILEGES"; }; + name = "slurm"; + }]; + extraOptions = '' + # recommendations from: https://slurm.schedmd.com/accounting.html#mysql-configuration + innodb_buffer_pool_size=1024M + innodb_log_file_size=64M + innodb_lock_wait_timeout=900 + ''; + }; + }; + + node1 = computeNode; + node2 = computeNode; + node3 = computeNode; + }; + + + testScript = + '' + startAll; + + # Set up authentification across the cluster + foreach my $node (($submit,$control,$dbd,$node1,$node2,$node3)) + { + $node->waitForUnit("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->waitForUnit("munged"); + }; + + # Restart the services since they have probably failed due to the munge init + # failure + subtest "can_start_slurmdbd", sub { + $dbd->succeed("systemctl restart slurmdbd"); + $dbd->waitForUnit("slurmdbd.service"); + $dbd->waitForOpenPort(6819); + }; + + # there needs to be an entry for the current + # cluster in the database before slurmctld is restarted + subtest "add_account", sub { + $control->succeed("sacctmgr -i add cluster default"); + }; + + subtest "can_start_slurmctld", sub { + $control->succeed("systemctl restart slurmctld"); + $control->waitForUnit("slurmctld.service"); + }; + + subtest "can_start_slurmd", sub { + foreach my $node (($node1,$node2,$node3)) + { + $node->succeed("systemctl restart slurmd.service"); + $node->waitForUnit("slurmd"); + } + }; + + # Test that the cluster works and can distribute jobs; + + subtest "run_distributed_command", sub { + # Run `hostname` on 3 nodes of the partition (so on all the 3 nodes). + # The output must contain the 3 different names + $submit->succeed("srun -N 3 hostname | sort | uniq | wc -l | xargs test 3 -eq"); + }; + + subtest "check_slurm_dbd", sub { + # find the srun job from above in the database + $submit->succeed("sacct | grep hostname"); + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/smokeping.nix b/nixpkgs/nixos/tests/smokeping.nix new file mode 100644 index 000000000000..07d228051127 --- /dev/null +++ b/nixpkgs/nixos/tests/smokeping.nix @@ -0,0 +1,33 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "smokeping"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ cransom ]; + }; + + nodes = { + sm = + { ... }: + { + services.smokeping = { + enable = true; + port = 8081; + mailHost = "127.0.0.2"; + probeConfig = '' + + FPing + binary = /run/wrappers/bin/fping + offset = 0% + ''; + }; + }; + }; + + testScript = '' + startAll; + $sm->waitForUnit("smokeping"); + $sm->waitForUnit("thttpd"); + $sm->waitForFile("/var/lib/smokeping/data/Local/LocalMachine.rrd"); + $sm->succeed("curl -s -f localhost:8081/smokeping.fcgi?target=Local"); + $sm->succeed("ls /var/lib/smokeping/cache/Local/LocalMachine_mini.png"); + $sm->succeed("ls /var/lib/smokeping/cache/index.html"); + ''; +}) diff --git a/nixpkgs/nixos/tests/snapper.nix b/nixpkgs/nixos/tests/snapper.nix new file mode 100644 index 000000000000..74ec22fd3499 --- /dev/null +++ b/nixpkgs/nixos/tests/snapper.nix @@ -0,0 +1,43 @@ +import ./make-test.nix ({ ... }: +{ + name = "snapper"; + + machine = { pkgs, lib, ... }: { + boot.initrd.postDeviceCommands = '' + ${pkgs.btrfs-progs}/bin/mkfs.btrfs -f -L aux /dev/vdb + ''; + + virtualisation.emptyDiskImages = [ 4096 ]; + + fileSystems = lib.mkVMOverride { + "/home" = { + device = "/dev/disk/by-label/aux"; + fsType = "btrfs"; + }; + }; + services.snapper.configs.home.subvolume = "/home"; + services.snapper.filters = "/nix"; + }; + + testScript = '' + $machine->succeed("btrfs subvolume create /home/.snapshots"); + + $machine->succeed("snapper -c home list"); + + $machine->succeed("snapper -c home create --description empty"); + + $machine->succeed("echo test > /home/file"); + $machine->succeed("snapper -c home create --description file"); + + $machine->succeed("snapper -c home status 1..2"); + + $machine->succeed("snapper -c home undochange 1..2"); + $machine->fail("ls /home/file"); + + $machine->succeed("snapper -c home delete 2"); + + $machine->succeed("systemctl --wait start snapper-timeline.service"); + + $machine->succeed("systemctl --wait start snapper-cleanup.service"); + ''; +}) diff --git a/nixpkgs/nixos/tests/solr.nix b/nixpkgs/nixos/tests/solr.nix new file mode 100644 index 000000000000..9ba3863411ea --- /dev/null +++ b/nixpkgs/nixos/tests/solr.nix @@ -0,0 +1,47 @@ +import ./make-test.nix ({ pkgs, lib, ... }: +{ + name = "solr"; + meta.maintainers = [ lib.maintainers.aanderse ]; + + machine = + { config, pkgs, ... }: + { + # Ensure the virtual machine has enough memory for Solr to avoid the following error: + # + # OpenJDK 64-Bit Server VM warning: + # INFO: os::commit_memory(0x00000000e8000000, 402653184, 0) + # failed; error='Cannot allocate memory' (errno=12) + # + # There is insufficient memory for the Java Runtime Environment to continue. + # Native memory allocation (mmap) failed to map 402653184 bytes for committing reserved memory. + virtualisation.memorySize = 2000; + + services.solr.enable = true; + }; + + testScript = '' + startAll; + + $machine->waitForUnit('solr.service'); + $machine->waitForOpenPort('8983'); + $machine->succeed('curl --fail http://localhost:8983/solr/'); + + # adapted from pkgs.solr/examples/films/README.txt + $machine->succeed('sudo -u solr solr create -c films'); + $machine->succeed(q(curl http://localhost:8983/solr/films/schema -X POST -H 'Content-type:application/json' --data-binary '{ + "add-field" : { + "name":"name", + "type":"text_general", + "multiValued":false, + "stored":true + }, + "add-field" : { + "name":"initial_release_date", + "type":"pdate", + "stored":true + } + }')) =~ /"status":0/ or die; + $machine->succeed('sudo -u solr post -c films ${pkgs.solr}/example/films/films.json'); + $machine->succeed('curl http://localhost:8983/solr/films/query?q=name:batman') =~ /"name":"Batman Begins"/ or die; + ''; +}) diff --git a/nixpkgs/nixos/tests/sonarr.nix b/nixpkgs/nixos/tests/sonarr.nix new file mode 100644 index 000000000000..3d5c3b19b6ea --- /dev/null +++ b/nixpkgs/nixos/tests/sonarr.nix @@ -0,0 +1,18 @@ +import ./make-test.nix ({ lib, ... }: + +with lib; + +rec { + name = "sonarr"; + meta.maintainers = with maintainers; [ etu ]; + + nodes.machine = + { pkgs, ... }: + { services.sonarr.enable = true; }; + + testScript = '' + $machine->waitForUnit('sonarr.service'); + $machine->waitForOpenPort('8989'); + $machine->succeed("curl --fail http://localhost:8989/"); + ''; +}) diff --git a/nixpkgs/nixos/tests/ssh-keys.nix b/nixpkgs/nixos/tests/ssh-keys.nix new file mode 100644 index 000000000000..07d422196efa --- /dev/null +++ b/nixpkgs/nixos/tests/ssh-keys.nix @@ -0,0 +1,15 @@ +pkgs: +{ snakeOilPrivateKey = pkgs.writeText "privkey.snakeoil" '' + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEIHQf/khLvYrQ8IOika5yqtWvI0oquHlpRLTZiJy5dRJmoAoGCCqGSM49 + AwEHoUQDQgAEKF0DYGbBwbj06tA3fd/+yP44cvmwmHBWXZCKbS+RQlAKvLXMWkpN + r1lwMyJZoSGgBHoUahoYjTh9/sJL7XLJtA== + -----END EC PRIVATE KEY----- + ''; + + snakeOilPublicKey = pkgs.lib.concatStrings [ + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHA" + "yNTYAAABBBChdA2BmwcG49OrQN33f/sj+OHL5sJhwVl2Qim0vkUJQCry1zFpKTa" + "9ZcDMiWaEhoAR6FGoaGI04ff7CS+1yybQ= sakeoil" + ]; +} diff --git a/nixpkgs/nixos/tests/strongswan-swanctl.nix b/nixpkgs/nixos/tests/strongswan-swanctl.nix new file mode 100644 index 000000000000..8bbebd423003 --- /dev/null +++ b/nixpkgs/nixos/tests/strongswan-swanctl.nix @@ -0,0 +1,148 @@ +# This strongswan-swanctl test is based on: +# https://www.strongswan.org/testing/testresults/swanctl/rw-psk-ipv4/index.html +# https://github.com/strongswan/strongswan/tree/master/testing/tests/swanctl/rw-psk-ipv4 +# +# The roadwarrior carol sets up a connection to gateway moon. The authentication +# is based on pre-shared keys and IPv4 addresses. Upon the successful +# establishment of the IPsec tunnels, the specified updown script automatically +# inserts iptables-based firewall rules that let pass the tunneled traffic. In +# order to test both tunnel and firewall, carol pings the client alice behind +# the gateway moon. +# +# alice moon carol +# eth1------vlan_0------eth1 eth2------vlan_1------eth1 +# 192.168.0.1 192.168.0.3 192.168.1.3 192.168.1.2 +# +# See the NixOS manual for how to run this test: +# https://nixos.org/nixos/manual/index.html#sec-running-nixos-tests-interactively + +import ./make-test.nix ({ pkgs, ...} : + +let + allowESP = "iptables --insert INPUT --protocol ESP --jump ACCEPT"; + + # Shared VPN settings: + vlan0 = "192.168.0.0/24"; + carolIp = "192.168.1.2"; + moonIp = "192.168.1.3"; + version = 2; + secret = "0sFpZAZqEN6Ti9sqt4ZP5EWcqx"; + esp_proposals = [ "aes128gcm128-x25519" ]; + proposals = [ "aes128-sha256-x25519" ]; +in { + name = "strongswan-swanctl"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ basvandijk ]; + nodes = { + + alice = { ... } : { + virtualisation.vlans = [ 0 ]; + networking = { + dhcpcd.enable = false; + defaultGateway = "192.168.0.3"; + }; + }; + + moon = { config, ...} : + let strongswan = config.services.strongswan-swanctl.package; + in { + virtualisation.vlans = [ 0 1 ]; + networking = { + dhcpcd.enable = false; + firewall = { + allowedUDPPorts = [ 4500 500 ]; + extraCommands = allowESP; + }; + nat = { + enable = true; + internalIPs = [ vlan0 ]; + internalInterfaces = [ "eth1" ]; + externalIP = moonIp; + externalInterface = "eth2"; + }; + }; + environment.systemPackages = [ strongswan ]; + services.strongswan-swanctl = { + enable = true; + swanctl = { + connections = { + "rw" = { + local_addrs = [ moonIp ]; + local."main" = { + auth = "psk"; + }; + remote."main" = { + auth = "psk"; + }; + children = { + "net" = { + local_ts = [ vlan0 ]; + updown = "${strongswan}/libexec/ipsec/_updown iptables"; + inherit esp_proposals; + }; + }; + inherit version; + inherit proposals; + }; + }; + secrets = { + ike."carol" = { + id."main" = carolIp; + inherit secret; + }; + }; + }; + }; + }; + + carol = { config, ...} : + let strongswan = config.services.strongswan-swanctl.package; + in { + virtualisation.vlans = [ 1 ]; + networking = { + dhcpcd.enable = false; + firewall.extraCommands = allowESP; + }; + environment.systemPackages = [ strongswan ]; + services.strongswan-swanctl = { + enable = true; + swanctl = { + connections = { + "home" = { + local_addrs = [ carolIp ]; + remote_addrs = [ moonIp ]; + local."main" = { + auth = "psk"; + id = carolIp; + }; + remote."main" = { + auth = "psk"; + id = moonIp; + }; + children = { + "home" = { + remote_ts = [ vlan0 ]; + start_action = "trap"; + updown = "${strongswan}/libexec/ipsec/_updown iptables"; + inherit esp_proposals; + }; + }; + inherit version; + inherit proposals; + }; + }; + secrets = { + ike."moon" = { + id."main" = moonIp; + inherit secret; + }; + }; + }; + }; + }; + + }; + testScript = '' + startAll(); + $carol->waitUntilSucceeds("ping -c 1 alice"); + ''; +}) diff --git a/nixpkgs/nixos/tests/subversion.nix b/nixpkgs/nixos/tests/subversion.nix new file mode 100644 index 000000000000..6175155cdfc9 --- /dev/null +++ b/nixpkgs/nixos/tests/subversion.nix @@ -0,0 +1,121 @@ +import ./make-test.nix ({ pkgs, ...} : + +let + + # Build some packages with coverage instrumentation. + overrides = pkgs: + with pkgs.stdenvAdapters; + let + do = pkg: pkg.override (args: { + stdenv = addCoverageInstrumentation args.stdenv; + }); + in + rec { + apr = do pkgs.apr; + aprutil = do pkgs.aprutil; + apacheHttpd = do pkgs.apacheHttpd; + mod_python = do pkgs.mod_python; + subversion = do pkgs.subversion; + + # To build the kernel with coverage instrumentation, we need a + # special patch to make coverage data available under /proc. + linux = pkgs.linux.override (orig: { + stdenv = overrideInStdenv pkgs.stdenv [ pkgs.keepBuildTree ]; + extraConfig = + '' + GCOV_KERNEL y + GCOV_PROFILE_ALL y + ''; + }); + }; + +in + +{ + name = "subversion"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + nodes = + { webserver = + { ... }: + + { + services.httpd.enable = true; + services.httpd.adminAddr = "e.dolstra@tudelft.nl"; + services.httpd.extraSubservices = + [ { function = import <services/subversion>; + urlPrefix = ""; + dataDir = "/data/subversion"; + userCreationDomain = "192.168.0.0/16"; + } + ]; + nixpkgs.config.packageOverrides = overrides; + }; + + client = + { pkgs, ... }: + + { + environment.systemPackages = [ pkgs.subversion ]; + nixpkgs.config.packageOverrides = overrides; + }; + + }; + + testScript = + '' + startAll; + + $webserver->waitForOpenPort(80); + + print STDERR $client->succeed("svn --version"); + + print STDERR $client->succeed("curl --fail http://webserver/"); + + # Create a new user through the web interface. + $client->succeed("curl --fail -F username=alice -F fullname='Alice Lastname' -F address=alice\@example.org -F password=foobar -F password_again=foobar http://webserver/repoman/adduser"); + + # Let Alice create a new repository. + $client->succeed("curl --fail -u alice:foobar --form repo=xyzzy --form description=Xyzzy http://webserver/repoman/create"); + + $client->succeed("curl --fail http://webserver/") =~ /alice/ or die; + + # Let Alice do a checkout. + my $svnFlags = "--non-interactive --username alice --password foobar"; + $client->succeed("svn co $svnFlags http://webserver/repos/xyzzy wc"); + $client->succeed("echo hello > wc/world"); + $client->succeed("svn add wc/world"); + $client->succeed("svn ci $svnFlags -m 'Added world.' wc/world"); + + # Create a new user on the server through the create-user.pl script. + $webserver->execute("svn-server-create-user.pl bob bob\@example.org Bob"); + $webserver->succeed("svn-server-resetpw.pl bob fnord"); + $client->succeed("curl --fail http://webserver/") =~ /bob/ or die; + + # Bob should not have access to the repo. + my $svnFlagsBob = "--non-interactive --username bob --password fnord"; + $client->fail("svn co $svnFlagsBob http://webserver/repos/xyzzy wc2"); + + # Bob should not be able change the ACLs of the repo. + # !!! Repoman should really return a 403 here. + $client->succeed("curl --fail -u bob:fnord -F description=Xyzzy -F readers=alice,bob -F writers=alice -F watchers= -F tardirs= http://webserver/repoman/update/xyzzy") + =~ /not authorised/ or die; + + # Give Bob access. + $client->succeed("curl --fail -u alice:foobar -F description=Xyzzy -F readers=alice,bob -F writers=alice -F watchers= -F tardirs= http://webserver/repoman/update/xyzzy"); + + # So now his checkout should succeed. + $client->succeed("svn co $svnFlagsBob http://webserver/repos/xyzzy wc2"); + + # Test ViewVC and WebSVN + $client->succeed("curl --fail -u alice:foobar http://webserver/viewvc/xyzzy"); + $client->succeed("curl --fail -u alice:foobar http://webserver/websvn/xyzzy"); + $client->succeed("curl --fail -u alice:foobar http://webserver/repos-xml/xyzzy"); + + # Stop Apache to gather all the coverage data. + $webserver->stopJob("httpd"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/sudo.nix b/nixpkgs/nixos/tests/sudo.nix new file mode 100644 index 000000000000..fc16b99cc19c --- /dev/null +++ b/nixpkgs/nixos/tests/sudo.nix @@ -0,0 +1,93 @@ +# Some tests to ensure sudo is working properly. + +let + password = "helloworld"; + +in + import ./make-test.nix ({ pkgs, ...} : { + name = "sudo"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ lschuermann ]; + }; + + machine = + { lib, ... }: + with lib; + { + users.groups = { foobar = {}; barfoo = {}; baz = { gid = 1337; }; }; + users.users = { + test0 = { isNormalUser = true; extraGroups = [ "wheel" ]; }; + test1 = { isNormalUser = true; password = password; }; + test2 = { isNormalUser = true; extraGroups = [ "foobar" ]; password = password; }; + test3 = { isNormalUser = true; extraGroups = [ "barfoo" ]; }; + test4 = { isNormalUser = true; extraGroups = [ "baz" ]; }; + test5 = { isNormalUser = true; }; + }; + + security.sudo = { + enable = true; + wheelNeedsPassword = false; + + extraRules = [ + # SUDOERS SYNTAX CHECK (Test whether the module produces a valid output; + # errors being detected by the visudo checks. + + # These should not create any entries + { users = [ "notest1" ]; commands = [ ]; } + { commands = [ { command = "ALL"; options = [ ]; } ]; } + + # Test defining commands with the options syntax, though not setting any options + { users = [ "notest2" ]; commands = [ { command = "ALL"; options = [ ]; } ]; } + + + # CONFIGURATION FOR TEST CASES + { users = [ "test1" ]; groups = [ "foobar" ]; commands = [ "ALL" ]; } + { groups = [ "barfoo" 1337 ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" "NOSETENV" ]; } ]; } + { users = [ "test5" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" "SETENV" ]; } ]; runAs = "test1:barfoo"; } + ]; + }; + }; + + testScript = + '' + subtest "users in wheel group should have passwordless sudo", sub { + $machine->succeed("su - test0 -c \"sudo -u root true\""); + }; + + subtest "test1 user should have sudo with password", sub { + $machine->succeed("su - test1 -c \"echo ${password} | sudo -S -u root true\""); + }; + + subtest "test1 user should not be able to use sudo without password", sub { + $machine->fail("su - test1 -c \"sudo -n -u root true\""); + }; + + subtest "users in group 'foobar' should be able to use sudo with password", sub { + $machine->succeed("sudo -u test2 echo ${password} | sudo -S -u root true"); + }; + + subtest "users in group 'barfoo' should be able to use sudo without password", sub { + $machine->succeed("sudo -u test3 sudo -n -u root true"); + }; + + subtest "users in group 'baz' (GID 1337) should be able to use sudo without password", sub { + $machine->succeed("sudo -u test4 sudo -n -u root echo true"); + }; + + subtest "test5 user should be able to run commands under test1", sub { + $machine->succeed("sudo -u test5 sudo -n -u test1 true"); + }; + + subtest "test5 user should not be able to run commands under root", sub { + $machine->fail("sudo -u test5 sudo -n -u root true"); + }; + + subtest "test5 user should be able to keep his environment", sub { + $machine->succeed("sudo -u test5 sudo -n -E -u test1 true"); + }; + + subtest "users in group 'barfoo' should not be able to keep their environment", sub { + $machine->fail("sudo -u test3 sudo -n -E -u root true"); + }; + ''; + }) diff --git a/nixpkgs/nixos/tests/switch-test.nix b/nixpkgs/nixos/tests/switch-test.nix new file mode 100644 index 000000000000..32010838e67b --- /dev/null +++ b/nixpkgs/nixos/tests/switch-test.nix @@ -0,0 +1,25 @@ +# Test configuration switching. + +import ./make-test.nix ({ pkgs, ...} : { + name = "switch-test"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ gleber ]; + }; + + nodes = { + machine = { ... }: { + users.mutableUsers = false; + }; + other = { ... }: { + users.mutableUsers = true; + }; + }; + + testScript = {nodes, ...}: let + originalSystem = nodes.machine.config.system.build.toplevel; + otherSystem = nodes.other.config.system.build.toplevel; + in '' + $machine->succeed("env -i ${originalSystem}/bin/switch-to-configuration test | tee /dev/stderr"); + $machine->succeed("env -i ${otherSystem}/bin/switch-to-configuration test | tee /dev/stderr"); + ''; +}) diff --git a/nixpkgs/nixos/tests/syncthing-relay.nix b/nixpkgs/nixos/tests/syncthing-relay.nix new file mode 100644 index 000000000000..f1ceb4993337 --- /dev/null +++ b/nixpkgs/nixos/tests/syncthing-relay.nix @@ -0,0 +1,22 @@ +import ./make-test.nix ({ lib, pkgs, ... }: { + name = "syncthing-relay"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ delroth ]; + + machine = { + environment.systemPackages = [ pkgs.jq ]; + services.syncthing.relay = { + enable = true; + providedBy = "nixos-test"; + pools = []; # Don't connect to any pool while testing. + port = 12345; + statusPort = 12346; + }; + }; + + testScript = '' + $machine->waitForUnit("syncthing-relay.service"); + $machine->waitForOpenPort(12345); + $machine->waitForOpenPort(12346); + $machine->succeed("curl http://localhost:12346/status | jq -r '.options.\"provided-by\"'") =~ /nixos-test/ or die; + ''; +}) diff --git a/nixpkgs/nixos/tests/systemd.nix b/nixpkgs/nixos/tests/systemd.nix new file mode 100644 index 000000000000..4d470126abee --- /dev/null +++ b/nixpkgs/nixos/tests/systemd.nix @@ -0,0 +1,73 @@ +import ./make-test.nix { + name = "systemd"; + + machine = { lib, ... }: { + imports = [ common/user-account.nix common/x11.nix ]; + + virtualisation.emptyDiskImages = [ 512 ]; + + fileSystems = lib.mkVMOverride { + "/test-x-initrd-mount" = { + device = "/dev/vdb"; + fsType = "ext2"; + autoFormat = true; + noCheck = true; + options = [ "x-initrd.mount" ]; + }; + }; + + systemd.extraConfig = "DefaultEnvironment=\"XXX_SYSTEM=foo\""; + systemd.user.extraConfig = "DefaultEnvironment=\"XXX_USER=bar\""; + services.journald.extraConfig = "Storage=volatile"; + services.xserver.displayManager.auto.user = "alice"; + + systemd.services.testservice1 = { + description = "Test Service 1"; + wantedBy = [ "multi-user.target" ]; + serviceConfig.Type = "oneshot"; + script = '' + if [ "$XXX_SYSTEM" = foo ]; then + touch /system_conf_read + fi + ''; + }; + + systemd.user.services.testservice2 = { + description = "Test Service 2"; + wantedBy = [ "default.target" ]; + serviceConfig.Type = "oneshot"; + script = '' + if [ "$XXX_USER" = bar ]; then + touch "$HOME/user_conf_read" + fi + ''; + }; + }; + + testScript = '' + $machine->waitForX; + # wait for user services + $machine->waitForUnit("default.target","alice"); + + # Regression test for https://github.com/NixOS/nixpkgs/issues/35415 + subtest "configuration files are recognized by systemd", sub { + $machine->succeed('test -e /system_conf_read'); + $machine->succeed('test -e /home/alice/user_conf_read'); + $machine->succeed('test -z $(ls -1 /var/log/journal)'); + }; + + # Regression test for https://github.com/NixOS/nixpkgs/issues/50273 + subtest "DynamicUser actually allocates a user", sub { + $machine->succeed('systemd-run --pty --property=Type=oneshot --property=DynamicUser=yes --property=User=iamatest whoami | grep iamatest'); + }; + + # Regression test for https://github.com/NixOS/nixpkgs/issues/35268 + subtest "file system with x-initrd.mount is not unmounted", sub { + $machine->shutdown; + $machine->waitForUnit('multi-user.target'); + # If the file system was unmounted during the shutdown the file system + # has a last mount time, because the file system wasn't checked. + $machine->fail('dumpe2fs /dev/vdb | grep -q "^Last mount time: *n/a"'); + }; + ''; +} diff --git a/nixpkgs/nixos/tests/taskserver.nix b/nixpkgs/nixos/tests/taskserver.nix new file mode 100644 index 000000000000..ab9b589f8593 --- /dev/null +++ b/nixpkgs/nixos/tests/taskserver.nix @@ -0,0 +1,290 @@ +import ./make-test.nix ({ pkgs, ... }: let + snakeOil = pkgs.runCommand "snakeoil-certs" { + outputs = [ "out" "cacert" "cert" "key" "crl" ]; + buildInputs = [ pkgs.gnutls.bin ]; + caTemplate = pkgs.writeText "snakeoil-ca.template" '' + cn = server + expiration_days = -1 + cert_signing_key + ca + ''; + certTemplate = pkgs.writeText "snakeoil-cert.template" '' + cn = server + expiration_days = -1 + tls_www_server + encryption_key + signing_key + ''; + crlTemplate = pkgs.writeText "snakeoil-crl.template" '' + expiration_days = -1 + ''; + userCertTemplate = pkgs.writeText "snakeoil-user-cert.template" '' + organization = snakeoil + cn = server + expiration_days = -1 + tls_www_client + encryption_key + signing_key + ''; + } '' + certtool -p --bits 4096 --outfile ca.key + certtool -s --template "$caTemplate" --load-privkey ca.key \ + --outfile "$cacert" + certtool -p --bits 4096 --outfile "$key" + certtool -c --template "$certTemplate" \ + --load-ca-privkey ca.key \ + --load-ca-certificate "$cacert" \ + --load-privkey "$key" \ + --outfile "$cert" + certtool --generate-crl --template "$crlTemplate" \ + --load-ca-privkey ca.key \ + --load-ca-certificate "$cacert" \ + --outfile "$crl" + + mkdir "$out" + + # Stripping key information before the actual PEM-encoded values is solely + # to make test output a bit less verbose when copying the client key to the + # actual client. + certtool -p --bits 4096 | sed -n \ + -e '/^----* *BEGIN/,/^----* *END/p' > "$out/alice.key" + + certtool -c --template "$userCertTemplate" \ + --load-privkey "$out/alice.key" \ + --load-ca-privkey ca.key \ + --load-ca-certificate "$cacert" \ + --outfile "$out/alice.cert" + ''; + +in { + name = "taskserver"; + + nodes = rec { + server = { + services.taskserver.enable = true; + services.taskserver.listenHost = "::"; + services.taskserver.fqdn = "server"; + services.taskserver.organisations = { + testOrganisation.users = [ "alice" "foo" ]; + anotherOrganisation.users = [ "bob" ]; + }; + }; + + # New generation of the server with manual config + newServer = { lib, nodes, ... }: { + imports = [ server ]; + services.taskserver.pki.manual = { + ca.cert = snakeOil.cacert; + server.cert = snakeOil.cert; + server.key = snakeOil.key; + server.crl = snakeOil.crl; + }; + # This is to avoid assigning a different network address to the new + # generation. + networking = lib.mapAttrs (lib.const lib.mkForce) { + interfaces.eth1.ipv4 = nodes.server.config.networking.interfaces.eth1.ipv4; + inherit (nodes.server.config.networking) + hostName primaryIPAddress extraHosts; + }; + }; + + client1 = { pkgs, ... }: { + environment.systemPackages = [ pkgs.taskwarrior pkgs.gnutls ]; + users.users.alice.isNormalUser = true; + users.users.bob.isNormalUser = true; + users.users.foo.isNormalUser = true; + users.users.bar.isNormalUser = true; + }; + + client2 = client1; + }; + + testScript = { nodes, ... }: let + cfg = nodes.server.config.services.taskserver; + portStr = toString cfg.listenPort; + 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; + } + + # Explicitly start the VMs so that we don't accidentally start newServer + $server->start; + $client1->start; + $client2->start; + + $server->waitForUnit("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->waitForOpenPort(${portStr}); + + $client1->waitForUnit("multi-user.target"); + $client2->waitForUnit("multi-user.target"); + + setupClientsFor "testOrganisation", "alice"; + setupClientsFor "testOrganisation", "foo"; + setupClientsFor "anotherOrganisation", "bob"; + + testSync $_ for ("alice", "bob", "foo"); + + $server->fail("nixos-taskserver user add imperativeOrg bar"); + readdImperativeUser; + + testSync "bar"; + + subtest "checking certificate revocation of user bar", sub { + $client1->succeed(checkClientCert "bar"); + + $server->succeed("nixos-taskserver user remove imperativeOrg bar"); + restartServer; + + $client1->fail(checkClientCert "bar"); + + $client1->succeed(su "bar", "task add destroy everything >&2"); + $client1->fail(su "bar", "task sync >&2"); + }; + + readdImperativeUser; + + subtest "checking certificate revocation of org imperativeOrg", sub { + $client1->succeed(checkClientCert "bar"); + + $server->succeed("nixos-taskserver org remove imperativeOrg"); + restartServer; + + $client1->fail(checkClientCert "bar"); + + $client1->succeed(su "bar", "task add destroy even more >&2"); + $client1->fail(su "bar", "task sync >&2"); + }; + + readdImperativeUser; + + subtest "check whether declarative config overrides user bar", sub { + restartServer; + testSync "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"; + }; + ''; +}) diff --git a/nixpkgs/nixos/tests/telegraf.nix b/nixpkgs/nixos/tests/telegraf.nix new file mode 100644 index 000000000000..6776f8d8c37f --- /dev/null +++ b/nixpkgs/nixos/tests/telegraf.nix @@ -0,0 +1,30 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "telegraf"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ mic92 ]; + }; + + machine = { ... }: { + services.telegraf.enable = true; + services.telegraf.extraConfig = { + agent.interval = "1s"; + agent.flush_interval = "1s"; + inputs.exec = { + commands = [ + "${pkgs.runtimeShell} -c 'echo example,tag=a i=42i'" + ]; + timeout = "5s"; + data_format = "influx"; + }; + outputs.file.files = ["/tmp/metrics.out"]; + outputs.file.data_format = "influx"; + }; + }; + + testScript = '' + startAll; + + $machine->waitForUnit("telegraf.service"); + $machine->waitUntilSucceeds("grep -q example /tmp/metrics.out"); + ''; +}) diff --git a/nixpkgs/nixos/tests/testdb.sql b/nixpkgs/nixos/tests/testdb.sql new file mode 100644 index 000000000000..3c68c49ae82c --- /dev/null +++ b/nixpkgs/nixos/tests/testdb.sql @@ -0,0 +1,11 @@ +create table tests +( Id INTEGER NOT NULL, + Name VARCHAR(255) NOT NULL, + primary key(Id) +); + +insert into tests values (1, 'a'); +insert into tests values (2, 'b'); +insert into tests values (3, 'c'); +insert into tests values (4, 'd'); +insert into tests values (5, 'hello'); diff --git a/nixpkgs/nixos/tests/timezone.nix b/nixpkgs/nixos/tests/timezone.nix new file mode 100644 index 000000000000..2204649a3fc4 --- /dev/null +++ b/nixpkgs/nixos/tests/timezone.nix @@ -0,0 +1,45 @@ +{ + timezone-static = import ./make-test.nix ({ pkgs, ... }: { + name = "timezone-static"; + meta.maintainers = with pkgs.lib.maintainers; [ lheckemann ]; + + machine.time.timeZone = "Europe/Amsterdam"; + + testScript = '' + $machine->waitForUnit("dbus.socket"); + $machine->fail("timedatectl set-timezone Asia/Tokyo"); + my @dateResult = $machine->execute('date -d @0 "+%Y-%m-%d %H:%M:%S"'); + $dateResult[1] eq "1970-01-01 01:00:00\n" or die "Timezone seems to be wrong"; + ''; + }); + + timezone-imperative = import ./make-test.nix ({ pkgs, ... }: { + name = "timezone-imperative"; + meta.maintainers = with pkgs.lib.maintainers; [ lheckemann ]; + + machine.time.timeZone = null; + + testScript = '' + $machine->waitForUnit("dbus.socket"); + + # Should default to UTC + my @dateResult = $machine->execute('date -d @0 "+%Y-%m-%d %H:%M:%S"'); + print $dateResult[1]; + $dateResult[1] eq "1970-01-01 00:00:00\n" or die "Timezone seems to be wrong"; + + $machine->succeed("timedatectl set-timezone Asia/Tokyo"); + + # Adjustment should be taken into account + my @dateResult = $machine->execute('date -d @0 "+%Y-%m-%d %H:%M:%S"'); + print $dateResult[1]; + $dateResult[1] eq "1970-01-01 09:00:00\n" or die "Timezone was not adjusted"; + + # Adjustment should persist across a reboot + $machine->shutdown; + $machine->waitForUnit("dbus.socket"); + my @dateResult = $machine->execute('date -d @0 "+%Y-%m-%d %H:%M:%S"'); + print $dateResult[1]; + $dateResult[1] eq "1970-01-01 09:00:00\n" or die "Timezone adjustment was not persisted"; + ''; + }); +} diff --git a/nixpkgs/nixos/tests/tomcat.nix b/nixpkgs/nixos/tests/tomcat.nix new file mode 100644 index 000000000000..af63c7ee8e02 --- /dev/null +++ b/nixpkgs/nixos/tests/tomcat.nix @@ -0,0 +1,30 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "tomcat"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + nodes = { + server = + { ... }: + + { services.tomcat.enable = true; + services.httpd.enable = true; + services.httpd.adminAddr = "foo@bar.com"; + services.httpd.extraSubservices = + [ { serviceType = "tomcat-connector"; } ]; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + + client = { }; + }; + + testScript = '' + startAll; + + $server->waitForUnit("tomcat"); + $client->waitForUnit("network.target"); + $client->waitUntilSucceeds("curl --fail http://server/examples/servlets/servlet/HelloWorldExample"); + $client->waitUntilSucceeds("curl --fail http://server/examples/jsp/jsp2/simpletag/hello.jsp"); + ''; +}) diff --git a/nixpkgs/nixos/tests/tor.nix b/nixpkgs/nixos/tests/tor.nix new file mode 100644 index 000000000000..0cb44ddff248 --- /dev/null +++ b/nixpkgs/nixos/tests/tor.nix @@ -0,0 +1,28 @@ +import ./make-test.nix ({ lib, ... }: with lib; + +rec { + name = "tor"; + meta.maintainers = with maintainers; [ joachifm ]; + + common = + { ... }: + { boot.kernelParams = [ "audit=0" "apparmor=0" "quiet" ]; + networking.firewall.enable = false; + networking.useDHCP = false; + }; + + nodes.client = + { pkgs, ... }: + { imports = [ common ]; + environment.systemPackages = with pkgs; [ netcat ]; + services.tor.enable = true; + services.tor.client.enable = true; + services.tor.controlPort = 9051; + }; + + testScript = '' + $client->waitForUnit("tor.service"); + $client->waitForOpenPort(9051); + $client->succeed("echo GETINFO version | nc 127.0.0.1 9051") =~ /514 Authentication required./ or die; + ''; +}) diff --git a/nixpkgs/nixos/tests/trac.nix b/nixpkgs/nixos/tests/trac.nix new file mode 100644 index 000000000000..4599885acde6 --- /dev/null +++ b/nixpkgs/nixos/tests/trac.nix @@ -0,0 +1,74 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "trac"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + nodes = { + storage = + { ... }: + { services.nfs.server.enable = true; + services.nfs.server.exports = '' + /repos 192.168.1.0/255.255.255.0(rw,no_root_squash) + ''; + services.nfs.server.createMountPoints = true; + }; + + postgresql = + { pkgs, ... }: + { services.postgresql.enable = true; + services.postgresql.package = pkgs.postgresql; + services.postgresql.enableTCPIP = true; + services.postgresql.authentication = '' + # Generated file; do not edit! + local all all trust + host all all 127.0.0.1/32 trust + host all all ::1/128 trust + host all all 192.168.1.0/24 trust + ''; + }; + + webserver = + { pkgs, ... }: + { fileSystems = pkgs.lib.mkVMOverride + [ { mountPoint = "/repos"; + device = "storage:/repos"; + fsType = "nfs"; + } + ]; + services.httpd.enable = true; + services.httpd.adminAddr = "root@localhost"; + services.httpd.extraSubservices = [ { serviceType = "trac"; } ]; + environment.systemPackages = [ pkgs.pythonPackages.trac pkgs.subversion ]; + }; + + client = + { ... }: + { imports = [ ./common/x11.nix ]; + services.xserver.desktopManager.plasma5.enable = true; + }; + }; + + testScript = + '' + startAll; + + $postgresql->waitForUnit("postgresql"); + $postgresql->succeed("createdb trac"); + + $webserver->succeed("mkdir -p /repos/trac"); + $webserver->succeed("svnadmin create /repos/trac"); + + $webserver->waitForUnit("httpd"); + $webserver->waitForFile("/var/trac"); + $webserver->succeed("mkdir -p /var/trac/projects/test"); + $webserver->succeed("PYTHONPATH=${pkgs.pythonPackages.psycopg2}/lib/${pkgs.python.libPrefix}/site-packages trac-admin /var/trac/projects/test initenv Test postgres://root\@postgresql/trac svn /repos/trac"); + + $client->waitForX; + $client->execute("konqueror http://webserver/projects/test &"); + $client->waitForWindow(qr/Test.*Konqueror/); + $client->sleep(30); # loading takes a long time + + $client->screenshot("screen"); + ''; +}) diff --git a/nixpkgs/nixos/tests/transmission.nix b/nixpkgs/nixos/tests/transmission.nix new file mode 100644 index 000000000000..f1c238730ebb --- /dev/null +++ b/nixpkgs/nixos/tests/transmission.nix @@ -0,0 +1,21 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "transmission"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ coconnor ]; + }; + + machine = { ... }: { + imports = [ ../modules/profiles/minimal.nix ]; + + networking.firewall.allowedTCPPorts = [ 9091 ]; + + services.transmission.enable = true; + }; + + testScript = + '' + startAll; + $machine->waitForUnit("transmission"); + $machine->shutdown; + ''; +}) diff --git a/nixpkgs/nixos/tests/udisks2.nix b/nixpkgs/nixos/tests/udisks2.nix new file mode 100644 index 000000000000..8bbbe286efcf --- /dev/null +++ b/nixpkgs/nixos/tests/udisks2.nix @@ -0,0 +1,61 @@ +import ./make-test.nix ({ pkgs, ... }: + +let + + stick = pkgs.fetchurl { + url = http://nixos.org/~eelco/nix/udisks-test.img.xz; + sha256 = "0was1xgjkjad91nipzclaz5biv3m4b2nk029ga6nk7iklwi19l8b"; + }; + +in + +{ + name = "udisks2"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow ]; + }; + + machine = + { ... }: + { services.udisks2.enable = true; + imports = [ ./common/user-account.nix ]; + + security.polkit.extraConfig = + '' + polkit.addRule(function(action, subject) { + if (subject.user == "alice") return "yes"; + }); + ''; + }; + + testScript = + '' + my $stick = $machine->stateDir . "/usbstick.img"; + system("xz -d < ${stick} > $stick") == 0 or die; + + $machine->succeed("udisksctl info -b /dev/vda >&2"); + $machine->fail("udisksctl info -b /dev/sda1"); + + # Attach a USB stick and wait for it to show up. + $machine->sendMonitorCommand("drive_add 0 id=stick,if=none,file=$stick,format=raw"); + $machine->sendMonitorCommand("device_add usb-storage,id=stick,drive=stick"); + $machine->waitUntilSucceeds("udisksctl info -b /dev/sda1"); + $machine->succeed("udisksctl info -b /dev/sda1 | grep 'IdLabel:.*USBSTICK'"); + + # Mount the stick as a non-root user and do some stuff with it. + $machine->succeed("su - alice -c 'udisksctl info -b /dev/sda1'"); + $machine->succeed("su - alice -c 'udisksctl mount -b /dev/sda1'"); + $machine->succeed("su - alice -c 'cat /run/media/alice/USBSTICK/test.txt'") =~ /Hello World/ or die; + $machine->succeed("su - alice -c 'echo foo > /run/media/alice/USBSTICK/bar.txt'"); + + # Unmounting the stick should make the mountpoint disappear. + $machine->succeed("su - alice -c 'udisksctl unmount -b /dev/sda1'"); + $machine->fail("[ -d /run/media/alice/USBSTICK ]"); + + # Remove the USB stick. + $machine->sendMonitorCommand("device_del stick"); + $machine->waitUntilFails("udisksctl info -b /dev/sda1"); + $machine->fail("[ -e /dev/sda ]"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/upnp.nix b/nixpkgs/nixos/tests/upnp.nix new file mode 100644 index 000000000000..3f2dd13fb560 --- /dev/null +++ b/nixpkgs/nixos/tests/upnp.nix @@ -0,0 +1,94 @@ +# This tests whether UPnP port mappings can be created using Miniupnpd +# and Miniupnpc. +# It runs a Miniupnpd service on one machine, and verifies +# a client can indeed create a port mapping using Miniupnpc. If +# this succeeds an external client will try to connect to the port +# mapping. + +import ./make-test.nix ({ pkgs, ... }: + +let + internalRouterAddress = "192.168.3.1"; + internalClient1Address = "192.168.3.2"; + externalRouterAddress = "80.100.100.1"; + externalClient2Address = "80.100.100.2"; +in +{ + name = "upnp"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ bobvanderlinden ]; + }; + + nodes = + { + router = + { pkgs, nodes, ... }: + { virtualisation.vlans = [ 1 2 ]; + networking.nat.enable = true; + networking.nat.internalInterfaces = [ "eth2" ]; + networking.nat.externalInterface = "eth1"; + networking.firewall.enable = true; + networking.firewall.trustedInterfaces = [ "eth2" ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalRouterAddress; prefixLength = 24; } + ]; + networking.interfaces.eth2.ipv4.addresses = [ + { address = internalRouterAddress; prefixLength = 24; } + ]; + services.miniupnpd = { + enable = true; + externalInterface = "eth1"; + internalIPs = [ "eth2" ]; + appendConfig = '' + ext_ip=${externalRouterAddress} + ''; + }; + }; + + client1 = + { pkgs, nodes, ... }: + { environment.systemPackages = [ pkgs.miniupnpc pkgs.netcat ]; + virtualisation.vlans = [ 2 ]; + networking.defaultGateway = internalRouterAddress; + networking.interfaces.eth1.ipv4.addresses = [ + { address = internalClient1Address; prefixLength = 24; } + ]; + networking.firewall.enable = false; + + services.httpd.enable = true; + services.httpd.listen = [{ ip = "*"; port = 9000; }]; + services.httpd.adminAddr = "foo@example.org"; + services.httpd.documentRoot = "/tmp"; + }; + + client2 = + { pkgs, ... }: + { environment.systemPackages = [ pkgs.miniupnpc ]; + virtualisation.vlans = [ 1 ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalClient2Address; prefixLength = 24; } + ]; + networking.firewall.enable = false; + }; + }; + + testScript = + { nodes, ... }: + '' + startAll; + + # Wait for network and miniupnpd. + $router->waitForUnit("network-online.target"); + # $router->waitForUnit("nat"); + $router->waitForUnit("firewall.service"); + $router->waitForUnit("miniupnpd"); + + $client1->waitForUnit("network-online.target"); + + $client1->succeed("upnpc -a ${internalClient1Address} 9000 9000 TCP"); + + $client1->waitForUnit("httpd"); + $client2->waitUntilSucceeds("curl http://${externalRouterAddress}:9000/"); + ''; + +}) diff --git a/nixpkgs/nixos/tests/vault.nix b/nixpkgs/nixos/tests/vault.nix new file mode 100644 index 000000000000..caf0cbb2abfe --- /dev/null +++ b/nixpkgs/nixos/tests/vault.nix @@ -0,0 +1,23 @@ +import ./make-test.nix ({ pkgs, ... }: +{ + name = "vault"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ lnl7 ]; + }; + machine = { pkgs, ... }: { + environment.systemPackages = [ pkgs.vault ]; + environment.variables.VAULT_ADDR = "http://127.0.0.1:8200"; + services.vault.enable = true; + }; + + testScript = + '' + startAll; + + $machine->waitForUnit('multi-user.target'); + $machine->waitForUnit('vault.service'); + $machine->waitForOpenPort(8200); + $machine->succeed('vault operator init'); + $machine->succeed('vault status | grep Sealed | grep true'); + ''; +}) diff --git a/nixpkgs/nixos/tests/virtualbox.nix b/nixpkgs/nixos/tests/virtualbox.nix new file mode 100644 index 000000000000..84d5f3e1530e --- /dev/null +++ b/nixpkgs/nixos/tests/virtualbox.nix @@ -0,0 +1,512 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; }, + debug ? false, + enableUnfree ? false +}: + +with import ../lib/testing.nix { inherit system pkgs; }; +with pkgs.lib; + +let + testVMConfig = vmName: attrs: { config, pkgs, lib, ... }: let + guestAdditions = pkgs.linuxPackages.virtualboxGuestAdditions; + + miniInit = '' + #!${pkgs.stdenv.shell} -xe + export PATH="${lib.makeBinPath [ pkgs.coreutils pkgs.utillinux ]}" + + mkdir -p /run/dbus + cat > /etc/passwd <<EOF + root:x:0:0::/root:/bin/false + messagebus:x:1:1::/run/dbus:/bin/false + EOF + cat > /etc/group <<EOF + root:x:0: + messagebus:x:1: + EOF + + "${pkgs.dbus.daemon}/bin/dbus-daemon" --fork \ + --config-file="${pkgs.dbus.daemon}/share/dbus-1/system.conf" + + ${guestAdditions}/bin/VBoxService + ${(attrs.vmScript or (const "")) pkgs} + + i=0 + while [ ! -e /mnt-root/shutdown ]; do + sleep 10 + i=$(($i + 10)) + [ $i -le 120 ] || fail + done + + rm -f /mnt-root/boot-done /mnt-root/shutdown + ''; + in { + boot.kernelParams = [ + "console=tty0" "console=ttyS0" "ignore_loglevel" + "boot.trace" "panic=1" "boot.panic_on_fail" + "init=${pkgs.writeScript "mini-init.sh" miniInit}" + ]; + + # XXX: Remove this once TSS location detection has been fixed in VirtualBox + boot.kernelPackages = pkgs.linuxPackages_4_9; + + fileSystems."/" = { + device = "vboxshare"; + fsType = "vboxsf"; + }; + + virtualisation.virtualbox.guest.enable = true; + + boot.initrd.kernelModules = [ + "af_packet" "vboxsf" + "virtio" "virtio_pci" "virtio_ring" "virtio_net" "vboxguest" + ]; + + boot.initrd.extraUtilsCommands = '' + copy_bin_and_libs "${guestAdditions}/bin/mount.vboxsf" + copy_bin_and_libs "${pkgs.utillinux}/bin/unshare" + ${(attrs.extraUtilsCommands or (const "")) pkgs} + ''; + + boot.initrd.postMountCommands = '' + touch /mnt-root/boot-done + hostname "${vmName}" + mkdir -p /nix/store + unshare -m ${escapeShellArg pkgs.stdenv.shell} -c ' + mount -t vboxsf nixstore /nix/store + exec "$stage2Init" + ' + poweroff -f + ''; + + system.requiredKernelConfig = with config.lib.kernelConfig; [ + (isYes "SERIAL_8250_CONSOLE") + (isYes "SERIAL_8250") + ]; + }; + + mkLog = logfile: tag: let + rotated = map (i: "${logfile}.${toString i}") (range 1 9); + all = concatMapStringsSep " " (f: "\"${f}\"") ([logfile] ++ rotated); + logcmd = "tail -F ${all} 2> /dev/null | logger -t \"${tag}\""; + in optionalString debug "$machine->execute(ru '${logcmd} & disown');"; + + testVM = vmName: vmScript: let + cfg = (import ../lib/eval-config.nix { + system = "i686-linux"; + modules = [ + ../modules/profiles/minimal.nix + (testVMConfig vmName vmScript) + ]; + }).config; + in pkgs.vmTools.runInLinuxVM (pkgs.runCommand "virtualbox-image" { + preVM = '' + mkdir -p "$out" + diskImage="$(pwd)/qimage" + ${pkgs.vmTools.qemu}/bin/qemu-img create -f raw "$diskImage" 100M + ''; + + postVM = '' + echo "creating VirtualBox disk image..." + ${pkgs.vmTools.qemu}/bin/qemu-img convert -f raw -O vdi \ + "$diskImage" "$out/disk.vdi" + ''; + + buildInputs = [ pkgs.utillinux pkgs.perl ]; + } '' + ${pkgs.parted}/sbin/parted --script /dev/vda mklabel msdos + ${pkgs.parted}/sbin/parted --script /dev/vda -- mkpart primary ext2 1M -1s + ${pkgs.e2fsprogs}/sbin/mkfs.ext4 /dev/vda1 + ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1 + mkdir /mnt + mount /dev/vda1 /mnt + cp "${cfg.system.build.kernel}/bzImage" /mnt/linux + cp "${cfg.system.build.initialRamdisk}/initrd" /mnt/initrd + + ${pkgs.grub2}/bin/grub-install --boot-directory=/mnt /dev/vda + + cat > /mnt/grub/grub.cfg <<GRUB + set root=hd0,1 + linux /linux ${concatStringsSep " " cfg.boot.kernelParams} + initrd /initrd + boot + GRUB + umount /mnt + ''); + + createVM = name: attrs: let + mkFlags = concatStringsSep " "; + + sharePath = "/home/alice/vboxshare-${name}"; + + createFlags = mkFlags [ + "--ostype Linux26" + "--register" + ]; + + vmFlags = mkFlags ([ + "--uart1 0x3F8 4" + "--uartmode1 client /run/virtualbox-log-${name}.sock" + "--memory 768" + "--audio none" + ] ++ (attrs.vmFlags or [])); + + controllerFlags = mkFlags [ + "--name SATA" + "--add sata" + "--bootable on" + "--hostiocache on" + ]; + + diskFlags = mkFlags [ + "--storagectl SATA" + "--port 0" + "--device 0" + "--type hdd" + "--mtype immutable" + "--medium ${testVM name attrs}/disk.vdi" + ]; + + sharedFlags = mkFlags [ + "--name vboxshare" + "--hostpath ${sharePath}" + ]; + + nixstoreFlags = mkFlags [ + "--name nixstore" + "--hostpath /nix/store" + "--readonly" + ]; + in { + machine = { + systemd.sockets."vboxtestlog-${name}" = { + description = "VirtualBox Test Machine Log Socket For ${name}"; + wantedBy = [ "sockets.target" ]; + before = [ "multi-user.target" ]; + socketConfig.ListenStream = "/run/virtualbox-log-${name}.sock"; + socketConfig.Accept = true; + }; + + 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"; + }; + }; + + testSubs = '' + my ${"$" + name}_sharepath = '${sharePath}'; + + sub checkRunning_${name} { + my $cmd = 'VBoxManage list runningvms | grep -q "^\"${name}\""'; + my ($status, $out) = $machine->execute(ru $cmd); + return $status == 0; + } + + sub cleanup_${name} { + $machine->execute(ru "VBoxManage controlvm ${name} poweroff") + if checkRunning_${name}; + $machine->succeed("rm -rf ${sharePath}"); + $machine->succeed("mkdir -p ${sharePath}"); + $machine->succeed("chown alice.users ${sharePath}"); + } + + sub createVM_${name} { + vbm("createvm --name ${name} ${createFlags}"); + vbm("modifyvm ${name} ${vmFlags}"); + vbm("setextradata ${name} VBoxInternal/PDM/HaltOnReset 1"); + vbm("storagectl ${name} ${controllerFlags}"); + vbm("storageattach ${name} ${diskFlags}"); + vbm("sharedfolder add ${name} ${sharedFlags}"); + vbm("sharedfolder add ${name} ${nixstoreFlags}"); + cleanup_${name}; + + ${mkLog "$HOME/VirtualBox VMs/${name}/Logs/VBox.log" "HOST-${name}"} + } + + sub destroyVM_${name} { + cleanup_${name}; + vbm("unregistervm ${name} --delete"); + } + + sub waitForVMBoot_${name} { + $machine->execute(ru( + 'set -e; i=0; '. + 'while ! test -e ${sharePath}/boot-done; do '. + 'sleep 10; i=$(($i + 10)); [ $i -le 3600 ]; '. + 'VBoxManage list runningvms | grep -q "^\"${name}\""; '. + 'done' + )); + } + + sub waitForIP_${name} ($) { + my $property = "/VirtualBox/GuestInfo/Net/$_[0]/V4/IP"; + my $getip = "VBoxManage guestproperty get ${name} $property | ". + "sed -n -e 's/^Value: //p'"; + my $ip = $machine->succeed(ru( + 'for i in $(seq 1000); do '. + 'if ipaddr="$('.$getip.')" && [ -n "$ipaddr" ]; then '. + 'echo "$ipaddr"; exit 0; '. + 'fi; '. + 'sleep 1; '. + 'done; '. + 'echo "Could not get IPv4 address for ${name}!" >&2; '. + 'exit 1' + )); + chomp $ip; + return $ip; + } + + sub waitForStartup_${name} { + for (my $i = 0; $i <= 120; $i += 10) { + $machine->sleep(10); + return if checkRunning_${name}; + eval { $_[0]->() } if defined $_[0]; + } + die "VirtualBox VM didn't start up within 2 minutes"; + } + + sub waitForShutdown_${name} { + for (my $i = 0; $i <= 120; $i += 10) { + $machine->sleep(10); + return unless checkRunning_${name}; + } + die "VirtualBox VM didn't shut down within 2 minutes"; + } + + sub shutdownVM_${name} { + $machine->succeed(ru "touch ${sharePath}/shutdown"); + $machine->execute( + 'set -e; i=0; '. + 'while test -e ${sharePath}/shutdown '. + ' -o -e ${sharePath}/boot-done; do '. + 'sleep 1; i=$(($i + 1)); [ $i -le 3600 ]; '. + 'done' + ); + waitForShutdown_${name}; + } + ''; + }; + + hostonlyVMFlags = [ + "--nictype1 virtio" + "--nictype2 virtio" + "--nic2 hostonly" + "--hostonlyadapter2 vboxnet0" + ]; + + # The VirtualBox Oracle Extension Pack lets you use USB 3.0 (xHCI). + enableExtensionPackVMFlags = [ + "--usbxhci on" + ]; + + dhcpScript = pkgs: '' + ${pkgs.dhcp}/bin/dhclient \ + -lf /run/dhcp.leases \ + -pf /run/dhclient.pid \ + -v eth0 eth1 + + otherIP="$(${pkgs.netcat}/bin/nc -l 1234 || :)" + ${pkgs.iputils}/bin/ping -I eth1 -c1 "$otherIP" + echo "$otherIP reachable" | ${pkgs.netcat}/bin/nc -l 5678 || : + ''; + + sysdDetectVirt = pkgs: '' + ${pkgs.systemd}/bin/systemd-detect-virt > /mnt-root/result + ''; + + vboxVMs = mapAttrs createVM { + simple = {}; + + detectvirt.vmScript = sysdDetectVirt; + + test1.vmFlags = hostonlyVMFlags; + test1.vmScript = dhcpScript; + + test2.vmFlags = hostonlyVMFlags; + test2.vmScript = dhcpScript; + + headless.virtualisation.virtualbox.headless = true; + headless.services.xserver.enable = false; + }; + + vboxVMsWithExtpack = mapAttrs createVM { + testExtensionPack.vmFlags = enableExtensionPackVMFlags; + }; + + mkVBoxTest = useExtensionPack: vms: name: testScript: makeTest { + name = "virtualbox-${name}"; + + machine = { lib, config, ... }: { + imports = let + mkVMConf = name: val: val.machine // { key = "${name}-config"; }; + vmConfigs = mapAttrsToList mkVMConf vms; + in [ ./common/user-account.nix ./common/x11.nix ] ++ vmConfigs; + virtualisation.memorySize = 2048; + virtualisation.virtualbox.host.enable = true; + services.xserver.displayManager.auto.user = "alice"; + users.users.alice.extraGroups = let + inherit (config.virtualisation.virtualbox.host) enableHardening; + in lib.mkIf enableHardening (lib.singleton "vboxusers"); + virtualisation.virtualbox.host.enableExtensionPack = useExtensionPack; + nixpkgs.config.allowUnfree = useExtensionPack; + }; + + testScript = '' + sub ru ($) { + my $esc = $_[0] =~ s/'/'\\${"'"}'/gr; + return "su - alice -c '$esc'"; + } + + sub vbm { + $machine->succeed(ru("VBoxManage ".$_[0])); + }; + + sub removeUUIDs { + return join("\n", grep { $_ !~ /^UUID:/ } split(/\n/, $_[0]))."\n"; + } + + ${concatStrings (mapAttrsToList (_: getAttr "testSubs") vms)} + + $machine->waitForX; + + ${mkLog "$HOME/.config/VirtualBox/VBoxSVC.log" "HOST-SVC"} + + ${testScript} + ''; + + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aszlig cdepillabout ]; + }; + }; + + unfreeTests = mapAttrs (mkVBoxTest true vboxVMsWithExtpack) { + enable-extension-pack = '' + createVM_testExtensionPack; + vbm("startvm testExtensionPack"); + waitForStartup_testExtensionPack; + $machine->screenshot("cli_started"); + waitForVMBoot_testExtensionPack; + $machine->screenshot("cli_booted"); + + $machine->nest("Checking for privilege escalation", sub { + $machine->fail("test -e '/root/VirtualBox VMs'"); + $machine->fail("test -e '/root/.config/VirtualBox'"); + $machine->succeed("test -e '/home/alice/VirtualBox VMs'"); + }); + + shutdownVM_testExtensionPack; + destroyVM_testExtensionPack; + ''; + }; + +in mapAttrs (mkVBoxTest false vboxVMs) { + simple-gui = '' + createVM_simple; + $machine->succeed(ru "VirtualBox &"); + $machine->waitUntilSucceeds( + ru "xprop -name 'Oracle VM VirtualBox Manager'" + ); + $machine->sleep(5); + $machine->screenshot("gui_manager_started"); + $machine->sendKeys("ret"); + $machine->screenshot("gui_manager_sent_startup"); + waitForStartup_simple (sub { + $machine->sendKeys("ret"); + }); + $machine->screenshot("gui_started"); + waitForVMBoot_simple; + $machine->screenshot("gui_booted"); + shutdownVM_simple; + $machine->sleep(5); + $machine->screenshot("gui_stopped"); + $machine->sendKeys("ctrl-q"); + $machine->sleep(5); + $machine->screenshot("gui_manager_stopped"); + destroyVM_simple; + ''; + + simple-cli = '' + createVM_simple; + vbm("startvm simple"); + waitForStartup_simple; + $machine->screenshot("cli_started"); + waitForVMBoot_simple; + $machine->screenshot("cli_booted"); + + $machine->nest("Checking for privilege escalation", sub { + $machine->fail("test -e '/root/VirtualBox VMs'"); + $machine->fail("test -e '/root/.config/VirtualBox'"); + $machine->succeed("test -e '/home/alice/VirtualBox VMs'"); + }); + + shutdownVM_simple; + destroyVM_simple; + ''; + + headless = '' + createVM_headless; + $machine->succeed(ru("VBoxHeadless --startvm headless & disown %1")); + waitForStartup_headless; + waitForVMBoot_headless; + shutdownVM_headless; + destroyVM_headless; + ''; + + host-usb-permissions = '' + my $userUSB = removeUUIDs vbm("list usbhost"); + print STDERR $userUSB; + my $rootUSB = removeUUIDs $machine->succeed("VBoxManage list usbhost"); + print STDERR $rootUSB; + + die "USB host devices differ for root and normal user" + if $userUSB ne $rootUSB; + die "No USB host devices found" if $userUSB =~ /<none>/; + ''; + + systemd-detect-virt = '' + createVM_detectvirt; + vbm("startvm detectvirt"); + waitForStartup_detectvirt; + waitForVMBoot_detectvirt; + shutdownVM_detectvirt; + my $result = $machine->succeed("cat '$detectvirt_sharepath/result'"); + chomp $result; + destroyVM_detectvirt; + die "systemd-detect-virt returned \"$result\" instead of \"oracle\"" + if $result ne "oracle"; + ''; + + net-hostonlyif = '' + createVM_test1; + createVM_test2; + + vbm("startvm test1"); + waitForStartup_test1; + waitForVMBoot_test1; + + vbm("startvm test2"); + waitForStartup_test2; + waitForVMBoot_test2; + + $machine->screenshot("net_booted"); + + my $test1IP = waitForIP_test1 1; + my $test2IP = waitForIP_test2 1; + + $machine->succeed("echo '$test2IP' | nc -N '$test1IP' 1234"); + $machine->succeed("echo '$test1IP' | nc -N '$test2IP' 1234"); + + $machine->waitUntilSucceeds("nc -N '$test1IP' 5678 < /dev/null >&2"); + $machine->waitUntilSucceeds("nc -N '$test2IP' 5678 < /dev/null >&2"); + + shutdownVM_test1; + shutdownVM_test2; + + destroyVM_test1; + destroyVM_test2; + ''; +} // (if enableUnfree then unfreeTests else {}) diff --git a/nixpkgs/nixos/tests/wordpress.nix b/nixpkgs/nixos/tests/wordpress.nix new file mode 100644 index 000000000000..5003e25a7d5b --- /dev/null +++ b/nixpkgs/nixos/tests/wordpress.nix @@ -0,0 +1,53 @@ +import ./make-test.nix ({ pkgs, ... }: + +{ + name = "wordpress"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ grahamc ]; # under duress! + }; + + nodes = + { web = + { pkgs, ... }: + { + services.mysql = { + enable = true; + package = pkgs.mysql; + }; + services.httpd = { + enable = true; + logPerVirtualHost = true; + adminAddr="js@lastlog.de"; + + virtualHosts = [ + { + hostName = "wordpress"; + extraSubservices = + [ + { + serviceType = "wordpress"; + dbPassword = "wordpress"; + dbHost = "127.0.0.1"; + languages = [ "de_DE" "en_GB" ]; + } + ]; + } + ]; + }; + }; + }; + + testScript = + { ... }: + '' + startAll; + + $web->waitForUnit("mysql"); + $web->waitForUnit("httpd"); + + $web->succeed("curl -L 127.0.0.1:80 | grep 'Welcome to the famous'"); + + + ''; + +}) diff --git a/nixpkgs/nixos/tests/xautolock.nix b/nixpkgs/nixos/tests/xautolock.nix new file mode 100644 index 000000000000..ee46d9e05b06 --- /dev/null +++ b/nixpkgs/nixos/tests/xautolock.nix @@ -0,0 +1,24 @@ +import ./make-test.nix ({ pkgs, lib, ... }: + +with lib; + +{ + name = "xautolock"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ ma27 ]; + + nodes.machine = { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + + services.xserver.displayManager.auto.user = "bob"; + services.xserver.xautolock.enable = true; + services.xserver.xautolock.time = 1; + }; + + testScript = '' + $machine->start; + $machine->waitForX; + $machine->mustFail("pgrep xlock"); + $machine->sleep(120); + $machine->mustSucceed("pgrep xlock"); + ''; +}) diff --git a/nixpkgs/nixos/tests/xdg-desktop-portal.nix b/nixpkgs/nixos/tests/xdg-desktop-portal.nix new file mode 100644 index 000000000000..79ebb83c49a5 --- /dev/null +++ b/nixpkgs/nixos/tests/xdg-desktop-portal.nix @@ -0,0 +1,17 @@ +# run installed tests +import ./make-test.nix ({ pkgs, ... }: + +{ + name = "xdg-desktop-portal"; + meta = { + maintainers = pkgs.xdg-desktop-portal.meta.maintainers; + }; + + machine = { pkgs, ... }: { + environment.systemPackages = with pkgs; [ gnome-desktop-testing ]; + }; + + testScript = '' + $machine->succeed("gnome-desktop-testing-runner -d '${pkgs.xdg-desktop-portal.installedTests}/share'"); + ''; +}) diff --git a/nixpkgs/nixos/tests/xfce.nix b/nixpkgs/nixos/tests/xfce.nix new file mode 100644 index 000000000000..47717e8cf7d9 --- /dev/null +++ b/nixpkgs/nixos/tests/xfce.nix @@ -0,0 +1,42 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "xfce"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow shlevy ]; + }; + + machine = + { pkgs, ... }: + + { imports = [ ./common/user-account.nix ]; + + services.xserver.enable = true; + + services.xserver.displayManager.auto.enable = true; + services.xserver.displayManager.auto.user = "alice"; + + services.xserver.desktopManager.xfce.enable = true; + + environment.systemPackages = [ pkgs.xorg.xmessage ]; + }; + + testScript = + '' + $machine->waitForX; + $machine->waitForFile("/home/alice/.Xauthority"); + $machine->succeed("xauth merge ~alice/.Xauthority"); + $machine->waitForWindow(qr/xfce4-panel/); + $machine->sleep(10); + + # Check that logging in has given the user ownership of devices. + $machine->succeed("getfacl /dev/snd/timer | grep -q alice"); + + $machine->succeed("su - alice -c 'DISPLAY=:0.0 xfce4-terminal &'"); + $machine->waitForWindow(qr/Terminal/); + $machine->sleep(10); + $machine->screenshot("screen"); + + # Ensure that the X server does proper access control. + $machine->mustFail("su - bob -c 'DISPLAY=:0.0 xmessage Foo'"); + $machine->mustFail("su - bob -c 'DISPLAY=:0 xmessage Foo'"); + ''; +}) diff --git a/nixpkgs/nixos/tests/xmonad.nix b/nixpkgs/nixos/tests/xmonad.nix new file mode 100644 index 000000000000..6d6db6b0ea97 --- /dev/null +++ b/nixpkgs/nixos/tests/xmonad.nix @@ -0,0 +1,29 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "xmonad"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ nequissimus ]; + }; + + machine = { lib, pkgs, ... }: { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + services.xserver.displayManager.auto.user = "alice"; + services.xserver.windowManager.default = lib.mkForce "xmonad"; + services.xserver.windowManager.xmonad = { + enable = true; + enableContribAndExtras = true; + extraPackages = with pkgs.haskellPackages; haskellPackages: [ xmobar ]; + }; + }; + + testScript = { ... }: '' + $machine->waitForX; + $machine->waitForFile("/home/alice/.Xauthority"); + $machine->succeed("xauth merge ~alice/.Xauthority"); + $machine->waitUntilSucceeds("xmonad --restart"); + $machine->sleep(3); + $machine->sendKeys("alt-shift-ret"); + $machine->waitForWindow(qr/alice.*machine/); + $machine->sleep(1); + $machine->screenshot("terminal"); + ''; +}) diff --git a/nixpkgs/nixos/tests/xrdp.nix b/nixpkgs/nixos/tests/xrdp.nix new file mode 100644 index 000000000000..0106aefe8318 --- /dev/null +++ b/nixpkgs/nixos/tests/xrdp.nix @@ -0,0 +1,45 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "xrdp"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ volth ]; + }; + + nodes = { + server = { pkgs, ... }: { + imports = [ ./common/user-account.nix ]; + services.xrdp.enable = true; + services.xrdp.defaultWindowManager = "${pkgs.xterm}/bin/xterm"; + networking.firewall.allowedTCPPorts = [ 3389 ]; + }; + + client = { pkgs, ... }: { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + services.xserver.displayManager.auto.user = "alice"; + environment.systemPackages = [ pkgs.freerdp ]; + services.xrdp.enable = true; + services.xrdp.defaultWindowManager = "${pkgs.icewm}/bin/icewm"; + }; + }; + + testScript = { ... }: '' + startAll; + + $client->waitForX; + $client->waitForFile("/home/alice/.Xauthority"); + $client->succeed("xauth merge ~alice/.Xauthority"); + + $client->sleep(5); + + $client->execute("xterm &"); + $client->sleep(1); + $client->sendChars("xfreerdp /cert-tofu /w:640 /h:480 /v:127.0.0.1 /u:alice /p:foobar\n"); + $client->sleep(5); + $client->screenshot("localrdp"); + + $client->execute("xterm &"); + $client->sleep(1); + $client->sendChars("xfreerdp /cert-tofu /w:640 /h:480 /v:server /u:alice /p:foobar\n"); + $client->sleep(5); + $client->screenshot("remoterdp"); + ''; +}) diff --git a/nixpkgs/nixos/tests/xss-lock.nix b/nixpkgs/nixos/tests/xss-lock.nix new file mode 100644 index 000000000000..b46bb1a8f6e9 --- /dev/null +++ b/nixpkgs/nixos/tests/xss-lock.nix @@ -0,0 +1,24 @@ +import ./make-test.nix ({ pkgs, lib, ... }: + +with lib; + +{ + name = "xss-lock"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ ma27 ]; + + machine = { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + programs.xss-lock.enable = true; + services.xserver.displayManager.auto.user = "alice"; + }; + + testScript = '' + $machine->start; + $machine->waitForX; + $machine->waitForUnit("xss-lock.service", "alice"); + + $machine->fail("pgrep xlock"); + $machine->succeed("su -l alice -c 'xset dpms force standby'"); + $machine->waitUntilSucceeds("pgrep i3lock"); + ''; +}) diff --git a/nixpkgs/nixos/tests/yabar.nix b/nixpkgs/nixos/tests/yabar.nix new file mode 100644 index 000000000000..bbc0cf4c7dd7 --- /dev/null +++ b/nixpkgs/nixos/tests/yabar.nix @@ -0,0 +1,33 @@ +import ./make-test.nix ({ pkgs, lib, ... }: + +with lib; + +{ + name = "yabar"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ma27 ]; + }; + + machine = { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + + services.xserver.displayManager.auto.user = "bob"; + + programs.yabar.enable = true; + programs.yabar.bars = { + top.indicators.date.exec = "YABAR_DATE"; + }; + }; + + testScript = '' + $machine->start; + $machine->waitForX; + + # confirm proper startup + $machine->waitForUnit("yabar.service", "bob"); + $machine->sleep(10); + $machine->waitForUnit("yabar.service", "bob"); + + $machine->screenshot("top_bar"); + ''; +}) diff --git a/nixpkgs/nixos/tests/zfs.nix b/nixpkgs/nixos/tests/zfs.nix new file mode 100644 index 000000000000..d7a08268e984 --- /dev/null +++ b/nixpkgs/nixos/tests/zfs.nix @@ -0,0 +1,88 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing.nix { inherit system pkgs; }; + +let + + makeTest = import ./make-test.nix; + + makeZfsTest = name: + { kernelPackage ? pkgs.linuxPackages_latest + , enableUnstable ? false + , extraTest ? "" + }: + makeTest { + name = "zfs-" + name; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ adisbladis ]; + }; + + machine = { pkgs, ... }: + { + virtualisation.emptyDiskImages = [ 4096 ]; + networking.hostId = "deadbeef"; + boot.kernelPackages = kernelPackage; + boot.supportedFilesystems = [ "zfs" ]; + boot.zfs.enableUnstable = enableUnstable; + + environment.systemPackages = with pkgs; [ + parted + ]; + }; + + testScript = '' + $machine->succeed("modprobe zfs"); + $machine->succeed("zpool status"); + + $machine->succeed("ls /dev"); + + $machine->succeed( + "mkdir /tmp/mnt", + + "udevadm settle", + "parted --script /dev/vdb mklabel msdos", + "parted --script /dev/vdb -- mkpart primary 1024M -1s", + "udevadm settle", + + "zpool create 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; + + }; + + +in { + + stable = makeZfsTest "stable" { }; + + 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/nixpkgs/nixos/tests/zookeeper.nix b/nixpkgs/nixos/tests/zookeeper.nix new file mode 100644 index 000000000000..f343ebd39e44 --- /dev/null +++ b/nixpkgs/nixos/tests/zookeeper.nix @@ -0,0 +1,28 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "zookeeper"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ nequissimus ]; + }; + + nodes = { + server = { ... }: { + services.zookeeper = { + enable = true; + }; + + networking.firewall.allowedTCPPorts = [ 2181 ]; + }; + }; + + testScript = '' + startAll; + + $server->waitForUnit("zookeeper"); + $server->waitForUnit("network.target"); + $server->waitForOpenPort(2181); + + $server->waitUntilSucceeds("${pkgs.zookeeper}/bin/zkCli.sh -server localhost:2181 create /foo bar"); + $server->waitUntilSucceeds("${pkgs.zookeeper}/bin/zkCli.sh -server localhost:2181 set /foo hello"); + $server->waitUntilSucceeds("${pkgs.zookeeper}/bin/zkCli.sh -server localhost:2181 get /foo | grep hello"); + ''; +}) |