diff options
Diffstat (limited to 'nixos/doc/manual/from_md/development')
5 files changed, 701 insertions, 0 deletions
diff --git a/nixos/doc/manual/from_md/development/assertions.section.xml b/nixos/doc/manual/from_md/development/assertions.section.xml new file mode 100644 index 00000000000..0844d484d60 --- /dev/null +++ b/nixos/doc/manual/from_md/development/assertions.section.xml @@ -0,0 +1,58 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-assertions"> + <title>Warnings and Assertions</title> + <para> + When configuration problems are detectable in a module, it is a good + idea to write an assertion or warning. Doing so provides clear + feedback to the user and prevents errors after the build. + </para> + <para> + Although Nix has the <literal>abort</literal> and + <literal>builtins.trace</literal> + <link xlink:href="https://nixos.org/nix/manual/#ssec-builtins">functions</link> + to perform such tasks, they are not ideally suited for NixOS + modules. Instead of these functions, you can declare your warnings + and assertions using the NixOS module system. + </para> + <section xml:id="sec-assertions-warnings"> + <title>Warnings</title> + <para> + This is an example of using <literal>warnings</literal>. + </para> + <programlisting language="bash"> +{ config, lib, ... }: +{ + config = lib.mkIf config.services.foo.enable { + warnings = + if config.services.foo.bar + then [ ''You have enabled the bar feature of the foo service. + This is known to cause some specific problems in certain situations. + '' ] + else []; + } +} +</programlisting> + </section> + <section xml:id="sec-assertions-assetions"> + <title>Assertions</title> + <para> + This example, extracted from the + <link xlink:href="https://github.com/NixOS/nixpkgs/blob/release-17.09/nixos/modules/services/logging/syslogd.nix"><literal>syslogd</literal> + module</link> shows how to use <literal>assertions</literal>. + Since there can only be one active syslog daemon at a time, an + assertion is useful to prevent such a broken system from being + built. + </para> + <programlisting language="bash"> +{ config, lib, ... }: +{ + config = lib.mkIf config.services.syslogd.enable { + assertions = + [ { assertion = !config.services.rsyslogd.enable; + message = "rsyslogd conflicts with syslogd"; + } + ]; + } +} +</programlisting> + </section> +</section> diff --git a/nixos/doc/manual/from_md/development/building-nixos.chapter.xml b/nixos/doc/manual/from_md/development/building-nixos.chapter.xml new file mode 100644 index 00000000000..ceb744447da --- /dev/null +++ b/nixos/doc/manual/from_md/development/building-nixos.chapter.xml @@ -0,0 +1,33 @@ +<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-building-cd"> + <title>Building Your Own NixOS CD</title> + <para> + Building a NixOS CD is as easy as configuring your own computer. The + idea is to use another module which will replace your + <literal>configuration.nix</literal> to configure the system that + would be installed on the CD. + </para> + <para> + Default CD/DVD configurations are available inside + <literal>nixos/modules/installer/cd-dvd</literal> + </para> + <programlisting> +$ git clone https://github.com/NixOS/nixpkgs.git +$ cd nixpkgs/nixos +$ nix-build -A config.system.build.isoImage -I nixos-config=modules/installer/cd-dvd/installation-cd-minimal.nix default.nix +</programlisting> + <para> + Before burning your CD/DVD, you can check the content of the image + by mounting anywhere like suggested by the following command: + </para> + <programlisting> +# mount -o loop -t iso9660 ./result/iso/cd.iso /mnt/iso</screen> +</programlisting> + <para> + If you want to customize your NixOS CD in more detail, or generate + other kinds of images, you might want to check out + <link xlink:href="https://github.com/nix-community/nixos-generators">nixos-generators</link>. + This can also be a good starting point when you want to use Nix to + build a <quote>minimal</quote> image that doesn’t include a NixOS + installation. + </para> +</chapter> 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 00000000000..a2030e9c073 --- /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 00000000000..7159b95b22b --- /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 00000000000..83a96d5bb22 --- /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> |