diff options
Diffstat (limited to 'nixos')
47 files changed, 1220 insertions, 530 deletions
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix index d61bbaddf764..ecd62eb4e848 100644 --- a/nixos/doc/manual/default.nix +++ b/nixos/doc/manual/default.nix @@ -13,6 +13,8 @@ with pkgs; let + inherit (lib) hasPrefix removePrefix; + lib = pkgs.lib; docbook_xsl_ns = pkgs.docbook-xsl-ns.override { @@ -36,6 +38,33 @@ let }; }; + nixos-lib = import ../../lib { }; + + testOptionsDoc = let + eval = nixos-lib.evalTest { + # Avoid evaluating a NixOS config prototype. + config.node.type = lib.types.deferredModule; + options._module.args = lib.mkOption { internal = true; }; + }; + in buildPackages.nixosOptionsDoc { + inherit (eval) options; + inherit (revision); + transformOptions = opt: opt // { + # Clean up declaration sites to not refer to the NixOS source tree. + declarations = + map + (decl: + if hasPrefix (toString ../../..) (toString decl) + then + let subpath = removePrefix "/" (removePrefix (toString ../../..) (toString decl)); + in { url = "https://github.com/NixOS/nixpkgs/blob/master/${subpath}"; name = subpath; } + else decl) + opt.declarations; + }; + documentType = "none"; + variablelistId = "test-options-list"; + }; + sources = lib.sourceFilesBySuffices ./. [".xml"]; modulesDoc = builtins.toFile "modules.xml" '' @@ -50,6 +79,7 @@ let mkdir $out ln -s ${modulesDoc} $out/modules.xml ln -s ${optionsDoc.optionsDocBook} $out/options-db.xml + ln -s ${testOptionsDoc.optionsDocBook} $out/test-options-db.xml printf "%s" "${version}" > $out/version ''; diff --git a/nixos/doc/manual/development/running-nixos-tests-interactively.section.md b/nixos/doc/manual/development/running-nixos-tests-interactively.section.md index a1431859ff59..d9c316f4b139 100644 --- a/nixos/doc/manual/development/running-nixos-tests-interactively.section.md +++ b/nixos/doc/manual/development/running-nixos-tests-interactively.section.md @@ -24,6 +24,8 @@ back into the test driver command line upon its completion. This allows you to inspect the state of the VMs after the test (e.g. to debug the test script). +## Reuse VM state {#sec-nixos-test-reuse-vm-state} + You can re-use the VM states coming from a previous run by setting the `--keep-vm-state` flag. @@ -33,3 +35,15 @@ $ ./result/bin/nixos-test-driver --keep-vm-state The machine state is stored in the `$TMPDIR/vm-state-machinename` directory. + +## Interactive-only test configuration {#sec-nixos-test-interactive-configuration} + +The `.driverInteractive` attribute combines the regular test configuration with +definitions from the [`interactive` submodule](#opt-interactive). This gives you +a more usable, graphical, but slightly different configuration. + +You can add your own interactive-only test configuration by adding extra +configuration to the [`interactive` submodule](#opt-interactive). + +To interactively run only the regular configuration, build the `<test>.driver` attribute +instead, and call it with the flag `result/bin/nixos-test-driver --interactive`. diff --git a/nixos/doc/manual/development/running-nixos-tests.section.md b/nixos/doc/manual/development/running-nixos-tests.section.md index 1bec023b613a..33076f5dc2a7 100644 --- a/nixos/doc/manual/development/running-nixos-tests.section.md +++ b/nixos/doc/manual/development/running-nixos-tests.section.md @@ -2,22 +2,11 @@ You can run tests using `nix-build`. For example, to run the test [`login.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix), -you just do: +you do: ```ShellSession -$ nix-build '<nixpkgs/nixos/tests/login.nix>' -``` - -or, if you don't want to rely on `NIX_PATH`: - -```ShellSession -$ cd /my/nixpkgs/nixos/tests -$ nix-build login.nix -… -running the VM test script -machine: QEMU running (pid 8841) -… -6 out of 6 tests succeeded +$ cd /my/git/clone/of/nixpkgs +$ nix-build -A nixosTests.login ``` After building/downloading all required dependencies, this will perform diff --git a/nixos/doc/manual/development/writing-nixos-tests.section.md b/nixos/doc/manual/development/writing-nixos-tests.section.md index 6934bb0face7..99704ec3c141 100644 --- a/nixos/doc/manual/development/writing-nixos-tests.section.md +++ b/nixos/doc/manual/development/writing-nixos-tests.section.md @@ -1,9 +1,9 @@ # Writing Tests {#sec-writing-nixos-tests} -A NixOS test is a Nix expression that has the following structure: +A NixOS test is a module that has the following structure: ```nix -import ./make-test-python.nix { +{ # One or more machines: nodes = @@ -21,10 +21,13 @@ import ./make-test-python.nix { } ``` -The attribute `testScript` is a bit of Python code that executes the +We refer to the whole test above as a test module, whereas the values +in [`nodes.<name>`](#opt-nodes) are NixOS modules themselves. + +The option [`testScript`](#opt-testScript) is a piece of Python code that executes the test (described below). During the test, it will start one or more virtual machines, the configuration of which is described by -the attribute `nodes`. +the option [`nodes`](#opt-nodes). An example of a single-node test is [`login.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix). @@ -34,7 +37,54 @@ when switching between consoles, and so on. An interesting multi-node test is [`nfs/simple.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nfs/simple.nix). It uses two client nodes to test correct locking across server crashes. -There are a few special NixOS configuration options for test VMs: +## Calling a test {#sec-calling-nixos-tests} + +Tests are invoked differently depending on whether the test is part of NixOS or lives in a different project. + +### Testing within NixOS {#sec-call-nixos-test-in-nixos} + +Tests that are part of NixOS are added to [`nixos/tests/all-tests.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/all-tests.nix). + +```nix + hostname = runTest ./hostname.nix; +``` + +Overrides can be added by defining an anonymous module in `all-tests.nix`. + +```nix + hostname = runTest { + imports = [ ./hostname.nix ]; + defaults.networking.firewall.enable = false; + }; +``` + +You can run a test with attribute name `hostname` in `nixos/tests/all-tests.nix` by invoking: + +```shell +cd /my/git/clone/of/nixpkgs +nix-build -A nixosTests.hostname +``` + +### Testing outside the NixOS project {#sec-call-nixos-test-outside-nixos} + +Outside the `nixpkgs` repository, you can instantiate the test by first importing the NixOS library, + +```nix +let nixos-lib = import (nixpkgs + "/nixos/lib") { }; +in + +nixos-lib.runTest { + imports = [ ./test.nix ]; + hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs + defaults.services.foo.package = mypkg; +} +``` + +`runTest` returns a derivation that runs the test. + +## Configuring the nodes {#sec-nixos-test-nodes} + +There are a few special NixOS options for test VMs: `virtualisation.memorySize` @@ -121,7 +171,7 @@ The following methods are available on machine objects: least one will be returned. ::: {.note} - This requires passing `enableOCR` to the test attribute set. + This requires [`enableOCR`](#opt-enableOCR) to be set to `true`. ::: `get_screen_text` @@ -130,7 +180,7 @@ The following methods are available on machine objects: machine\'s screen using optical character recognition. ::: {.note} - This requires passing `enableOCR` to the test attribute set. + This requires [`enableOCR`](#opt-enableOCR) to be set to `true`. ::: `send_monitor_command` @@ -241,7 +291,7 @@ The following methods are available on machine objects: `get_screen_text` and `get_screen_text_variants`). ::: {.note} - This requires passing `enableOCR` to the test attribute set. + This requires [`enableOCR`](#opt-enableOCR) to be set to `true`. ::: `wait_for_console_text` @@ -304,7 +354,7 @@ For faster dev cycles it\'s also possible to disable the code-linters (this shouldn\'t be commited though): ```nix -import ./make-test-python.nix { +{ skipLint = true; nodes.machine = { config, pkgs, ... }: @@ -336,7 +386,7 @@ Similarly, the type checking of test scripts can be disabled in the following way: ```nix -import ./make-test-python.nix { +{ skipTypeCheck = true; nodes.machine = { config, pkgs, ... }: @@ -400,7 +450,6 @@ added using the parameter `extraPythonPackages`. For example, you could add `numpy` like this: ```nix -import ./make-test-python.nix { extraPythonPackages = p: [ p.numpy ]; @@ -417,3 +466,11 @@ import ./make-test-python.nix ``` In that case, `numpy` is chosen from the generic `python3Packages`. + +## Test Options Reference {#sec-test-options-reference} + +The following options can be used when writing tests. + +```{=docbook} +<xi:include href="../../generated/test-options-db.xml" xpointer="test-options-list"/> +``` diff --git a/nixos/doc/manual/from_md/development/running-nixos-tests-interactively.section.xml b/nixos/doc/manual/from_md/development/running-nixos-tests-interactively.section.xml index 0e47350a0d24..35d9bbd1c1fe 100644 --- a/nixos/doc/manual/from_md/development/running-nixos-tests-interactively.section.xml +++ b/nixos/doc/manual/from_md/development/running-nixos-tests-interactively.section.xml @@ -25,15 +25,40 @@ $ ./result/bin/nixos-test-driver completion. This allows you to inspect the state of the VMs after the test (e.g. to debug the test script). </para> - <para> - You can re-use the VM states coming from a previous run by setting - the <literal>--keep-vm-state</literal> flag. - </para> - <programlisting> + <section xml:id="sec-nixos-test-reuse-vm-state"> + <title>Reuse VM state</title> + <para> + You can re-use the VM states coming from a previous run by setting + the <literal>--keep-vm-state</literal> flag. + </para> + <programlisting> $ ./result/bin/nixos-test-driver --keep-vm-state </programlisting> - <para> - The machine state is stored in the - <literal>$TMPDIR/vm-state-machinename</literal> directory. - </para> + <para> + The machine state is stored in the + <literal>$TMPDIR/vm-state-machinename</literal> directory. + </para> + </section> + <section xml:id="sec-nixos-test-interactive-configuration"> + <title>Interactive-only test configuration</title> + <para> + The <literal>.driverInteractive</literal> attribute combines the + regular test configuration with definitions from the + <link linkend="opt-interactive"><literal>interactive</literal> + submodule</link>. This gives you a more usable, graphical, but + slightly different configuration. + </para> + <para> + You can add your own interactive-only test configuration by adding + extra configuration to the + <link linkend="opt-interactive"><literal>interactive</literal> + submodule</link>. + </para> + <para> + To interactively run only the regular configuration, build the + <literal><test>.driver</literal> attribute instead, and call + it with the flag + <literal>result/bin/nixos-test-driver --interactive</literal>. + </para> + </section> </section> diff --git a/nixos/doc/manual/from_md/development/running-nixos-tests.section.xml b/nixos/doc/manual/from_md/development/running-nixos-tests.section.xml index da2e5076c956..23abb546899f 100644 --- a/nixos/doc/manual/from_md/development/running-nixos-tests.section.xml +++ b/nixos/doc/manual/from_md/development/running-nixos-tests.section.xml @@ -4,22 +4,11 @@ You can run tests using <literal>nix-build</literal>. For example, to run the test <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix"><literal>login.nix</literal></link>, - you just do: + you do: </para> <programlisting> -$ nix-build '<nixpkgs/nixos/tests/login.nix>' -</programlisting> - <para> - or, if you don’t want to rely on <literal>NIX_PATH</literal>: - </para> - <programlisting> -$ cd /my/nixpkgs/nixos/tests -$ nix-build login.nix -… -running the VM test script -machine: QEMU running (pid 8841) -… -6 out of 6 tests succeeded +$ cd /my/git/clone/of/nixpkgs +$ nix-build -A nixosTests.login </programlisting> <para> After building/downloading all required dependencies, this will diff --git a/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml b/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml index d6f4f61c0645..32f5fdb77f50 100644 --- a/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml +++ b/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml @@ -1,10 +1,10 @@ -<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-writing-nixos-tests"> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="sec-writing-nixos-tests"> <title>Writing Tests</title> <para> - A NixOS test is a Nix expression that has the following structure: + A NixOS test is a module that has the following structure: </para> <programlisting language="bash"> -import ./make-test-python.nix { +{ # One or more machines: nodes = @@ -22,10 +22,18 @@ import ./make-test-python.nix { } </programlisting> <para> - The attribute <literal>testScript</literal> is a bit of Python code - that executes the test (described below). During the test, it will - start one or more virtual machines, the configuration of which is - described by the attribute <literal>nodes</literal>. + We refer to the whole test above as a test module, whereas the + values in + <link linkend="opt-nodes"><literal>nodes.<name></literal></link> + are NixOS modules themselves. + </para> + <para> + The option + <link linkend="opt-testScript"><literal>testScript</literal></link> + is a piece of Python code that executes the test (described below). + During the test, it will start one or more virtual machines, the + configuration of which is described by the option + <link linkend="opt-nodes"><literal>nodes</literal></link>. </para> <para> An example of a single-node test is @@ -38,78 +46,138 @@ import ./make-test-python.nix { It uses two client nodes to test correct locking across server crashes. </para> - <para> - There are a few special NixOS configuration options for test VMs: - </para> - <variablelist> - <varlistentry> - <term> - <literal>virtualisation.memorySize</literal> - </term> - <listitem> - <para> - The memory of the VM in megabytes. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <literal>virtualisation.vlans</literal> - </term> - <listitem> - <para> - The virtual networks to which the VM is connected. See - <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix"><literal>nat.nix</literal></link> - for an example. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <literal>virtualisation.writableStore</literal> - </term> - <listitem> - <para> - By default, the Nix store in the VM is not writable. If you - enable this option, a writable union file system is mounted on - top of the Nix store to make it appear writable. This is - necessary for tests that run Nix operations that modify the - store. - </para> - </listitem> - </varlistentry> - </variablelist> - <para> - For more options, see the module - <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix"><literal>qemu-vm.nix</literal></link>. - </para> - <para> - The test script is a sequence of Python statements that perform - various actions, such as starting VMs, executing commands in the - VMs, and so on. Each virtual machine is represented as an object - stored in the variable <literal>name</literal> if this is also the - identifier of the machine in the declarative config. If you - specified a node <literal>nodes.machine</literal>, the following - example starts the machine, waits until it has finished booting, - then executes a command and checks that the output is more-or-less - correct: - </para> - <programlisting language="python"> + <section xml:id="sec-calling-nixos-tests"> + <title>Calling a test</title> + <para> + Tests are invoked differently depending on whether the test is + part of NixOS or lives in a different project. + </para> + <section xml:id="sec-call-nixos-test-in-nixos"> + <title>Testing within NixOS</title> + <para> + Tests that are part of NixOS are added to + <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/all-tests.nix"><literal>nixos/tests/all-tests.nix</literal></link>. + </para> + <programlisting language="bash"> + hostname = runTest ./hostname.nix; +</programlisting> + <para> + Overrides can be added by defining an anonymous module in + <literal>all-tests.nix</literal>. + </para> + <programlisting language="bash"> + hostname = runTest { + imports = [ ./hostname.nix ]; + defaults.networking.firewall.enable = false; + }; +</programlisting> + <para> + You can run a test with attribute name + <literal>hostname</literal> in + <literal>nixos/tests/all-tests.nix</literal> by invoking: + </para> + <programlisting> +cd /my/git/clone/of/nixpkgs +nix-build -A nixosTests.hostname +</programlisting> + </section> + <section xml:id="sec-call-nixos-test-outside-nixos"> + <title>Testing outside the NixOS project</title> + <para> + Outside the <literal>nixpkgs</literal> repository, you can + instantiate the test by first importing the NixOS library, + </para> + <programlisting language="bash"> +let nixos-lib = import (nixpkgs + "/nixos/lib") { }; +in + +nixos-lib.runTest { + imports = [ ./test.nix ]; + hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs + defaults.services.foo.package = mypkg; +} +</programlisting> + <para> + <literal>runTest</literal> returns a derivation that runs the + test. + </para> + </section> + </section> + <section xml:id="sec-nixos-test-nodes"> + <title>Configuring the nodes</title> + <para> + There are a few special NixOS options for test VMs: + </para> + <variablelist> + <varlistentry> + <term> + <literal>virtualisation.memorySize</literal> + </term> + <listitem> + <para> + The memory of the VM in megabytes. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>virtualisation.vlans</literal> + </term> + <listitem> + <para> + The virtual networks to which the VM is connected. See + <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix"><literal>nat.nix</literal></link> + for an example. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>virtualisation.writableStore</literal> + </term> + <listitem> + <para> + By default, the Nix store in the VM is not writable. If you + enable this option, a writable union file system is mounted + on top of the Nix store to make it appear writable. This is + necessary for tests that run Nix operations that modify the + store. + </para> + </listitem> + </varlistentry> + </variablelist> + <para> + For more options, see the module + <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix"><literal>qemu-vm.nix</literal></link>. + </para> + <para> + The test script is a sequence of Python statements that perform + various actions, such as starting VMs, executing commands in the + VMs, and so on. Each virtual machine is represented as an object + stored in the variable <literal>name</literal> if this is also the + identifier of the machine in the declarative config. If you + specified a node <literal>nodes.machine</literal>, the following + example starts the machine, waits until it has finished booting, + then executes a command and checks that the output is more-or-less + correct: + </para> + <programlisting language="python"> machine.start() machine.wait_for_unit("default.target") if not "Linux" in machine.succeed("uname"): raise Exception("Wrong OS") </programlisting> - <para> - The first line is technically unnecessary; machines are implicitly - started when you first execute an action on them (such as - <literal>wait_for_unit</literal> or <literal>succeed</literal>). If - you have multiple machines, you can speed up the test by starting - them in parallel: - </para> - <programlisting language="python"> + <para> + The first line is technically unnecessary; machines are implicitly + started when you first execute an action on them (such as + <literal>wait_for_unit</literal> or <literal>succeed</literal>). + If you have multiple machines, you can speed up the test by + starting them in parallel: + </para> + <programlisting language="python"> start_all() </programlisting> + </section> <section xml:id="ssec-machine-objects"> <title>Machine objects</title> <para> @@ -194,8 +262,9 @@ start_all() </para> <note> <para> - This requires passing <literal>enableOCR</literal> to the - test attribute set. + This requires + <link linkend="opt-enableOCR"><literal>enableOCR</literal></link> + to be set to <literal>true</literal>. </para> </note> </listitem> @@ -211,8 +280,9 @@ start_all() </para> <note> <para> - This requires passing <literal>enableOCR</literal> to the - test attribute set. + This requires + <link linkend="opt-enableOCR"><literal>enableOCR</literal></link> + to be set to <literal>true</literal>. </para> </note> </listitem> @@ -451,8 +521,9 @@ start_all() </para> <note> <para> - This requires passing <literal>enableOCR</literal> to the - test attribute set. + This requires + <link linkend="opt-enableOCR"><literal>enableOCR</literal></link> + to be set to <literal>true</literal>. </para> </note> </listitem> @@ -563,7 +634,7 @@ machine.wait_for_unit("xautolock.service", "x-session-user") code-linters (this shouldn't be commited though): </para> <programlisting language="bash"> -import ./make-test-python.nix { +{ skipLint = true; nodes.machine = { config, pkgs, ... }: @@ -595,7 +666,7 @@ import ./make-test-python.nix { the following way: </para> <programlisting language="bash"> -import ./make-test-python.nix { +{ skipTypeCheck = true; nodes.machine = { config, pkgs, ... }: @@ -669,7 +740,6 @@ def foo_running(): <literal>numpy</literal> like this: </para> <programlisting language="bash"> -import ./make-test-python.nix { extraPythonPackages = p: [ p.numpy ]; @@ -689,4 +759,11 @@ import ./make-test-python.nix <literal>python3Packages</literal>. </para> </section> + <section xml:id="sec-test-options-reference"> + <title>Test Options Reference</title> + <para> + The following options can be used when writing tests. + </para> + <xi:include href="../../generated/test-options-db.xml" xpointer="test-options-list"/> + </section> </section> diff --git a/nixos/lib/build-vms.nix b/nixos/lib/build-vms.nix deleted file mode 100644 index 18af49db1777..000000000000 --- a/nixos/lib/build-vms.nix +++ /dev/null @@ -1,113 +0,0 @@ -{ system -, # Use a minimal kernel? - minimal ? false -, # Ignored - config ? null -, # Nixpkgs, for qemu, lib and more - pkgs, lib -, # !!! See comment about args in lib/modules.nix - specialArgs ? {} -, # NixOS configuration to add to the VMs - extraConfigurations ? [] -}: - -with lib; - -rec { - - inherit pkgs; - - # Build a virtual network from an attribute set `{ machine1 = - # config1; ... machineN = configN; }', where `machineX' is the - # hostname and `configX' is a NixOS system configuration. Each - # machine is given an arbitrary IP address in the virtual network. - buildVirtualNetwork = - nodes: let nodesOut = mapAttrs (n: buildVM nodesOut) (assignIPAddresses nodes); in nodesOut; - - - buildVM = - nodes: configurations: - - import ./eval-config.nix { - inherit system specialArgs; - modules = configurations ++ extraConfigurations; - baseModules = (import ../modules/module-list.nix) ++ - [ ../modules/virtualisation/qemu-vm.nix - ../modules/testing/test-instrumentation.nix # !!! should only get added for automated test runs - { key = "no-manual"; documentation.nixos.enable = false; } - { key = "no-revision"; - # Make the revision metadata constant, in order to avoid needless retesting. - # The human version (e.g. 21.05-pre) is left as is, because it is useful - # for external modules that test with e.g. testers.nixosTest and rely on that - # version number. - config.system.nixos.revision = mkForce "constant-nixos-revision"; - } - { key = "nodes"; _module.args.nodes = nodes; } - ] ++ optional minimal ../modules/testing/minimal-kernel.nix; - }; - - - # Given an attribute set { machine1 = config1; ... machineN = - # configN; }, sequentially assign IP addresses in the 192.168.1.0/24 - # range to each machine, and set the hostname to the attribute name. - assignIPAddresses = nodes: - - let - - machines = attrNames nodes; - - machinesNumbered = zipLists machines (range 1 254); - - nodes_ = forEach machinesNumbered (m: nameValuePair m.fst - [ ( { config, nodes, ... }: - let - interfacesNumbered = zipLists config.virtualisation.vlans (range 1 255); - interfaces = forEach interfacesNumbered ({ fst, snd }: - nameValuePair "eth${toString snd}" { ipv4.addresses = - [ { address = "192.168.${toString fst}.${toString m.snd}"; - prefixLength = 24; - } ]; - }); - - networkConfig = - { networking.hostName = mkDefault m.fst; - - networking.interfaces = listToAttrs interfaces; - - networking.primaryIPAddress = - optionalString (interfaces != []) (head (head interfaces).value.ipv4.addresses).address; - - # Put the IP addresses of all VMs in this machine's - # /etc/hosts file. If a machine has multiple - # interfaces, use the IP address corresponding to - # the first interface (i.e. the first network in its - # virtualisation.vlans option). - networking.extraHosts = flip concatMapStrings machines - (m': let config = (getAttr m' nodes).config; in - optionalString (config.networking.primaryIPAddress != "") - ("${config.networking.primaryIPAddress} " + - optionalString (config.networking.domain != null) - "${config.networking.hostName}.${config.networking.domain} " + - "${config.networking.hostName}\n")); - - virtualisation.qemu.options = - let qemu-common = import ../lib/qemu-common.nix { inherit lib pkgs; }; - in flip concatMap interfacesNumbered - ({ fst, snd }: qemu-common.qemuNICFlags snd fst m.snd); - }; - - in - { key = "ip-address"; - config = networkConfig // { - # Expose the networkConfig items for tests like nixops - # that need to recreate the network config. - system.build.networkConfig = networkConfig; - }; - } - ) - (getAttr m.fst nodes) - ] ); - - in listToAttrs nodes_; - -} diff --git a/nixos/lib/default.nix b/nixos/lib/default.nix index 2b3056e01457..65d91342d4d1 100644 --- a/nixos/lib/default.nix +++ b/nixos/lib/default.nix @@ -21,6 +21,8 @@ let seqAttrsIf = cond: a: lib.mapAttrs (_: v: seqIf cond a v); eval-config-minimal = import ./eval-config-minimal.nix { inherit lib; }; + + testing-lib = import ./testing/default.nix { inherit lib; }; in /* This attribute set appears as lib.nixos in the flake, or can be imported @@ -30,4 +32,10 @@ in inherit (seqAttrsIf (!featureFlags?minimalModules) minimalModulesWarning eval-config-minimal) evalModules ; + + inherit (testing-lib) + evalTest + runTest + ; + } diff --git a/nixos/lib/eval-config.nix b/nixos/lib/eval-config.nix index 791a03a3ba3c..1e086271e523 100644 --- a/nixos/lib/eval-config.nix +++ b/nixos/lib/eval-config.nix @@ -17,6 +17,8 @@ evalConfigArgs@ # be set modularly anyway. pkgs ? null , # !!! what do we gain by making this configurable? + # we can add modules that are included in specialisations, regardless + # of inheritParentConfig. baseModules ? import ../modules/module-list.nix , # !!! See comment about args in lib/modules.nix extraArgs ? {} diff --git a/nixos/lib/testing-python.nix b/nixos/lib/testing-python.nix index 4bb1689ffd78..c303b0bf17bc 100644 --- a/nixos/lib/testing-python.nix +++ b/nixos/lib/testing-python.nix @@ -12,159 +12,22 @@ with pkgs; +let + nixos-lib = import ./default.nix { inherit (pkgs) lib; }; +in + rec { inherit pkgs; - # Run an automated test suite in the given virtual network. - runTests = { driver, driverInteractive, pos }: - stdenv.mkDerivation { - name = "vm-test-run-${driver.testName}"; - - requiredSystemFeatures = [ "kvm" "nixos-test" ]; - - buildCommand = - '' - mkdir -p $out - - # effectively mute the XMLLogger - export LOGFILE=/dev/null - - ${driver}/bin/nixos-test-driver -o $out - ''; + evalTest = module: nixos-lib.evalTest { imports = [ extraTestModule module ]; }; + runTest = module: nixos-lib.runTest { imports = [ extraTestModule module ]; }; - passthru = driver.passthru // { - inherit driver driverInteractive; - }; - - inherit pos; # for better debugging + extraTestModule = { + config = { + hostPkgs = pkgs; }; - - # Generate convenience wrappers for running the test driver - # has vlans, vms and test script defaulted through env variables - # also instantiates test script with nodes, if it's a function (contract) - setupDriverForTest = { - testScript - , testName - , nodes - , qemu_pkg ? pkgs.qemu_test - , enableOCR ? false - , skipLint ? false - , skipTypeCheck ? false - , passthru ? {} - , interactive ? false - , extraPythonPackages ? (_ :[]) - }: - let - # Reifies and correctly wraps the python test driver for - # the respective qemu version and with or without ocr support - testDriver = pkgs.callPackage ./test-driver { - inherit enableOCR extraPythonPackages; - qemu_pkg = qemu_test; - imagemagick_light = imagemagick_light.override { inherit libtiff; }; - tesseract4 = tesseract4.override { enableLanguages = [ "eng" ]; }; - }; - - - testDriverName = - let - # A standard store path to the vm monitor is built like this: - # /tmp/nix-build-vm-test-run-$name.drv-0/vm-state-machine/monitor - # The max filename length of a unix domain socket is 108 bytes. - # This means $name can at most be 50 bytes long. - maxTestNameLen = 50; - testNameLen = builtins.stringLength testName; - in with builtins; - if testNameLen > maxTestNameLen then - abort - ("The name of the test '${testName}' must not be longer than ${toString maxTestNameLen} " + - "it's currently ${toString testNameLen} characters long.") - else - "nixos-test-driver-${testName}"; - - vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes); - vms = map (m: m.config.system.build.vm) (lib.attrValues nodes); - - nodeHostNames = let - nodesList = map (c: c.config.system.name) (lib.attrValues nodes); - in nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine"; - - # TODO: This is an implementation error and needs fixing - # the testing famework cannot legitimately restrict hostnames further - # beyond RFC1035 - invalidNodeNames = lib.filter - (node: builtins.match "^[A-z_]([A-z0-9_]+)?$" node == null) - nodeHostNames; - - testScript' = - # Call the test script with the computed nodes. - if lib.isFunction testScript - then testScript { inherit nodes; } - else testScript; - - uniqueVlans = lib.unique (builtins.concatLists vlans); - vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans; - machineNames = map (name: "${name}: Machine;") nodeHostNames; - in - if lib.length invalidNodeNames > 0 then - throw '' - Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})! - All machines are referenced as python variables in the testing framework which will break the - script when special characters are used. - - This is an IMPLEMENTATION ERROR and needs to be fixed. Meanwhile, - please stick to alphanumeric chars and underscores as separation. - '' - else lib.warnIf skipLint "Linting is disabled" (runCommand testDriverName - { - inherit testName; - nativeBuildInputs = [ makeWrapper mypy ]; - buildInputs = [ testDriver ]; - testScript = testScript'; - preferLocalBuild = true; - passthru = passthru // { - inherit nodes; - }; - meta.mainProgram = "nixos-test-driver"; - } - '' - mkdir -p $out/bin - - vmStartScripts=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done)) - - ${lib.optionalString (!skipTypeCheck) '' - # prepend type hints so the test script can be type checked with mypy - cat "${./test-script-prepend.py}" >> testScriptWithTypes - echo "${builtins.toString machineNames}" >> testScriptWithTypes - echo "${builtins.toString vlanNames}" >> testScriptWithTypes - echo -n "$testScript" >> testScriptWithTypes - - mypy --no-implicit-optional \ - --pretty \ - --no-color-output \ - testScriptWithTypes - ''} - - echo -n "$testScript" >> $out/test-script - - ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver - - ${testDriver}/bin/generate-driver-symbols - ${lib.optionalString (!skipLint) '' - PYFLAKES_BUILTINS="$( - echo -n ${lib.escapeShellArg (lib.concatStringsSep "," nodeHostNames)}, - < ${lib.escapeShellArg "driver-symbols"} - )" ${python3Packages.pyflakes}/bin/pyflakes $out/test-script - ''} - - # set defaults through environment - # see: ./test-driver/test-driver.py argparse implementation - wrapProgram $out/bin/nixos-test-driver \ - --set startScripts "''${vmStartScripts[*]}" \ - --set testScript "$out/test-script" \ - --set vlans '${toString vlans}' \ - ${lib.optionalString (interactive) "--add-flags --interactive"} - ''); + }; # Make a full-blown test makeTest = @@ -184,91 +47,20 @@ rec { then builtins.unsafeGetAttrPos "description" meta else builtins.unsafeGetAttrPos "testScript" t) , extraPythonPackages ? (_ : []) + , interactive ? {} } @ t: - let - mkNodes = qemu_pkg: - let - testScript' = - # Call the test script with the computed nodes. - if lib.isFunction testScript - then testScript { nodes = mkNodes qemu_pkg; } - else testScript; - - build-vms = import ./build-vms.nix { - inherit system lib pkgs minimal specialArgs; - extraConfigurations = extraConfigurations ++ [( - { config, ... }: - { - virtualisation.qemu.package = qemu_pkg; - - # Make sure all derivations referenced by the test - # script are available on the nodes. When the store is - # accessed through 9p, this isn't important, since - # everything in the store is available to the guest, - # but when building a root image it is, as all paths - # that should be available to the guest has to be - # copied to the image. - virtualisation.additionalPaths = - lib.optional - # A testScript may evaluate nodes, which has caused - # infinite recursions. The demand cycle involves: - # testScript --> - # nodes --> - # toplevel --> - # additionalPaths --> - # hasContext testScript' --> - # testScript (ad infinitum) - # If we don't need to build an image, we can break this - # cycle by short-circuiting when useNixStoreImage is false. - (config.virtualisation.useNixStoreImage && builtins.hasContext testScript') - (pkgs.writeStringReferencesToFile testScript'); - - # Ensure we do not use aliases. Ideally this is only set - # when the test framework is used by Nixpkgs NixOS tests. - nixpkgs.config.allowAliases = false; - } - )]; - }; - in - lib.warnIf (t?machine) "In test `${name}': The `machine' attribute in NixOS tests (pkgs.nixosTest / make-test-python.nix / testing-python.nix / makeTest) is deprecated. Please use the equivalent `nodes.machine'." - build-vms.buildVirtualNetwork ( - nodes // lib.optionalAttrs (machine != null) { inherit machine; } - ); - - driver = setupDriverForTest { - inherit testScript enableOCR skipTypeCheck skipLint passthru extraPythonPackages; - testName = name; - qemu_pkg = pkgs.qemu_test; - nodes = mkNodes pkgs.qemu_test; - }; - driverInteractive = setupDriverForTest { - inherit testScript enableOCR skipTypeCheck skipLint passthru extraPythonPackages; - testName = name; - qemu_pkg = pkgs.qemu; - nodes = mkNodes pkgs.qemu; - interactive = true; + runTest { + imports = [ + { _file = "makeTest parameters"; config = t; } + { + defaults = { + _file = "makeTest: extraConfigurations"; + imports = extraConfigurations; + }; + } + ]; }; - test = lib.addMetaAttrs meta (runTests { inherit driver pos driverInteractive; }); - - in - test // { - inherit test driver driverInteractive; - inherit (driver) nodes; - }; - - abortForFunction = functionName: abort ''The ${functionName} function was - removed because it is not an essential part of the NixOS testing - infrastructure. It had no usage in NixOS or Nixpkgs and it had no designated - maintainer. You are free to reintroduce it by documenting it in the manual - and adding yourself as maintainer. It was removed in - https://github.com/NixOS/nixpkgs/pull/137013 - ''; - - runInMachine = abortForFunction "runInMachine"; - - runInMachineWithX = abortForFunction "runInMachineWithX"; - simpleTest = as: (makeTest as).test; } diff --git a/nixos/lib/testing/call-test.nix b/nixos/lib/testing/call-test.nix new file mode 100644 index 000000000000..3e137e78cd47 --- /dev/null +++ b/nixos/lib/testing/call-test.nix @@ -0,0 +1,16 @@ +{ config, lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options = { + callTest = mkOption { + internal = true; + type = types.functionTo types.raw; + }; + result = mkOption { + internal = true; + default = config; + }; + }; +} diff --git a/nixos/lib/testing/default.nix b/nixos/lib/testing/default.nix new file mode 100644 index 000000000000..676d52f5c3fb --- /dev/null +++ b/nixos/lib/testing/default.nix @@ -0,0 +1,24 @@ +{ lib }: +let + + evalTest = module: lib.evalModules { modules = testModules ++ [ module ]; }; + runTest = module: (evalTest module).config.result; + + testModules = [ + ./call-test.nix + ./driver.nix + ./interactive.nix + ./legacy.nix + ./meta.nix + ./name.nix + ./network.nix + ./nodes.nix + ./pkgs.nix + ./run.nix + ./testScript.nix + ]; + +in +{ + inherit evalTest runTest testModules; +} diff --git a/nixos/lib/testing/driver.nix b/nixos/lib/testing/driver.nix new file mode 100644 index 000000000000..04e99f9e21d6 --- /dev/null +++ b/nixos/lib/testing/driver.nix @@ -0,0 +1,188 @@ +{ config, lib, hostPkgs, ... }: +let + inherit (lib) mkOption types literalMD mdDoc; + + # Reifies and correctly wraps the python test driver for + # the respective qemu version and with or without ocr support + testDriver = hostPkgs.callPackage ../test-driver { + inherit (config) enableOCR extraPythonPackages; + qemu_pkg = config.qemu.package; + imagemagick_light = hostPkgs.imagemagick_light.override { inherit (hostPkgs) libtiff; }; + tesseract4 = hostPkgs.tesseract4.override { enableLanguages = [ "eng" ]; }; + }; + + + vlans = map (m: m.virtualisation.vlans) (lib.attrValues config.nodes); + vms = map (m: m.system.build.vm) (lib.attrValues config.nodes); + + nodeHostNames = + let + nodesList = map (c: c.system.name) (lib.attrValues config.nodes); + in + nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine"; + + # TODO: This is an implementation error and needs fixing + # the testing famework cannot legitimately restrict hostnames further + # beyond RFC1035 + invalidNodeNames = lib.filter + (node: builtins.match "^[A-z_]([A-z0-9_]+)?$" node == null) + nodeHostNames; + + uniqueVlans = lib.unique (builtins.concatLists vlans); + vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans; + machineNames = map (name: "${name}: Machine;") nodeHostNames; + + withChecks = + if lib.length invalidNodeNames > 0 then + throw '' + Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})! + All machines are referenced as python variables in the testing framework which will break the + script when special characters are used. + + This is an IMPLEMENTATION ERROR and needs to be fixed. Meanwhile, + please stick to alphanumeric chars and underscores as separation. + '' + else + lib.warnIf config.skipLint "Linting is disabled"; + + driver = + hostPkgs.runCommand "nixos-test-driver-${config.name}" + { + # inherit testName; TODO (roberth): need this? + nativeBuildInputs = [ + hostPkgs.makeWrapper + ] ++ lib.optionals (!config.skipTypeCheck) [ hostPkgs.mypy ]; + buildInputs = [ testDriver ]; + testScript = config.testScriptString; + preferLocalBuild = true; + passthru = config.passthru; + meta = config.meta // { + mainProgram = "nixos-test-driver"; + }; + } + '' + mkdir -p $out/bin + + vmStartScripts=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done)) + + ${lib.optionalString (!config.skipTypeCheck) '' + # prepend type hints so the test script can be type checked with mypy + cat "${../test-script-prepend.py}" >> testScriptWithTypes + echo "${builtins.toString machineNames}" >> testScriptWithTypes + echo "${builtins.toString vlanNames}" >> testScriptWithTypes + echo -n "$testScript" >> testScriptWithTypes + + cat -n testScriptWithTypes + + mypy --no-implicit-optional \ + --pretty \ + --no-color-output \ + testScriptWithTypes + ''} + + echo -n "$testScript" >> $out/test-script + + ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver + + ${testDriver}/bin/generate-driver-symbols + ${lib.optionalString (!config.skipLint) '' + PYFLAKES_BUILTINS="$( + echo -n ${lib.escapeShellArg (lib.concatStringsSep "," nodeHostNames)}, + < ${lib.escapeShellArg "driver-symbols"} + )" ${hostPkgs.python3Packages.pyflakes}/bin/pyflakes $out/test-script + ''} + + # set defaults through environment + # see: ./test-driver/test-driver.py argparse implementation + wrapProgram $out/bin/nixos-test-driver \ + --set startScripts "''${vmStartScripts[*]}" \ + --set testScript "$out/test-script" \ + --set vlans '${toString vlans}' \ + ${lib.escapeShellArgs (lib.concatMap (arg: ["--add-flags" arg]) config.extraDriverArgs)} + ''; + +in +{ + options = { + + driver = mkOption { + description = mdDoc "Package containing a script that runs the test."; + type = types.package; + defaultText = literalMD "set by the test framework"; + }; + + hostPkgs = mkOption { + description = mdDoc "Nixpkgs attrset used outside the nodes."; + type = types.raw; + example = lib.literalExpression '' + import nixpkgs { inherit system config overlays; } + ''; + }; + + qemu.package = mkOption { + description = mdDoc "Which qemu package to use for the virtualisation of [{option}`nodes`](#opt-nodes)."; + type = types.package; + default = hostPkgs.qemu_test; + defaultText = "hostPkgs.qemu_test"; + }; + + enableOCR = mkOption { + description = mdDoc '' + Whether to enable Optical Character Recognition functionality for + testing graphical programs. See [Machine objects](`ssec-machine-objects`). + ''; + type = types.bool; + default = false; + }; + + extraPythonPackages = mkOption { + description = mdDoc '' + Python packages to add to the test driver. + + The argument is a Python package set, similar to `pkgs.pythonPackages`. + ''; + example = lib.literalExpression '' + p: [ p.numpy ] + ''; + type = types.functionTo (types.listOf types.package); + default = ps: [ ]; + }; + + extraDriverArgs = mkOption { + description = mdDoc '' + Extra arguments to pass to the test driver. + + They become part of [{option}`driver`](#opt-driver) via `wrapProgram`. + ''; + type = types.listOf types.str; + default = []; + }; + + skipLint = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Do not run the linters. This may speed up your iteration cycle, but it is not something you should commit. + ''; + }; + + skipTypeCheck = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Disable type checking. This must not be enabled for new NixOS tests. + + This may speed up your iteration cycle, unless you're working on the [{option}`testScript`](#opt-testScript). + ''; + }; + }; + + config = { + _module.args.hostPkgs = config.hostPkgs; + + driver = withChecks driver; + + # make available on the test runner + passthru.driver = config.driver; + }; +} diff --git a/nixos/lib/testing/interactive.nix b/nixos/lib/testing/interactive.nix new file mode 100644 index 000000000000..317ed4241882 --- /dev/null +++ b/nixos/lib/testing/interactive.nix @@ -0,0 +1,45 @@ +{ config, lib, moduleType, hostPkgs, ... }: +let + inherit (lib) mkOption types mdDoc; +in +{ + options = { + interactive = mkOption { + description = mdDoc '' + Tests [can be run interactively](#sec-running-nixos-tests-interactively) + using the program in the test derivation's `.driverInteractive` attribute. + + When they are, the configuration will include anything set in this submodule. + + You can set any top-level test option here. + + Example test module: + + ```nix + { config, lib, ... }: { + + nodes.rabbitmq = { + services.rabbitmq.enable = true; + }; + + # When running interactively ... + interactive.nodes.rabbitmq = { + # ... enable the web ui. + services.rabbitmq.managementPlugin.enable = true; + }; + } + ``` + + For details, see the section about [running tests interactively](#sec-running-nixos-tests-interactively). + ''; + type = moduleType; + visible = "shallow"; + }; + }; + + config = { + interactive.qemu.package = hostPkgs.qemu; + interactive.extraDriverArgs = [ "--interactive" ]; + passthru.driverInteractive = config.interactive.driver; + }; +} diff --git a/nixos/lib/testing/legacy.nix b/nixos/lib/testing/legacy.nix new file mode 100644 index 000000000000..868b8b65b17d --- /dev/null +++ b/nixos/lib/testing/legacy.nix @@ -0,0 +1,25 @@ +{ config, options, lib, ... }: +let + inherit (lib) mkIf mkOption types; +in +{ + # This needs options.warnings, which we don't have (yet?). + # imports = [ + # (lib.mkRenamedOptionModule [ "machine" ] [ "nodes" "machine" ]) + # ]; + + options = { + machine = mkOption { + internal = true; + type = types.raw; + }; + }; + + config = { + nodes = mkIf options.machine.isDefined ( + lib.warn + "In test `${config.name}': The `machine' attribute in NixOS tests (pkgs.nixosTest / make-test-python.nix / testing-python.nix / makeTest) is deprecated. Please set the equivalent `nodes.machine'." + { inherit (config) machine; } + ); + }; +} diff --git a/nixos/lib/testing/meta.nix b/nixos/lib/testing/meta.nix new file mode 100644 index 000000000000..4d8b0e0f1c43 --- /dev/null +++ b/nixos/lib/testing/meta.nix @@ -0,0 +1,42 @@ +{ lib, ... }: +let + inherit (lib) types mkOption mdDoc; +in +{ + options = { + meta = lib.mkOption { + description = mdDoc '' + The [`meta`](https://nixos.org/manual/nixpkgs/stable/#chap-meta) attributes that will be set on the returned derivations. + + Not all [`meta`](https://nixos.org/manual/nixpkgs/stable/#chap-meta) attributes are supported, but more can be added as desired. + ''; + apply = lib.filterAttrs (k: v: v != null); + type = types.submodule { + options = { + maintainers = lib.mkOption { + type = types.listOf types.raw; + default = []; + description = mdDoc '' + The [list of maintainers](https://nixos.org/manual/nixpkgs/stable/#var-meta-maintainers) for this test. + ''; + }; + timeout = lib.mkOption { + type = types.nullOr types.int; + default = null; # NOTE: null values are filtered out by `meta`. + description = mdDoc '' + The [{option}`test`](#opt-test)'s [`meta.timeout`](https://nixos.org/manual/nixpkgs/stable/#var-meta-timeout) in seconds. + ''; + }; + broken = lib.mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Sets the [`meta.broken`](https://nixos.org/manual/nixpkgs/stable/#var-meta-broken) attribute on the [{option}`test`](#opt-test) derivation. + ''; + }; + }; + }; + default = {}; + }; + }; +} diff --git a/nixos/lib/testing/name.nix b/nixos/lib/testing/name.nix new file mode 100644 index 000000000000..a54622e139bf --- /dev/null +++ b/nixos/lib/testing/name.nix @@ -0,0 +1,14 @@ +{ lib, ... }: +let + inherit (lib) mkOption types mdDoc; +in +{ + options.name = mkOption { + description = mdDoc '' + The name of the test. + + This is used in the derivation names of the [{option}`driver`](#opt-driver) and [{option}`test`](#opt-test) runner. + ''; + type = types.str; + }; +} diff --git a/nixos/lib/testing/network.nix b/nixos/lib/testing/network.nix new file mode 100644 index 000000000000..04ea9a2bc9f7 --- /dev/null +++ b/nixos/lib/testing/network.nix @@ -0,0 +1,117 @@ +{ lib, nodes, ... }: + +let + inherit (lib) + attrNames concatMap concatMapStrings flip forEach head + listToAttrs mkDefault mkOption nameValuePair optionalString + range types zipListsWith zipLists + mdDoc + ; + + nodeNumbers = + listToAttrs + (zipListsWith + nameValuePair + (attrNames nodes) + (range 1 254) + ); + + networkModule = { config, nodes, pkgs, ... }: + let + interfacesNumbered = zipLists config.virtualisation.vlans (range 1 255); + interfaces = forEach interfacesNumbered ({ fst, snd }: + nameValuePair "eth${toString snd}" { + ipv4.addresses = + [{ + address = "192.168.${toString fst}.${toString config.virtualisation.test.nodeNumber}"; + prefixLength = 24; + }]; + }); + + networkConfig = + { + networking.hostName = mkDefault config.virtualisation.test.nodeName; + + networking.interfaces = listToAttrs interfaces; + + networking.primaryIPAddress = + optionalString (interfaces != [ ]) (head (head interfaces).value.ipv4.addresses).address; + + # Put the IP addresses of all VMs in this machine's + # /etc/hosts file. If a machine has multiple + # interfaces, use the IP address corresponding to + # the first interface (i.e. the first network in its + # virtualisation.vlans option). + networking.extraHosts = flip concatMapStrings (attrNames nodes) + (m': + let config = nodes.${m'}; in + optionalString (config.networking.primaryIPAddress != "") + ("${config.networking.primaryIPAddress} " + + optionalString (config.networking.domain != null) + "${config.networking.hostName}.${config.networking.domain} " + + "${config.networking.hostName}\n")); + + virtualisation.qemu.options = + let qemu-common = import ../qemu-common.nix { inherit lib pkgs; }; + in + flip concatMap interfacesNumbered + ({ fst, snd }: qemu-common.qemuNICFlags snd fst config.virtualisation.test.nodeNumber); + }; + + in + { + key = "ip-address"; + config = networkConfig // { + # Expose the networkConfig items for tests like nixops + # that need to recreate the network config. + system.build.networkConfig = networkConfig; + }; + }; + + nodeNumberModule = (regular@{ config, name, ... }: { + options = { + virtualisation.test.nodeName = mkOption { + internal = true; + default = name; + # We need to force this in specilisations, otherwise it'd be + # readOnly = true; + description = mdDoc '' + The `name` in `nodes.<name>`; stable across `specialisations`. + ''; + }; + virtualisation.test.nodeNumber = mkOption { + internal = true; + type = types.int; + readOnly = true; + default = nodeNumbers.${config.virtualisation.test.nodeName}; + description = mdDoc '' + A unique number assigned for each node in `nodes`. + ''; + }; + + # specialisations override the `name` module argument, + # so we push the real `virtualisation.test.nodeName`. + specialisation = mkOption { + type = types.attrsOf (types.submodule { + options.configuration = mkOption { + type = types.submoduleWith { + modules = [ + { + config.virtualisation.test.nodeName = + # assert regular.config.virtualisation.test.nodeName != "configuration"; + regular.config.virtualisation.test.nodeName; + } + ]; + }; + }; + }); + }; + }; + }); + +in +{ + config = { + extraBaseModules = { imports = [ networkModule nodeNumberModule ]; }; + }; +} diff --git a/nixos/lib/testing/nixos-test-base.nix b/nixos/lib/testing/nixos-test-base.nix new file mode 100644 index 000000000000..59e6e3843367 --- /dev/null +++ b/nixos/lib/testing/nixos-test-base.nix @@ -0,0 +1,23 @@ +# A module containing the base imports and overrides that +# are always applied in NixOS VM tests, unconditionally, +# even in `inheritParentConfig = false` specialisations. +{ lib, ... }: +let + inherit (lib) mkForce; +in +{ + imports = [ + ../../modules/virtualisation/qemu-vm.nix + ../../modules/testing/test-instrumentation.nix # !!! should only get added for automated test runs + { key = "no-manual"; documentation.nixos.enable = false; } + { + key = "no-revision"; + # Make the revision metadata constant, in order to avoid needless retesting. + # The human version (e.g. 21.05-pre) is left as is, because it is useful + # for external modules that test with e.g. testers.nixosTest and rely on that + # version number. + config.system.nixos.revision = mkForce "constant-nixos-revision"; + } + + ]; +} diff --git a/nixos/lib/testing/nodes.nix b/nixos/lib/testing/nodes.nix new file mode 100644 index 000000000000..765af2878dfe --- /dev/null +++ b/nixos/lib/testing/nodes.nix @@ -0,0 +1,112 @@ +testModuleArgs@{ config, lib, hostPkgs, nodes, ... }: + +let + inherit (lib) mkOption mkForce optional types mapAttrs mkDefault mdDoc; + + system = hostPkgs.stdenv.hostPlatform.system; + + baseOS = + import ../eval-config.nix { + inherit system; + inherit (config.node) specialArgs; + modules = [ config.defaults ]; + baseModules = (import ../../modules/module-list.nix) ++ + [ + ./nixos-test-base.nix + { key = "nodes"; _module.args.nodes = config.nodesCompat; } + ({ config, ... }: + { + virtualisation.qemu.package = testModuleArgs.config.qemu.package; + + # Ensure we do not use aliases. Ideally this is only set + # when the test framework is used by Nixpkgs NixOS tests. + nixpkgs.config.allowAliases = false; + }) + testModuleArgs.config.extraBaseModules + ] ++ optional config.minimal ../../modules/testing/minimal-kernel.nix; + }; + + +in + +{ + + options = { + node.type = mkOption { + type = types.raw; + default = baseOS.type; + internal = true; + }; + + nodes = mkOption { + type = types.lazyAttrsOf config.node.type; + visible = "shallow"; + description = mdDoc '' + An attribute set of NixOS configuration modules. + + The configurations are augmented by the [`defaults`](#opt-defaults) option. + + They are assigned network addresses according to the `nixos/lib/testing/network.nix` module. + + A few special options are available, that aren't in a plain NixOS configuration. See [Configuring the nodes](#sec-nixos-test-nodes) + ''; + }; + + defaults = mkOption { + description = mdDoc '' + NixOS configuration that is applied to all [{option}`nodes`](#opt-nodes). + ''; + type = types.deferredModule; + default = { }; + }; + + extraBaseModules = mkOption { + description = mdDoc '' + NixOS configuration that, like [{option}`defaults`](#opt-defaults), is applied to all [{option}`nodes`](#opt-nodes) and can not be undone with [`specialisation.<name>.inheritParentConfig`](https://search.nixos.org/options?show=specialisation.%3Cname%3E.inheritParentConfig&from=0&size=50&sort=relevance&type=packages&query=specialisation). + ''; + type = types.deferredModule; + default = { }; + }; + + node.specialArgs = mkOption { + type = types.lazyAttrsOf types.raw; + default = { }; + description = mdDoc '' + An attribute set of arbitrary values that will be made available as module arguments during the resolution of module `imports`. + + Note that it is not possible to override these from within the NixOS configurations. If you argument is not relevant to `imports`, consider setting {option}`defaults._module.args.<name>` instead. + ''; + }; + + minimal = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Enable to configure all [{option}`nodes`](#opt-nodes) to run with a minimal kernel. + ''; + }; + + nodesCompat = mkOption { + internal = true; + description = mdDoc '' + Basically `_module.args.nodes`, but with backcompat and warnings added. + + This will go away. + ''; + }; + }; + + config = { + _module.args.nodes = config.nodesCompat; + nodesCompat = + mapAttrs + (name: config: config // { + config = lib.warn + "Module argument `nodes.${name}.config` is deprecated. Use `nodes.${name}` instead." + config; + }) + config.nodes; + + passthru.nodes = config.nodesCompat; + }; +} diff --git a/nixos/lib/testing/pkgs.nix b/nixos/lib/testing/pkgs.nix new file mode 100644 index 000000000000..22dd586868e3 --- /dev/null +++ b/nixos/lib/testing/pkgs.nix @@ -0,0 +1,11 @@ +{ config, lib, hostPkgs, ... }: +{ + config = { + # default pkgs for use in VMs + _module.args.pkgs = hostPkgs; + + defaults = { + # TODO: a module to set a shared pkgs, if options.nixpkgs.* is untouched by user (highestPrio) */ + }; + }; +} diff --git a/nixos/lib/testing/run.nix b/nixos/lib/testing/run.nix new file mode 100644 index 000000000000..0cd07d8afd21 --- /dev/null +++ b/nixos/lib/testing/run.nix @@ -0,0 +1,57 @@ +{ config, hostPkgs, lib, ... }: +let + inherit (lib) types mkOption mdDoc; +in +{ + options = { + passthru = mkOption { + type = types.lazyAttrsOf types.raw; + description = mdDoc '' + Attributes to add to the returned derivations, + which are not necessarily part of the build. + + This is a bit like doing `drv // { myAttr = true; }` (which would be lost by `overrideAttrs`). + It does not change the actual derivation, but adds the attribute nonetheless, so that + consumers of what would be `drv` have more information. + ''; + }; + + test = mkOption { + type = types.package; + # TODO: can the interactive driver be configured to access the network? + description = mdDoc '' + Derivation that runs the test as its "build" process. + + This implies that NixOS tests run isolated from the network, making them + more dependable. + ''; + }; + }; + + config = { + test = lib.lazyDerivation { # lazyDerivation improves performance when only passthru items and/or meta are used. + derivation = hostPkgs.stdenv.mkDerivation { + name = "vm-test-run-${config.name}"; + + requiredSystemFeatures = [ "kvm" "nixos-test" ]; + + buildCommand = '' + mkdir -p $out + + # effectively mute the XMLLogger + export LOGFILE=/dev/null + + ${config.driver}/bin/nixos-test-driver -o $out + ''; + + passthru = config.passthru; + + meta = config.meta; + }; + inherit (config) passthru meta; + }; + + # useful for inspection (debugging / exploration) + passthru.config = config; + }; +} diff --git a/nixos/lib/testing/testScript.nix b/nixos/lib/testing/testScript.nix new file mode 100644 index 000000000000..5d4181c5f5dd --- /dev/null +++ b/nixos/lib/testing/testScript.nix @@ -0,0 +1,84 @@ +testModuleArgs@{ config, lib, hostPkgs, nodes, moduleType, ... }: +let + inherit (lib) mkOption types mdDoc; + inherit (types) either str functionTo; +in +{ + options = { + testScript = mkOption { + type = either str (functionTo str); + description = '' + A series of python declarations and statements that you write to perform + the test. + ''; + }; + testScriptString = mkOption { + type = str; + readOnly = true; + internal = true; + }; + + includeTestScriptReferences = mkOption { + type = types.bool; + default = true; + internal = true; + }; + withoutTestScriptReferences = mkOption { + type = moduleType; + description = mdDoc '' + A parallel universe where the testScript is invalid and has no references. + ''; + internal = true; + visible = false; + }; + }; + config = { + withoutTestScriptReferences.includeTestScriptReferences = false; + withoutTestScriptReferences.testScript = lib.mkForce "testscript omitted"; + + testScriptString = + if lib.isFunction config.testScript + then + config.testScript + { + nodes = + lib.mapAttrs + (k: v: + if v.virtualisation.useNixStoreImage + then + # prevent infinite recursion when testScript would + # reference v's toplevel + config.withoutTestScriptReferences.nodesCompat.${k} + else + # reuse memoized config + v + ) + config.nodesCompat; + } + else config.testScript; + + defaults = { config, name, ... }: { + # Make sure all derivations referenced by the test + # script are available on the nodes. When the store is + # accessed through 9p, this isn't important, since + # everything in the store is available to the guest, + # but when building a root image it is, as all paths + # that should be available to the guest has to be + # copied to the image. + virtualisation.additionalPaths = + lib.optional + # A testScript may evaluate nodes, which has caused + # infinite recursions. The demand cycle involves: + # testScript --> + # nodes --> + # toplevel --> + # additionalPaths --> + # hasContext testScript' --> + # testScript (ad infinitum) + # If we don't need to build an image, we can break this + # cycle by short-circuiting when useNixStoreImage is false. + (config.virtualisation.useNixStoreImage && builtins.hasContext testModuleArgs.config.testScriptString && testModuleArgs.config.includeTestScriptReferences) + (hostPkgs.writeStringReferencesToFile testModuleArgs.config.testScriptString); + }; + }; +} diff --git a/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix b/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix index b4a94f62ad93..ced344bce234 100644 --- a/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix +++ b/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix @@ -15,7 +15,7 @@ let inherit system pkgs; }; - interactiveDriver = (testing.makeTest { inherit nodes; testScript = "start_all(); join_all();"; }).driverInteractive; + interactiveDriver = (testing.makeTest { inherit nodes; name = "network"; testScript = "start_all(); join_all();"; }).driverInteractive; in diff --git a/nixos/release.nix b/nixos/release.nix index f70b02c4292b..4f27e5dbb215 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -22,8 +22,8 @@ let import ./tests/all-tests.nix { inherit system; pkgs = import ./.. { inherit system; }; - callTest = t: { - ${system} = hydraJob t.test; + callTest = config: { + ${system} = hydraJob config.test; }; } // { # for typechecking of the scripts and evaluation of @@ -32,8 +32,8 @@ let import ./tests/all-tests.nix { inherit system; pkgs = import ./.. { inherit system; }; - callTest = t: { - ${system} = hydraJob t.test.driver; + callTest = config: { + ${system} = hydraJob config.driver; }; }; }; diff --git a/nixos/tests/3proxy.nix b/nixos/tests/3proxy.nix index 8127438fabd9..647d9d57c7ff 100644 --- a/nixos/tests/3proxy.nix +++ b/nixos/tests/3proxy.nix @@ -1,6 +1,6 @@ -import ./make-test-python.nix ({ pkgs, ...} : { +{ lib, pkgs, ... }: { name = "3proxy"; - meta = with pkgs.lib.maintainers; { + meta = with lib.maintainers; { maintainers = [ misuzu ]; }; @@ -92,7 +92,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { networking.firewall.allowedTCPPorts = [ 3128 9999 ]; }; - peer3 = { lib, ... }: { + peer3 = { lib, pkgs, ... }: { networking.useDHCP = false; networking.interfaces.eth1 = { ipv4.addresses = [ @@ -186,4 +186,4 @@ import ./make-test-python.nix ({ pkgs, ...} : { "${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://192.168.0.4:3128 -S -O /dev/null http://127.0.0.1:9999" ) ''; -}) +} diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index c07f99c5db3a..d3a436080ebf 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -1,7 +1,7 @@ -import ./make-test-python.nix ({ pkgs, lib, ... }: let +{ pkgs, lib, ... }: let commonConfig = ./common/acme/client; - dnsServerIP = nodes: nodes.dnsserver.config.networking.primaryIPAddress; + dnsServerIP = nodes: nodes.dnsserver.networking.primaryIPAddress; dnsScript = nodes: let dnsAddress = dnsServerIP nodes; @@ -153,7 +153,7 @@ in { description = "Pebble ACME challenge test server"; wantedBy = [ "network.target" ]; serviceConfig = { - ExecStart = "${pkgs.pebble}/bin/pebble-challtestsrv -dns01 ':53' -defaultIPv6 '' -defaultIPv4 '${nodes.webserver.config.networking.primaryIPAddress}'"; + ExecStart = "${pkgs.pebble}/bin/pebble-challtestsrv -dns01 ':53' -defaultIPv6 '' -defaultIPv4 '${nodes.webserver.networking.primaryIPAddress}'"; # Required to bind on privileged ports. AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; }; @@ -175,7 +175,7 @@ in { specialisation = { # First derivation used to test general ACME features general.configuration = { ... }: let - caDomain = nodes.acme.config.test-support.acme.caDomain; + caDomain = nodes.acme.test-support.acme.caDomain; email = config.security.acme.defaults.email; # Exit 99 to make it easier to track if this is the reason a renew failed accountCreateTester = '' @@ -316,7 +316,7 @@ in { testScript = { nodes, ... }: let - caDomain = nodes.acme.config.test-support.acme.caDomain; + caDomain = nodes.acme.test-support.acme.caDomain; newServerSystem = nodes.webserver.config.system.build.toplevel; switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test"; in @@ -438,7 +438,7 @@ in { client.wait_for_unit("default.target") client.succeed( - 'curl --data \'{"host": "${caDomain}", "addresses": ["${nodes.acme.config.networking.primaryIPAddress}"]}\' http://${dnsServerIP nodes}:8055/add-a' + 'curl --data \'{"host": "${caDomain}", "addresses": ["${nodes.acme.networking.primaryIPAddress}"]}\' http://${dnsServerIP nodes}:8055/add-a' ) acme.wait_for_unit("network-online.target") @@ -594,4 +594,4 @@ in { wait_for_server() check_connection_key_bits(client, test_domain, "384") ''; -}) +} diff --git a/nixos/tests/adguardhome.nix b/nixos/tests/adguardhome.nix index ddbe8ff9c117..1a220f996998 100644 --- a/nixos/tests/adguardhome.nix +++ b/nixos/tests/adguardhome.nix @@ -1,4 +1,4 @@ -import ./make-test-python.nix { +{ name = "adguardhome"; nodes = { diff --git a/nixos/tests/aesmd.nix b/nixos/tests/aesmd.nix index 9f07426be8d8..5da661afd548 100644 --- a/nixos/tests/aesmd.nix +++ b/nixos/tests/aesmd.nix @@ -1,4 +1,4 @@ -import ./make-test-python.nix ({ pkgs, lib, ... }: { +{ pkgs, lib, ... }: { name = "aesmd"; meta = { maintainers = with lib.maintainers; [ veehaitch ]; @@ -59,4 +59,4 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { assert aesmd_config == "whitelist url = http://nixos.org\nproxy type = direct\ndefault quoting type = ecdsa_256\n", "aesmd.conf differs" ''; -}) +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 5ea2c94ccb1c..a4ade096ef8c 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1,4 +1,11 @@ -{ system, pkgs, callTest }: +{ system, + pkgs, + + # Projects the test configuration into a the desired value; usually + # the test runner: `config: config.test`. + 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 @@ -11,9 +18,18 @@ 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; + if isAttrs val + then + if hasAttr "test" val then callTest val + else mapAttrs (n: s: discoverTests s) val + else if isFunction val + then + # Tests based on make-test-python.nix will return the second lambda + # in that file, which are then forwarded to the test definition + # following the `import make-test-python.nix` expression + # (if it is a function). + discoverTests (val { inherit system pkgs; }) + else val; handleTest = path: args: discoverTests (import path ({ inherit system pkgs; } // args)); handleTestOn = systems: path: args: @@ -27,12 +43,34 @@ let }; evalMinimalConfig = module: nixosLib.evalModules { modules = [ module ]; }; + inherit + (rec { + doRunTest = arg: (import ../lib/testing-python.nix { inherit system pkgs; }).runTest { + imports = [ arg { inherit callTest; } ]; + }; + findTests = tree: + if tree?recurseForDerivations && tree.recurseForDerivations + then + mapAttrs + (k: findTests) + (builtins.removeAttrs tree ["recurseForDerivations"]) + else callTest tree; + + runTest = arg: let r = doRunTest arg; in findTests r; + runTestOn = systems: arg: + if elem system systems then runTest arg + else {}; + }) + runTest + runTestOn + ; + in { - _3proxy = handleTest ./3proxy.nix {}; - acme = handleTest ./acme.nix {}; - adguardhome = handleTest ./adguardhome.nix {}; - aesmd = handleTest ./aesmd.nix {}; - agate = handleTest ./web-servers/agate.nix {}; + _3proxy = runTest ./3proxy.nix; + acme = runTest ./acme.nix; + adguardhome = runTest ./adguardhome.nix; + aesmd = runTest ./aesmd.nix; + agate = runTest ./web-servers/agate.nix; agda = handleTest ./agda.nix {}; airsonic = handleTest ./airsonic.nix {}; allTerminfo = handleTest ./all-terminfo.nix {}; diff --git a/nixos/tests/common/acme/client/default.nix b/nixos/tests/common/acme/client/default.nix index 9dbe345e7a01..503e610d1ac9 100644 --- a/nixos/tests/common/acme/client/default.nix +++ b/nixos/tests/common/acme/client/default.nix @@ -1,7 +1,7 @@ { lib, nodes, pkgs, ... }: let - caCert = nodes.acme.config.test-support.acme.caCert; - caDomain = nodes.acme.config.test-support.acme.caDomain; + caCert = nodes.acme.test-support.acme.caCert; + caDomain = nodes.acme.test-support.acme.caDomain; in { security.acme = { diff --git a/nixos/tests/common/acme/server/default.nix b/nixos/tests/common/acme/server/default.nix index fa1b9b545d09..b81f860125c8 100644 --- a/nixos/tests/common/acme/server/default.nix +++ b/nixos/tests/common/acme/server/default.nix @@ -18,10 +18,10 @@ # # example = { nodes, ... }: { # networking.nameservers = [ -# nodes.acme.config.networking.primaryIPAddress +# nodes.acme.networking.primaryIPAddress # ]; # security.pki.certificateFiles = [ -# nodes.acme.config.test-support.acme.caCert +# nodes.acme.test-support.acme.caCert # ]; # }; # } @@ -36,7 +36,7 @@ # acme = { nodes, lib, ... }: { # imports = [ ./common/acme/server ]; # networking.nameservers = lib.mkForce [ -# nodes.myresolver.config.networking.primaryIPAddress +# nodes.myresolver.networking.primaryIPAddress # ]; # }; # diff --git a/nixos/tests/corerad.nix b/nixos/tests/corerad.nix index 638010f92f44..b6f5d7fc6f75 100644 --- a/nixos/tests/corerad.nix +++ b/nixos/tests/corerad.nix @@ -1,5 +1,6 @@ import ./make-test-python.nix ( { + name = "corerad"; nodes = { router = {config, pkgs, ...}: { config = { diff --git a/nixos/tests/cri-o.nix b/nixos/tests/cri-o.nix index d3a8713d6a9b..08e1e8f36b06 100644 --- a/nixos/tests/cri-o.nix +++ b/nixos/tests/cri-o.nix @@ -1,7 +1,7 @@ # This test runs CRI-O and verifies via critest import ./make-test-python.nix ({ pkgs, ... }: { name = "cri-o"; - meta.maintainers = with pkgs.lib.maintainers; teams.podman.members; + meta.maintainers = with pkgs.lib; teams.podman.members; nodes = { crio = { diff --git a/nixos/tests/ghostunnel.nix b/nixos/tests/ghostunnel.nix index 8bea64854021..91a7b7085f67 100644 --- a/nixos/tests/ghostunnel.nix +++ b/nixos/tests/ghostunnel.nix @@ -1,4 +1,5 @@ import ./make-test-python.nix ({ pkgs, ... }: { + name = "ghostunnel"; nodes = { backend = { pkgs, ... }: { services.nginx.enable = true; diff --git a/nixos/tests/installed-tests/default.nix b/nixos/tests/installed-tests/default.nix index 3bb678d36782..b2c1b43f90ee 100644 --- a/nixos/tests/installed-tests/default.nix +++ b/nixos/tests/installed-tests/default.nix @@ -40,7 +40,7 @@ let name = tested.name; meta = { - maintainers = tested.meta.maintainers; + maintainers = tested.meta.maintainers or []; }; nodes.machine = { ... }: { diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index 8bef4fad3dd2..d9f64a781c57 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -324,6 +324,9 @@ let desktop-file-utils docbook5 docbook_xsl_ns + (docbook-xsl-ns.override { + withManOptDedupPatch = true; + }) kmod.dev libarchive.dev libxml2.bin @@ -333,6 +336,13 @@ let perlPackages.ListCompare perlPackages.XMLLibXML python3Minimal + # make-options-doc/default.nix + (let + self = (pkgs.python3Minimal.override { + inherit self; + includeSiteCustomize = true; + }); + in self.withPackages (p: [ p.mistune ])) shared-mime-info sudo texinfo diff --git a/nixos/tests/lorri/default.nix b/nixos/tests/lorri/default.nix index 209b87f9f26a..a4bdc92490ce 100644 --- a/nixos/tests/lorri/default.nix +++ b/nixos/tests/lorri/default.nix @@ -1,4 +1,6 @@ import ../make-test-python.nix { + name = "lorri"; + nodes.machine = { pkgs, ... }: { imports = [ ../../modules/profiles/minimal.nix ]; environment.systemPackages = [ pkgs.lorri ]; diff --git a/nixos/tests/matomo.nix b/nixos/tests/matomo.nix index 526a24fc4db7..0e09ad295f95 100644 --- a/nixos/tests/matomo.nix +++ b/nixos/tests/matomo.nix @@ -7,6 +7,8 @@ with pkgs.lib; let matomoTest = package: makeTest { + name = "matomo"; + nodes.machine = { config, pkgs, ... }: { services.matomo = { package = package; diff --git a/nixos/tests/matrix/conduit.nix b/nixos/tests/matrix/conduit.nix index 780837f962fa..2b81c23598eb 100644 --- a/nixos/tests/matrix/conduit.nix +++ b/nixos/tests/matrix/conduit.nix @@ -3,6 +3,8 @@ import ../make-test-python.nix ({ pkgs, ... }: name = "conduit"; in { + name = "matrix-conduit"; + nodes = { conduit = args: { services.matrix-conduit = { diff --git a/nixos/tests/nixops/default.nix b/nixos/tests/nixops/default.nix index 227b38815073..b77ac2476398 100644 --- a/nixos/tests/nixops/default.nix +++ b/nixos/tests/nixops/default.nix @@ -19,6 +19,7 @@ let }); testLegacyNetwork = { nixopsPkg }: pkgs.nixosTest ({ + name = "nixops-legacy-network"; nodes = { deployer = { config, lib, nodes, pkgs, ... }: { imports = [ ../../modules/installer/cd-dvd/channel.nix ]; diff --git a/nixos/tests/pam/pam-file-contents.nix b/nixos/tests/pam/pam-file-contents.nix index 86c61003aeb6..2bafd90618e9 100644 --- a/nixos/tests/pam/pam-file-contents.nix +++ b/nixos/tests/pam/pam-file-contents.nix @@ -2,6 +2,7 @@ let name = "pam"; in import ../make-test-python.nix ({ pkgs, ... }: { + name = "pam-file-contents"; nodes.machine = { ... }: { imports = [ ../../modules/profiles/minimal.nix ]; diff --git a/nixos/tests/pppd.nix b/nixos/tests/pppd.nix index bda0aa75bb50..e714a6c21a6c 100644 --- a/nixos/tests/pppd.nix +++ b/nixos/tests/pppd.nix @@ -5,6 +5,8 @@ import ./make-test-python.nix ( mode = "0640"; }; in { + name = "pppd"; + nodes = { server = {config, pkgs, ...}: { config = { diff --git a/nixos/tests/thelounge.nix b/nixos/tests/thelounge.nix index e9b85685bf2d..8d5a37d46c46 100644 --- a/nixos/tests/thelounge.nix +++ b/nixos/tests/thelounge.nix @@ -1,4 +1,6 @@ import ./make-test-python.nix { + name = "thelounge"; + nodes = { private = { config, pkgs, ... }: { services.thelounge = { diff --git a/nixos/tests/web-servers/agate.nix b/nixos/tests/web-servers/agate.nix index e364e134cfda..e8d789a9ca44 100644 --- a/nixos/tests/web-servers/agate.nix +++ b/nixos/tests/web-servers/agate.nix @@ -1,29 +1,27 @@ -import ../make-test-python.nix ( - { pkgs, lib, ... }: - { - name = "agate"; - meta = with lib.maintainers; { maintainers = [ jk ]; }; +{ pkgs, lib, ... }: +{ + name = "agate"; + meta = with lib.maintainers; { maintainers = [ jk ]; }; - nodes = { - geminiserver = { pkgs, ... }: { - services.agate = { - enable = true; - hostnames = [ "localhost" ]; - contentDir = pkgs.writeTextDir "index.gmi" '' - # Hello NixOS! - ''; - }; + nodes = { + geminiserver = { pkgs, ... }: { + services.agate = { + enable = true; + hostnames = [ "localhost" ]; + contentDir = pkgs.writeTextDir "index.gmi" '' + # Hello NixOS! + ''; }; }; + }; - testScript = { nodes, ... }: '' - geminiserver.wait_for_unit("agate") - geminiserver.wait_for_open_port(1965) + testScript = { nodes, ... }: '' + geminiserver.wait_for_unit("agate") + geminiserver.wait_for_open_port(1965) - with subtest("check is serving over gemini"): - response = geminiserver.succeed("${pkgs.gmni}/bin/gmni -j once -i -N gemini://localhost:1965") - print(response) - assert "Hello NixOS!" in response - ''; - } -) + with subtest("check is serving over gemini"): + response = geminiserver.succeed("${pkgs.gmni}/bin/gmni -j once -i -N gemini://localhost:1965") + print(response) + assert "Hello NixOS!" in response + ''; +} diff --git a/nixos/tests/zrepl.nix b/nixos/tests/zrepl.nix index 85dd834a6aaf..0ed73fea34b0 100644 --- a/nixos/tests/zrepl.nix +++ b/nixos/tests/zrepl.nix @@ -1,5 +1,7 @@ import ./make-test-python.nix ( { + name = "zrepl"; + nodes.host = {config, pkgs, ...}: { config = { # Prerequisites for ZFS and tests. |