2007-02-06 22:25:36 +01:00
|
|
|
|
<chapter xmlns="http://docbook.org/ns/docbook"
|
2014-04-14 19:07:35 +02:00
|
|
|
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
|
|
|
xml:id="ch-development">
|
2007-02-06 22:25:36 +01:00
|
|
|
|
|
|
|
|
|
<title>Development</title>
|
|
|
|
|
|
2013-10-31 20:52:40 +01:00
|
|
|
|
<para>This chapter describes how you can modify and extend
|
2007-02-06 22:25:36 +01:00
|
|
|
|
NixOS.</para>
|
|
|
|
|
|
|
|
|
|
|
2012-05-14 05:59:38 +02:00
|
|
|
|
<!--===============================================================-->
|
|
|
|
|
|
2014-04-14 19:07:35 +02:00
|
|
|
|
<section xml:id="sec-getting-sources">
|
2012-05-14 05:59:38 +02:00
|
|
|
|
|
2013-10-31 20:52:40 +01:00
|
|
|
|
<title>Getting the sources</title>
|
2012-05-14 05:59:38 +02:00
|
|
|
|
|
|
|
|
|
<para>By default, NixOS’s <command>nixos-rebuild</command> command
|
|
|
|
|
uses the NixOS and Nixpkgs sources provided by the
|
|
|
|
|
<literal>nixos-unstable</literal> channel (kept in
|
|
|
|
|
<filename>/nix/var/nix/profiles/per-user/root/channels/nixos</filename>).
|
|
|
|
|
To modify NixOS, however, you should check out the latest sources from
|
2013-08-06 18:22:20 +02:00
|
|
|
|
Git. This is done using the following command:
|
2012-05-14 05:59:38 +02:00
|
|
|
|
|
|
|
|
|
<screen>
|
2012-05-14 06:12:43 +02:00
|
|
|
|
$ nixos-checkout <replaceable>/my/sources</replaceable>
|
2012-05-14 05:59:38 +02:00
|
|
|
|
</screen>
|
|
|
|
|
|
2013-08-06 18:22:20 +02:00
|
|
|
|
or
|
|
|
|
|
|
|
|
|
|
<screen>
|
|
|
|
|
$ mkdir -p <replaceable>/my/sources</replaceable>
|
|
|
|
|
$ cd <replaceable>/my/sources</replaceable>
|
|
|
|
|
$ nix-env -i git
|
|
|
|
|
$ git clone git://github.com/NixOS/nixpkgs.git
|
|
|
|
|
</screen>
|
|
|
|
|
|
2012-05-14 05:59:38 +02:00
|
|
|
|
This will check out the latest NixOS sources to
|
2013-10-31 20:52:40 +01:00
|
|
|
|
<filename><replaceable>/my/sources</replaceable>/nixpkgs/nixos</filename>
|
|
|
|
|
and the Nixpkgs sources to
|
2012-05-14 06:12:43 +02:00
|
|
|
|
<filename><replaceable>/my/sources</replaceable>/nixpkgs</filename>.
|
2013-10-31 20:52:40 +01:00
|
|
|
|
(The NixOS source tree lives in a subdirectory of the Nixpkgs
|
2014-05-28 14:38:07 +02:00
|
|
|
|
repository.)</para>
|
|
|
|
|
|
|
|
|
|
<para>It’s often inconvenient to develop directly on the master
|
|
|
|
|
branch, since if somebody has just committed (say) a change to GCC,
|
|
|
|
|
then the binary cache may not have caught up yet and you’ll have to
|
|
|
|
|
rebuild everything from source. So you may want to create a local
|
|
|
|
|
branch based on your current NixOS version:
|
|
|
|
|
|
|
|
|
|
<screen>
|
|
|
|
|
$ nixos-version
|
|
|
|
|
14.04.273.ea1952b (Baboon)
|
|
|
|
|
|
|
|
|
|
$ git checkout -b local ea1952b
|
|
|
|
|
</screen>
|
|
|
|
|
|
|
|
|
|
Or, to base your local branch on the latest version available in the
|
|
|
|
|
NixOS channel:
|
|
|
|
|
|
|
|
|
|
<screen>
|
|
|
|
|
$ curl -sI http://nixos.org/channels/nixos-unstable/ | grep Location
|
|
|
|
|
Location: http://releases.nixos.org/nixos/unstable/nixos-14.10pre43986.acaf4a6/
|
|
|
|
|
|
|
|
|
|
$ git checkout -b local acaf4a6
|
|
|
|
|
</screen>
|
|
|
|
|
|
|
|
|
|
You can then use <command>git rebase</command> to sync your local
|
|
|
|
|
branch with the upstream branch, and use <command>git
|
|
|
|
|
cherry-pick</command> to copy commits from your local branch to the
|
|
|
|
|
upstream branch.</para>
|
|
|
|
|
|
|
|
|
|
<para>If you want to rebuild your system using your (modified)
|
2013-10-31 20:52:40 +01:00
|
|
|
|
sources, you need to tell <command>nixos-rebuild</command> about them
|
|
|
|
|
using the <option>-I</option> flag:
|
2012-05-14 05:59:38 +02:00
|
|
|
|
|
|
|
|
|
<screen>
|
2013-10-30 11:02:55 +01:00
|
|
|
|
$ nixos-rebuild switch -I nixpkgs=<replaceable>/my/sources</replaceable>/nixpkgs
|
2012-05-14 05:59:38 +02:00
|
|
|
|
</screen>
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
|
2013-10-31 20:52:40 +01:00
|
|
|
|
<para>If you want <command>nix-env</command> to use the expressions in
|
|
|
|
|
<replaceable>/my/sources</replaceable>, use <command>nix-env -f
|
|
|
|
|
<replaceable>/my/sources</replaceable>/nixpkgs</command>, or change
|
|
|
|
|
the default by adding a symlink in
|
2013-08-11 09:42:03 +02:00
|
|
|
|
<filename>~/.nix-defexpr</filename>:
|
|
|
|
|
|
|
|
|
|
<screen>
|
|
|
|
|
$ ln -s <replaceable>/my/sources</replaceable>/nixpkgs ~/.nix-defexpr/nixpkgs
|
|
|
|
|
</screen>
|
|
|
|
|
|
2013-10-31 20:52:40 +01:00
|
|
|
|
You may want to delete the symlink
|
|
|
|
|
<filename>~/.nix-defexpr/channels_root</filename> to prevent root’s
|
|
|
|
|
NixOS channel from clashing with your own tree.</para>
|
2013-08-11 09:42:03 +02:00
|
|
|
|
|
2013-10-31 20:52:40 +01:00
|
|
|
|
<!-- FIXME: not sure what this means.
|
2013-08-11 09:42:03 +02:00
|
|
|
|
<para>You should not pass the base directory
|
|
|
|
|
<filename><replaceable>/my/sources</replaceable></filename>
|
|
|
|
|
to <command>nix-env</command>, as it will break after interpreting expressions
|
|
|
|
|
in <filename>nixos/</filename> as packages.</para>
|
2013-10-31 20:52:40 +01:00
|
|
|
|
-->
|
2013-08-11 09:42:03 +02:00
|
|
|
|
|
2012-05-14 05:59:38 +02:00
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
2011-05-01 12:24:37 +02:00
|
|
|
|
<!--===============================================================-->
|
|
|
|
|
|
2014-04-14 19:07:35 +02:00
|
|
|
|
<section xml:id="sec-writing-modules">
|
2009-01-25 16:49:27 +01:00
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<title>Writing NixOS modules</title>
|
|
|
|
|
|
|
|
|
|
<para>NixOS has a modular system for declarative configuration. This
|
|
|
|
|
system combines multiple <emphasis>modules</emphasis> to produce the
|
|
|
|
|
full system configuration. One of the modules that constitute the
|
|
|
|
|
configuration is <filename>/etc/nixos/configuration.nix</filename>.
|
|
|
|
|
Most of the others live in the <link
|
|
|
|
|
xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/modules"><filename>nixos/modules</filename></link>
|
|
|
|
|
subdirectory of the Nixpkgs tree.</para>
|
|
|
|
|
|
|
|
|
|
<para>Each NixOS module is a file that handles one logical aspect of
|
|
|
|
|
the configuration, such as a specific kind of hardware, a service, or
|
|
|
|
|
network settings. A module configuration does not have to handle
|
|
|
|
|
everything from scratch; it can use the functionality provided by
|
|
|
|
|
other modules for its implementation. Thus a module can
|
|
|
|
|
<emphasis>declare</emphasis> options that can be used by other
|
|
|
|
|
modules, and conversely can <emphasis>define</emphasis> options
|
|
|
|
|
provided by other modules in its own implementation. For example, the
|
|
|
|
|
module <link
|
|
|
|
|
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/pam.nix"><filename>pam.nix</filename></link>
|
|
|
|
|
declares the option <option>security.pam.services</option> that allows
|
|
|
|
|
other modules (e.g. <link
|
|
|
|
|
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/ssh/sshd.nix"><filename>sshd.nix</filename></link>)
|
|
|
|
|
to define PAM services; and it defines the option
|
|
|
|
|
<option>environment.etc</option> (declared by <link
|
|
|
|
|
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/system/etc/etc.nix"><filename>etc.nix</filename></link>)
|
|
|
|
|
to cause files to be created in
|
|
|
|
|
<filename>/etc/pam.d</filename>.</para>
|
|
|
|
|
|
|
|
|
|
<para xml:id="para-module-syn">In <xref
|
|
|
|
|
linkend="sec-configuration-syntax"/>, we saw the following structure
|
|
|
|
|
of NixOS modules:
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
{ config, pkgs, ... }:
|
|
|
|
|
|
|
|
|
|
{ <replaceable>option definitions</replaceable>
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
This is actually an <emphasis>abbreviated</emphasis> form of module
|
|
|
|
|
that only defines options, but does not declare any. The structure of
|
|
|
|
|
full NixOS modules is shown in <xref linkend='ex-module-syntax' />.</para>
|
|
|
|
|
|
|
|
|
|
<example xml:id='ex-module-syntax'><title>Structure of NixOS modules</title>
|
2011-05-01 12:24:37 +02:00
|
|
|
|
<programlisting>
|
2012-05-14 05:59:38 +02:00
|
|
|
|
{ config, pkgs, ... }: <co xml:id='module-syntax-1' />
|
2009-01-25 16:49:27 +01:00
|
|
|
|
|
2011-05-01 12:24:37 +02:00
|
|
|
|
{
|
2012-05-14 05:59:38 +02:00
|
|
|
|
imports =
|
2013-10-31 23:01:07 +01:00
|
|
|
|
[ <replaceable>paths of other modules</replaceable> <co xml:id='module-syntax-2' />
|
2012-05-14 05:59:38 +02:00
|
|
|
|
];
|
2009-01-25 16:49:27 +01:00
|
|
|
|
|
2011-05-01 12:24:37 +02:00
|
|
|
|
options = {
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<replaceable>option declarations</replaceable> <co xml:id='module-syntax-3' />
|
2011-05-01 12:24:37 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
config = {
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<replaceable>option definitions</replaceable> <co xml:id='module-syntax-4' />
|
2011-05-01 12:24:37 +02:00
|
|
|
|
};
|
|
|
|
|
}</programlisting>
|
|
|
|
|
</example>
|
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<para>The meaning of each part is as follows.
|
2011-05-01 12:24:37 +02:00
|
|
|
|
|
|
|
|
|
<calloutlist>
|
|
|
|
|
<callout arearefs='module-syntax-1'>
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<para>This line makes the current Nix expression a function. The
|
|
|
|
|
variable <varname>pkgs</varname> contains Nixpkgs, while
|
|
|
|
|
<varname>config</varname> contains the full system configuration.
|
|
|
|
|
This line can be omitted if there is no reference to
|
|
|
|
|
<varname>pkgs</varname> and <varname>config</varname> inside the
|
|
|
|
|
module.</para>
|
2011-05-01 12:24:37 +02:00
|
|
|
|
</callout>
|
|
|
|
|
|
|
|
|
|
<callout arearefs='module-syntax-2'>
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<para>This list enumerates the paths to other NixOS modules that
|
|
|
|
|
should be included in the evaluation of the system configuration.
|
|
|
|
|
A default set of modules is defined in the file
|
|
|
|
|
<filename>modules/module-list.nix</filename>. These don't need to
|
|
|
|
|
be added in the import list.</para>
|
2011-05-01 12:24:37 +02:00
|
|
|
|
</callout>
|
|
|
|
|
|
|
|
|
|
<callout arearefs='module-syntax-3'>
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<para>The attribute <varname>options</varname> is a nested set of
|
|
|
|
|
<emphasis>option declarations</emphasis> (described below).</para>
|
2011-05-01 12:24:37 +02:00
|
|
|
|
</callout>
|
|
|
|
|
|
|
|
|
|
<callout arearefs='module-syntax-4'>
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<para>The attribute <varname>config</varname> is a nested set of
|
|
|
|
|
<emphasis>option definitions</emphasis> (also described
|
|
|
|
|
below).</para>
|
2011-05-01 12:24:37 +02:00
|
|
|
|
</callout>
|
|
|
|
|
</calloutlist>
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<para><xref linkend='locate-example' /> shows a module that handles
|
|
|
|
|
the regular update of the “locate” database, an index of all files in
|
|
|
|
|
the file system. This module declares two options that can be defined
|
|
|
|
|
by other modules (typically the user’s
|
|
|
|
|
<filename>configuration.nix</filename>):
|
|
|
|
|
<option>services.locate.enable</option> (whether the database should
|
|
|
|
|
be updated) and <option>services.locate.period</option> (when the
|
|
|
|
|
update should be done). It implements its functionality by defining
|
|
|
|
|
two options declared by other modules:
|
|
|
|
|
<option>systemd.services</option> (the set of all systemd services)
|
|
|
|
|
and <option>services.cron.systemCronJobs</option> (the list of
|
|
|
|
|
commands to be executed periodically by <command>cron</command>).</para>
|
|
|
|
|
|
|
|
|
|
<example xml:id='locate-example'><title>NixOS module for the “locate” service</title>
|
2009-01-25 16:49:27 +01:00
|
|
|
|
<programlisting>
|
2014-04-14 16:26:48 +02:00
|
|
|
|
{ config, lib, pkgs, ... }:
|
2009-01-25 16:49:27 +01:00
|
|
|
|
|
2014-04-14 16:26:48 +02:00
|
|
|
|
with lib;
|
2009-01-25 16:49:27 +01:00
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
let locatedb = "/var/cache/locatedb"; in
|
2009-01-25 16:49:27 +01:00
|
|
|
|
|
2009-09-14 00:13:19 +02:00
|
|
|
|
{
|
2011-05-01 12:24:37 +02:00
|
|
|
|
options = {
|
2013-10-31 23:01:07 +01:00
|
|
|
|
|
2009-09-14 00:13:19 +02:00
|
|
|
|
services.locate = {
|
2013-10-31 23:01:07 +01:00
|
|
|
|
|
2009-09-14 00:13:19 +02:00
|
|
|
|
enable = mkOption {
|
2013-10-31 23:01:07 +01:00
|
|
|
|
type = types.bool;
|
2009-09-14 00:13:19 +02:00
|
|
|
|
default = false;
|
|
|
|
|
description = ''
|
|
|
|
|
If enabled, NixOS will periodically update the database of
|
|
|
|
|
files used by the <command>locate</command> command.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
period = mkOption {
|
2013-10-30 11:02:04 +01:00
|
|
|
|
type = types.str;
|
2013-10-31 23:01:07 +01:00
|
|
|
|
default = "15 02 * * *";
|
2009-09-14 00:13:19 +02:00
|
|
|
|
description = ''
|
|
|
|
|
This option defines (in the format used by cron) when the
|
2013-10-31 23:01:07 +01:00
|
|
|
|
locate database is updated. The default is to update at
|
|
|
|
|
02:15 at night every day.
|
2009-09-14 00:13:19 +02:00
|
|
|
|
'';
|
|
|
|
|
};
|
2013-10-31 23:01:07 +01:00
|
|
|
|
|
2009-09-14 00:13:19 +02:00
|
|
|
|
};
|
2013-10-31 23:01:07 +01:00
|
|
|
|
|
2009-09-14 00:13:19 +02:00
|
|
|
|
};
|
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
config = {
|
|
|
|
|
|
|
|
|
|
systemd.services.update-locatedb =
|
|
|
|
|
{ description = "Update Locate Database";
|
|
|
|
|
path = [ pkgs.su ];
|
|
|
|
|
script =
|
|
|
|
|
''
|
|
|
|
|
mkdir -m 0755 -p $(dirname ${locatedb})
|
|
|
|
|
exec updatedb --localuser=nobody --output=${locatedb} --prunepaths='/tmp /var/tmp /media /run'
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
services.cron.systemCronJobs = optional config.services.locate.enable
|
|
|
|
|
"${config.services.locate.period} root ${config.systemd.package}/bin/systemctl start update-locatedb.service";
|
|
|
|
|
|
2009-01-25 16:49:27 +01:00
|
|
|
|
};
|
|
|
|
|
}</programlisting>
|
|
|
|
|
</example>
|
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<section><title>Option declarations</title>
|
|
|
|
|
|
|
|
|
|
<para>An option declaration specifies the name, type and description
|
|
|
|
|
of a NixOS configuration option. It is illegal to define an option
|
|
|
|
|
that hasn’t been declared in any module. A option declaration
|
|
|
|
|
generally looks like this:
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
options = {
|
|
|
|
|
<replaceable>name</replaceable> = mkOption {
|
|
|
|
|
type = <replaceable>type specification</replaceable>;
|
|
|
|
|
default = <replaceable>default value</replaceable>;
|
|
|
|
|
example = <replaceable>example value</replaceable>;
|
|
|
|
|
description = "<replaceable>Description for use in the NixOS manual.</replaceable>";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>The function <varname>mkOption</varname> accepts the following arguments.
|
|
|
|
|
|
|
|
|
|
<variablelist>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>type</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>The type of the option (see below). It may be omitted,
|
|
|
|
|
but that’s not advisable since it may lead to errors that are
|
|
|
|
|
hard to diagnose.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>default</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>The default value used if no value is defined by any
|
|
|
|
|
module. A default is not required; in that case, if the option
|
|
|
|
|
value is ever used, an error will be thrown.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>example</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>An example value that will be shown in the NixOS manual.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>description</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>A textual description of the option, in DocBook format,
|
|
|
|
|
that will be included in the NixOS manual.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
</variablelist>
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>Here is a non-exhaustive list of option types:
|
|
|
|
|
|
|
|
|
|
<variablelist>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>types.bool</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>A Boolean.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>types.int</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>An integer.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>types.str</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>A string.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>types.lines</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>A string. If there are multiple definitions, they are
|
|
|
|
|
concatenated, with newline characters in between.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>types.path</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>A path, defined as anything that, when coerced to a
|
|
|
|
|
string, starts with a slash. This includes derivations.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>types.listOf</varname> <replaceable>t</replaceable></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>A list of elements of type <replaceable>t</replaceable>
|
|
|
|
|
(e.g., <literal>types.listOf types.str</literal> is a list of
|
|
|
|
|
strings). Multiple definitions are concatenated together.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>types.attrsOf</varname> <replaceable>t</replaceable></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>A set of elements of type <replaceable>t</replaceable>
|
|
|
|
|
(e.g., <literal>types.attrsOf types.int</literal> is a set of
|
|
|
|
|
name/value pairs, the values being integers).</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>types.nullOr</varname> <replaceable>t</replaceable></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>Either the value <literal>null</literal> or something of
|
|
|
|
|
type <replaceable>t</replaceable>.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
</variablelist>
|
|
|
|
|
|
|
|
|
|
You can also create new types using the function
|
|
|
|
|
<varname>mkOptionType</varname>. See
|
|
|
|
|
<filename>lib/types.nix</filename> in Nixpkgs for details.</para>
|
|
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<section><title>Option definitions</title>
|
|
|
|
|
|
|
|
|
|
<para>Option definitions are generally straight-forward bindings of values to option names, like
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
config = {
|
|
|
|
|
services.httpd.enable = true;
|
|
|
|
|
};
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
However, sometimes you need to wrap an option definition or set of
|
|
|
|
|
option definitions in a <emphasis>property</emphasis> to achieve
|
|
|
|
|
certain effects:</para>
|
|
|
|
|
|
|
|
|
|
<simplesect><title>Delaying conditionals</title>
|
|
|
|
|
|
|
|
|
|
<para>If a set of option definitions is conditional on the value of
|
|
|
|
|
another option, you may need to use <varname>mkIf</varname>.
|
|
|
|
|
Consider, for instance:
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
config = if config.services.httpd.enable then {
|
|
|
|
|
environment.systemPackages = [ <replaceable>...</replaceable> ];
|
|
|
|
|
<replaceable>...</replaceable>
|
|
|
|
|
} else {};
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
This definition will cause Nix to fail with an “infinite recursion”
|
|
|
|
|
error. Why? Because the value of
|
|
|
|
|
<option>config.services.httpd.enable</option> depends on the value
|
|
|
|
|
being constructed here. After all, you could also write the clearly
|
|
|
|
|
circular and contradictory:
|
|
|
|
|
<programlisting>
|
|
|
|
|
config = if config.services.httpd.enable then {
|
|
|
|
|
services.httpd.enable = false;
|
|
|
|
|
} else {
|
|
|
|
|
services.httpd.enable = true;
|
|
|
|
|
};
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
The solution is to write:
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
config = mkIf config.services.httpd.enable {
|
|
|
|
|
environment.systemPackages = [ <replaceable>...</replaceable> ];
|
|
|
|
|
<replaceable>...</replaceable>
|
|
|
|
|
};
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
The special function <varname>mkIf</varname> causes the evaluation of
|
|
|
|
|
the conditional to be “pushed down” into the individual definitions,
|
|
|
|
|
as if you had written:
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
config = {
|
|
|
|
|
environment.systemPackages = if config.services.httpd.enable then [ <replaceable>...</replaceable> ] else [];
|
|
|
|
|
<replaceable>...</replaceable>
|
|
|
|
|
};
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
|
|
<simplesect><title>Setting priorities</title>
|
|
|
|
|
|
|
|
|
|
<para>A module can override the definitions of an option in other
|
|
|
|
|
modules by setting a <emphasis>priority</emphasis>. All option
|
|
|
|
|
definitions that do not have the lowest priority value are discarded.
|
|
|
|
|
By default, option definitions have priority 1000. You can specify an
|
|
|
|
|
explicit priority by using <varname>mkOverride</varname>, e.g.
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
services.openssh.enable = mkOverride 10 false;
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
This definition causes all other definitions with priorities above 10
|
|
|
|
|
to be discarded. The function <varname>mkForce</varname> is
|
|
|
|
|
equal to <varname>mkOverride 50</varname>.</para>
|
|
|
|
|
|
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
|
|
<simplesect><title>Merging configurations</title>
|
|
|
|
|
|
|
|
|
|
<para>In conjunction with <literal>mkIf</literal>, it is sometimes
|
|
|
|
|
useful for a module to return multiple sets of option definitions, to
|
|
|
|
|
be merged together as if they were declared in separate modules. This
|
|
|
|
|
can be done using <varname>mkMerge</varname>:
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
config = mkMerge
|
|
|
|
|
[ # Unconditional stuff.
|
|
|
|
|
{ environment.systemPackages = [ <replaceable>...</replaceable> ];
|
|
|
|
|
}
|
|
|
|
|
# Conditional stuff.
|
|
|
|
|
(mkIf config.services.bla.enable {
|
|
|
|
|
environment.systemPackages = [ <replaceable>...</replaceable> ];
|
|
|
|
|
})
|
|
|
|
|
];
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<section><title>Important options</title>
|
|
|
|
|
|
|
|
|
|
<para>NixOS has many options, but some are of particular importance to
|
|
|
|
|
module writers.</para>
|
|
|
|
|
|
|
|
|
|
<variablelist>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
2013-12-12 10:37:29 +01:00
|
|
|
|
<term><option>environment.etc</option></term>
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<listitem>
|
|
|
|
|
<para>This set defines files in <filename>/etc</filename>. A
|
|
|
|
|
typical use is:
|
|
|
|
|
<programlisting>
|
|
|
|
|
environment.etc."os-release".text =
|
|
|
|
|
''
|
|
|
|
|
NAME=NixOS
|
|
|
|
|
<replaceable>...</replaceable>
|
|
|
|
|
'';
|
|
|
|
|
</programlisting>
|
|
|
|
|
which causes a file named <filename>/etc/os-release</filename>
|
|
|
|
|
to be created with the given contents.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><option>system.activationScripts</option></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>A set of shell script fragments that must be executed
|
|
|
|
|
whenever the configuration is activated (i.e., at boot time, or
|
|
|
|
|
after running <command>nixos-rebuild switch</command>). For instance,
|
|
|
|
|
<programlisting>
|
|
|
|
|
system.activationScripts.media =
|
|
|
|
|
''
|
|
|
|
|
mkdir -m 0755 -p /media
|
|
|
|
|
'';
|
|
|
|
|
</programlisting>
|
|
|
|
|
causes the directory <filename>/media</filename> to be created.
|
|
|
|
|
Activation scripts must be idempotent. They should not start
|
|
|
|
|
background processes such as daemons; use
|
|
|
|
|
<option>systemd.services</option> for that.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><option>systemd.services</option></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>This is the set of systemd services. Example:
|
|
|
|
|
<programlisting>
|
|
|
|
|
systemd.services.dhcpcd =
|
|
|
|
|
{ description = "DHCP Client";
|
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
|
after = [ "systemd-udev-settle.service" ];
|
|
|
|
|
path = [ dhcpcd pkgs.nettools pkgs.openresolv ];
|
|
|
|
|
serviceConfig =
|
|
|
|
|
{ Type = "forking";
|
|
|
|
|
PIDFile = "/run/dhcpcd.pid";
|
|
|
|
|
ExecStart = "${dhcpcd}/sbin/dhcpcd --config ${dhcpcdConf}";
|
|
|
|
|
Restart = "always";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
</programlisting>
|
|
|
|
|
which creates the systemd unit
|
|
|
|
|
<literal>dhcpcd.service</literal>. The option
|
|
|
|
|
<option>wantedBy</option> determined which other units pull this
|
|
|
|
|
one in; <literal>multi-user.target</literal> is the default
|
|
|
|
|
target of the system, so <literal>dhcpcd.service</literal> will
|
|
|
|
|
always be started. The option
|
|
|
|
|
<option>serviceConfig.ExecStart</option> provides the main
|
|
|
|
|
command for the service; it’s also possible to provide pre-start
|
|
|
|
|
actions, stop scripts, and so on.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><option>users.extraUsers</option></term>
|
|
|
|
|
<term><option>users.extraGroups</option></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>If your service requires special UIDs or GIDs, you can
|
|
|
|
|
define them with these options. See <xref
|
|
|
|
|
linkend="sec-user-management"/> for details.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
</variablelist>
|
|
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
2009-01-25 16:49:27 +01:00
|
|
|
|
|
2011-05-01 12:24:37 +02:00
|
|
|
|
</section>
|
2009-01-25 16:49:27 +01:00
|
|
|
|
|
|
|
|
|
|
2011-05-01 12:24:37 +02:00
|
|
|
|
<!--===============================================================-->
|
2009-01-25 16:49:27 +01:00
|
|
|
|
|
2014-04-14 19:07:35 +02:00
|
|
|
|
<section xml:id="sec-building-parts">
|
2007-02-06 22:25:36 +01:00
|
|
|
|
|
|
|
|
|
<title>Building specific parts of NixOS</title>
|
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<para>With the command <command>nix-build</command>, you can build
|
|
|
|
|
specific parts of your NixOS configuration. This is done as follows:
|
2007-02-06 22:25:36 +01:00
|
|
|
|
|
|
|
|
|
<screen>
|
2013-10-31 23:01:07 +01:00
|
|
|
|
$ cd <replaceable>/path/to/nixpkgs/nixos</replaceable>
|
|
|
|
|
$ nix-build -A config.<replaceable>option</replaceable></screen>
|
2007-02-06 22:25:36 +01:00
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
where <replaceable>option</replaceable> is a NixOS option with type
|
|
|
|
|
“derivation” (i.e. something that can be built). Attributes of
|
|
|
|
|
interest include:
|
2008-02-08 23:43:59 +01:00
|
|
|
|
|
|
|
|
|
<variablelist>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<term><varname>system.build.toplevel</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>The top-level option that builds the entire NixOS system.
|
|
|
|
|
Everything else in your configuration is indirectly pulled in by
|
|
|
|
|
this option. This is what <command>nixos-rebuild</command>
|
|
|
|
|
builds and what <filename>/run/current-system</filename> points
|
|
|
|
|
to afterwards.</para>
|
|
|
|
|
|
|
|
|
|
<para>A shortcut to build this is:
|
|
|
|
|
|
|
|
|
|
<screen>
|
|
|
|
|
$ nix-build -A system</screen>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
2008-02-08 23:43:59 +01:00
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<term><varname>system.build.manual.manual</varname></term>
|
|
|
|
|
<listitem><para>The NixOS manual.</para></listitem>
|
2008-02-08 23:43:59 +01:00
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<term><varname>system.build.etc</varname></term>
|
|
|
|
|
<listitem><para>A tree of symlinks that form the static parts of
|
|
|
|
|
<filename>/etc</filename>.</para></listitem>
|
2008-02-08 23:43:59 +01:00
|
|
|
|
</varlistentry>
|
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>system.build.initialRamdisk</varname></term>
|
|
|
|
|
<term><varname>system.build.kernel</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>The initial ramdisk and kernel of the system. This allows
|
|
|
|
|
a quick way to test whether the kernel and the initial ramdisk
|
|
|
|
|
boot correctly, by using QEMU’s <option>-kernel</option> and
|
|
|
|
|
<option>-initrd</option> options:
|
|
|
|
|
|
|
|
|
|
<screen>
|
|
|
|
|
$ nix-build -A config.system.build.initialRamdisk -o initrd
|
|
|
|
|
$ nix-build -A config.system.build.kernel -o kernel
|
|
|
|
|
$ qemu-system-x86_64 -kernel ./kernel/bzImage -initrd ./initrd/initrd -hda /dev/null
|
|
|
|
|
</screen>
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>system.build.nixos-rebuild</varname></term>
|
|
|
|
|
<term><varname>system.build.nixos-install</varname></term>
|
|
|
|
|
<term><varname>system.build.nixos-generate-config</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>These build the corresponding NixOS commands.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
2013-11-18 13:18:58 +01:00
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><varname>systemd.units.<replaceable>unit-name</replaceable>.unit</varname></term>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>This builds the unit with the specified name. Note that
|
|
|
|
|
since unit names contain dots
|
|
|
|
|
(e.g. <literal>httpd.service</literal>), you need to put them
|
|
|
|
|
between quotes, like this:
|
|
|
|
|
|
|
|
|
|
<screen>
|
|
|
|
|
$ nix-build -A 'config.systemd.units."httpd.service".unit'
|
|
|
|
|
</screen>
|
|
|
|
|
|
|
|
|
|
You can also test individual units, without rebuilding the whole
|
|
|
|
|
system, by putting them in
|
|
|
|
|
<filename>/run/systemd/system</filename>:
|
|
|
|
|
|
|
|
|
|
<screen>
|
|
|
|
|
$ cp $(nix-build -A 'config.systemd.units."httpd.service".unit')/httpd.service \
|
|
|
|
|
/run/systemd/system/tmp-httpd.service
|
|
|
|
|
$ systemctl daemon-reload
|
|
|
|
|
$ systemctl start tmp-httpd.service
|
|
|
|
|
</screen>
|
|
|
|
|
|
|
|
|
|
Note that the unit must not have the same name as any unit in
|
|
|
|
|
<filename>/etc/systemd/system</filename> since those take
|
|
|
|
|
precedence over <filename>/run/systemd/system</filename>.
|
|
|
|
|
That’s why the unit is installed as
|
|
|
|
|
<filename>tmp-httpd.service</filename> here.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
</variablelist>
|
2008-02-08 23:43:59 +01:00
|
|
|
|
|
2009-10-05 15:17:45 +02:00
|
|
|
|
</para>
|
2008-02-08 23:43:59 +01:00
|
|
|
|
|
2009-09-14 00:13:19 +02:00
|
|
|
|
</section>
|
2008-02-08 23:43:59 +01:00
|
|
|
|
|
2009-09-14 00:13:19 +02:00
|
|
|
|
|
2011-05-01 12:24:37 +02:00
|
|
|
|
<!--===============================================================-->
|
|
|
|
|
|
2014-04-14 19:07:35 +02:00
|
|
|
|
<section xml:id="sec-building-cd">
|
2009-09-14 00:13:19 +02:00
|
|
|
|
|
|
|
|
|
<title>Building your own NixOS CD</title>
|
|
|
|
|
|
|
|
|
|
<para>Building a NixOS CD is as easy as configuring your own computer. The
|
|
|
|
|
idea is to use another module which will replace
|
|
|
|
|
your <filename>configuration.nix</filename> to configure the system that
|
2013-08-10 23:07:13 +02:00
|
|
|
|
would be installed on the CD.</para>
|
2009-09-14 00:13:19 +02:00
|
|
|
|
|
|
|
|
|
<para>Default CD/DVD configurations are available
|
|
|
|
|
inside <filename>nixos/modules/installer/cd-dvd</filename>. To build them
|
2009-09-14 08:56:00 +02:00
|
|
|
|
you have to set <envar>NIXOS_CONFIG</envar> before
|
2009-09-14 00:13:19 +02:00
|
|
|
|
running <command>nix-build</command> to build the ISO.
|
|
|
|
|
|
|
|
|
|
<screen>
|
2013-10-31 23:01:07 +01:00
|
|
|
|
$ nix-build -A config.system.build.isoImage -I nixos-config=modules/installer/cd-dvd/installation-cd-minimal.nix</screen>
|
2008-02-08 23:43:59 +01:00
|
|
|
|
|
|
|
|
|
</para>
|
2007-02-06 22:25:36 +01:00
|
|
|
|
|
2009-09-14 00:13:19 +02:00
|
|
|
|
<para>Before burning your CD/DVD, you can check the content of the image by mounting anywhere like
|
|
|
|
|
suggested by the following command:
|
2007-02-06 22:25:36 +01:00
|
|
|
|
|
2009-09-14 00:13:19 +02:00
|
|
|
|
<screen>
|
|
|
|
|
$ mount -o loop -t iso9660 ./result/iso/cd.iso /mnt/iso</screen>
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</section>
|
2007-02-06 22:25:36 +01:00
|
|
|
|
|
2012-04-24 17:14:26 +02:00
|
|
|
|
|
|
|
|
|
<!--===============================================================-->
|
|
|
|
|
|
2007-02-06 22:25:36 +01:00
|
|
|
|
<section>
|
|
|
|
|
|
|
|
|
|
<title>Testing the installer</title>
|
|
|
|
|
|
2014-05-09 00:25:05 +02:00
|
|
|
|
<para>Building, burning, and booting from an installation CD is rather
|
2007-02-06 22:25:36 +01:00
|
|
|
|
tedious, so here is a quick way to see if the installer works
|
|
|
|
|
properly:
|
|
|
|
|
|
|
|
|
|
<screen>
|
2013-10-31 23:01:07 +01:00
|
|
|
|
$ nix-build -A config.system.build.nixos-install
|
2014-05-09 00:25:05 +02:00
|
|
|
|
$ mount -t tmpfs none /mnt
|
2007-02-06 22:25:36 +01:00
|
|
|
|
$ ./result/bin/nixos-install</screen>
|
|
|
|
|
|
2014-05-09 00:25:05 +02:00
|
|
|
|
To start a login shell in the new NixOS installation in
|
|
|
|
|
<filename>/mnt</filename>:
|
|
|
|
|
|
|
|
|
|
<screen>
|
|
|
|
|
$ ./result/bin/nixos-install --chroot
|
|
|
|
|
</screen>
|
|
|
|
|
|
2007-02-06 22:25:36 +01:00
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
2013-08-06 17:03:50 +02:00
|
|
|
|
|
2011-05-01 12:24:37 +02:00
|
|
|
|
<!--===============================================================-->
|
|
|
|
|
|
2014-04-14 19:07:35 +02:00
|
|
|
|
<section xml:id="sec-nixos-tests">
|
|
|
|
|
|
|
|
|
|
<title>NixOS tests</title>
|
|
|
|
|
|
|
|
|
|
<para>When you add some feature to NixOS, you should write a test for
|
|
|
|
|
it. NixOS tests are kept in the directory <filename
|
|
|
|
|
xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/tests">nixos/tests</filename>,
|
|
|
|
|
and are executed (using Nix) by a testing framework that automatically
|
|
|
|
|
starts one or more virtual machines containing the NixOS system(s)
|
|
|
|
|
required for the test.</para>
|
|
|
|
|
|
|
|
|
|
<simplesect><title>Writing tests</title>
|
|
|
|
|
|
|
|
|
|
<para>A NixOS test is a Nix expression that has the following structure:
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
import ./make-test.nix {
|
|
|
|
|
|
|
|
|
|
# Either the configuration of a single machine:
|
|
|
|
|
machine =
|
|
|
|
|
{ config, pkgs, ... }:
|
|
|
|
|
{ <replaceable>configuration…</replaceable>
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# Or a set of machines:
|
|
|
|
|
nodes =
|
|
|
|
|
{ <replaceable>machine1</replaceable> =
|
|
|
|
|
{ config, pkgs, ... }: { <replaceable>…</replaceable> };
|
|
|
|
|
<replaceable>machine2</replaceable> =
|
|
|
|
|
{ config, pkgs, ... }: { <replaceable>…</replaceable> };
|
|
|
|
|
…
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
testScript =
|
|
|
|
|
''
|
|
|
|
|
<replaceable>Perl code…</replaceable>
|
|
|
|
|
'';
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
The attribute <literal>testScript</literal> is a bit of Perl code that
|
|
|
|
|
executes the test (described below). During the test, it will start
|
|
|
|
|
one or more virtual machines, the configuration of which is described
|
|
|
|
|
by the attribute <literal>machine</literal> (if you need only one
|
|
|
|
|
machine in your test) or by the attribute <literal>nodes</literal> (if
|
|
|
|
|
you need multiple machines). For instance, <filename
|
|
|
|
|
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix">login.nix</filename>
|
|
|
|
|
only needs a single machine to test whether users can log in on the
|
|
|
|
|
virtual console, whether device ownership is correctly maintained when
|
|
|
|
|
switching between consoles, and so on. On the other hand, <filename
|
|
|
|
|
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nfs.nix">nfs.nix</filename>,
|
|
|
|
|
which tests NFS client and server functionality in the Linux kernel
|
|
|
|
|
(including whether locks are maintained across server crashes),
|
|
|
|
|
requires three machines: a server and two clients.</para>
|
|
|
|
|
|
|
|
|
|
<para>There are a few special NixOS configuration options for test
|
|
|
|
|
VMs:
|
|
|
|
|
|
|
|
|
|
<!-- FIXME: would be nice to generate this automatically. -->
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2014-04-14 19:07:35 +02:00
|
|
|
|
<variablelist>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><option>virtualisation.memorySize</option></term>
|
|
|
|
|
<listitem><para>The memory of the VM in
|
|
|
|
|
megabytes.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><option>virtualisation.vlans</option></term>
|
|
|
|
|
<listitem><para>The virtual networks to which the VM is
|
|
|
|
|
connected. See <filename
|
|
|
|
|
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix">nat.nix</filename>
|
|
|
|
|
for an example.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><option>virtualisation.writableStore</option></term>
|
|
|
|
|
<listitem><para>By default, the Nix store in the VM is not
|
|
|
|
|
writable. If you enable this option, a writable union file system
|
|
|
|
|
is mounted on top of the Nix store to make it appear
|
|
|
|
|
writable. This is necessary for tests that run Nix operations that
|
|
|
|
|
modify the store.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
</variablelist>
|
|
|
|
|
|
|
|
|
|
For more options, see the module <filename
|
|
|
|
|
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix">qemu-vm.nix</filename>.</para>
|
|
|
|
|
|
|
|
|
|
<para>The test script is a sequence of Perl statements that perform
|
|
|
|
|
various actions, such as starting VMs, executing commands in the VMs,
|
|
|
|
|
and so on. Each virtual machine is represented as an object stored in
|
|
|
|
|
the variable <literal>$<replaceable>name</replaceable></literal>,
|
|
|
|
|
where <replaceable>name</replaceable> is the identifier of the machine
|
|
|
|
|
(which is just <literal>machine</literal> if you didn’t specify
|
|
|
|
|
multiple machines using the <literal>nodes</literal> attribute). For
|
|
|
|
|
instance, the following starts the machine, waits until it has
|
|
|
|
|
finished booting, then executes a command and checks that the output
|
|
|
|
|
is more-or-less correct:
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
$machine->start;
|
|
|
|
|
$machine->waitForUnit("default.target");
|
|
|
|
|
$machine->succeed("uname") =~ /Linux/;
|
|
|
|
|
</programlisting>
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2014-04-14 19:07:35 +02:00
|
|
|
|
The first line is actually unnecessary; machines are implicitly
|
|
|
|
|
started when you first execute an action on them (such as
|
|
|
|
|
<literal>waitForUnit</literal> or <literal>succeed</literal>). If you
|
|
|
|
|
have multiple machines, you can speed up the test by starting them in
|
|
|
|
|
parallel:
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
startAll;
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>The following methods are available on machine objects:
|
|
|
|
|
|
|
|
|
|
<variablelist>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>start</methodname></term>
|
|
|
|
|
<listitem><para>Start the virtual machine. This method is
|
|
|
|
|
asynchronous — it does not wait for the machine to finish
|
|
|
|
|
booting.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>shutdown</methodname></term>
|
|
|
|
|
<listitem><para>Shut down the machine, waiting for the VM to
|
|
|
|
|
exit.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>crash</methodname></term>
|
|
|
|
|
<listitem><para>Simulate a sudden power failure, by telling the VM
|
|
|
|
|
to exit immediately.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>block</methodname></term>
|
|
|
|
|
<listitem><para>Simulate unplugging the Ethernet cable that
|
|
|
|
|
connects the machine to the other machines.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>unblock</methodname></term>
|
|
|
|
|
<listitem><para>Undo the effect of
|
|
|
|
|
<methodname>block</methodname>.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>screenshot</methodname></term>
|
|
|
|
|
<listitem><para>Take a picture of the display of the virtual
|
|
|
|
|
machine, in PNG format. The screenshot is linked from the HTML
|
|
|
|
|
log.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>sendMonitorCommand</methodname></term>
|
|
|
|
|
<listitem><para>Send a command to the QEMU monitor. This is rarely
|
|
|
|
|
used, but allows doing stuff such as attaching virtual USB disks
|
|
|
|
|
to a running machine.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>sendKeys</methodname></term>
|
|
|
|
|
<listitem><para>Simulate pressing keys on the virtual keyboard,
|
|
|
|
|
e.g., <literal>sendKeys("ctrl-alt-delete")</literal>.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>sendChars</methodname></term>
|
|
|
|
|
<listitem><para>Simulate typing a sequence of characters on the
|
|
|
|
|
virtual keyboard, e.g., <literal>sendKeys("foobar\n")</literal>
|
|
|
|
|
will type the string <literal>foobar</literal> followed by the
|
|
|
|
|
Enter key.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>execute</methodname></term>
|
|
|
|
|
<listitem><para>Execute a shell command, returning a list
|
|
|
|
|
<literal>(<replaceable>status</replaceable>,
|
|
|
|
|
<replaceable>stdout</replaceable>)</literal>.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>succeed</methodname></term>
|
|
|
|
|
<listitem><para>Execute a shell command, raising an exception if
|
|
|
|
|
the exit status is not zero, otherwise returning the standard
|
|
|
|
|
output.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>fail</methodname></term>
|
|
|
|
|
<listitem><para>Like <methodname>succeed</methodname>, but raising
|
|
|
|
|
an exception if the command returns a zero status.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>waitUntilSucceeds</methodname></term>
|
|
|
|
|
<listitem><para>Repeat a shell command with 1-second intervals
|
|
|
|
|
until it succeeds.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>waitUntilFails</methodname></term>
|
|
|
|
|
<listitem><para>Repeat a shell command with 1-second intervals
|
|
|
|
|
until it fails.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>waitForUnit</methodname></term>
|
|
|
|
|
<listitem><para>Wait until the specified systemd unit has reached
|
|
|
|
|
the “active” state.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>waitForFile</methodname></term>
|
|
|
|
|
<listitem><para>Wait until the specified file
|
|
|
|
|
exists.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>waitForOpenPort</methodname></term>
|
|
|
|
|
<listitem><para>Wait until a process is listening on the given TCP
|
|
|
|
|
port (on <literal>localhost</literal>, at least).</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>waitForClosedPort</methodname></term>
|
|
|
|
|
<listitem><para>Wait until nobody is listening on the given TCP
|
|
|
|
|
port.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>waitForX</methodname></term>
|
|
|
|
|
<listitem><para>Wait until the X11 server is accepting
|
|
|
|
|
connections.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
|
<term><methodname>waitForWindow</methodname></term>
|
|
|
|
|
<listitem><para>Wait until an X11 window has appeared whose name
|
|
|
|
|
matches the given regular expression, e.g.,
|
|
|
|
|
<literal>waitForWindow(qr/Terminal/)</literal>.</para></listitem>
|
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
</variablelist>
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<simplesect><title>Running tests</title>
|
|
|
|
|
|
|
|
|
|
<para>You can run tests using <command>nix-build</command>. For
|
|
|
|
|
example, to run the test <filename
|
|
|
|
|
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix">login.nix</filename>,
|
|
|
|
|
you just do:
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2012-05-14 05:59:38 +02:00
|
|
|
|
<screen>
|
2014-04-14 19:07:35 +02:00
|
|
|
|
$ nix-build '<nixpkgs/nixos/tests/login.nix>'
|
2012-05-14 05:59:38 +02:00
|
|
|
|
</screen>
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2014-04-14 19:07:35 +02:00
|
|
|
|
or, if you don’t want to rely on <envar>NIX_PATH</envar>:
|
|
|
|
|
|
|
|
|
|
<screen>
|
|
|
|
|
$ cd /my/nixpkgs/nixos/tests
|
|
|
|
|
$ nix-build login.nix
|
|
|
|
|
…
|
|
|
|
|
running the VM test script
|
|
|
|
|
machine: QEMU running (pid 8841)
|
|
|
|
|
…
|
|
|
|
|
6 out of 6 tests succeeded
|
|
|
|
|
</screen>
|
|
|
|
|
|
|
|
|
|
After building/downloading all required dependencies, this will
|
|
|
|
|
perform a build that starts a QEMU/KVM virtual machine containing a
|
|
|
|
|
NixOS system. The virtual machine mounts the Nix store of the host;
|
|
|
|
|
this makes VM creation very fast, as no disk image needs to be
|
|
|
|
|
created. Afterwards, you can view a pretty-printed log of the test:
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2012-05-14 05:59:38 +02:00
|
|
|
|
<screen>
|
2013-10-09 15:33:23 +02:00
|
|
|
|
$ firefox result/log.html
|
2012-05-14 05:59:38 +02:00
|
|
|
|
</screen>
|
2014-04-14 19:07:35 +02:00
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
</para>
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<para>It is also possible to run the test environment interactively,
|
|
|
|
|
allowing you to experiment with the VMs. For example:
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2012-05-14 05:59:38 +02:00
|
|
|
|
<screen>
|
2014-04-14 19:07:35 +02:00
|
|
|
|
$ nix-build login.nix -A driver
|
2012-05-14 05:59:38 +02:00
|
|
|
|
$ ./result/bin/nixos-run-vms
|
|
|
|
|
</screen>
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2014-04-14 19:07:35 +02:00
|
|
|
|
The script <command>nixos-run-vms</command> starts the virtual
|
|
|
|
|
machines defined by test. The root file system of the VMs is created
|
|
|
|
|
on the fly and kept across VM restarts in
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<filename>./</filename><varname>hostname</varname><filename>.qcow2</filename>.</para>
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
<para>Finally, the test itself can be run interactively. This is
|
|
|
|
|
particularly useful when developing or debugging a test:
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2012-05-14 05:59:38 +02:00
|
|
|
|
<screen>
|
|
|
|
|
$ nix-build tests/ -A nfs.driver
|
|
|
|
|
$ ./result/bin/nixos-test-driver
|
|
|
|
|
starting VDE switch for network 1
|
|
|
|
|
>
|
|
|
|
|
</screen>
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2014-04-14 19:07:35 +02:00
|
|
|
|
You can then take any Perl statement, e.g.
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2012-05-14 05:59:38 +02:00
|
|
|
|
<screen>
|
2014-04-14 19:07:35 +02:00
|
|
|
|
> startAll
|
|
|
|
|
> $machine->succeed("touch /tmp/foo")
|
2012-05-14 05:59:38 +02:00
|
|
|
|
</screen>
|
2011-03-08 21:55:43 +01:00
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
The function <command>testScript</command> executes the entire test
|
|
|
|
|
script and drops you back into the test driver command line upon its
|
|
|
|
|
completion. This allows you to inspect the state of the VMs after the
|
|
|
|
|
test (e.g. to debug the test script).</para>
|
|
|
|
|
|
2014-04-14 19:07:35 +02:00
|
|
|
|
</simplesect>
|
2011-06-15 16:12:45 +02:00
|
|
|
|
|
2011-03-08 21:55:43 +01:00
|
|
|
|
</section>
|
2008-08-26 14:48:27 +02:00
|
|
|
|
|
2013-10-31 23:01:07 +01:00
|
|
|
|
|
2007-02-06 22:25:36 +01:00
|
|
|
|
</chapter>
|