diff options
author | Silvan Mosberger <contact@infinisil.com> | 2020-12-18 14:17:52 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-18 14:17:52 +0100 |
commit | 7698aa9776244ae69aa6b3714f05d676dd33b100 (patch) | |
tree | ea309cb20a06127342d2b04a2ba87c82157381af /nixos/doc | |
parent | c23db78bbd474c4d0c5c3c551877523b4a50db06 (diff) | |
parent | a6a70d14a9f7b885e65a51c5e6bd02145884ee50 (diff) |
Merge pull request #97023 from Infinisil/module-assertions
Module-builtin assertions, disabling assertions and submodule assertions
Diffstat (limited to 'nixos/doc')
-rw-r--r-- | nixos/doc/manual/development/assertions.xml | 159 |
1 files changed, 121 insertions, 38 deletions
diff --git a/nixos/doc/manual/development/assertions.xml b/nixos/doc/manual/development/assertions.xml index 32f90cf2e7c4..31d09f958af5 100644 --- a/nixos/doc/manual/development/assertions.xml +++ b/nixos/doc/manual/development/assertions.xml @@ -3,72 +3,155 @@ xmlns:xi="http://www.w3.org/2001/XInclude" version="5.0" xml:id="sec-assertions"> - <title>Warnings and Assertions</title> + <title>Evaluation Checks</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. + write a check for catching it early. Doing so can provide clear feedback to + the user and can prevent errors before 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. + 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. </para> - <section xml:id="sec-assertions-warnings"> - <title>Warnings</title> + <section xml:id="sec-assertions-define"> + <title>Defining Checks</title> <para> - This is an example of using <literal>warnings</literal>. + Checks can be defined using the <xref linkend="opt-_module.checks"/> option. + Each check needs an attribute name, under which you can define a trigger + assertion using <xref linkend="opt-_module.checks._name_.check"/> and a + message using <xref linkend="opt-_module.checks._name_.message"/>. + For the 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 an option path. Here is an example showing how this can be + done. + </para> + +<programlisting> +{ config, options, ... }: { + _module.checks.gpgSshAgent = { + check = config.programs.gnupg.agent.enableSSHSupport -> !config.programs.ssh.startAgent; + message = "If you have ${options.programs.gnupg.agent.enableSSHSupport} enabled," + + " you can't enable ${options.programs.ssh.startAgent} as well!"; + }; + + _module.checks.grafanaPassword = { + check = 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> + + </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] If you have programs.gnupg.agent.enableSSHSupport + enabled, you can't enable programs.ssh.startAgent as well! +</programlisting> + + <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: </para> <programlisting> -<![CDATA[ -{ 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 []; - } + # 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 = false; } -]]> </programlisting> + + </section> + <section xml:id="sec-assertions-submodules"> + <title>Checks in Submodules</title> - <section xml:id="sec-assertions-assertions"> - <title>Assertions</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 = { + check = config.port != 80; + message = "Port ${toString config.port} defined using" + + " ${options.port} is usually used for HTTP"; + type = "warning"; + }; + })); + }; + +} +</programlisting> <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. + 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> -<![CDATA[ -{ config, lib, ... }: { - config = lib.mkIf config.services.syslogd.enable { - assertions = - [ { assertion = !config.services.rsyslogd.enable; - message = "rsyslogd conflicts with syslogd"; - } - ]; - } + myServices.foo._module.checks.portConflict.enable = 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> + </section> </section> |