uboot: (firmwareOdroidC2/C4) don't invoke patch tool, use patches = [] instead

https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/setup.sh#L948
this can do it nicely.

Signed-off-by: Anton Arapov <anton@deadbeef.mx>
This commit is contained in:
Anton Arapov 2021-04-03 12:58:10 +02:00 committed by Alan Daniels
commit 56de2bcd43
30691 changed files with 3076956 additions and 0 deletions

View file

@ -0,0 +1,150 @@
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-activation-script">
<title>Activation script</title>
<para>
The activation script is a bash script called to activate the new
configuration which resides in a NixOS system in
<literal>$out/activate</literal>. Since its contents depend on your
system configuration, the contents may differ. This chapter explains
how the script works in general and some common NixOS snippets.
Please be aware that the script is executed on every boot and system
switch, so tasks that can be performed in other places should be
performed there (for example letting a directory of a service be
created by systemd using mechanisms like
<literal>StateDirectory</literal>,
<literal>CacheDirectory</literal>, … or if thats not possible using
<literal>preStart</literal> of the service).
</para>
<para>
Activation scripts are defined as snippets using
<xref linkend="opt-system.activationScripts" />. They can either be
a simple multiline string or an attribute set that can depend on
other snippets. The builder for the activation script will take
these dependencies into account and order the snippets accordingly.
As a simple example:
</para>
<programlisting language="bash">
system.activationScripts.my-activation-script = {
deps = [ &quot;etc&quot; ];
# supportsDryActivation = true;
text = ''
echo &quot;Hallo i bims&quot;
'';
};
</programlisting>
<para>
This example creates an activation script snippet that is run after
the <literal>etc</literal> snippet. The special variable
<literal>supportsDryActivation</literal> can be set so the snippet
is also run when <literal>nixos-rebuild dry-activate</literal> is
run. To differentiate between real and dry activation, the
<literal>$NIXOS_ACTION</literal> environment variable can be read
which is set to <literal>dry-activate</literal> when a dry
activation is done.
</para>
<para>
An activation script can write to special files instructing
<literal>switch-to-configuration</literal> to restart/reload units.
The script will take these requests into account and will
incorperate the unit configuration as described above. This means
that the activation script will <quote>fake</quote> a modified unit
file and <literal>switch-to-configuration</literal> will act
accordingly. By doing so, configuration like
<link linkend="opt-systemd.services">systemd.services.&lt;name&gt;.restartIfChanged</link>
is respected. Since the activation script is run
<emphasis role="strong">after</emphasis> services are already
stopped,
<link linkend="opt-systemd.services">systemd.services.&lt;name&gt;.stopIfChanged</link>
cannot be taken into account anymore and the unit is always
restarted instead of being stopped and started afterwards.
</para>
<para>
The files that can be written to are
<literal>/run/nixos/activation-restart-list</literal> and
<literal>/run/nixos/activation-reload-list</literal> with their
respective counterparts for dry activation being
<literal>/run/nixos/dry-activation-restart-list</literal> and
<literal>/run/nixos/dry-activation-reload-list</literal>. Those
files can contain newline-separated lists of unit names where
duplicates are being ignored. These files are not create
automatically and activation scripts must take the possiblility into
account that they have to create them first.
</para>
<section xml:id="sec-activation-script-nixos-snippets">
<title>NixOS snippets</title>
<para>
There are some snippets NixOS enables by default because disabling
them would most likely break you system. This section lists a few
of them and what they do:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>binsh</literal> creates <literal>/bin/sh</literal>
which points to the runtime shell
</para>
</listitem>
<listitem>
<para>
<literal>etc</literal> sets up the contents of
<literal>/etc</literal>, this includes systemd units and
excludes <literal>/etc/passwd</literal>,
<literal>/etc/group</literal>, and
<literal>/etc/shadow</literal> (which are managed by the
<literal>users</literal> snippet)
</para>
</listitem>
<listitem>
<para>
<literal>hostname</literal> sets the systems hostname in the
kernel (not in <literal>/etc</literal>)
</para>
</listitem>
<listitem>
<para>
<literal>modprobe</literal> sets the path to the
<literal>modprobe</literal> binary for module auto-loading
</para>
</listitem>
<listitem>
<para>
<literal>nix</literal> prepares the nix store and adds a
default initial channel
</para>
</listitem>
<listitem>
<para>
<literal>specialfs</literal> is responsible for mounting
filesystems like <literal>/proc</literal> and
<literal>sys</literal>
</para>
</listitem>
<listitem>
<para>
<literal>users</literal> creates and removes users and groups
by managing <literal>/etc/passwd</literal>,
<literal>/etc/group</literal> and
<literal>/etc/shadow</literal>. This also creates home
directories
</para>
</listitem>
<listitem>
<para>
<literal>usrbinenv</literal> creates
<literal>/usr/bin/env</literal>
</para>
</listitem>
<listitem>
<para>
<literal>var</literal> creates some directories in
<literal>/var</literal> that are not service-specific
</para>
</listitem>
<listitem>
<para>
<literal>wrappers</literal> creates setuid wrappers like
<literal>ping</literal> and <literal>sudo</literal>
</para>
</listitem>
</itemizedlist>
</section>
</section>

View file

@ -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 = &quot;rsyslogd conflicts with syslogd&quot;;
}
];
}
}
</programlisting>
</section>
</section>

View file

@ -0,0 +1,124 @@
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-building-parts">
<title>Building Specific Parts of NixOS</title>
<para>
With the command <literal>nix-build</literal>, you can build
specific parts of your NixOS configuration. This is done as follows:
</para>
<programlisting>
$ cd /path/to/nixpkgs/nixos
$ nix-build -A config.option
</programlisting>
<para>
where <literal>option</literal> is a NixOS option with type
<quote>derivation</quote> (i.e. something that can be built).
Attributes of interest include:
</para>
<variablelist>
<varlistentry>
<term>
<literal>system.build.toplevel</literal>
</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 <literal>nixos-rebuild</literal>
builds and what <literal>/run/current-system</literal> points
to afterwards.
</para>
<para>
A shortcut to build this is:
</para>
<programlisting>
$ nix-build -A system
</programlisting>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>system.build.manual.manualHTML</literal>
</term>
<listitem>
<para>
The NixOS manual.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>system.build.etc</literal>
</term>
<listitem>
<para>
A tree of symlinks that form the static parts of
<literal>/etc</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>system.build.initialRamdisk</literal> ,
<literal>system.build.kernel</literal>
</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 QEMUs <literal>-kernel</literal> and
<literal>-initrd</literal> options:
</para>
<programlisting>
$ 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
</programlisting>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>system.build.nixos-rebuild</literal> ,
<literal>system.build.nixos-install</literal> ,
<literal>system.build.nixos-generate-config</literal>
</term>
<listitem>
<para>
These build the corresponding NixOS commands.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>systemd.units.unit-name.unit</literal>
</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:
</para>
<programlisting>
$ nix-build -A 'config.systemd.units.&quot;httpd.service&quot;.unit'
</programlisting>
<para>
You can also test individual units, without rebuilding the
whole system, by putting them in
<literal>/run/systemd/system</literal>:
</para>
<programlisting>
$ cp $(nix-build -A 'config.systemd.units.&quot;httpd.service&quot;.unit')/httpd.service \
/run/systemd/system/tmp-httpd.service
# systemctl daemon-reload
# systemctl start tmp-httpd.service
</programlisting>
<para>
Note that the unit must not have the same name as any unit in
<literal>/etc/systemd/system</literal> since those take
precedence over <literal>/run/systemd/system</literal>. Thats
why the unit is installed as
<literal>tmp-httpd.service</literal> here.
</para>
</listitem>
</varlistentry>
</variablelist>
</chapter>

View file

@ -0,0 +1,87 @@
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-freeform-modules">
<title>Freeform modules</title>
<para>
Freeform modules allow you to define values for option paths that
have not been declared explicitly. This can be used to add
attribute-specific types to what would otherwise have to be
<literal>attrsOf</literal> options in order to accept all attribute
names.
</para>
<para>
This feature can be enabled by using the attribute
<literal>freeformType</literal> to define a freeform type. By doing
this, all assignments without an associated option will be merged
using the freeform type and combined into the resulting
<literal>config</literal> set. Since this feature nullifies name
checking for entire option trees, it is only recommended for use in
submodules.
</para>
<anchor xml:id="ex-freeform-module" />
<para>
<emphasis role="strong">Example: Freeform submodule</emphasis>
</para>
<para>
The following shows a submodule assigning a freeform type that
allows arbitrary attributes with <literal>str</literal> values below
<literal>settings</literal>, but also declares an option for the
<literal>settings.port</literal> attribute to have it type-checked
and assign a default value. See
<link linkend="ex-settings-typed-attrs">Example: Declaring a
type-checked <literal>settings</literal> attribute</link> for a more
complete example.
</para>
<programlisting language="bash">
{ lib, config, ... }: {
options.settings = lib.mkOption {
type = lib.types.submodule {
freeformType = with lib.types; attrsOf str;
# We want this attribute to be checked for the correct type
options.port = lib.mkOption {
type = lib.types.port;
# Declaring the option also allows defining a default value
default = 8080;
};
};
};
}
</programlisting>
<para>
And the following shows what such a module then allows
</para>
<programlisting language="bash">
{
# Not a declared option, but the freeform type allows this
settings.logLevel = &quot;debug&quot;;
# Not allowed because the the freeform type only allows strings
# settings.enable = true;
# Allowed because there is a port option declared
settings.port = 80;
# Not allowed because the port option doesn't allow strings
# settings.port = &quot;443&quot;;
}
</programlisting>
<note>
<para>
Freeform attributes cannot depend on other attributes of the same
set without infinite recursion:
</para>
<programlisting language="bash">
{
# This throws infinite recursion encountered
settings.logLevel = lib.mkIf (config.settings.port == 80) &quot;debug&quot;;
}
</programlisting>
<para>
To prevent this, declare options for all attributes that need to
depend on others. For above example this means to declare
<literal>logLevel</literal> to be an option.
</para>
</note>
</section>

View file

@ -0,0 +1,47 @@
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-importing-modules">
<title>Importing Modules</title>
<para>
Sometimes NixOS modules need to be used in configuration but exist
outside of Nixpkgs. These modules can be imported:
</para>
<programlisting language="bash">
{ config, lib, pkgs, ... }:
{
imports =
[ # Use a locally-available module definition in
# ./example-module/default.nix
./example-module
];
services.exampleModule.enable = true;
}
</programlisting>
<para>
The environment variable <literal>NIXOS_EXTRA_MODULE_PATH</literal>
is an absolute path to a NixOS module that is included alongside the
Nixpkgs NixOS modules. Like any NixOS module, this module can import
additional modules:
</para>
<programlisting language="bash">
# ./module-list/default.nix
[
./example-module1
./example-module2
]
</programlisting>
<programlisting language="bash">
# ./extra-module/default.nix
{ imports = import ./module-list.nix; }
</programlisting>
<programlisting language="bash">
# NIXOS_EXTRA_MODULE_PATH=/absolute/path/to/extra-module
{ config, lib, pkgs, ... }:
{
# No `imports` needed
services.exampleModule1.enable = true;
}
</programlisting>
</section>

View file

@ -0,0 +1,10 @@
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-linking-nixos-tests-to-packages">
<title>Linking NixOS tests to packages</title>
<para>
You can link NixOS module tests to the packages that they exercised,
so that the tests can be run automatically during code review when
the package gets changed. This is
<link xlink:href="https://nixos.org/manual/nixpkgs/stable/#ssec-nixos-tests-linking">described
in the nixpkgs manual</link>.
</para>
</section>

View file

@ -0,0 +1,95 @@
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-meta-attributes">
<title>Meta Attributes</title>
<para>
Like Nix packages, NixOS modules can declare meta-attributes to
provide extra information. Module meta attributes are defined in the
<literal>meta.nix</literal> special module.
</para>
<para>
<literal>meta</literal> is a top level attribute like
<literal>options</literal> and <literal>config</literal>. Available
meta-attributes are <literal>maintainers</literal>,
<literal>doc</literal>, and <literal>buildDocsInSandbox</literal>.
</para>
<para>
Each of the meta-attributes must be defined at most once per module
file.
</para>
<programlisting language="bash">
{ config, lib, pkgs, ... }:
{
options = {
...
};
config = {
...
};
meta = {
maintainers = with lib.maintainers; [ ericsagnes ];
doc = ./default.xml;
buildDocsInSandbox = true;
};
}
</programlisting>
<itemizedlist>
<listitem>
<para>
<literal>maintainers</literal> contains a list of the module
maintainers.
</para>
</listitem>
<listitem>
<para>
<literal>doc</literal> points to a valid DocBook file containing
the module documentation. Its contents is automatically added to
<xref linkend="ch-configuration" />. Changes to a module
documentation have to be checked to not break building the NixOS
manual:
</para>
<programlisting>
$ nix-build nixos/release.nix -A manual.x86_64-linux
</programlisting>
</listitem>
<listitem>
<para>
<literal>buildDocsInSandbox</literal> indicates whether the
option documentation for the module can be built in a derivation
sandbox. This option is currently only honored for modules
shipped by nixpkgs. User modules and modules taken from
<literal>NIXOS_EXTRA_MODULE_PATH</literal> are always built
outside of the sandbox, as has been the case in previous
releases.
</para>
<para>
Building NixOS option documentation in a sandbox allows caching
of the built documentation, which greatly decreases the amount
of time needed to evaluate a system configuration that has NixOS
documentation enabled. The sandbox also restricts which
attributes may be referenced by documentation attributes (such
as option descriptions) to the <literal>options</literal> and
<literal>lib</literal> module arguments and the
<literal>pkgs.formats</literal> attribute of the
<literal>pkgs</literal> argument, <literal>config</literal> and
the rest of <literal>pkgs</literal> are disallowed and will
cause doc build failures when used. This restriction is
necessary because we cannot reproduce the full nixpkgs
instantiation with configuration and overlays from a system
configuration inside the sandbox. The <literal>options</literal>
argument only includes options of modules that are also built
inside the sandbox, referencing an option of a module that isnt
built in the sandbox is also forbidden.
</para>
<para>
The default is <literal>true</literal> and should usually not be
changed; set it to <literal>false</literal> only if the module
requires access to <literal>pkgs</literal> in its documentation
(e.g. because it loads information from a linked package to
build an option type) or if its documentation depends on other
modules that also arent sandboxed (e.g. by using types defined
in the other module).
</para>
</listitem>
</itemizedlist>
</section>

View file

@ -0,0 +1,14 @@
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="sec-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
<literal>nixos/tests</literal>, 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>
<xi:include href="writing-nixos-tests.section.xml" />
<xi:include href="running-nixos-tests.section.xml" />
<xi:include href="running-nixos-tests-interactively.section.xml" />
<xi:include href="linking-nixos-tests-to-packages.section.xml" />
</chapter>

View file

@ -0,0 +1,327 @@
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-option-declarations">
<title>Option Declarations</title>
<para>
An option declaration specifies the name, type and description of a
NixOS configuration option. It is invalid to define an option that
hasnt been declared in any module. An option declaration generally
looks like this:
</para>
<programlisting language="bash">
options = {
name = mkOption {
type = type specification;
default = default value;
example = example value;
description = &quot;Description for use in the NixOS manual.&quot;;
};
};
</programlisting>
<para>
The attribute names within the <literal>name</literal> attribute
path must be camel cased in general but should, as an exception,
match the
<link xlink:href="https://nixos.org/nixpkgs/manual/#sec-package-naming">
package attribute name</link> when referencing a Nixpkgs package.
For example, the option
<literal>services.nix-serve.bindAddress</literal> references the
<literal>nix-serve</literal> Nixpkgs package.
</para>
<para>
The function <literal>mkOption</literal> accepts the following
arguments.
</para>
<variablelist>
<varlistentry>
<term>
<literal>type</literal>
</term>
<listitem>
<para>
The type of the option (see
<xref linkend="sec-option-types" />). This argument is
mandatory for nixpkgs modules. Setting this is highly
recommended for the sake of documentation and type checking.
In case it is not set, a fallback type with unspecified
behavior is used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>default</literal>
</term>
<listitem>
<para>
The default value used if no value is defined by any module. A
default is not required; but if a default is not given, then
users of the module will have to define the value of the
option, otherwise an error will be thrown.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>defaultText</literal>
</term>
<listitem>
<para>
A textual representation of the default value to be rendered
verbatim in the manual. Useful if the default value is a
complex expression or depends on other values or packages. Use
<literal>lib.literalExpression</literal> for a Nix expression,
<literal>lib.literalDocBook</literal> for a plain English
description in DocBook format.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>example</literal>
</term>
<listitem>
<para>
An example value that will be shown in the NixOS manual. You
can use <literal>lib.literalExpression</literal> and
<literal>lib.literalDocBook</literal> in the same way as in
<literal>defaultText</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>description</literal>
</term>
<listitem>
<para>
A textual description of the option, in DocBook format, that
will be included in the NixOS manual.
</para>
</listitem>
</varlistentry>
</variablelist>
<section xml:id="sec-option-declarations-util">
<title>Utility functions for common option patterns</title>
<section xml:id="sec-option-declarations-util-mkEnableOption">
<title><literal>mkEnableOption</literal></title>
<para>
Creates an Option attribute set for a boolean value option i.e
an option to be toggled on or off.
</para>
<para>
This function takes a single string argument, the name of the
thing to be toggled.
</para>
<para>
The options description is <quote>Whether to enable
&lt;name&gt;.</quote>.
</para>
<para>
For example:
</para>
<anchor xml:id="ex-options-declarations-util-mkEnableOption-magic" />
<programlisting language="bash">
lib.mkEnableOption &quot;magic&quot;
# is like
lib.mkOption {
type = lib.types.bool;
default = false;
example = true;
description = &quot;Whether to enable magic.&quot;;
}
</programlisting>
<section xml:id="sec-option-declarations-util-mkPackageOption">
<title><literal>mkPackageOption</literal></title>
<para>
Usage:
</para>
<programlisting language="bash">
mkPackageOption pkgs &quot;name&quot; { default = [ &quot;path&quot; &quot;in&quot; &quot;pkgs&quot; ]; example = &quot;literal example&quot;; }
</programlisting>
<para>
Creates an Option attribute set for an option that specifies
the package a module should use for some purpose.
</para>
<para>
<emphasis role="strong">Note</emphasis>: You shouldnt
necessarily make package options for all of your modules. You
can always overwrite a specific package throughout nixpkgs by
using
<link xlink:href="https://nixos.org/manual/nixpkgs/stable/#chap-overlays">nixpkgs
overlays</link>.
</para>
<para>
The default package is specified as a list of strings
representing its attribute path in nixpkgs. Because of this,
you need to pass nixpkgs itself as the first argument.
</para>
<para>
The second argument is the name of the option, used in the
description <quote>The &lt;name&gt; package to use.</quote>.
You can also pass an example value, either a literal string or
a packages attribute path.
</para>
<para>
You can omit the default path if the name of the option is
also attribute path in nixpkgs.
</para>
<anchor xml:id="ex-options-declarations-util-mkPackageOption" />
<para>
Examples:
</para>
<anchor xml:id="ex-options-declarations-util-mkPackageOption-hello" />
<programlisting language="bash">
lib.mkPackageOption pkgs &quot;hello&quot; { }
# is like
lib.mkOption {
type = lib.types.package;
default = pkgs.hello;
defaultText = lib.literalExpression &quot;pkgs.hello&quot;;
description = &quot;The hello package to use.&quot;;
}
</programlisting>
<anchor xml:id="ex-options-declarations-util-mkPackageOption-ghc" />
<programlisting language="bash">
lib.mkPackageOption pkgs &quot;GHC&quot; {
default = [ &quot;ghc&quot; ];
example = &quot;pkgs.haskell.package.ghc923.ghc.withPackages (hkgs: [ hkgs.primes ])&quot;;
}
# is like
lib.mkOption {
type = lib.types.package;
default = pkgs.ghc;
defaultText = lib.literalExpression &quot;pkgs.ghc&quot;;
example = lib.literalExpression &quot;pkgs.haskell.package.ghc923.ghc.withPackages (hkgs: [ hkgs.primes ])&quot;;
description = &quot;The GHC package to use.&quot;;
}
</programlisting>
<section xml:id="sec-option-declarations-eot">
<title>Extensible Option Types</title>
<para>
Extensible option types is a feature that allow to extend
certain types declaration through multiple module files.
This feature only work with a restricted set of types,
namely <literal>enum</literal> and
<literal>submodules</literal> and any composed forms of
them.
</para>
<para>
Extensible option types can be used for
<literal>enum</literal> options that affects multiple
modules, or as an alternative to related
<literal>enable</literal> options.
</para>
<para>
As an example, we will take the case of display managers.
There is a central display manager module for generic
display manager options and a module file per display
manager backend (sddm, gdm ...).
</para>
<para>
There are two approaches we could take with this module
structure:
</para>
<itemizedlist>
<listitem>
<para>
Configuring the display managers independently by adding
an enable option to every display manager module
backend. (NixOS)
</para>
</listitem>
<listitem>
<para>
Configuring the display managers in the central module
by adding an option to select which display manager
backend to use.
</para>
</listitem>
</itemizedlist>
<para>
Both approaches have problems.
</para>
<para>
Making backends independent can quickly become hard to
manage. For display managers, there can only be one enabled
at a time, but the type system cannot enforce this
restriction as there is no relation between each backends
<literal>enable</literal> option. As a result, this
restriction has to be done explicitly by adding assertions
in each display manager backend module.
</para>
<para>
On the other hand, managing the display manager backends in
the central module will require changing the central module
option every time a new backend is added or removed.
</para>
<para>
By using extensible option types, it is possible to create a
placeholder option in the central module
(<link linkend="ex-option-declaration-eot-service">Example:
Extensible type placeholder in the service module</link>),
and to extend it in each backend module
(<link linkend="ex-option-declaration-eot-backend-gdm">Example:
Extending
<literal>services.xserver.displayManager.enable</literal> in
the <literal>gdm</literal> module</link>,
<link linkend="ex-option-declaration-eot-backend-sddm">Example:
Extending
<literal>services.xserver.displayManager.enable</literal> in
the <literal>sddm</literal> module</link>).
</para>
<para>
As a result, <literal>displayManager.enable</literal> option
values can be added without changing the main service module
file and the type system automatically enforces that there
can only be a single display manager enabled.
</para>
<anchor xml:id="ex-option-declaration-eot-service" />
<para>
<emphasis role="strong">Example: Extensible type placeholder
in the service module</emphasis>
</para>
<programlisting language="bash">
services.xserver.displayManager.enable = mkOption {
description = &quot;Display manager to use&quot;;
type = with types; nullOr (enum [ ]);
};
</programlisting>
<anchor xml:id="ex-option-declaration-eot-backend-gdm" />
<para>
<emphasis role="strong">Example: Extending
<literal>services.xserver.displayManager.enable</literal> in
the <literal>gdm</literal> module</emphasis>
</para>
<programlisting language="bash">
services.xserver.displayManager.enable = mkOption {
type = with types; nullOr (enum [ &quot;gdm&quot; ]);
};
</programlisting>
<anchor xml:id="ex-option-declaration-eot-backend-sddm" />
<para>
<emphasis role="strong">Example: Extending
<literal>services.xserver.displayManager.enable</literal> in
the <literal>sddm</literal> module</emphasis>
</para>
<programlisting language="bash">
services.xserver.displayManager.enable = mkOption {
type = with types; nullOr (enum [ &quot;sddm&quot; ]);
};
</programlisting>
<para>
The placeholder declaration is a standard
<literal>mkOption</literal> declaration, but it is important
that extensible option declarations only use the
<literal>type</literal> argument.
</para>
<para>
Extensible option types work with any of the composed
variants of <literal>enum</literal> such as
<literal>with types; nullOr (enum [ &quot;foo&quot; &quot;bar&quot; ])</literal>
or
<literal>with types; listOf (enum [ &quot;foo&quot; &quot;bar&quot; ])</literal>.
</para>
</section>
</section>
</section>
</section>
</section>

View file

@ -0,0 +1,104 @@
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-option-definitions">
<title>Option Definitions</title>
<para>
Option definitions are generally straight-forward bindings of values
to option names, like
</para>
<programlisting language="bash">
config = {
services.httpd.enable = true;
};
</programlisting>
<para>
However, sometimes you need to wrap an option definition or set of
option definitions in a <emphasis>property</emphasis> to achieve
certain effects:
</para>
<section xml:id="sec-option-definitions-delaying-conditionals">
<title>Delaying Conditionals</title>
<para>
If a set of option definitions is conditional on the value of
another option, you may need to use <literal>mkIf</literal>.
Consider, for instance:
</para>
<programlisting language="bash">
config = if config.services.httpd.enable then {
environment.systemPackages = [ ... ];
...
} else {};
</programlisting>
<para>
This definition will cause Nix to fail with an <quote>infinite
recursion</quote> error. Why? Because the value of
<literal>config.services.httpd.enable</literal> depends on the
value being constructed here. After all, you could also write the
clearly circular and contradictory:
</para>
<programlisting language="bash">
config = if config.services.httpd.enable then {
services.httpd.enable = false;
} else {
services.httpd.enable = true;
};
</programlisting>
<para>
The solution is to write:
</para>
<programlisting language="bash">
config = mkIf config.services.httpd.enable {
environment.systemPackages = [ ... ];
...
};
</programlisting>
<para>
The special function <literal>mkIf</literal> causes the evaluation
of the conditional to be <quote>pushed down</quote> into the
individual definitions, as if you had written:
</para>
<programlisting language="bash">
config = {
environment.systemPackages = if config.services.httpd.enable then [ ... ] else [];
...
};
</programlisting>
</section>
<section xml:id="sec-option-definitions-setting-priorities">
<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
<literal>mkOverride</literal>, e.g.
</para>
<programlisting language="bash">
services.openssh.enable = mkOverride 10 false;
</programlisting>
<para>
This definition causes all other definitions with priorities above
10 to be discarded. The function <literal>mkForce</literal> is
equal to <literal>mkOverride 50</literal>.
</para>
</section>
<section xml:id="sec-option-definitions-merging">
<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 <literal>mkMerge</literal>:
</para>
<programlisting language="bash">
config = mkMerge
[ # Unconditional stuff.
{ environment.systemPackages = [ ... ];
}
# Conditional stuff.
(mkIf config.services.bla.enable {
environment.systemPackages = [ ... ];
})
];
</programlisting>
</section>
</section>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,70 @@
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-replace-modules">
<title>Replace Modules</title>
<para>
Modules that are imported can also be disabled. The option
declarations, config implementation and the imports of a disabled
module will be ignored, allowing another to take it's place. This
can be used to import a set of modules from another channel while
keeping the rest of the system on a stable release.
</para>
<para>
<literal>disabledModules</literal> is a top level attribute like
<literal>imports</literal>, <literal>options</literal> and
<literal>config</literal>. It contains a list of modules that will
be disabled. This can either be the full path to the module or a
string with the filename relative to the modules path (eg.
&lt;nixpkgs/nixos/modules&gt; for nixos).
</para>
<para>
This example will replace the existing postgresql module with the
version defined in the nixos-unstable channel while keeping the rest
of the modules and packages from the original nixos channel. This
only overrides the module definition, this won't use postgresql from
nixos-unstable unless explicitly configured to do so.
</para>
<programlisting language="bash">
{ config, lib, pkgs, ... }:
{
disabledModules = [ &quot;services/databases/postgresql.nix&quot; ];
imports =
[ # Use postgresql service from nixos-unstable channel.
# sudo nix-channel --add https://nixos.org/channels/nixos-unstable nixos-unstable
&lt;nixos-unstable/nixos/modules/services/databases/postgresql.nix&gt;
];
services.postgresql.enable = true;
}
</programlisting>
<para>
This example shows how to define a custom module as a replacement
for an existing module. Importing this module will disable the
original module without having to know it's implementation details.
</para>
<programlisting language="bash">
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.man;
in
{
disabledModules = [ &quot;services/programs/man.nix&quot; ];
options = {
programs.man.enable = mkOption {
type = types.bool;
default = true;
description = &quot;Whether to enable manual pages.&quot;;
};
};
config = mkIf cfg.enabled {
warnings = [ &quot;disabled manpages for production deployments.&quot; ];
};
}
</programlisting>
</section>

View file

@ -0,0 +1,39 @@
<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 . -A nixosTests.login.driverInteractive
$ ./result/bin/nixos-test-driver
[...]
&gt;&gt;&gt;
</programlisting>
<para>
You can then take any Python statement, e.g.
</para>
<programlisting language="python">
&gt;&gt;&gt; start_all()
&gt;&gt;&gt; test_script()
&gt;&gt;&gt; machine.succeed(&quot;touch /tmp/foo&quot;)
&gt;&gt;&gt; print(machine.succeed(&quot;pwd&quot;)) # 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>
You can re-use the VM states coming from a previous run by setting
the <literal>--keep-vm-state</literal> flag.
</para>
<programlisting>
$ ./result/bin/nixos-test-driver --keep-vm-state
</programlisting>
<para>
The machine state is stored in the
<literal>$TMPDIR/vm-state-machinename</literal> directory.
</para>
</section>

View file

@ -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 '&lt;nixpkgs/nixos/tests/login.nix&gt;'
</programlisting>
<para>
or, if you dont 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 log of the test:
</para>
<programlisting>
$ nix-store --read-log result
</programlisting>
</section>

View file

@ -0,0 +1,421 @@
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-settings-options">
<title>Options for Program Settings</title>
<para>
Many programs have configuration files where program-specific
settings can be declared. File formats can be separated into two
categories:
</para>
<itemizedlist>
<listitem>
<para>
Nix-representable ones: These can trivially be mapped to a
subset of Nix syntax. E.g. JSON is an example, since its values
like <literal>{&quot;foo&quot;:{&quot;bar&quot;:10}}</literal>
can be mapped directly to Nix:
<literal>{ foo = { bar = 10; }; }</literal>. Other examples are
INI, YAML and TOML. The following section explains the
convention for these settings.
</para>
</listitem>
<listitem>
<para>
Non-nix-representable ones: These can't be trivially mapped to a
subset of Nix syntax. Most generic programming languages are in
this group, e.g. bash, since the statement
<literal>if true; then echo hi; fi</literal> doesn't have a
trivial representation in Nix.
</para>
<para>
Currently there are no fixed conventions for these, but it is
common to have a <literal>configFile</literal> option for
setting the configuration file path directly. The default value
of <literal>configFile</literal> can be an auto-generated file,
with convenient options for controlling the contents. For
example an option of type <literal>attrsOf str</literal> can be
used for representing environment variables which generates a
section like <literal>export FOO=&quot;foo&quot;</literal>.
Often it can also be useful to also include an
<literal>extraConfig</literal> option of type
<literal>lines</literal> to allow arbitrary text after the
autogenerated part of the file.
</para>
</listitem>
</itemizedlist>
<section xml:id="sec-settings-nix-representable">
<title>Nix-representable Formats (JSON, YAML, TOML, INI,
...)</title>
<para>
By convention, formats like this are handled with a generic
<literal>settings</literal> option, representing the full program
configuration as a Nix value. The type of this option should
represent the format. The most common formats have a predefined
type and string generator already declared under
<literal>pkgs.formats</literal>:
</para>
<variablelist>
<varlistentry>
<term>
<literal>pkgs.formats.javaProperties</literal> {
<emphasis><literal>comment</literal></emphasis> ?
<literal>&quot;Generated with Nix&quot;</literal> }
</term>
<listitem>
<para>
A function taking an attribute set with values
</para>
<variablelist>
<varlistentry>
<term>
<literal>comment</literal>
</term>
<listitem>
<para>
A string to put at the start of the file in a comment.
It can have multiple lines.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
It returns the <literal>type</literal>:
<literal>attrsOf str</literal> and a function
<literal>generate</literal> to build a Java
<literal>.properties</literal> file, taking care of the
correct escaping, etc.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>pkgs.formats.json</literal> { }
</term>
<listitem>
<para>
A function taking an empty attribute set (for future
extensibility) and returning a set with JSON-specific
attributes <literal>type</literal> and
<literal>generate</literal> as specified
<link linkend="pkgs-formats-result">below</link>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>pkgs.formats.yaml</literal> { }
</term>
<listitem>
<para>
A function taking an empty attribute set (for future
extensibility) and returning a set with YAML-specific
attributes <literal>type</literal> and
<literal>generate</literal> as specified
<link linkend="pkgs-formats-result">below</link>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>pkgs.formats.ini</literal> {
<emphasis><literal>listsAsDuplicateKeys</literal></emphasis> ?
false, <emphasis><literal>listToValue</literal></emphasis> ?
null, ... }
</term>
<listitem>
<para>
A function taking an attribute set with values
</para>
<variablelist>
<varlistentry>
<term>
<literal>listsAsDuplicateKeys</literal>
</term>
<listitem>
<para>
A boolean for controlling whether list values can be
used to represent duplicate INI keys
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>listToValue</literal>
</term>
<listitem>
<para>
A function for turning a list of values into a single
value.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
It returns a set with INI-specific attributes
<literal>type</literal> and <literal>generate</literal> as
specified <link linkend="pkgs-formats-result">below</link>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>pkgs.formats.toml</literal> { }
</term>
<listitem>
<para>
A function taking an empty attribute set (for future
extensibility) and returning a set with TOML-specific
attributes <literal>type</literal> and
<literal>generate</literal> as specified
<link linkend="pkgs-formats-result">below</link>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>pkgs.formats.elixirConf { elixir ? pkgs.elixir }</literal>
</term>
<listitem>
<para>
A function taking an attribute set with values
</para>
<variablelist>
<varlistentry>
<term>
<literal>elixir</literal>
</term>
<listitem>
<para>
The Elixir package which will be used to format the
generated output
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
It returns a set with Elixir-Config-specific attributes
<literal>type</literal>, <literal>lib</literal>, and
<literal>generate</literal> as specified
<link linkend="pkgs-formats-result">below</link>.
</para>
<para>
The <literal>lib</literal> attribute contains functions to
be used in settings, for generating special Elixir values:
</para>
<variablelist>
<varlistentry>
<term>
<literal>mkRaw elixirCode</literal>
</term>
<listitem>
<para>
Outputs the given string as raw Elixir code
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>mkGetEnv { envVariable, fallback ? null }</literal>
</term>
<listitem>
<para>
Makes the configuration fetch an environment variable
at runtime
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>mkAtom atom</literal>
</term>
<listitem>
<para>
Outputs the given string as an Elixir atom, instead of
the default Elixir binary string. Note: lowercase
atoms still needs to be prefixed with
<literal>:</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>mkTuple array</literal>
</term>
<listitem>
<para>
Outputs the given array as an Elixir tuple, instead of
the default Elixir list
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>mkMap attrset</literal>
</term>
<listitem>
<para>
Outputs the given attribute set as an Elixir map,
instead of the default Elixir keyword list
</para>
</listitem>
</varlistentry>
</variablelist>
</listitem>
</varlistentry>
</variablelist>
<para xml:id="pkgs-formats-result">
These functions all return an attribute set with these values:
</para>
<variablelist>
<varlistentry>
<term>
<literal>type</literal>
</term>
<listitem>
<para>
A module system type representing a value of the format
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>lib</literal>
</term>
<listitem>
<para>
Utility functions for convenience, or special interactions
with the format. This attribute is optional. It may contain
inside a <literal>types</literal> attribute containing types
specific to this format.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>generate</literal>
<emphasis><literal>filename jsonValue</literal></emphasis>
</term>
<listitem>
<para>
A function that can render a value of the format to a file.
Returns a file path.
</para>
<note>
<para>
This function puts the value contents in the Nix store. So
this should be avoided for secrets.
</para>
</note>
</listitem>
</varlistentry>
</variablelist>
<anchor xml:id="ex-settings-nix-representable" />
<para>
<emphasis role="strong">Example: Module with conventional
<literal>settings</literal> option</emphasis>
</para>
<para>
The following shows a module for an example program that uses a
JSON configuration file. It demonstrates how above values can be
used, along with some other related best practices. See the
comments for explanations.
</para>
<programlisting language="bash">
{ options, config, lib, pkgs, ... }:
let
cfg = config.services.foo;
# Define the settings format used for this program
settingsFormat = pkgs.formats.json {};
in {
options.services.foo = {
enable = lib.mkEnableOption &quot;foo service&quot;;
settings = lib.mkOption {
# Setting this type allows for correct merging behavior
type = settingsFormat.type;
default = {};
description = ''
Configuration for foo, see
&lt;link xlink:href=&quot;https://example.com/docs/foo&quot;/&gt;
for supported settings.
'';
};
};
config = lib.mkIf cfg.enable {
# We can assign some default settings here to make the service work by just
# enabling it. We use `mkDefault` for values that can be changed without
# problems
services.foo.settings = {
# Fails at runtime without any value set
log_level = lib.mkDefault &quot;WARN&quot;;
# We assume systemd's `StateDirectory` is used, so we require this value,
# therefore no mkDefault
data_path = &quot;/var/lib/foo&quot;;
# Since we use this to create a user we need to know the default value at
# eval time
user = lib.mkDefault &quot;foo&quot;;
};
environment.etc.&quot;foo.json&quot;.source =
# The formats generator function takes a filename and the Nix value
# representing the format value and produces a filepath with that value
# rendered in the format
settingsFormat.generate &quot;foo-config.json&quot; cfg.settings;
# We know that the `user` attribute exists because we set a default value
# for it above, allowing us to use it without worries here
users.users.${cfg.settings.user} = { isSystemUser = true; };
# ...
};
}
</programlisting>
<section xml:id="sec-settings-attrs-options">
<title>Option declarations for attributes</title>
<para>
Some <literal>settings</literal> attributes may deserve some
extra care. They may need a different type, default or merging
behavior, or they are essential options that should show their
documentation in the manual. This can be done using
<xref linkend="sec-freeform-modules" />.
</para>
<para>
We extend above example using freeform modules to declare an
option for the port, which will enforce it to be a valid integer
and make it show up in the manual.
</para>
<anchor xml:id="ex-settings-typed-attrs" />
<para>
<emphasis role="strong">Example: Declaring a type-checked
<literal>settings</literal> attribute</emphasis>
</para>
<programlisting language="bash">
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = settingsFormat.type;
# Declare an option for the port such that the type is checked and this option
# is shown in the manual.
options.port = lib.mkOption {
type = lib.types.port;
default = 8080;
description = ''
Which port this service should listen on.
'';
};
};
default = {};
description = ''
Configuration for Foo, see
&lt;link xlink:href=&quot;https://example.com/docs/foo&quot;/&gt;
for supported values.
'';
};
</programlisting>
</section>
</section>
</section>

View file

@ -0,0 +1,90 @@
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-getting-sources">
<title>Getting the Sources</title>
<para>
By default, NixOSs <literal>nixos-rebuild</literal> command uses
the NixOS and Nixpkgs sources provided by the
<literal>nixos</literal> channel (kept in
<literal>/nix/var/nix/profiles/per-user/root/channels/nixos</literal>).
To modify NixOS, however, you should check out the latest sources
from Git. This is as follows:
</para>
<programlisting>
$ git clone https://github.com/NixOS/nixpkgs
$ cd nixpkgs
$ git remote update origin
</programlisting>
<para>
This will check out the latest Nixpkgs sources to
<literal>./nixpkgs</literal> the NixOS sources to
<literal>./nixpkgs/nixos</literal>. (The NixOS source tree lives in
a subdirectory of the Nixpkgs repository.) The
<literal>nixpkgs</literal> repository has branches that correspond
to each Nixpkgs/NixOS channel (see <xref linkend="sec-upgrading" />
for more information about channels). Thus, the Git branch
<literal>origin/nixos-17.03</literal> will contain the latest built
and tested version available in the <literal>nixos-17.03</literal>
channel.
</para>
<para>
Its 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 youll have to rebuild
everything from source. So you may want to create a local branch
based on your current NixOS version:
</para>
<programlisting>
$ nixos-version
17.09pre104379.6e0b727 (Hummingbird)
$ git checkout -b local 6e0b727
</programlisting>
<para>
Or, to base your local branch on the latest version available in a
NixOS channel:
</para>
<programlisting>
$ git remote update origin
$ git checkout -b local origin/nixos-17.03
</programlisting>
<para>
(Replace <literal>nixos-17.03</literal> with the name of the channel
you want to use.) You can use <literal>git merge</literal> or
<literal>git rebase</literal> to keep your local branch in sync with
the channel, e.g.
</para>
<programlisting>
$ git remote update origin
$ git merge origin/nixos-17.03
</programlisting>
<para>
You can use <literal>git cherry-pick</literal> 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 <literal>nixos-rebuild</literal> about them using
the <literal>-I</literal> flag:
</para>
<programlisting>
# nixos-rebuild switch -I nixpkgs=/my/sources/nixpkgs
</programlisting>
<para>
If you want <literal>nix-env</literal> to use the expressions in
<literal>/my/sources</literal>, use
<literal>nix-env -f /my/sources/nixpkgs</literal>, or change the
default by adding a symlink in <literal>~/.nix-defexpr</literal>:
</para>
<programlisting>
$ ln -s /my/sources/nixpkgs ~/.nix-defexpr/nixpkgs
</programlisting>
<para>
You may want to delete the symlink
<literal>~/.nix-defexpr/channels_root</literal> to prevent roots
NixOS channel from clashing with your own tree (this may break the
command-not-found utility though). If you want to go back to the
default state, you may just remove the
<literal>~/.nix-defexpr</literal> directory completely, log out and
log in again and it should have been recreated with a link to the
root channels.
</para>
</chapter>

View file

@ -0,0 +1,22 @@
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="ch-testing-installer">
<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:
</para>
<programlisting>
# mount -t tmpfs none /mnt
# nixos-generate-config --root /mnt
$ nix-build '&lt;nixpkgs/nixos&gt;' -A config.system.build.nixos-install
# ./result/bin/nixos-install
</programlisting>
<para>
To start a login shell in the new NixOS installation in
<literal>/mnt</literal>:
</para>
<programlisting>
$ nix-build '&lt;nixpkgs/nixos&gt;' -A config.system.build.nixos-enter
# ./result/bin/nixos-enter
</programlisting>
</chapter>

View file

@ -0,0 +1,131 @@
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-unit-handling">
<title>Unit handling</title>
<para>
To figure out what units need to be
started/stopped/restarted/reloaded, the script first checks the
current state of the system, similar to what
<literal>systemctl list-units</literal> shows. For each of the
units, the script goes through the following checks:
</para>
<itemizedlist>
<listitem>
<para>
Is the unit file still in the new system? If not,
<emphasis role="strong">stop</emphasis> the service unless it
sets <literal>X-StopOnRemoval</literal> in the
<literal>[Unit]</literal> section to <literal>false</literal>.
</para>
</listitem>
<listitem>
<para>
Is it a <literal>.target</literal> unit? If so,
<emphasis role="strong">start</emphasis> it unless it sets
<literal>RefuseManualStart</literal> in the
<literal>[Unit]</literal> section to <literal>true</literal> or
<literal>X-OnlyManualStart</literal> in the
<literal>[Unit]</literal> section to <literal>true</literal>.
Also <emphasis role="strong">stop</emphasis> the unit again
unless it sets <literal>X-StopOnReconfiguration</literal> to
<literal>false</literal>.
</para>
</listitem>
<listitem>
<para>
Are the contents of the unit files different? They are compared
by parsing them and comparing their contents. If they are
different but only <literal>X-Reload-Triggers</literal> in the
<literal>[Unit]</literal> section is changed,
<emphasis role="strong">reload</emphasis> the unit. The NixOS
module system allows setting these triggers with the option
<link linkend="opt-systemd.services">systemd.services.&lt;name&gt;.reloadTriggers</link>.
There are some additional keys in the <literal>[Unit]</literal>
section that are ignored as well. If the unit files differ in
any way, the following actions are performed:
</para>
<itemizedlist>
<listitem>
<para>
<literal>.path</literal> and <literal>.slice</literal> units
are ignored. There is no need to restart them since changes
in their values are applied by systemd when systemd is
reloaded.
</para>
</listitem>
<listitem>
<para>
<literal>.mount</literal> units are
<emphasis role="strong">reload</emphasis>ed. These mostly
come from the <literal>/etc/fstab</literal> parser.
</para>
</listitem>
<listitem>
<para>
<literal>.socket</literal> units are currently ignored. This
is to be fixed at a later point.
</para>
</listitem>
<listitem>
<para>
The rest of the units (mostly <literal>.service</literal>
units) are then <emphasis role="strong">reload</emphasis>ed
if <literal>X-ReloadIfChanged</literal> in the
<literal>[Service]</literal> section is set to
<literal>true</literal> (exposed via
<link linkend="opt-systemd.services">systemd.services.&lt;name&gt;.reloadIfChanged</link>).
A little exception is done for units that were deactivated
in the meantime, for example because they require a unit
that got stopped before. These are
<emphasis role="strong">start</emphasis>ed instead of
reloaded.
</para>
</listitem>
<listitem>
<para>
If the reload flag is not set, some more flags decide if the
unit is skipped. These flags are
<literal>X-RestartIfChanged</literal> in the
<literal>[Service]</literal> section (exposed via
<link linkend="opt-systemd.services">systemd.services.&lt;name&gt;.restartIfChanged</link>),
<literal>RefuseManualStop</literal> in the
<literal>[Unit]</literal> section, and
<literal>X-OnlyManualStart</literal> in the
<literal>[Unit]</literal> section.
</para>
</listitem>
<listitem>
<para>
Further behavior depends on the unit having
<literal>X-StopIfChanged</literal> in the
<literal>[Service]</literal> section set to
<literal>true</literal> (exposed via
<link linkend="opt-systemd.services">systemd.services.&lt;name&gt;.stopIfChanged</link>).
This is set to <literal>true</literal> by default and must
be explicitly turned off if not wanted. If the flag is
enabled, the unit is
<emphasis role="strong">stop</emphasis>ped and then
<emphasis role="strong">start</emphasis>ed. If not, the unit
is <emphasis role="strong">restart</emphasis>ed. The goal of
the flag is to make sure that the new unit never runs in the
old environment which is still in place before the
activation script is run. This behavior is different when
the service is socket-activated, as outlined in the
following steps.
</para>
</listitem>
<listitem>
<para>
The last thing that is taken into account is whether the
unit is a service and socket-activated. If
<literal>X-StopIfChanged</literal> is
<emphasis role="strong">not</emphasis> set, the service is
<emphasis role="strong">restart</emphasis>ed with the
others. If it is set, both the service and the socket are
<emphasis role="strong">stop</emphasis>ped and the socket is
<emphasis role="strong">start</emphasis>ed, leaving socket
activation to start the service when its needed.
</para>
</listitem>
</itemizedlist>
</listitem>
</itemizedlist>
</section>

View file

@ -0,0 +1,122 @@
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="sec-switching-systems">
<title>What happens during a system switch?</title>
<para>
Running <literal>nixos-rebuild switch</literal> is one of the more
common tasks under NixOS. This chapter explains some of the
internals of this command to make it simpler for new module
developers to configure their units correctly and to make it easier
to understand what is happening and why for curious administrators.
</para>
<para>
<literal>nixos-rebuild</literal>, like many deployment solutions,
calls <literal>switch-to-configuration</literal> which resides in a
NixOS system at <literal>$out/bin/switch-to-configuration</literal>.
The script is called with the action that is to be performed like
<literal>switch</literal>, <literal>test</literal>,
<literal>boot</literal>. There is also the
<literal>dry-activate</literal> action which does not really perform
the actions but rather prints what it would do if you called it with
<literal>test</literal>. This feature can be used to check what
service states would be changed if the configuration was switched
to.
</para>
<para>
If the action is <literal>switch</literal> or
<literal>boot</literal>, the bootloader is updated first so the
configuration will be the next one to boot. Unless
<literal>NIXOS_NO_SYNC</literal> is set to <literal>1</literal>,
<literal>/nix/store</literal> is synced to disk.
</para>
<para>
If the action is <literal>switch</literal> or
<literal>test</literal>, the currently running system is inspected
and the actions to switch to the new system are calculated. This
process takes two data sources into account:
<literal>/etc/fstab</literal> and the current systemd status. Mounts
and swaps are read from <literal>/etc/fstab</literal> and the
corresponding actions are generated. If a new mount is added, for
example, the proper <literal>.mount</literal> unit is marked to be
started. The current systemd state is inspected, the difference
between the current system and the desired configuration is
calculated and actions are generated to get to this state. There are
a lot of nuances that can be controlled by the units which are
explained here.
</para>
<para>
After calculating what should be done, the actions are carried out.
The order of actions is always the same:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Stop units (<literal>systemctl stop</literal>)
</para>
</listitem>
<listitem>
<para>
Run activation script (<literal>$out/activate</literal>)
</para>
</listitem>
<listitem>
<para>
See if the activation script requested more units to restart
</para>
</listitem>
<listitem>
<para>
Restart systemd if needed
(<literal>systemd daemon-reexec</literal>)
</para>
</listitem>
<listitem>
<para>
Forget about the failed state of units
(<literal>systemctl reset-failed</literal>)
</para>
</listitem>
<listitem>
<para>
Reload systemd (<literal>systemctl daemon-reload</literal>)
</para>
</listitem>
<listitem>
<para>
Reload systemd user instances
(<literal>systemctl --user daemon-reload</literal>)
</para>
</listitem>
<listitem>
<para>
Set up tmpfiles (<literal>systemd-tmpfiles --create</literal>)
</para>
</listitem>
<listitem>
<para>
Reload units (<literal>systemctl reload</literal>)
</para>
</listitem>
<listitem>
<para>
Restart units (<literal>systemctl restart</literal>)
</para>
</listitem>
<listitem>
<para>
Start units (<literal>systemctl start</literal>)
</para>
</listitem>
<listitem>
<para>
Inspect what changed during these actions and print units that
failed and that were newly started
</para>
</listitem>
</itemizedlist>
<para>
Most of these actions are either self-explaining but some of them
have to do with our units or the activation script. For this reason,
these topics are explained in the next sections.
</para>
<xi:include href="unit-handling.section.xml" />
<xi:include href="activation-script.section.xml" />
</chapter>

View file

@ -0,0 +1,144 @@
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="sec-writing-documentation">
<title>Writing NixOS Documentation</title>
<para>
As NixOS grows, so too does the need for a catalogue and explanation
of its extensive functionality. Collecting pertinent information
from disparate sources and presenting it in an accessible style
would be a worthy contribution to the project.
</para>
<section xml:id="sec-writing-docs-building-the-manual">
<title>Building the Manual</title>
<para>
The DocBook sources of the <xref linkend="book-nixos-manual" />
are in the
<link xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/doc/manual"><literal>nixos/doc/manual</literal></link>
subdirectory of the Nixpkgs repository.
</para>
<para>
You can quickly validate your edits with <literal>make</literal>:
</para>
<programlisting>
$ cd /path/to/nixpkgs/nixos/doc/manual
$ nix-shell
nix-shell$ make
</programlisting>
<para>
Once you are done making modifications to the manual, it's
important to build it before committing. You can do that as
follows:
</para>
<programlisting>
nix-build nixos/release.nix -A manual.x86_64-linux
</programlisting>
<para>
When this command successfully finishes, it will tell you where
the manual got generated. The HTML will be accessible through the
<literal>result</literal> symlink at
<literal>./result/share/doc/nixos/index.html</literal>.
</para>
</section>
<section xml:id="sec-writing-docs-editing-docbook-xml">
<title>Editing DocBook XML</title>
<para>
For general information on how to write in DocBook, see
<link xlink:href="http://www.docbook.org/tdg5/en/html/docbook.html">DocBook
5: The Definitive Guide</link>.
</para>
<para>
Emacs nXML Mode is very helpful for editing DocBook XML because it
validates the document as you write, and precisely locates errors.
To use it, see <xref linkend="sec-emacs-docbook-xml" />.
</para>
<para>
<link xlink:href="http://pandoc.org">Pandoc</link> can generate
DocBook XML from a multitude of formats, which makes a good
starting point. Here is an example of Pandoc invocation to convert
GitHub-Flavoured MarkDown to DocBook 5 XML:
</para>
<programlisting>
pandoc -f markdown_github -t docbook5 docs.md -o my-section.md
</programlisting>
<para>
Pandoc can also quickly convert a single
<literal>section.xml</literal> to HTML, which is helpful when
drafting.
</para>
<para>
Sometimes writing valid DocBook is simply too difficult. In this
case, submit your documentation updates in a
<link xlink:href="https://github.com/NixOS/nixpkgs/issues/new">GitHub
Issue</link> and someone will handle the conversion to XML for
you.
</para>
</section>
<section xml:id="sec-writing-docs-creating-a-topic">
<title>Creating a Topic</title>
<para>
You can use an existing topic as a basis for the new topic or
create a topic from scratch.
</para>
<para>
Keep the following guidelines in mind when you create and add a
topic:
</para>
<itemizedlist>
<listitem>
<para>
The NixOS
<link xlink:href="http://www.docbook.org/tdg5/en/html/book.html"><literal>book</literal></link>
element is in <literal>nixos/doc/manual/manual.xml</literal>.
It includes several
<link xlink:href="http://www.docbook.org/tdg5/en/html/book.html"><literal>parts</literal></link>
which are in subdirectories.
</para>
</listitem>
<listitem>
<para>
Store the topic file in the same directory as the
<literal>part</literal> to which it belongs. If your topic is
about configuring a NixOS module, then the XML file can be
stored alongside the module definition <literal>nix</literal>
file.
</para>
</listitem>
<listitem>
<para>
If you include multiple words in the file name, separate the
words with a dash. For example:
<literal>ipv6-config.xml</literal>.
</para>
</listitem>
<listitem>
<para>
Make sure that the <literal>xml:id</literal> value is unique.
You can use abbreviations if the ID is too long. For example:
<literal>nixos-config</literal>.
</para>
</listitem>
<listitem>
<para>
Determine whether your topic is a chapter or a section. If you
are unsure, open an existing topic file and check whether the
main element is chapter or section.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="sec-writing-docs-adding-a-topic">
<title>Adding a Topic to the Book</title>
<para>
Open the parent XML file and add an <literal>xi:include</literal>
element to the list of chapters with the file name of the topic
that you created. If you created a <literal>section</literal>, you
add the file to the <literal>chapter</literal> file. If you
created a <literal>chapter</literal>, you add the file to the
<literal>part</literal> file.
</para>
<para>
If the topic is about configuring a NixOS module, it can be
automatically included in the manual by using the
<literal>meta.doc</literal> attribute. See
<xref linkend="sec-meta-attributes" /> for an explanation.
</para>
</section>
</chapter>

View file

@ -0,0 +1,245 @@
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="sec-writing-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 <literal>/etc/nixos/configuration.nix</literal>.
Most of the others live in the
<link xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/modules"><literal>nixos/modules</literal></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"><literal>pam.nix</literal></link>
declares the option <literal>security.pam.services</literal> that
allows other modules (e.g.
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/ssh/sshd.nix"><literal>sshd.nix</literal></link>)
to define PAM services; and it defines the option
<literal>environment.etc</literal> (declared by
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/system/etc/etc.nix"><literal>etc.nix</literal></link>)
to cause files to be created in <literal>/etc/pam.d</literal>.
</para>
<para>
In <xref linkend="sec-configuration-syntax" />, we saw the following
structure of NixOS modules:
</para>
<programlisting language="bash">
{ config, pkgs, ... }:
{ option definitions
}
</programlisting>
<para>
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
<link linkend="ex-module-syntax">Example: Structure of NixOS
Modules</link>.
</para>
<anchor xml:id="ex-module-syntax" />
<para>
<emphasis role="strong">Example: Structure of NixOS
Modules</emphasis>
</para>
<programlisting language="bash">
{ config, pkgs, ... }:
{
imports =
[ paths of other modules
];
options = {
option declarations
};
config = {
option definitions
};
}
</programlisting>
<para>
The meaning of each part is as follows.
</para>
<itemizedlist>
<listitem>
<para>
The first line makes the current Nix expression a function. The
variable <literal>pkgs</literal> contains Nixpkgs (by default,
it takes the <literal>nixpkgs</literal> entry of
<literal>NIX_PATH</literal>, see the
<link xlink:href="https://nixos.org/manual/nix/stable/#sec-common-env">Nix
manual</link> for further details), while
<literal>config</literal> contains the full system
configuration. This line can be omitted if there is no reference
to <literal>pkgs</literal> and <literal>config</literal> inside
the module.
</para>
</listitem>
<listitem>
<para>
This <literal>imports</literal> 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 <literal>modules/module-list.nix</literal>. These don't
need to be added in the import list.
</para>
</listitem>
<listitem>
<para>
The attribute <literal>options</literal> is a nested set of
<emphasis>option declarations</emphasis> (described below).
</para>
</listitem>
<listitem>
<para>
The attribute <literal>config</literal> is a nested set of
<emphasis>option definitions</emphasis> (also described below).
</para>
</listitem>
</itemizedlist>
<para>
<link linkend="locate-example">Example: NixOS Module for the
<quote>locate</quote> Service</link> shows a module that handles the
regular update of the <quote>locate</quote> database, an index of
all files in the file system. This module declares two options that
can be defined by other modules (typically the users
<literal>configuration.nix</literal>):
<literal>services.locate.enable</literal> (whether the database
should be updated) and <literal>services.locate.interval</literal>
(when the update should be done). It implements its functionality by
defining two options declared by other modules:
<literal>systemd.services</literal> (the set of all systemd
services) and <literal>systemd.timers</literal> (the list of
commands to be executed periodically by <literal>systemd</literal>).
</para>
<para>
Care must be taken when writing systemd services using
<literal>Exec*</literal> directives. By default systemd performs
substitution on <literal>%&lt;char&gt;</literal> specifiers in these
directives, expands environment variables from
<literal>$FOO</literal> and <literal>${FOO}</literal>, splits
arguments on whitespace, and splits commands on
<literal>;</literal>. All of these must be escaped to avoid
unexpected substitution or splitting when interpolating into an
<literal>Exec*</literal> directive, e.g. when using an
<literal>extraArgs</literal> option to pass additional arguments to
the service. The functions
<literal>utils.escapeSystemdExecArg</literal> and
<literal>utils.escapeSystemdExecArgs</literal> are provided for
this, see <link linkend="exec-escaping-example">Example: Escaping in
Exec directives</link> for an example. When using these functions
system environment substitution should <emphasis>not</emphasis> be
disabled explicitly.
</para>
<anchor xml:id="locate-example" />
<para>
<emphasis role="strong">Example: NixOS Module for the
<quote>locate</quote> Service</emphasis>
</para>
<programlisting language="bash">
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.locate;
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 locate command.
'';
};
interval = mkOption {
type = types.str;
default = &quot;02:15&quot;;
example = &quot;hourly&quot;;
description = ''
Update the locate database at this interval. Updates by
default at 2:15 AM every day.
The format is described in
systemd.time(7).
'';
};
# Other options omitted for documentation
};
config = {
systemd.services.update-locatedb =
{ description = &quot;Update Locate Database&quot;;
path = [ pkgs.su ];
script =
''
mkdir -m 0755 -p $(dirname ${toString cfg.output})
exec updatedb \
--localuser=${cfg.localuser} \
${optionalString (!cfg.includeStore) &quot;--prunepaths='/nix/store'&quot;} \
--output=${toString cfg.output} ${concatStringsSep &quot; &quot; cfg.extraFlags}
'';
};
systemd.timers.update-locatedb = mkIf cfg.enable
{ description = &quot;Update timer for locate database&quot;;
partOf = [ &quot;update-locatedb.service&quot; ];
wantedBy = [ &quot;timers.target&quot; ];
timerConfig.OnCalendar = cfg.interval;
};
};
}
</programlisting>
<anchor xml:id="exec-escaping-example" />
<para>
<emphasis role="strong">Example: Escaping in Exec
directives</emphasis>
</para>
<programlisting language="bash">
{ config, lib, pkgs, utils, ... }:
with lib;
let
cfg = config.services.echo;
echoAll = pkgs.writeScript &quot;echo-all&quot; ''
#! ${pkgs.runtimeShell}
for s in &quot;$@&quot;; do
printf '%s\n' &quot;$s&quot;
done
'';
args = [ &quot;a%Nything&quot; &quot;lang=\${LANG}&quot; &quot;;&quot; &quot;/bin/sh -c date&quot; ];
in {
systemd.services.echo =
{ description = &quot;Echo to the journal&quot;;
wantedBy = [ &quot;multi-user.target&quot; ];
serviceConfig.Type = &quot;oneshot&quot;;
serviceConfig.ExecStart = ''
${echoAll} ${utils.escapeSystemdExecArgs args}
'';
};
}
</programlisting>
<xi:include href="option-declarations.section.xml" />
<xi:include href="option-types.section.xml" />
<xi:include href="option-def.section.xml" />
<xi:include href="assertions.section.xml" />
<xi:include href="meta-attributes.section.xml" />
<xi:include href="importing-modules.section.xml" />
<xi:include href="replace-modules.section.xml" />
<xi:include href="freeform-modules.section.xml" />
<xi:include href="settings-options.section.xml" />
</chapter>

View file

@ -0,0 +1,694 @@
<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 {
# One or more machines:
nodes =
{ machine =
{ 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>nodes</literal>.
</para>
<para>
An example of a single-node test is
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix"><literal>login.nix</literal></link>.
It 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. An
interesting multi-node test is
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nfs/simple.nix"><literal>nfs/simple.nix</literal></link>.
It uses two client nodes to test correct locking across server
crashes.
</para>
<para>
There are a few special NixOS configuration options for test VMs:
</para>
<variablelist>
<varlistentry>
<term>
<literal>virtualisation.memorySize</literal>
</term>
<listitem>
<para>
The memory of the VM in megabytes.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>virtualisation.vlans</literal>
</term>
<listitem>
<para>
The virtual networks to which the VM is connected. See
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix"><literal>nat.nix</literal></link>
for an example.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>virtualisation.writableStore</literal>
</term>
<listitem>
<para>
By default, the Nix store in the VM is not writable. If you
enable this option, a writable union file system is mounted on
top of the Nix store to make it appear writable. This is
necessary for tests that run Nix operations that modify the
store.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
For more options, see the module
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix"><literal>qemu-vm.nix</literal></link>.
</para>
<para>
The test script is a sequence of Python statements that perform
various actions, such as starting VMs, executing commands in the
VMs, and so on. Each virtual machine is represented as an object
stored in the variable <literal>name</literal> if this is also the
identifier of the machine in the declarative config. If you
specified a node <literal>nodes.machine</literal>, the following
example starts the machine, waits until it has finished booting,
then executes a command and checks that the output is more-or-less
correct:
</para>
<programlisting language="python">
machine.start()
machine.wait_for_unit(&quot;default.target&quot;)
if not &quot;Linux&quot; in machine.succeed(&quot;uname&quot;):
raise Exception(&quot;Wrong OS&quot;)
</programlisting>
<para>
The first line is technically unnecessary; machines are implicitly
started when you first execute an action on them (such as
<literal>wait_for_unit</literal> or <literal>succeed</literal>). If
you have multiple machines, you can speed up the test by starting
them in parallel:
</para>
<programlisting language="python">
start_all()
</programlisting>
<section xml:id="ssec-machine-objects">
<title>Machine objects</title>
<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(&quot;ctrl-alt-delete&quot;)</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(&quot;foobar\n&quot;)</literal> will
type the string <literal>foobar</literal> followed by the
Enter key.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>send_console</literal>
</term>
<listitem>
<para>
Send keys to the kernel console. This allows interaction
with the systemd emergency mode, for example. Takes a string
that is sent, e.g.,
<literal>send_console(&quot;\n\nsystemctl default\n&quot;)</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>execute</literal>
</term>
<listitem>
<para>
Execute a shell command, returning a list
<literal>(status, stdout)</literal>.
</para>
<para>
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; otherwise zero will be
returned).
</para>
</listitem>
<listitem>
<para>
Dereferencing unset variables fails the command.
</para>
</listitem>
<listitem>
<para>
It will wait for stdout to be closed.
</para>
</listitem>
</itemizedlist>
<para>
If the command detaches, it must close stdout, as
<literal>execute</literal> will wait for this to consume all
output reliably. This can be achieved by redirecting stdout
to stderr <literal>&gt;&amp;2</literal>, to
<literal>/dev/console</literal>,
<literal>/dev/null</literal> or a file. Examples of
detaching commands are <literal>sleep 365d &amp;</literal>,
where the shell forks a new process that can write to stdout
and <literal>xclip -i</literal>, where the
<literal>xclip</literal> command itself forks without
closing stdout.
</para>
<para>
Takes an optional parameter <literal>check_return</literal>
that defaults to <literal>True</literal>. Setting this
parameter to <literal>False</literal> will not check for the
return code and return -1 instead. This can be used for
commands that shut down the VM and would therefore break the
pipe that would be used for retrieving the return code.
</para>
<para>
A timeout for the command can be specified (in seconds)
using the optional <literal>timeout</literal> parameter,
e.g., <literal>execute(cmd, timeout=10)</literal> or
<literal>execute(cmd, timeout=None)</literal>. The default
is 900 seconds.
</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.
Similar to <literal>execute</literal>, except that the
timeout is <literal>None</literal> by default. See
<literal>execute</literal> for details on command execution.
</para>
</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. Has a default timeout of 900 seconds which can be
modified, e.g.
<literal>wait_until_succeeds(cmd, timeout=10)</literal>. See
<literal>execute</literal> for details on command execution.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>wait_until_fails</literal>
</term>
<listitem>
<para>
Like <literal>wait_until_succeeds</literal>, but repeating
the command 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(&quot;Terminal&quot;)</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(&quot;myfile&quot;, &quot;/etc/my/important/file&quot;)</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(&quot;list-jobs --no-pager&quot;) # runs `systemctl list-jobs --no-pager`
machine.systemctl(&quot;list-jobs --no-pager&quot;, &quot;any-user&quot;) # 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>
<varlistentry>
<term>
<literal>console_interact</literal>
</term>
<listitem>
<para>
Allows you to directly interact with QEMUs stdin. This
should only be used during test development, not in
production tests. Output from QEMU is only read line-wise.
<literal>Ctrl-c</literal> kills QEMU and
<literal>Ctrl-d</literal> closes console and returns to the
test runner.
</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(&quot;xautolock.service&quot;, &quot;x-session-user&quot;)
</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;
nodes.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>
<para>
Similarly, the type checking of test scripts can be disabled in
the following way:
</para>
<programlisting language="bash">
import ./make-test-python.nix {
skipTypeCheck = true;
nodes.machine =
{ config, pkgs, ... }:
{ configuration…
};
}
</programlisting>
</section>
<section xml:id="ssec-failing-tests-early">
<title>Failing tests early</title>
<para>
To fail tests early when certain invariables are no longer met
(instead of waiting for the build to time out), the decorator
<literal>polling_condition</literal> is provided. For example, if
we are testing a program <literal>foo</literal> that should not
quit after being started, we might write the following:
</para>
<programlisting language="python">
@polling_condition
def foo_running():
machine.succeed(&quot;pgrep -x foo&quot;)
machine.succeed(&quot;foo --start&quot;)
machine.wait_until_succeeds(&quot;pgrep -x foo&quot;)
with foo_running:
... # Put `foo` through its paces
</programlisting>
<para>
<literal>polling_condition</literal> takes the following
(optional) arguments:
</para>
<para>
<literal>seconds_interval</literal>
</para>
<para>
: specifies how often the condition should be polled:
</para>
<programlisting>
```py
@polling_condition(seconds_interval=10)
def foo_running():
machine.succeed(&quot;pgrep -x foo&quot;)
```
</programlisting>
<para>
<literal>description</literal>
</para>
<para>
: is used in the log when the condition is checked. If this is not
provided, the description is pulled from the docstring of the
function. These two are therefore equivalent:
</para>
<programlisting>
```py
@polling_condition
def foo_running():
&quot;check that foo is running&quot;
machine.succeed(&quot;pgrep -x foo&quot;)
```
```py
@polling_condition(description=&quot;check that foo is running&quot;)
def foo_running():
machine.succeed(&quot;pgrep -x foo&quot;)
```
</programlisting>
</section>
<section xml:id="ssec-python-packages-in-test-script">
<title>Adding Python packages to the test script</title>
<para>
When additional Python libraries are required in the test script,
they can be added using the parameter
<literal>extraPythonPackages</literal>. For example, you could add
<literal>numpy</literal> like this:
</para>
<programlisting language="bash">
import ./make-test-python.nix
{
extraPythonPackages = p: [ p.numpy ];
nodes = { };
testScript = ''
import numpy as np
assert str(np.zeros(4) == &quot;array([0., 0., 0., 0.])&quot;)
'';
}
</programlisting>
<para>
In that case, <literal>numpy</literal> is chosen from the generic
<literal>python3Packages</literal>.
</para>
</section>
</section>