nixpkgs/nixos/doc/manual/development/assertions.xml

160 lines
5.4 KiB
XML
Raw Normal View History

<section xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="sec-assertions">
<title>Evaluation Checks</title>
2018-05-01 23:57:09 +00:00
<para>
When configuration problems are detectable in a module, it is a good idea to
write a check for catching it early. Doing so can provide clear feedback to
the user and can prevent errors before the build.
2018-05-01 23:57:09 +00:00
</para>
2018-05-01 23:57:09 +00:00
<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 generally, they are not ideally suited for NixOS
modules. Instead of these functions, you can declare your evaluation checks
using the NixOS module system.
2018-05-01 23:57:09 +00:00
</para>
<section xml:id="sec-assertions-define">
<title>Defining Checks</title>
2018-05-01 23:57:09 +00:00
<para>
Checks can be defined using the <xref linkend="opt-_module.checks"/> option.
Each check needs an attribute name, under which you have to define an enable
condition using <xref linkend="opt-_module.checks._name_.enable"/> and a
message using <xref linkend="opt-_module.checks._name_.message"/>. Note that
the enable condition is <emphasis>inverse</emphasis> of what an assertion
would be: To assert a value being true, the enable condition should be false
in that case, so that it isn't triggered. For the check message, you can add
<literal>options</literal> to the module arguments and use
<literal>${options.path.to.option}</literal> to print a context-aware string
representation of the option path. Here is an example showing how this can be
done.
2018-05-01 23:57:09 +00:00
</para>
<programlisting>
{ config, options, ... }: {
_module.checks.gpgSshAgent = {
enable = config.programs.gnupg.agent.enableSSHSupport &amp;&amp; config.programs.ssh.startAgent;
message = "You can't enable both ${options.programs.ssh.startAgent}"
+ " and ${options.programs.gnupg.agent.enableSSHSupport}!";
};
_module.checks.grafanaPassword = {
enable = config.services.grafana.database.password != "";
message = "The grafana password defined with ${options.services.grafana.database.password}"
+ " will be stored as plaintext in the Nix store!";
# This is a non-fatal warning
type = "warning";
};
}
</programlisting>
2018-05-01 23:57:09 +00:00
</section>
<section xml:id="sec-assertions-ignoring">
<title>Ignoring Checks</title>
<para>
Sometimes you can get failing checks that don't apply to your specific case
and you wish to ignore them, or at least make errors non-fatal. You can do so
for all checks defined using <xref linkend="opt-_module.checks"/> by
using the attribute name of the definition, which is conveniently printed
using <literal>[...]</literal> when the check is triggered. For above
example, the evaluation output when the checks are triggered looks as
follows:
</para>
<programlisting>
trace: warning: [grafanaPassword] The grafana password defined with
services.grafana.database.password will be stored as plaintext in the Nix store!
error: Failed checks:
- [gpgSshAgent] You can't enable both programs.ssh.startAgent and
programs.gnupg.agent.enableSSHSupport!
</programlisting>
2018-05-01 23:57:09 +00:00
<para>
The <literal>[grafanaPassword]</literal> and <literal>[gpgSshAgent]</literal>
strings tell you that these were defined under the <literal>grafanaPassword
</literal> and <literal>gpgSshAgent</literal> attributes of
<xref linkend="opt-_module.checks"/> respectively. With this knowledge
you can adjust them to your liking:
2018-05-01 23:57:09 +00:00
</para>
<programlisting>
{ lib, ... }: {
# Change the error into a non-fatal warning
_module.checks.gpgSshAgent.type = "warning";
# We don't care about this warning, disable it
_module.checks.grafanaPassword.enable = lib.mkForce false;
}
</programlisting>
</section>
<section xml:id="sec-assertions-submodules">
<title>Checks in Submodules</title>
<para>
Evaluation checks can be defined within submodules in the same way. Here is an example:
</para>
<programlisting>
{ lib, ... }: {
options.myServices = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({ config, options, ... }: {
options.port = lib.mkOption {};
config._module.checks.portConflict = {
enable = config.port == 80;
message = "Port ${toString config.port} defined using"
+ " ${options.port} is usually used for HTTP";
type = "warning";
};
}));
};
}
</programlisting>
<para>
When this check is triggered, it shows both the submodule path along with
the check attribute within that submodule, joined by a
<literal>/</literal>. Note also how <literal>${options.port}</literal>
correctly shows the context of the option.
</para>
<programlisting>
trace: warning: [myServices.foo/portConflict] Port 80 defined using
myServices.foo.port is usually used for HTTP
</programlisting>
<para>
Therefore to disable such a check, you can do so by changing the
<xref linkend="opt-_module.checks"/> option within the
<literal>myServices.foo</literal> submodule:
</para>
<programlisting>
{ lib, ... }: {
myServices.foo._module.checks.portConflict.enable = lib.mkForce false;
}
</programlisting>
<note>
<para>
Checks defined in submodules under <literal>types.listOf</literal> can't be
ignored, since there's no way to change previously defined list items.
</para>
</note>
2018-05-01 23:57:09 +00:00
</section>
</section>