diff options
Diffstat (limited to 'nixos')
23 files changed, 1094 insertions, 679 deletions
diff --git a/nixos/doc/manual/development/nixos-tests.xml b/nixos/doc/manual/development/nixos-tests.xml index 2695082e3867..702fc03f6686 100644 --- a/nixos/doc/manual/development/nixos-tests.xml +++ b/nixos/doc/manual/development/nixos-tests.xml @@ -13,7 +13,7 @@ xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/tests">nixos/test one or more virtual machines containing the NixOS system(s) required for the test. </para> - <xi:include href="writing-nixos-tests.xml" /> - <xi:include href="running-nixos-tests.xml" /> - <xi:include href="running-nixos-tests-interactively.xml" /> + <xi:include href="../from_md/development/writing-nixos-tests.section.xml" /> + <xi:include href="../from_md/development/running-nixos-tests.section.xml" /> + <xi:include href="../from_md/development/running-nixos-tests-interactively.section.xml" /> </chapter> diff --git a/nixos/doc/manual/development/running-nixos-tests-interactively.section.md b/nixos/doc/manual/development/running-nixos-tests-interactively.section.md new file mode 100644 index 000000000000..3ba4e16e77f4 --- /dev/null +++ b/nixos/doc/manual/development/running-nixos-tests-interactively.section.md @@ -0,0 +1,44 @@ +# Running Tests interactively {#sec-running-nixos-tests-interactively} + +The test itself can be run interactively. This is particularly useful +when developing or debugging a test: + +```ShellSession +$ nix-build nixos/tests/login.nix -A driverInteractive +$ ./result/bin/nixos-test-driver +starting VDE switch for network 1 +> +``` + +You can then take any Python statement, e.g. + +```py +> start_all() +> test_script() +> machine.succeed("touch /tmp/foo") +> print(machine.succeed("pwd")) # Show stdout of command +``` + +The function `test_script` executes the entire test script and drops you +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). + +To just start and experiment with the VMs, run: + +```ShellSession +$ nix-build nixos/tests/login.nix -A driverInteractive +$ ./result/bin/nixos-run-vms +``` + +The script `nixos-run-vms` starts the virtual machines defined by test. + +You can re-use the VM states coming from a previous run by setting the +`--keep-vm-state` flag. + +```ShellSession +$ ./result/bin/nixos-run-vms --keep-vm-state +``` + +The machine state is stored in the `$TMPDIR/vm-state-machinename` +directory. diff --git a/nixos/doc/manual/development/running-nixos-tests-interactively.xml b/nixos/doc/manual/development/running-nixos-tests-interactively.xml deleted file mode 100644 index a6044d5f89e8..000000000000 --- a/nixos/doc/manual/development/running-nixos-tests-interactively.xml +++ /dev/null @@ -1,49 +0,0 @@ -<section xmlns="http://docbook.org/ns/docbook" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:xi="http://www.w3.org/2001/XInclude" - version="5.0" - xml:id="sec-running-nixos-tests-interactively"> - <title>Running Tests interactively</title> - - <para> - The test itself can be run interactively. This is particularly useful when - developing or debugging a test: -<screen> -<prompt>$ </prompt>nix-build nixos/tests/login.nix -A driverInteractive -<prompt>$ </prompt>./result/bin/nixos-test-driver -starting VDE switch for network 1 -<prompt>></prompt> -</screen> - You can then take any Python statement, e.g. -<screen> -<prompt>></prompt> start_all() -<prompt>></prompt> test_script() -<prompt>></prompt> machine.succeed("touch /tmp/foo") -<prompt>></prompt> print(machine.succeed("pwd")) # Show stdout of command -</screen> - The function <command>test_script</command> executes the entire test script - and drops you 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). - </para> - - <para> - To just start and experiment with the VMs, run: -<screen> -<prompt>$ </prompt>nix-build nixos/tests/login.nix -A driverInteractive -<prompt>$ </prompt>./result/bin/nixos-run-vms -</screen> - The script <command>nixos-run-vms</command> starts the virtual machines - defined by test. - </para> - - <para> - You can re-use the VM states coming from a previous run - by setting the <command>--keep-vm-state</command> flag. -<screen> -<prompt>$ </prompt>./result/bin/nixos-run-vms --keep-vm-state -</screen> - The machine state is stored in the - <filename>$TMPDIR/vm-state-</filename><varname>machinename</varname> directory. - </para> -</section> diff --git a/nixos/doc/manual/development/running-nixos-tests.section.md b/nixos/doc/manual/development/running-nixos-tests.section.md new file mode 100644 index 000000000000..d6a456f01883 --- /dev/null +++ b/nixos/doc/manual/development/running-nixos-tests.section.md @@ -0,0 +1,31 @@ +# Running Tests {#sec-running-nixos-tests} + +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: + +```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 +``` + +After building/downloading all required dependencies, this will perform +a build that starts a QEMU/KVM virtual machine containing a NixOS +system. The virtual machine mounts the Nix store of the host; this makes +VM creation very fast, as no disk image needs to be created. Afterwards, +you can view a pretty-printed log of the test: + +```ShellSession +$ firefox result/log.html +``` diff --git a/nixos/doc/manual/development/running-nixos-tests.xml b/nixos/doc/manual/development/running-nixos-tests.xml deleted file mode 100644 index e9257c907daf..000000000000 --- a/nixos/doc/manual/development/running-nixos-tests.xml +++ /dev/null @@ -1,36 +0,0 @@ -<section xmlns="http://docbook.org/ns/docbook" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:xi="http://www.w3.org/2001/XInclude" - version="5.0" - xml:id="sec-running-nixos-tests"> - <title>Running Tests</title> - - <para> - You can run tests using <command>nix-build</command>. For example, to run the - test - <filename -xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix">login.nix</filename>, - you just do: -<screen> -<prompt>$ </prompt>nix-build '<nixpkgs/nixos/tests/login.nix>' -</screen> - or, if you don’t want to rely on <envar>NIX_PATH</envar>: -<screen> -<prompt>$ </prompt>cd /my/nixpkgs/nixos/tests -<prompt>$ </prompt>nix-build login.nix -… -running the VM test script -machine: QEMU running (pid 8841) -… -6 out of 6 tests succeeded -</screen> - After building/downloading all required dependencies, this will perform a - build that starts a QEMU/KVM virtual machine containing a NixOS system. The - virtual machine mounts the Nix store of the host; this makes VM creation very - fast, as no disk image needs to be created. Afterwards, you can view a - pretty-printed log of the test: -<screen> -<prompt>$ </prompt>firefox result/log.html -</screen> - </para> -</section> diff --git a/nixos/doc/manual/development/writing-nixos-tests.section.md b/nixos/doc/manual/development/writing-nixos-tests.section.md new file mode 100644 index 000000000000..8471e7608af9 --- /dev/null +++ b/nixos/doc/manual/development/writing-nixos-tests.section.md @@ -0,0 +1,301 @@ +# Writing Tests {#sec-writing-nixos-tests} + +A NixOS test is a Nix expression that has the following structure: + +```nix +import ./make-test-python.nix { + + # Either the configuration of a single machine: + machine = + { config, pkgs, ... }: + { configuration… + }; + + # Or a set of machines: + nodes = + { machine1 = + { config, pkgs, ... }: { … }; + machine2 = + { config, pkgs, ... }: { … }; + … + }; + + testScript = + '' + Python code… + ''; +} +``` + +The attribute `testScript` 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 `machine` (if you need only one machine in your test) or by +the attribute `nodes` (if you need multiple machines). For instance, +[`login.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix) +only needs a single machine to test whether users can log in +on the virtual console, whether device ownership is correctly maintained +when switching between consoles, and so on. On the other hand, +[`nfs/simple.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nfs/simple.nix), +which tests NFS client and server functionality in the +Linux kernel (including whether locks are maintained across server +crashes), requires three machines: a server and two clients. + +There are a few special NixOS configuration options for test VMs: + +`virtualisation.memorySize` + +: The memory of the VM in megabytes. + +`virtualisation.vlans` + +: The virtual networks to which the VM is connected. See + [`nat.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix) + for an example. + +`virtualisation.writableStore` + +: 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. + +For more options, see the module +[`qemu-vm.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix). + +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 +`name` if this is also the identifier of the machine in the declarative +config. If you didn\'t specify multiple machines using the `nodes` +attribute, it is just `machine`. 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: + +```py +machine.start() +machine.wait_for_unit("default.target") +if not "Linux" in machine.succeed("uname"): + raise Exception("Wrong OS") +``` + +The first line is actually unnecessary; machines are implicitly started +when you first execute an action on them (such as `wait_for_unit` or +`succeed`). If you have multiple machines, you can speed up the test by +starting them in parallel: + +```py +start_all() +``` + +The following methods are available on machine objects: + +`start` + +: Start the virtual machine. This method is asynchronous --- it does + not wait for the machine to finish booting. + +`shutdown` + +: Shut down the machine, waiting for the VM to exit. + +`crash` + +: Simulate a sudden power failure, by telling the VM to exit + immediately. + +`block` + +: Simulate unplugging the Ethernet cable that connects the machine to + the other machines. + +`unblock` + +: Undo the effect of `block`. + +`screenshot` + +: Take a picture of the display of the virtual machine, in PNG format. + The screenshot is linked from the HTML log. + +`get_screen_text_variants` + +: Return a list of different interpretations of what is currently + visible on the machine\'s screen using optical character + recognition. The number and order of the interpretations is not + specified and is subject to change, but if no exception is raised at + least one will be returned. + + ::: {.note} + This requires passing `enableOCR` to the test attribute set. + ::: + +`get_screen_text` + +: Return a textual representation of what is currently visible on the + machine\'s screen using optical character recognition. + + ::: {.note} + This requires passing `enableOCR` to the test attribute set. + ::: + +`send_monitor_command` + +: Send a command to the QEMU monitor. This is rarely used, but allows + doing stuff such as attaching virtual USB disks to a running + machine. + +`send_key` + +: Simulate pressing keys on the virtual keyboard, e.g., + `send_key("ctrl-alt-delete")`. + +`send_chars` + +: Simulate typing a sequence of characters on the virtual keyboard, + e.g., `send_chars("foobar\n")` will type the string `foobar` + followed by the Enter key. + +`execute` + +: Execute a shell command, returning a list `(status, stdout)`. + +`succeed` + +: Execute a shell command, raising an exception if the exit status is + not zero, otherwise returning the standard output. Commands are run + with `set -euo pipefail` set: + + - If several commands are separated by `;` and one fails, the + command as a whole will fail. + + - For pipelines, the last non-zero exit status will be returned + (if there is one, zero will be returned otherwise). + + - Dereferencing unset variables fail the command. + +`fail` + +: Like `succeed`, but raising an exception if the command returns a zero + status. + +`wait_until_succeeds` + +: Repeat a shell command with 1-second intervals until it succeeds. + +`wait_until_fails` + +: Repeat a shell command with 1-second intervals until it fails. + +`wait_for_unit` + +: Wait until the specified systemd unit has reached the "active" + state. + +`wait_for_file` + +: Wait until the specified file exists. + +`wait_for_open_port` + +: Wait until a process is listening on the given TCP port (on + `localhost`, at least). + +`wait_for_closed_port` + +: Wait until nobody is listening on the given TCP port. + +`wait_for_x` + +: Wait until the X11 server is accepting connections. + +`wait_for_text` + +: Wait until the supplied regular expressions matches the textual + contents of the screen by using optical character recognition (see + `get_screen_text` and `get_screen_text_variants`). + + ::: {.note} + This requires passing `enableOCR` to the test attribute set. + ::: + +`wait_for_console_text` + +: Wait until the supplied regular expressions match a line of the + serial console output. This method is useful when OCR is not + possibile or accurate enough. + +`wait_for_window` + +: Wait until an X11 window has appeared whose name matches the given + regular expression, e.g., `wait_for_window("Terminal")`. + +`copy_from_host` + +: Copies a file from host to machine, e.g., + `copy_from_host("myfile", "/etc/my/important/file")`. + + The first argument is the file on the host. The file needs to be + accessible while building the nix derivation. The second argument is + the location of the file on the machine. + +`systemctl` + +: Runs `systemctl` commands with optional support for + `systemctl --user` + + ```py + machine.systemctl("list-jobs --no-pager") # runs `systemctl list-jobs --no-pager` + machine.systemctl("list-jobs --no-pager", "any-user") # spawns a shell for `any-user` and runs `systemctl --user list-jobs --no-pager` + ``` + +`shell_interact` + +: Allows you to directly interact with the guest shell. This should + only be used during test development, not in production tests. + Killing the interactive session with `Ctrl-d` or `Ctrl-c` also ends + the guest session. + +To test user units declared by `systemd.user.services` the optional +`user` argument can be used: + +```py +machine.start() +machine.wait_for_x() +machine.wait_for_unit("xautolock.service", "x-session-user") +``` + +This applies to `systemctl`, `get_unit_info`, `wait_for_unit`, +`start_job` and `stop_job`. + +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; + machine = + { config, pkgs, ... }: + { configuration… + }; + + testScript = + '' + Python code… + ''; +} +``` + +This will produce a Nix warning at evaluation time. To fully disable the +linter, wrap the test script in comment directives to disable the Black +linter directly (again, don\'t commit this within the Nixpkgs +repository): + +```nix + testScript = + '' + # fmt: off + Python code… + # fmt: on + ''; +``` diff --git a/nixos/doc/manual/development/writing-nixos-tests.xml b/nixos/doc/manual/development/writing-nixos-tests.xml deleted file mode 100644 index e372c66410de..000000000000 --- a/nixos/doc/manual/development/writing-nixos-tests.xml +++ /dev/null @@ -1,517 +0,0 @@ -<section xmlns="http://docbook.org/ns/docbook" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:xi="http://www.w3.org/2001/XInclude" - version="5.0" - xml:id="sec-writing-nixos-tests"> - <title>Writing Tests</title> - - <para> - A NixOS test is a Nix expression that has the following structure: -<programlisting> -import ./make-test-python.nix { - - # Either the configuration of a single machine: - machine = - { config, pkgs, ... }: - { <replaceable>configuration…</replaceable> - }; - - # Or a set of machines: - nodes = - { <replaceable>machine1</replaceable> = - { config, pkgs, ... }: { <replaceable>…</replaceable> }; - <replaceable>machine2</replaceable> = - { config, pkgs, ... }: { <replaceable>…</replaceable> }; - … - }; - - testScript = - '' - <replaceable>Python code…</replaceable> - ''; -} -</programlisting> - 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>machine</literal> (if you need only one machine in your - test) or by the attribute <literal>nodes</literal> (if you need multiple - machines). For instance, - <filename -xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix">login.nix</filename> - only needs a single machine to test whether users can log in on the virtual - console, whether device ownership is correctly maintained when switching - between consoles, and so on. On the other hand, - <filename -xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nfs/simple.nix">nfs/simple.nix</filename>, - which tests NFS client and server functionality in the Linux kernel - (including whether locks are maintained across server crashes), requires - three machines: a server and two clients. - </para> - - <para> - There are a few special NixOS configuration options for test VMs: -<!-- FIXME: would be nice to generate this automatically. --> - <variablelist> - <varlistentry> - <term> - <option>virtualisation.memorySize</option> - </term> - <listitem> - <para> - The memory of the VM in megabytes. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <option>virtualisation.vlans</option> - </term> - <listitem> - <para> - The virtual networks to which the VM is connected. See - <filename - xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix">nat.nix</filename> - for an example. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <option>virtualisation.writableStore</option> - </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> - For more options, see the module - <filename -xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix">qemu-vm.nix</filename>. - </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><replaceable>name</replaceable></literal> if this is also the - identifier of the machine in the declarative config. - If you didn't specify multiple machines using the <literal>nodes</literal> - attribute, it is just <literal>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: -<programlisting> -machine.start() -machine.wait_for_unit("default.target") -if not "Linux" in machine.succeed("uname"): - raise Exception("Wrong OS") -</programlisting> - The first line is actually 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: -<programlisting> -start_all() -</programlisting> - </para> - - <para> - The following methods are available on machine objects: - <variablelist> - <varlistentry> - <term> - <methodname>start</methodname> - </term> - <listitem> - <para> - Start the virtual machine. This method is asynchronous — it does not - wait for the machine to finish booting. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>shutdown</methodname> - </term> - <listitem> - <para> - Shut down the machine, waiting for the VM to exit. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>crash</methodname> - </term> - <listitem> - <para> - Simulate a sudden power failure, by telling the VM to exit immediately. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>block</methodname> - </term> - <listitem> - <para> - Simulate unplugging the Ethernet cable that connects the machine to the - other machines. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>unblock</methodname> - </term> - <listitem> - <para> - Undo the effect of <methodname>block</methodname>. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>screenshot</methodname> - </term> - <listitem> - <para> - Take a picture of the display of the virtual machine, in PNG format. The - screenshot is linked from the HTML log. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>get_screen_text_variants</methodname> - </term> - <listitem> - <para> - Return a list of different interpretations of what is currently visible - on the machine's screen using optical character recognition. The number - and order of the interpretations is not specified and is subject to - change, but if no exception is raised at least one will be returned. - </para> - <note> - <para> - This requires passing <option>enableOCR</option> to the test attribute - set. - </para> - </note> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>get_screen_text</methodname> - </term> - <listitem> - <para> - Return a textual representation of what is currently visible on the - machine's screen using optical character recognition. - </para> - <note> - <para> - This requires passing <option>enableOCR</option> to the test attribute - set. - </para> - </note> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>send_monitor_command</methodname> - </term> - <listitem> - <para> - Send a command to the QEMU monitor. This is rarely used, but allows doing - stuff such as attaching virtual USB disks to a running machine. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>send_key</methodname> - </term> - <listitem> - <para> - Simulate pressing keys on the virtual keyboard, e.g., - <literal>send_key("ctrl-alt-delete")</literal>. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>send_chars</methodname> - </term> - <listitem> - <para> - Simulate typing a sequence of characters on the virtual keyboard, e.g., - <literal>send_chars("foobar\n")</literal> will type the string - <literal>foobar</literal> followed by the Enter key. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>execute</methodname> - </term> - <listitem> - <para> - Execute a shell command, returning a list - <literal>(<replaceable>status</replaceable>, - <replaceable>stdout</replaceable>)</literal>. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>succeed</methodname> - </term> - <listitem> - <para> - Execute a shell command, raising an exception if the exit status - is not zero, otherwise returning the standard output. Commands - are run with <literal>set -euo pipefail</literal> set: - <itemizedlist> - <listitem> - <para> - If several commands are separated by <literal>;</literal> - and one fails, the command as a whole will fail. - </para> - </listitem> - <listitem> - <para> - For pipelines, the last non-zero exit status will be - returned (if there is one, zero will be returned - otherwise). - </para> - </listitem> - <listitem> - <para> - Dereferencing unset variables fail the command. - </para> - </listitem> - </itemizedlist> - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>fail</methodname> - </term> - <listitem> - <para> - Like <methodname>succeed</methodname>, but raising an exception if the - command returns a zero status. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>wait_until_succeeds</methodname> - </term> - <listitem> - <para> - Repeat a shell command with 1-second intervals until it succeeds. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>wait_until_fails</methodname> - </term> - <listitem> - <para> - Repeat a shell command with 1-second intervals until it fails. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>wait_for_unit</methodname> - </term> - <listitem> - <para> - Wait until the specified systemd unit has reached the “active” state. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>wait_for_file</methodname> - </term> - <listitem> - <para> - Wait until the specified file exists. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>wait_for_open_port</methodname> - </term> - <listitem> - <para> - Wait until a process is listening on the given TCP port (on - <literal>localhost</literal>, at least). - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>wait_for_closed_port</methodname> - </term> - <listitem> - <para> - Wait until nobody is listening on the given TCP port. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>wait_for_x</methodname> - </term> - <listitem> - <para> - Wait until the X11 server is accepting connections. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>wait_for_text</methodname> - </term> - <listitem> - <para> - Wait until the supplied regular expressions matches the textual contents - of the screen by using optical character recognition (see - <methodname>get_screen_text</methodname> and - <methodname>get_screen_text_variants</methodname>). - </para> - <note> - <para> - This requires passing <option>enableOCR</option> to the test attribute - set. - </para> - </note> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>wait_for_console_text</methodname> - </term> - <listitem> - <para> - Wait until the supplied regular expressions match a line of the serial - console output. This method is useful when OCR is not possibile or - accurate enough. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>wait_for_window</methodname> - </term> - <listitem> - <para> - Wait until an X11 window has appeared whose name matches the given - regular expression, e.g., <literal>wait_for_window("Terminal")</literal>. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>copy_from_host</methodname> - </term> - <listitem> - <para> - Copies a file from host to machine, e.g., - <literal>copy_from_host("myfile", "/etc/my/important/file")</literal>. - </para> - <para> - The first argument is the file on the host. The file needs to be - accessible while building the nix derivation. The second argument is the - location of the file on the machine. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>systemctl</methodname> - </term> - <listitem> - <para> - Runs <literal>systemctl</literal> commands with optional support for - <literal>systemctl --user</literal> - </para> - <para> -<programlisting> -machine.systemctl("list-jobs --no-pager") # runs `systemctl list-jobs --no-pager` -machine.systemctl("list-jobs --no-pager", "any-user") # spawns a shell for `any-user` and runs `systemctl --user list-jobs --no-pager` -</programlisting> - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - <methodname>shell_interact</methodname> - </term> - <listitem> - <para> - Allows you to directly interact with the guest shell. - This should only be used during test development, not in production tests. - Killing the interactive session with <literal>Ctrl-d</literal> or <literal>Ctrl-c</literal> also ends the guest session. - </para> - </listitem> - </varlistentry> - </variablelist> - </para> - - <para> - To test user units declared by <literal>systemd.user.services</literal> the - optional <literal>user</literal> argument can be used: -<programlisting> -machine.start() -machine.wait_for_x() -machine.wait_for_unit("xautolock.service", "x-session-user") -</programlisting> - This applies to <literal>systemctl</literal>, <literal>get_unit_info</literal>, - <literal>wait_for_unit</literal>, <literal>start_job</literal> and - <literal>stop_job</literal>. - </para> - - <para> - For faster dev cycles it's also possible to disable the code-linters (this shouldn't - be commited though): -<programlisting> -import ./make-test-python.nix { - skipLint = true; - machine = - { config, pkgs, ... }: - { <replaceable>configuration…</replaceable> - }; - - testScript = - '' - <replaceable>Python code…</replaceable> - ''; -} -</programlisting> - This will produce a Nix warning at evaluation time. To fully disable the - linter, wrap the test script in comment directives to disable the Black linter - directly (again, don't commit this within the Nixpkgs repository): -<programlisting> - testScript = - '' - # fmt: off - <replaceable>Python code…</replaceable> - # fmt: on - ''; -</programlisting> - </para> -</section> 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 new file mode 100644 index 000000000000..a2030e9c0739 --- /dev/null +++ b/nixos/doc/manual/from_md/development/running-nixos-tests-interactively.section.xml @@ -0,0 +1,50 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-running-nixos-tests-interactively"> + <title>Running Tests interactively</title> + <para> + The test itself can be run interactively. This is particularly + useful when developing or debugging a test: + </para> + <programlisting> +$ nix-build nixos/tests/login.nix -A driverInteractive +$ ./result/bin/nixos-test-driver +starting VDE switch for network 1 +> +</programlisting> + <para> + You can then take any Python statement, e.g. + </para> + <programlisting language="python"> +> start_all() +> test_script() +> machine.succeed("touch /tmp/foo") +> print(machine.succeed("pwd")) # Show stdout of command +</programlisting> + <para> + The function <literal>test_script</literal> executes the entire test + script and drops you 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). + </para> + <para> + To just start and experiment with the VMs, run: + </para> + <programlisting> +$ nix-build nixos/tests/login.nix -A driverInteractive +$ ./result/bin/nixos-run-vms +</programlisting> + <para> + The script <literal>nixos-run-vms</literal> starts the virtual + machines defined by test. + </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> +$ ./result/bin/nixos-run-vms --keep-vm-state +</programlisting> + <para> + The machine state is stored in the + <literal>$TMPDIR/vm-state-machinename</literal> directory. + </para> +</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 new file mode 100644 index 000000000000..7159b95b22b0 --- /dev/null +++ b/nixos/doc/manual/from_md/development/running-nixos-tests.section.xml @@ -0,0 +1,34 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-running-nixos-tests"> + <title>Running Tests</title> + <para> + 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: + </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 +</programlisting> + <para> + After building/downloading all required dependencies, this will + perform a build that starts a QEMU/KVM virtual machine containing a + NixOS system. The virtual machine mounts the Nix store of the host; + this makes VM creation very fast, as no disk image needs to be + created. Afterwards, you can view a pretty-printed log of the test: + </para> + <programlisting> +$ firefox result/log.html +</programlisting> +</section> 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 new file mode 100644 index 000000000000..83a96d5bb224 --- /dev/null +++ b/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml @@ -0,0 +1,526 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-writing-nixos-tests"> + <title>Writing Tests</title> + <para> + A NixOS test is a Nix expression that has the following structure: + </para> + <programlisting language="bash"> +import ./make-test-python.nix { + + # Either the configuration of a single machine: + machine = + { config, pkgs, ... }: + { configuration… + }; + + # Or a set of machines: + nodes = + { machine1 = + { config, pkgs, ... }: { … }; + machine2 = + { config, pkgs, ... }: { … }; + … + }; + + testScript = + '' + Python code… + ''; +} +</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>machine</literal> (if you need + only one machine in your test) or by the attribute + <literal>nodes</literal> (if you need multiple machines). For + instance, + <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix"><literal>login.nix</literal></link> + only needs a single machine to test whether users can log in on the + virtual console, whether device ownership is correctly maintained + when switching between consoles, and so on. On the other hand, + <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nfs/simple.nix"><literal>nfs/simple.nix</literal></link>, + which tests NFS client and server functionality in the Linux kernel + (including whether locks are maintained across server crashes), + requires three machines: a server and two clients. + </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 didn't + specify multiple machines using the <literal>nodes</literal> + attribute, it is just <literal>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 actually 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> + <para> + The following methods are available on machine objects: + </para> + <variablelist> + <varlistentry> + <term> + <literal>start</literal> + </term> + <listitem> + <para> + Start the virtual machine. This method is asynchronous — it + does not wait for the machine to finish booting. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>shutdown</literal> + </term> + <listitem> + <para> + Shut down the machine, waiting for the VM to exit. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>crash</literal> + </term> + <listitem> + <para> + Simulate a sudden power failure, by telling the VM to exit + immediately. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>block</literal> + </term> + <listitem> + <para> + Simulate unplugging the Ethernet cable that connects the + machine to the other machines. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>unblock</literal> + </term> + <listitem> + <para> + Undo the effect of <literal>block</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>screenshot</literal> + </term> + <listitem> + <para> + Take a picture of the display of the virtual machine, in PNG + format. The screenshot is linked from the HTML log. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>get_screen_text_variants</literal> + </term> + <listitem> + <para> + Return a list of different interpretations of what is + currently visible on the machine's screen using optical + character recognition. The number and order of the + interpretations is not specified and is subject to change, but + if no exception is raised at least one will be returned. + </para> + <note> + <para> + This requires passing <literal>enableOCR</literal> to the + test attribute set. + </para> + </note> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>get_screen_text</literal> + </term> + <listitem> + <para> + Return a textual representation of what is currently visible + on the machine's screen using optical character recognition. + </para> + <note> + <para> + This requires passing <literal>enableOCR</literal> to the + test attribute set. + </para> + </note> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>send_monitor_command</literal> + </term> + <listitem> + <para> + Send a command to the QEMU monitor. This is rarely used, but + allows doing stuff such as attaching virtual USB disks to a + running machine. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>send_key</literal> + </term> + <listitem> + <para> + Simulate pressing keys on the virtual keyboard, e.g., + <literal>send_key("ctrl-alt-delete")</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>send_chars</literal> + </term> + <listitem> + <para> + Simulate typing a sequence of characters on the virtual + keyboard, e.g., + <literal>send_chars("foobar\n")</literal> will type + the string <literal>foobar</literal> followed by the Enter + key. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>execute</literal> + </term> + <listitem> + <para> + Execute a shell command, returning a list + <literal>(status, stdout)</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>succeed</literal> + </term> + <listitem> + <para> + Execute a shell command, raising an exception if the exit + status is not zero, otherwise returning the standard output. + Commands are run with <literal>set -euo pipefail</literal> + set: + </para> + <itemizedlist> + <listitem> + <para> + If several commands are separated by <literal>;</literal> + and one fails, the command as a whole will fail. + </para> + </listitem> + <listitem> + <para> + For pipelines, the last non-zero exit status will be + returned (if there is one, zero will be returned + otherwise). + </para> + </listitem> + <listitem> + <para> + Dereferencing unset variables fail the command. + </para> + </listitem> + </itemizedlist> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>fail</literal> + </term> + <listitem> + <para> + Like <literal>succeed</literal>, but raising an exception if + the command returns a zero status. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>wait_until_succeeds</literal> + </term> + <listitem> + <para> + Repeat a shell command with 1-second intervals until it + succeeds. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>wait_until_fails</literal> + </term> + <listitem> + <para> + Repeat a shell command with 1-second intervals until it fails. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>wait_for_unit</literal> + </term> + <listitem> + <para> + Wait until the specified systemd unit has reached the + <quote>active</quote> state. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>wait_for_file</literal> + </term> + <listitem> + <para> + Wait until the specified file exists. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>wait_for_open_port</literal> + </term> + <listitem> + <para> + Wait until a process is listening on the given TCP port (on + <literal>localhost</literal>, at least). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>wait_for_closed_port</literal> + </term> + <listitem> + <para> + Wait until nobody is listening on the given TCP port. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>wait_for_x</literal> + </term> + <listitem> + <para> + Wait until the X11 server is accepting connections. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>wait_for_text</literal> + </term> + <listitem> + <para> + Wait until the supplied regular expressions matches the + textual contents of the screen by using optical character + recognition (see <literal>get_screen_text</literal> and + <literal>get_screen_text_variants</literal>). + </para> + <note> + <para> + This requires passing <literal>enableOCR</literal> to the + test attribute set. + </para> + </note> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>wait_for_console_text</literal> + </term> + <listitem> + <para> + Wait until the supplied regular expressions match a line of + the serial console output. This method is useful when OCR is + not possibile or accurate enough. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>wait_for_window</literal> + </term> + <listitem> + <para> + Wait until an X11 window has appeared whose name matches the + given regular expression, e.g., + <literal>wait_for_window("Terminal")</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>copy_from_host</literal> + </term> + <listitem> + <para> + Copies a file from host to machine, e.g., + <literal>copy_from_host("myfile", "/etc/my/important/file")</literal>. + </para> + <para> + The first argument is the file on the host. The file needs to + be accessible while building the nix derivation. The second + argument is the location of the file on the machine. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>systemctl</literal> + </term> + <listitem> + <para> + Runs <literal>systemctl</literal> commands with optional + support for <literal>systemctl --user</literal> + </para> + <programlisting language="python"> +machine.systemctl("list-jobs --no-pager") # runs `systemctl list-jobs --no-pager` +machine.systemctl("list-jobs --no-pager", "any-user") # spawns a shell for `any-user` and runs `systemctl --user list-jobs --no-pager` +</programlisting> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>shell_interact</literal> + </term> + <listitem> + <para> + Allows you to directly interact with the guest shell. This + should only be used during test development, not in production + tests. Killing the interactive session with + <literal>Ctrl-d</literal> or <literal>Ctrl-c</literal> also + ends the guest session. + </para> + </listitem> + </varlistentry> + </variablelist> + <para> + To test user units declared by + <literal>systemd.user.services</literal> the optional + <literal>user</literal> argument can be used: + </para> + <programlisting language="python"> +machine.start() +machine.wait_for_x() +machine.wait_for_unit("xautolock.service", "x-session-user") +</programlisting> + <para> + This applies to <literal>systemctl</literal>, + <literal>get_unit_info</literal>, <literal>wait_for_unit</literal>, + <literal>start_job</literal> and <literal>stop_job</literal>. + </para> + <para> + For faster dev cycles it's also possible to disable the code-linters + (this shouldn't be commited though): + </para> + <programlisting language="bash"> +import ./make-test-python.nix { + skipLint = true; + machine = + { config, pkgs, ... }: + { configuration… + }; + + testScript = + '' + Python code… + ''; +} +</programlisting> + <para> + This will produce a Nix warning at evaluation time. To fully disable + the linter, wrap the test script in comment directives to disable + the Black linter directly (again, don't commit this within the + Nixpkgs repository): + </para> + <programlisting language="bash"> + testScript = + '' + # fmt: off + Python code… + # fmt: on + ''; +</programlisting> +</section> diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix index a0a20228a742..34e558d8603d 100644 --- a/nixos/modules/config/shells-environment.nix +++ b/nixos/modules/config/shells-environment.nix @@ -126,6 +126,14 @@ in type = types.bool; }; + environment.localBinInPath = mkOption { + description = '' + Add ~/.local/bin/ to $PATH + ''; + default = false; + type = types.bool; + }; + environment.binsh = mkOption { default = "${config.system.build.binsh}/bin/sh"; defaultText = "\${config.system.build.binsh}/bin/sh"; @@ -198,6 +206,10 @@ in # ~/bin if it exists overrides other bin directories. export PATH="$HOME/bin:$PATH" ''} + + ${optionalString cfg.localBinInPath '' + export PATH="$HOME/.local/bin:$PATH" + ''} ''; system.activationScripts.binsh = stringAfter [ "stdio" ] diff --git a/nixos/modules/security/systemd-confinement.nix b/nixos/modules/security/systemd-confinement.nix index afb81a2b56be..0a09a755e93c 100644 --- a/nixos/modules/security/systemd-confinement.nix +++ b/nixos/modules/security/systemd-confinement.nix @@ -105,7 +105,7 @@ in { wantsAPIVFS = lib.mkDefault (config.confinement.mode == "full-apivfs"); in lib.mkIf config.confinement.enable { serviceConfig = { - RootDirectory = pkgs.runCommand rootName {} "mkdir \"$out\""; + RootDirectory = "/var/empty"; TemporaryFileSystem = "/"; PrivateMounts = lib.mkDefault true; diff --git a/nixos/modules/services/audio/slimserver.nix b/nixos/modules/services/audio/slimserver.nix index 8f94a2b49404..21632919699c 100644 --- a/nixos/modules/services/audio/slimserver.nix +++ b/nixos/modules/services/audio/slimserver.nix @@ -63,6 +63,7 @@ in { description = "Slimserver daemon user"; home = cfg.dataDir; group = "slimserver"; + isSystemUser = true; }; groups.slimserver = {}; }; diff --git a/nixos/modules/services/backup/sanoid.nix b/nixos/modules/services/backup/sanoid.nix index 0472fb4ba1e7..be44a43b6d3f 100644 --- a/nixos/modules/services/backup/sanoid.nix +++ b/nixos/modules/services/backup/sanoid.nix @@ -10,74 +10,51 @@ let description = "dataset/template options"; }; - # Default values from https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf - commonOptions = { hourly = mkOption { description = "Number of hourly snapshots."; - type = types.ints.unsigned; - default = 48; + type = with types; nullOr ints.unsigned; + default = null; }; daily = mkOption { description = "Number of daily snapshots."; - type = types.ints.unsigned; - default = 90; + type = with types; nullOr ints.unsigned; + default = null; }; monthly = mkOption { description = "Number of monthly snapshots."; - type = types.ints.unsigned; - default = 6; + type = with types; nullOr ints.unsigned; + default = null; }; yearly = mkOption { description = "Number of yearly snapshots."; - type = types.ints.unsigned; - default = 0; + type = with types; nullOr ints.unsigned; + default = null; }; autoprune = mkOption { description = "Whether to automatically prune old snapshots."; - type = types.bool; - default = true; + type = with types; nullOr bool; + default = null; }; autosnap = mkOption { description = "Whether to automatically take snapshots."; - type = types.bool; - default = true; - }; - - settings = mkOption { - description = '' - Free-form settings for this template/dataset. See - <link xlink:href="https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf"/> - for allowed values. - ''; - type = datasetSettingsType; - }; - }; - - commonConfig = config: { - settings = { - hourly = mkDefault config.hourly; - daily = mkDefault config.daily; - monthly = mkDefault config.monthly; - yearly = mkDefault config.yearly; - autoprune = mkDefault config.autoprune; - autosnap = mkDefault config.autosnap; + type = with types; nullOr bool; + default = null; }; }; - datasetOptions = { - useTemplate = mkOption { + datasetOptions = rec { + use_template = mkOption { description = "Names of the templates to use for this dataset."; - type = (types.listOf (types.enum (attrNames cfg.templates))) // { - description = "list of template names"; - }; + type = types.listOf (types.enum (attrNames cfg.templates)); default = []; }; + useTemplate = use_template; recursive = mkOption { description = "Whether to recursively snapshot dataset children."; @@ -85,19 +62,12 @@ let default = false; }; - processChildrenOnly = mkOption { + process_children_only = mkOption { description = "Whether to only snapshot child datasets if recursing."; type = types.bool; default = false; }; - }; - - datasetConfig = config: { - settings = { - use_template = mkDefault config.useTemplate; - recursive = mkDefault config.recursive; - process_children_only = mkDefault config.processChildrenOnly; - }; + processChildrenOnly = process_children_only; }; # Extract pool names from configured datasets @@ -109,11 +79,11 @@ let else generators.mkValueStringDefault {} v; mkKeyValue = k: v: if v == null then "" + else if k == "processChildrenOnly" then "" + else if k == "useTemplate" then "" else generators.mkKeyValueDefault { inherit mkValueString; } "=" k v; in generators.toINI { inherit mkKeyValue; } cfg.settings; - configDir = pkgs.writeTextDir "sanoid.conf" configFile; - in { # Interface @@ -135,19 +105,21 @@ in { }; datasets = mkOption { - type = types.attrsOf (types.submodule ({ config, ... }: { + type = types.attrsOf (types.submodule ({config, options, ...}: { + freeformType = datasetSettingsType; options = commonOptions // datasetOptions; - config = mkMerge [ (commonConfig config) (datasetConfig config) ]; + config.use_template = mkAliasDefinitions (options.useTemplate or {}); + config.process_children_only = mkAliasDefinitions (options.processChildrenOnly or {}); })); default = {}; description = "Datasets to snapshot."; }; templates = mkOption { - type = types.attrsOf (types.submodule ({ config, ... }: { + type = types.attrsOf (types.submodule { + freeformType = datasetSettingsType; options = commonOptions; - config = commonConfig config; - })); + }); default = {}; description = "Templates for datasets."; }; @@ -177,8 +149,8 @@ in { config = mkIf cfg.enable { services.sanoid.settings = mkMerge [ - (mapAttrs' (d: v: nameValuePair ("template_" + d) v.settings) cfg.templates) - (mapAttrs (d: v: v.settings) cfg.datasets) + (mapAttrs' (d: v: nameValuePair ("template_" + d) v) cfg.templates) + (mapAttrs (d: v: v) cfg.datasets) ]; systemd.services.sanoid = { @@ -191,7 +163,7 @@ in { ExecStart = lib.escapeShellArgs ([ "${pkgs.sanoid}/bin/sanoid" "--cron" - "--configdir" configDir + "--configdir" (pkgs.writeTextDir "sanoid.conf" configFile) ] ++ cfg.extraArgs); ExecStopPost = map (pool: lib.escapeShellArgs [ "+/run/booted-system/sw/bin/zfs" "unallow" "sanoid" pool diff --git a/nixos/modules/services/misc/geoipupdate.nix b/nixos/modules/services/misc/geoipupdate.nix index 5d87be928d98..3211d4d88e4d 100644 --- a/nixos/modules/services/misc/geoipupdate.nix +++ b/nixos/modules/services/misc/geoipupdate.nix @@ -99,9 +99,22 @@ in LockFile = "/run/geoipupdate/.lock"; }; + systemd.services.geoipupdate-create-db-dir = { + serviceConfig.Type = "oneshot"; + script = '' + mkdir -p ${cfg.settings.DatabaseDirectory} + chmod 0755 ${cfg.settings.DatabaseDirectory} + ''; + }; + systemd.services.geoipupdate = { description = "GeoIP Updater"; - after = [ "network-online.target" "nss-lookup.target" ]; + requires = [ "geoipupdate-create-db-dir.service" ]; + after = [ + "geoipupdate-create-db-dir.service" + "network-online.target" + "nss-lookup.target" + ]; wants = [ "network-online.target" ]; startAt = cfg.interval; serviceConfig = { @@ -119,11 +132,9 @@ in }; }; - geoipupdateConf = pkgs.writeText "discourse.conf" (geoipupdateKeyValue cfg.settings); + geoipupdateConf = pkgs.writeText "geoipupdate.conf" (geoipupdateKeyValue cfg.settings); script = '' - mkdir -p "${cfg.settings.DatabaseDirectory}" - chmod 755 "${cfg.settings.DatabaseDirectory}" chown geoip "${cfg.settings.DatabaseDirectory}" cp ${geoipupdateConf} /run/geoipupdate/GeoIP.conf @@ -139,7 +150,38 @@ in ReadWritePaths = cfg.settings.DatabaseDirectory; RuntimeDirectory = "geoipupdate"; RuntimeDirectoryMode = 0700; + CapabilityBoundingSet = ""; + PrivateDevices = true; + PrivateMounts = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictRealtime = true; + RestrictNamespaces = true; + MemoryDenyWriteExecute = true; + LockPersonality = true; + SystemCallArchitectures = "native"; + }; + }; + + systemd.timers.geoipupdate-initial-run = { + wantedBy = [ "timers.target" ]; + unitConfig.ConditionPathExists = "!${cfg.settings.DatabaseDirectory}"; + timerConfig = { + Unit = "geoipupdate.service"; + OnActiveSec = 0; }; }; }; + + meta.maintainers = [ lib.maintainers.talyz ]; } diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix index 6d8dfcce933c..2748571be1f7 100644 --- a/nixos/modules/services/network-filesystems/ipfs.nix +++ b/nixos/modules/services/network-filesystems/ipfs.nix @@ -231,9 +231,9 @@ in { } fi '' + optionalString cfg.autoMount '' - ipfs --local config Mounts.FuseAllowOther --json true - ipfs --local config Mounts.IPFS ${cfg.ipfsMountDir} - ipfs --local config Mounts.IPNS ${cfg.ipnsMountDir} + ipfs --offline config Mounts.FuseAllowOther --json true + ipfs --offline config Mounts.IPFS ${cfg.ipfsMountDir} + ipfs --offline config Mounts.IPNS ${cfg.ipnsMountDir} '' + concatStringsSep "\n" (collect isString (mapAttrsRecursive @@ -243,7 +243,7 @@ in { read value <<EOF ${builtins.toJSON value} EOF - ipfs --local config --json "${concatStringsSep "." path}" "$value" + ipfs --offline config --json "${concatStringsSep "." path}" "$value" '') ({ Addresses.API = cfg.apiAddress; Addresses.Gateway = cfg.gatewayAddress; diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index 91caa2ccb422..2c96b94ca43c 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -453,6 +453,7 @@ in { ExecStart = (optionalString cfg.startWhenNeeded "-") + "${cfgc.package}/bin/sshd " + (optionalString cfg.startWhenNeeded "-i ") + + "-D " + # don't detach into a daemon process "-f /etc/ssh/sshd_config"; KillMode = "process"; } // (if cfg.startWhenNeeded then { diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix index c33a38179ee4..3f88ff53dff0 100644 --- a/nixos/modules/services/networking/tailscale.nix +++ b/nixos/modules/services/networking/tailscale.nix @@ -34,7 +34,7 @@ in { systemd.packages = [ cfg.package ]; systemd.services.tailscaled = { wantedBy = [ "multi-user.target" ]; - path = [ pkgs.openresolv ]; + path = [ pkgs.openresolv pkgs.procps ]; serviceConfig.Environment = [ "PORT=${toString cfg.port}" ''"FLAGS=--tun ${lib.escapeShellArg cfg.interfaceName}"'' diff --git a/nixos/modules/services/web-apps/discourse.nix b/nixos/modules/services/web-apps/discourse.nix index 9c7166f381a3..d3ae072f86a8 100644 --- a/nixos/modules/services/web-apps/discourse.nix +++ b/nixos/modules/services/web-apps/discourse.nix @@ -30,6 +30,9 @@ in package = lib.mkOption { type = lib.types.package; default = pkgs.discourse; + apply = p: p.override { + plugins = lib.unique (p.enabledPlugins ++ cfg.plugins); + }; defaultText = "pkgs.discourse"; description = '' The discourse package to use. @@ -731,8 +734,6 @@ in cp -r ${cfg.package}/share/discourse/config.dist/* /run/discourse/config/ cp -r ${cfg.package}/share/discourse/public.dist/* /run/discourse/public/ - cp -r ${cfg.package}/share/discourse/plugins.dist/* /run/discourse/plugins/ - ${lib.concatMapStringsSep "\n" (p: "ln -sf ${p} /run/discourse/plugins/") cfg.plugins} ln -sf /var/lib/discourse/uploads /run/discourse/public/uploads ln -sf /var/lib/discourse/backups /run/discourse/public/backups diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py index 63e01dd054a5..7134b4321630 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py +++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py @@ -61,7 +61,7 @@ def write_loader_conf(profile: Optional[str], generation: int) -> None: def profile_path(profile: Optional[str], generation: int, name: str) -> str: - return os.readlink("%s/%s" % (system_dir(profile, generation), name)) + return os.path.realpath("%s/%s" % (system_dir(profile, generation), name)) def copy_from_profile(profile: Optional[str], generation: int, name: str, dry_run: bool = False) -> str: diff --git a/nixos/tests/sanoid.nix b/nixos/tests/sanoid.nix index c691bfc08ef7..1983945915fe 100644 --- a/nixos/tests/sanoid.nix +++ b/nixos/tests/sanoid.nix @@ -33,7 +33,7 @@ in { autosnap = true; }; - datasets."pool/sanoid".useTemplate = [ "test" ]; + datasets."pool/sanoid".use_template = [ "test" ]; extraArgs = [ "--verbose" ]; }; diff --git a/nixos/tests/vault.nix b/nixos/tests/vault.nix index 59bccbe25959..c3b28b62695a 100644 --- a/nixos/tests/vault.nix +++ b/nixos/tests/vault.nix @@ -19,6 +19,8 @@ import ./make-test-python.nix ({ pkgs, ... }: machine.wait_for_unit("vault.service") machine.wait_for_open_port(8200) machine.succeed("vault operator init") - machine.succeed("vault status | grep Sealed | grep true") + # vault now returns exit code 2 for sealed vaults + machine.fail("vault status") + machine.succeed("vault status || test $? -eq 2") ''; }) diff --git a/nixos/tests/zsh-history.nix b/nixos/tests/zsh-history.nix index 3109c3f65081..355687798406 100644 --- a/nixos/tests/zsh-history.nix +++ b/nixos/tests/zsh-history.nix @@ -23,7 +23,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { # Login default.wait_until_tty_matches(1, "login: ") default.send_chars("root\n") - default.wait_until_tty_matches(1, "root@default>") + default.wait_until_tty_matches(1, r"\nroot@default\b") # Generate some history default.send_chars("echo foobar\n") |