diff options
Diffstat (limited to 'nixos/doc/manual/development.xml')
-rw-r--r-- | nixos/doc/manual/development.xml | 1119 |
1 files changed, 0 insertions, 1119 deletions
diff --git a/nixos/doc/manual/development.xml b/nixos/doc/manual/development.xml deleted file mode 100644 index 2f0c2a7aa8da..000000000000 --- a/nixos/doc/manual/development.xml +++ /dev/null @@ -1,1119 +0,0 @@ -<chapter xmlns="http://docbook.org/ns/docbook" - xmlns:xlink="http://www.w3.org/1999/xlink" - xml:id="ch-development"> - -<title>Development</title> - -<para>This chapter describes how you can modify and extend -NixOS.</para> - - -<!--===============================================================--> - -<section xml:id="sec-getting-sources"> - -<title>Getting the sources</title> - -<para>By default, NixOS’s <command>nixos-rebuild</command> command -uses the NixOS and Nixpkgs sources provided by the -<literal>nixos-unstable</literal> channel (kept in -<filename>/nix/var/nix/profiles/per-user/root/channels/nixos</filename>). -To modify NixOS, however, you should check out the latest sources from -Git. This is done using the following command: - -<screen> -$ nixos-checkout <replaceable>/my/sources</replaceable> -</screen> - -or - -<screen> -$ mkdir -p <replaceable>/my/sources</replaceable> -$ cd <replaceable>/my/sources</replaceable> -$ nix-env -i git -$ git clone git://github.com/NixOS/nixpkgs.git -</screen> - -This will check out the latest NixOS sources to -<filename><replaceable>/my/sources</replaceable>/nixpkgs/nixos</filename> -and the Nixpkgs sources to -<filename><replaceable>/my/sources</replaceable>/nixpkgs</filename>. -(The NixOS source tree lives in a subdirectory of the Nixpkgs -repository.)</para> - -<para>It’s often inconvenient to develop directly on the master -branch, since if somebody has just committed (say) a change to GCC, -then the binary cache may not have caught up yet and you’ll have to -rebuild everything from source. So you may want to create a local -branch based on your current NixOS version: - -<screen> -$ nixos-version -14.04.273.ea1952b (Baboon) - -$ git checkout -b local ea1952b -</screen> - -Or, to base your local branch on the latest version available in the -NixOS channel: - -<screen> -$ curl -sI http://nixos.org/channels/nixos-unstable/ | grep Location -Location: http://releases.nixos.org/nixos/unstable/nixos-14.10pre43986.acaf4a6/ - -$ git checkout -b local acaf4a6 -</screen> - -You can then use <command>git rebase</command> to sync your local -branch with the upstream branch, and use <command>git -cherry-pick</command> to copy commits from your local branch to the -upstream branch.</para> - -<para>If you want to rebuild your system using your (modified) -sources, you need to tell <command>nixos-rebuild</command> about them -using the <option>-I</option> flag: - -<screen> -$ nixos-rebuild switch -I nixpkgs=<replaceable>/my/sources</replaceable>/nixpkgs -</screen> - -</para> - -<para>If you want <command>nix-env</command> to use the expressions in -<replaceable>/my/sources</replaceable>, use <command>nix-env -f -<replaceable>/my/sources</replaceable>/nixpkgs</command>, or change -the default by adding a symlink in -<filename>~/.nix-defexpr</filename>: - -<screen> -$ ln -s <replaceable>/my/sources</replaceable>/nixpkgs ~/.nix-defexpr/nixpkgs -</screen> - -You may want to delete the symlink -<filename>~/.nix-defexpr/channels_root</filename> to prevent root’s -NixOS channel from clashing with your own tree.</para> - -<!-- FIXME: not sure what this means. -<para>You should not pass the base directory -<filename><replaceable>/my/sources</replaceable></filename> -to <command>nix-env</command>, as it will break after interpreting expressions -in <filename>nixos/</filename> as packages.</para> ---> - -</section> - - -<!--===============================================================--> - -<section xml:id="sec-writing-modules"> - -<title>Writing NixOS modules</title> - -<para>NixOS has a modular system for declarative configuration. This -system combines multiple <emphasis>modules</emphasis> to produce the -full system configuration. One of the modules that constitute the -configuration is <filename>/etc/nixos/configuration.nix</filename>. -Most of the others live in the <link -xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/modules"><filename>nixos/modules</filename></link> -subdirectory of the Nixpkgs tree.</para> - -<para>Each NixOS module is a file that handles one logical aspect of -the configuration, such as a specific kind of hardware, a service, or -network settings. A module configuration does not have to handle -everything from scratch; it can use the functionality provided by -other modules for its implementation. Thus a module can -<emphasis>declare</emphasis> options that can be used by other -modules, and conversely can <emphasis>define</emphasis> options -provided by other modules in its own implementation. For example, the -module <link -xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/pam.nix"><filename>pam.nix</filename></link> -declares the option <option>security.pam.services</option> that allows -other modules (e.g. <link -xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/ssh/sshd.nix"><filename>sshd.nix</filename></link>) -to define PAM services; and it defines the option -<option>environment.etc</option> (declared by <link -xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/system/etc/etc.nix"><filename>etc.nix</filename></link>) -to cause files to be created in -<filename>/etc/pam.d</filename>.</para> - -<para xml:id="para-module-syn">In <xref -linkend="sec-configuration-syntax"/>, we saw the following structure -of NixOS modules: - -<programlisting> -{ config, pkgs, ... }: - -{ <replaceable>option definitions</replaceable> -} -</programlisting> - -This is actually an <emphasis>abbreviated</emphasis> form of module -that only defines options, but does not declare any. The structure of -full NixOS modules is shown in <xref linkend='ex-module-syntax' />.</para> - -<example xml:id='ex-module-syntax'><title>Structure of NixOS modules</title> -<programlisting> -{ config, pkgs, ... }: <co xml:id='module-syntax-1' /> - -{ - imports = - [ <replaceable>paths of other modules</replaceable> <co xml:id='module-syntax-2' /> - ]; - - options = { - <replaceable>option declarations</replaceable> <co xml:id='module-syntax-3' /> - }; - - config = { - <replaceable>option definitions</replaceable> <co xml:id='module-syntax-4' /> - }; -}</programlisting> -</example> - -<para>The meaning of each part is as follows. - -<calloutlist> - <callout arearefs='module-syntax-1'> - <para>This line makes the current Nix expression a function. The - variable <varname>pkgs</varname> contains Nixpkgs, while - <varname>config</varname> contains the full system configuration. - This line can be omitted if there is no reference to - <varname>pkgs</varname> and <varname>config</varname> inside the - module.</para> - </callout> - - <callout arearefs='module-syntax-2'> - <para>This list enumerates the paths to other NixOS modules that - should be included in the evaluation of the system configuration. - A default set of modules is defined in the file - <filename>modules/module-list.nix</filename>. These don't need to - be added in the import list.</para> - </callout> - - <callout arearefs='module-syntax-3'> - <para>The attribute <varname>options</varname> is a nested set of - <emphasis>option declarations</emphasis> (described below).</para> - </callout> - - <callout arearefs='module-syntax-4'> - <para>The attribute <varname>config</varname> is a nested set of - <emphasis>option definitions</emphasis> (also described - below).</para> - </callout> -</calloutlist> - -</para> - -<para><xref linkend='locate-example' /> shows a module that handles -the regular update of the “locate” database, an index of all files in -the file system. This module declares two options that can be defined -by other modules (typically the user’s -<filename>configuration.nix</filename>): -<option>services.locate.enable</option> (whether the database should -be updated) and <option>services.locate.period</option> (when the -update should be done). It implements its functionality by defining -two options declared by other modules: -<option>systemd.services</option> (the set of all systemd services) -and <option>services.cron.systemCronJobs</option> (the list of -commands to be executed periodically by <command>cron</command>).</para> - -<example xml:id='locate-example'><title>NixOS module for the “locate” service</title> -<programlisting> -{ config, lib, pkgs, ... }: - -with lib; - -let locatedb = "/var/cache/locatedb"; in - -{ - options = { - - services.locate = { - - enable = mkOption { - type = types.bool; - default = false; - description = '' - If enabled, NixOS will periodically update the database of - files used by the <command>locate</command> command. - ''; - }; - - period = mkOption { - type = types.str; - default = "15 02 * * *"; - description = '' - This option defines (in the format used by cron) when the - locate database is updated. The default is to update at - 02:15 at night every day. - ''; - }; - - }; - - }; - - config = { - - systemd.services.update-locatedb = - { description = "Update Locate Database"; - path = [ pkgs.su ]; - script = - '' - mkdir -m 0755 -p $(dirname ${locatedb}) - exec updatedb --localuser=nobody --output=${locatedb} --prunepaths='/tmp /var/tmp /media /run' - ''; - }; - - services.cron.systemCronJobs = optional config.services.locate.enable - "${config.services.locate.period} root ${config.systemd.package}/bin/systemctl start update-locatedb.service"; - - }; -}</programlisting> -</example> - -<section><title>Option declarations</title> - -<para>An option declaration specifies the name, type and description -of a NixOS configuration option. It is illegal to define an option -that hasn’t been declared in any module. A option declaration -generally looks like this: - -<programlisting> -options = { - <replaceable>name</replaceable> = mkOption { - type = <replaceable>type specification</replaceable>; - default = <replaceable>default value</replaceable>; - example = <replaceable>example value</replaceable>; - description = "<replaceable>Description for use in the NixOS manual.</replaceable>"; - }; -}; -</programlisting> - -</para> - -<para>The function <varname>mkOption</varname> accepts the following arguments. - -<variablelist> - - <varlistentry> - <term><varname>type</varname></term> - <listitem> - <para>The type of the option (see below). It may be omitted, - but that’s not advisable since it may lead to errors that are - hard to diagnose.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>default</varname></term> - <listitem> - <para>The default value used if no value is defined by any - module. A default is not required; in that case, if the option - value is ever used, an error will be thrown.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>example</varname></term> - <listitem> - <para>An example value that will be shown in the NixOS manual.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>description</varname></term> - <listitem> - <para>A textual description of the option, in DocBook format, - that will be included in the NixOS manual.</para> - </listitem> - </varlistentry> - -</variablelist> - -</para> - -<para>Here is a non-exhaustive list of option types: - -<variablelist> - - <varlistentry> - <term><varname>types.bool</varname></term> - <listitem> - <para>A Boolean.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.int</varname></term> - <listitem> - <para>An integer.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.str</varname></term> - <listitem> - <para>A string.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.lines</varname></term> - <listitem> - <para>A string. If there are multiple definitions, they are - concatenated, with newline characters in between.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.path</varname></term> - <listitem> - <para>A path, defined as anything that, when coerced to a - string, starts with a slash. This includes derivations.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.listOf</varname> <replaceable>t</replaceable></term> - <listitem> - <para>A list of elements of type <replaceable>t</replaceable> - (e.g., <literal>types.listOf types.str</literal> is a list of - strings). Multiple definitions are concatenated together.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.attrsOf</varname> <replaceable>t</replaceable></term> - <listitem> - <para>A set of elements of type <replaceable>t</replaceable> - (e.g., <literal>types.attrsOf types.int</literal> is a set of - name/value pairs, the values being integers).</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.nullOr</varname> <replaceable>t</replaceable></term> - <listitem> - <para>Either the value <literal>null</literal> or something of - type <replaceable>t</replaceable>.</para> - </listitem> - </varlistentry> - -</variablelist> - -You can also create new types using the function -<varname>mkOptionType</varname>. See -<filename>lib/types.nix</filename> in Nixpkgs for details.</para> - -</section> - - -<section><title>Option definitions</title> - -<para>Option definitions are generally straight-forward bindings of values to option names, like - -<programlisting> -config = { - services.httpd.enable = true; -}; -</programlisting> - -However, sometimes you need to wrap an option definition or set of -option definitions in a <emphasis>property</emphasis> to achieve -certain effects:</para> - -<simplesect><title>Delaying conditionals</title> - -<para>If a set of option definitions is conditional on the value of -another option, you may need to use <varname>mkIf</varname>. -Consider, for instance: - -<programlisting> -config = if config.services.httpd.enable then { - environment.systemPackages = [ <replaceable>...</replaceable> ]; - <replaceable>...</replaceable> -} else {}; -</programlisting> - -This definition will cause Nix to fail with an “infinite recursion” -error. Why? Because the value of -<option>config.services.httpd.enable</option> depends on the value -being constructed here. After all, you could also write the clearly -circular and contradictory: -<programlisting> -config = if config.services.httpd.enable then { - services.httpd.enable = false; -} else { - services.httpd.enable = true; -}; -</programlisting> - -The solution is to write: - -<programlisting> -config = mkIf config.services.httpd.enable { - environment.systemPackages = [ <replaceable>...</replaceable> ]; - <replaceable>...</replaceable> -}; -</programlisting> - -The special function <varname>mkIf</varname> causes the evaluation of -the conditional to be “pushed down” into the individual definitions, -as if you had written: - -<programlisting> -config = { - environment.systemPackages = if config.services.httpd.enable then [ <replaceable>...</replaceable> ] else []; - <replaceable>...</replaceable> -}; -</programlisting> - -</para> - -</simplesect> - -<simplesect><title>Setting priorities</title> - -<para>A module can override the definitions of an option in other -modules by setting a <emphasis>priority</emphasis>. All option -definitions that do not have the lowest priority value are discarded. -By default, option definitions have priority 1000. You can specify an -explicit priority by using <varname>mkOverride</varname>, e.g. - -<programlisting> -services.openssh.enable = mkOverride 10 false; -</programlisting> - -This definition causes all other definitions with priorities above 10 -to be discarded. The function <varname>mkForce</varname> is -equal to <varname>mkOverride 50</varname>.</para> - -</simplesect> - -<simplesect><title>Merging configurations</title> - -<para>In conjunction with <literal>mkIf</literal>, it is sometimes -useful for a module to return multiple sets of option definitions, to -be merged together as if they were declared in separate modules. This -can be done using <varname>mkMerge</varname>: - -<programlisting> -config = mkMerge - [ # Unconditional stuff. - { environment.systemPackages = [ <replaceable>...</replaceable> ]; - } - # Conditional stuff. - (mkIf config.services.bla.enable { - environment.systemPackages = [ <replaceable>...</replaceable> ]; - }) - ]; -</programlisting> - -</para> - -</simplesect> - -</section> - - -<section><title>Important options</title> - -<para>NixOS has many options, but some are of particular importance to -module writers.</para> - -<variablelist> - - <varlistentry> - <term><option>environment.etc</option></term> - <listitem> - <para>This set defines files in <filename>/etc</filename>. A - typical use is: -<programlisting> -environment.etc."os-release".text = - '' - NAME=NixOS - <replaceable>...</replaceable> - ''; -</programlisting> - which causes a file named <filename>/etc/os-release</filename> - to be created with the given contents.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>system.activationScripts</option></term> - <listitem> - <para>A set of shell script fragments that must be executed - whenever the configuration is activated (i.e., at boot time, or - after running <command>nixos-rebuild switch</command>). For instance, -<programlisting> -system.activationScripts.media = - '' - mkdir -m 0755 -p /media - ''; -</programlisting> - causes the directory <filename>/media</filename> to be created. - Activation scripts must be idempotent. They should not start - background processes such as daemons; use - <option>systemd.services</option> for that.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>systemd.services</option></term> - <listitem> - <para>This is the set of systemd services. Example: -<programlisting> -systemd.services.dhcpcd = - { description = "DHCP Client"; - wantedBy = [ "multi-user.target" ]; - after = [ "systemd-udev-settle.service" ]; - path = [ dhcpcd pkgs.nettools pkgs.openresolv ]; - serviceConfig = - { Type = "forking"; - PIDFile = "/run/dhcpcd.pid"; - ExecStart = "${dhcpcd}/sbin/dhcpcd --config ${dhcpcdConf}"; - Restart = "always"; - }; - }; -</programlisting> - which creates the systemd unit - <literal>dhcpcd.service</literal>. The option - <option>wantedBy</option> determined which other units pull this - one in; <literal>multi-user.target</literal> is the default - target of the system, so <literal>dhcpcd.service</literal> will - always be started. The option - <option>serviceConfig.ExecStart</option> provides the main - command for the service; it’s also possible to provide pre-start - actions, stop scripts, and so on.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>users.extraUsers</option></term> - <term><option>users.extraGroups</option></term> - <listitem> - <para>If your service requires special UIDs or GIDs, you can - define them with these options. See <xref - linkend="sec-user-management"/> for details.</para> - </listitem> - </varlistentry> - -</variablelist> - -</section> - - -</section> - - -<!--===============================================================--> - -<section xml:id="sec-building-parts"> - -<title>Building specific parts of NixOS</title> - -<para>With the command <command>nix-build</command>, you can build -specific parts of your NixOS configuration. This is done as follows: - -<screen> -$ cd <replaceable>/path/to/nixpkgs/nixos</replaceable> -$ nix-build -A config.<replaceable>option</replaceable></screen> - -where <replaceable>option</replaceable> is a NixOS option with type -“derivation” (i.e. something that can be built). Attributes of -interest include: - -<variablelist> - - <varlistentry> - <term><varname>system.build.toplevel</varname></term> - <listitem> - <para>The top-level option that builds the entire NixOS system. - Everything else in your configuration is indirectly pulled in by - this option. This is what <command>nixos-rebuild</command> - builds and what <filename>/run/current-system</filename> points - to afterwards.</para> - - <para>A shortcut to build this is: - -<screen> -$ nix-build -A system</screen> - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>system.build.manual.manual</varname></term> - <listitem><para>The NixOS manual.</para></listitem> - </varlistentry> - - <varlistentry> - <term><varname>system.build.etc</varname></term> - <listitem><para>A tree of symlinks that form the static parts of - <filename>/etc</filename>.</para></listitem> - </varlistentry> - - <varlistentry> - <term><varname>system.build.initialRamdisk</varname></term> - <term><varname>system.build.kernel</varname></term> - <listitem> - <para>The initial ramdisk and kernel of the system. This allows - a quick way to test whether the kernel and the initial ramdisk - boot correctly, by using QEMU’s <option>-kernel</option> and - <option>-initrd</option> options: - -<screen> -$ nix-build -A config.system.build.initialRamdisk -o initrd -$ nix-build -A config.system.build.kernel -o kernel -$ qemu-system-x86_64 -kernel ./kernel/bzImage -initrd ./initrd/initrd -hda /dev/null -</screen> - - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>system.build.nixos-rebuild</varname></term> - <term><varname>system.build.nixos-install</varname></term> - <term><varname>system.build.nixos-generate-config</varname></term> - <listitem> - <para>These build the corresponding NixOS commands.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>systemd.units.<replaceable>unit-name</replaceable>.unit</varname></term> - <listitem> - <para>This builds the unit with the specified name. Note that - since unit names contain dots - (e.g. <literal>httpd.service</literal>), you need to put them - between quotes, like this: - -<screen> -$ nix-build -A 'config.systemd.units."httpd.service".unit' -</screen> - - You can also test individual units, without rebuilding the whole - system, by putting them in - <filename>/run/systemd/system</filename>: - -<screen> -$ cp $(nix-build -A 'config.systemd.units."httpd.service".unit')/httpd.service \ - /run/systemd/system/tmp-httpd.service -$ systemctl daemon-reload -$ systemctl start tmp-httpd.service -</screen> - - Note that the unit must not have the same name as any unit in - <filename>/etc/systemd/system</filename> since those take - precedence over <filename>/run/systemd/system</filename>. - That’s why the unit is installed as - <filename>tmp-httpd.service</filename> here.</para> - </listitem> - </varlistentry> - -</variablelist> - -</para> - -</section> - - -<!--===============================================================--> - -<section 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 <filename>configuration.nix</filename> to configure the system that -would be installed on the CD.</para> - -<para>Default CD/DVD configurations are available -inside <filename>nixos/modules/installer/cd-dvd</filename>. To build them -you have to set <envar>NIXOS_CONFIG</envar> before -running <command>nix-build</command> to build the ISO. - -<screen> -$ nix-build -A config.system.build.isoImage -I nixos-config=modules/installer/cd-dvd/installation-cd-minimal.nix</screen> - -</para> - -<para>Before burning your CD/DVD, you can check the content of the image by mounting anywhere like -suggested by the following command: - -<screen> -$ mount -o loop -t iso9660 ./result/iso/cd.iso /mnt/iso</screen> - -</para> - -</section> - - -<!--===============================================================--> - -<section> - -<title>Testing the installer</title> - -<para>Building, burning, and booting from an installation CD is rather -tedious, so here is a quick way to see if the installer works -properly: - -<screen> -$ nix-build -A config.system.build.nixos-install -$ mount -t tmpfs none /mnt -$ ./result/bin/nixos-install</screen> - -To start a login shell in the new NixOS installation in -<filename>/mnt</filename>: - -<screen> -$ ./result/bin/nixos-install --chroot -</screen> - -</para> - -</section> - - - -<!--===============================================================--> - -<section xml:id="sec-nixos-tests"> - -<title>NixOS tests</title> - -<para>When you add some feature to NixOS, you should write a test for -it. NixOS tests are kept in the directory <filename -xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/tests">nixos/tests</filename>, -and are executed (using Nix) by a testing framework that automatically -starts one or more virtual machines containing the NixOS system(s) -required for the test.</para> - -<simplesect><title>Writing tests</title> - -<para>A NixOS test is a Nix expression that has the following structure: - -<programlisting> -import ./make-test.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>Perl code…</replaceable> - ''; -} -</programlisting> - -The attribute <literal>testScript</literal> is a bit of Perl 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.nix">nfs.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 Perl 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>, -where <replaceable>name</replaceable> is the identifier of the machine -(which is just <literal>machine</literal> if you didn’t specify -multiple machines using the <literal>nodes</literal> attribute). For -instance, the following 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->waitForUnit("default.target"); -$machine->succeed("uname") =~ /Linux/; -</programlisting> - -The first line is actually unnecessary; machines are implicitly -started when you first execute an action on them (such as -<literal>waitForUnit</literal> or <literal>succeed</literal>). If you -have multiple machines, you can speed up the test by starting them in -parallel: - -<programlisting> -startAll; -</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>sendMonitorCommand</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>sendKeys</methodname></term> - <listitem><para>Simulate pressing keys on the virtual keyboard, - e.g., <literal>sendKeys("ctrl-alt-delete")</literal>.</para></listitem> - </varlistentry> - - <varlistentry> - <term><methodname>sendChars</methodname></term> - <listitem><para>Simulate typing a sequence of characters on the - virtual keyboard, e.g., <literal>sendKeys("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.</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>waitUntilSucceeds</methodname></term> - <listitem><para>Repeat a shell command with 1-second intervals - until it succeeds.</para></listitem> - </varlistentry> - - <varlistentry> - <term><methodname>waitUntilFails</methodname></term> - <listitem><para>Repeat a shell command with 1-second intervals - until it fails.</para></listitem> - </varlistentry> - - <varlistentry> - <term><methodname>waitForUnit</methodname></term> - <listitem><para>Wait until the specified systemd unit has reached - the “active” state.</para></listitem> - </varlistentry> - - <varlistentry> - <term><methodname>waitForFile</methodname></term> - <listitem><para>Wait until the specified file - exists.</para></listitem> - </varlistentry> - - <varlistentry> - <term><methodname>waitForOpenPort</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>waitForClosedPort</methodname></term> - <listitem><para>Wait until nobody is listening on the given TCP - port.</para></listitem> - </varlistentry> - - <varlistentry> - <term><methodname>waitForX</methodname></term> - <listitem><para>Wait until the X11 server is accepting - connections.</para></listitem> - </varlistentry> - - <varlistentry> - <term><methodname>waitForWindow</methodname></term> - <listitem><para>Wait until an X11 window has appeared whose name - matches the given regular expression, e.g., - <literal>waitForWindow(qr/Terminal/)</literal>.</para></listitem> - </varlistentry> - -</variablelist> - -</para> - -</simplesect> - - -<simplesect><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> -$ nix-build '<nixpkgs/nixos/tests/login.nix>' -</screen> - -or, if you don’t want to rely on <envar>NIX_PATH</envar>: - -<screen> -$ 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 -</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> -$ firefox result/log.html -</screen> - -</para> - -<para>It is also possible to run the test environment interactively, -allowing you to experiment with the VMs. For example: - -<screen> -$ nix-build login.nix -A driver -$ ./result/bin/nixos-run-vms -</screen> - -The script <command>nixos-run-vms</command> starts the virtual -machines defined by test. The root file system of the VMs is created -on the fly and kept across VM restarts in -<filename>./</filename><varname>hostname</varname><filename>.qcow2</filename>.</para> - -<para>Finally, the test itself can be run interactively. This is -particularly useful when developing or debugging a test: - -<screen> -$ nix-build tests/ -A nfs.driver -$ ./result/bin/nixos-test-driver -starting VDE switch for network 1 -> -</screen> - -You can then take any Perl statement, e.g. - -<screen> -> startAll -> $machine->succeed("touch /tmp/foo") -</screen> - -The function <command>testScript</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> - -</simplesect> - -</section> - - -</chapter> |