diff --git a/lib/modules.nix b/lib/modules.nix index 23dbe962491..468c373d6aa 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -155,17 +155,22 @@ rec { default = {}; internal = prefix != []; type = types.attrsOf (types.submodule { - # TODO: Rename to assertion? Or allow also setting assertion? options.enable = mkOption { description = '' - Whether to enable this check. - - This is the inverse of asserting a condition: If a certain - condition should be true, then this - option should be set to false when that - case occurs - + Whether to enable this check. Set this to false to not trigger + any errors or warning messages. This is useful for ignoring a + check in case it doesn't make sense in certain scenarios. ''; + default = true; + type = types.bool; + }; + + options.check = mkOption { + description = '' + The condition that must succeed in order for this check to be + successful and not trigger a warning or error. + ''; + readOnly = true; type = types.bool; }; @@ -189,9 +194,7 @@ rec { and use ''${options.path.to.option}. ''; type = types.str; - example = literalExample '' - Enabling both ''${options.services.foo.enable} and ''${options.services.bar.enable} is not possible. - ''; + example = "Enabling both \${options.services.foo.enable} and \${options.services.bar.enable} is not possible."; }; }); }; @@ -244,7 +247,7 @@ rec { if lib.hasPrefix "_" name then value.message else "[${showOption prefix}${optionalString (prefix != []) "/"}${name}] ${value.message}"; in - if ! value.enable then errors + if value.enable -> value.check then errors else if value.type == "warning" then lib.warn show errors else if value.type == "error" then errors ++ [ show ] else abort "Unknown check type ${value.type}"; @@ -885,8 +888,7 @@ rec { }); config._module.checks = let opt = getAttrFromPath optionName options; in { - ${showOption optionName} = { - enable = mkDefault opt.isDefined; + ${showOption optionName} = lib.mkIf opt.isDefined { message = '' The option definition `${showOption optionName}' in ${showFiles opt.files} no longer has any effect; please remove it. ${replacementInstructions} @@ -958,8 +960,7 @@ rec { let val = getAttrFromPath f config; opt = getAttrFromPath f options; in { - ${showOption f} = { - enable = mkDefault (val != "_mkMergedOptionModule"); + ${showOption f} = lib.mkIf (val != "_mkMergedOptionModule") { type = "warning"; message = "The option `${showOption f}' defined in ${showFiles opt.files} has been changed to `${showOption to}' that has a different type. Please read `${showOption to}' documentation and update your configuration accordingly."; }; @@ -1024,8 +1025,7 @@ rec { }); config = mkMerge [ { - _module.checks.${showOption from} = { - enable = mkDefault (warn && fromOpt.isDefined); + _module.checks.${showOption from} = mkIf (warn && fromOpt.isDefined) { type = "warning"; message = "The option `${showOption from}' defined in ${showFiles fromOpt.files} has been renamed to `${showOption to}'."; }; diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index 9e85c90d15c..775be9f7209 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -279,7 +279,8 @@ checkConfigOutput baz config.value.nested.bar.baz ./types-anything/mk-mods.nix # Check that assertions are triggered by default for just evaluating config checkConfigError 'Failed checks:\n- \[test\] Assertion failed' config ./assertions/simple.nix -# Assertion is not triggered when enable is false +# Assertion is not triggered when enable is false or condition is true +checkConfigOutput '{ }' config ./assertions/condition-true.nix checkConfigOutput '{ }' config ./assertions/enable-false.nix # Warnings should be displayed on standard error diff --git a/lib/tests/modules/assertions/condition-true.nix b/lib/tests/modules/assertions/condition-true.nix new file mode 100644 index 00000000000..7ca0817a239 --- /dev/null +++ b/lib/tests/modules/assertions/condition-true.nix @@ -0,0 +1,8 @@ +{ + + _module.checks.test = { + check = true; + message = "Assertion failed"; + }; + +} diff --git a/lib/tests/modules/assertions/enable-false.nix b/lib/tests/modules/assertions/enable-false.nix index c326c086f03..11f753bb32e 100644 --- a/lib/tests/modules/assertions/enable-false.nix +++ b/lib/tests/modules/assertions/enable-false.nix @@ -2,6 +2,7 @@ _module.checks.test = { enable = false; + check = false; message = "Assertion failed"; }; diff --git a/lib/tests/modules/assertions/multi.nix b/lib/tests/modules/assertions/multi.nix index ebbe17f3a55..1e2e14b8643 100644 --- a/lib/tests/modules/assertions/multi.nix +++ b/lib/tests/modules/assertions/multi.nix @@ -2,20 +2,20 @@ _module.checks = { test1 = { - enable = true; + check = false; message = "Assertion 1 failed"; }; test2 = { - enable = true; + check = false; message = "Assertion 2 failed"; }; test3 = { - enable = true; + check = false; message = "Warning 3 failed"; type = "warning"; }; test4 = { - enable = true; + check = false; message = "Warning 4 failed"; type = "warning"; }; diff --git a/lib/tests/modules/assertions/simple.nix b/lib/tests/modules/assertions/simple.nix index a63b8090f91..115d89a3036 100644 --- a/lib/tests/modules/assertions/simple.nix +++ b/lib/tests/modules/assertions/simple.nix @@ -1,6 +1,6 @@ { _module.checks.test = { - enable = true; + check = false; message = "Assertion failed"; }; } diff --git a/lib/tests/modules/assertions/submodule-attrsOf-attrsOf.nix b/lib/tests/modules/assertions/submodule-attrsOf-attrsOf.nix index a5f92aa93c7..27a63d1e432 100644 --- a/lib/tests/modules/assertions/submodule-attrsOf-attrsOf.nix +++ b/lib/tests/modules/assertions/submodule-attrsOf-attrsOf.nix @@ -4,7 +4,7 @@ default = { bar.baz = {}; }; type = lib.types.attrsOf (lib.types.attrsOf (lib.types.submodule { _module.checks.test = { - enable = true; + check = false; message = "Assertion failed"; }; })); diff --git a/lib/tests/modules/assertions/submodule-attrsOf.nix b/lib/tests/modules/assertions/submodule-attrsOf.nix index 450cad0804d..aac5937cf7e 100644 --- a/lib/tests/modules/assertions/submodule-attrsOf.nix +++ b/lib/tests/modules/assertions/submodule-attrsOf.nix @@ -4,7 +4,7 @@ default = { bar = {}; }; type = lib.types.attrsOf (lib.types.submodule { _module.checks.test = { - enable = true; + check = false; message = "Assertion failed"; }; }); diff --git a/lib/tests/modules/assertions/submodule.nix b/lib/tests/modules/assertions/submodule.nix index a46734a326b..4e7e0b1bd61 100644 --- a/lib/tests/modules/assertions/submodule.nix +++ b/lib/tests/modules/assertions/submodule.nix @@ -4,7 +4,7 @@ default = {}; type = lib.types.submodule { _module.checks.test = { - enable = true; + check = false; message = "Assertion failed"; }; }; diff --git a/lib/tests/modules/assertions/underscore-attributes.nix b/lib/tests/modules/assertions/underscore-attributes.nix index c28c9dcd918..f9ee5c5787b 100644 --- a/lib/tests/modules/assertions/underscore-attributes.nix +++ b/lib/tests/modules/assertions/underscore-attributes.nix @@ -1,7 +1,7 @@ { _module.checks._test = { - enable = true; + check = false; message = "Assertion failed"; }; diff --git a/lib/tests/modules/assertions/warning.nix b/lib/tests/modules/assertions/warning.nix index 8fed9871aa2..72598ba3fdd 100644 --- a/lib/tests/modules/assertions/warning.nix +++ b/lib/tests/modules/assertions/warning.nix @@ -1,7 +1,7 @@ { _module.checks.test = { - enable = true; + check = false; type = "warning"; message = "Warning message"; }; diff --git a/nixos/doc/manual/development/assertions.xml b/nixos/doc/manual/development/assertions.xml index a873345ef43..31d09f958af 100644 --- a/nixos/doc/manual/development/assertions.xml +++ b/nixos/doc/manual/development/assertions.xml @@ -25,28 +25,26 @@ Checks can be defined using the option. - Each check needs an attribute name, under which you have to define an enable - condition using and a - message using . Note that - the enable condition is inverse 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 + Each check needs an attribute name, under which you can define a trigger + assertion using and a + message using . + For the message, you can add options to the module arguments and use ${options.path.to.option} to print a context-aware string - representation of the option path. Here is an example showing how this can be + representation of an option path. Here is an example showing how this can be done. { config, options, ... }: { _module.checks.gpgSshAgent = { - enable = config.programs.gnupg.agent.enableSSHSupport && config.programs.ssh.startAgent; - message = "You can't enable both ${options.programs.ssh.startAgent}" - + " and ${options.programs.gnupg.agent.enableSSHSupport}!"; + 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 = { - enable = config.services.grafana.database.password != ""; + 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 @@ -74,8 +72,8 @@ 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! +- [gpgSshAgent] If you have programs.gnupg.agent.enableSSHSupport + enabled, you can't enable programs.ssh.startAgent as well! @@ -87,12 +85,12 @@ error: Failed checks: -{ 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; + _module.checks.grafanaPassword.enable = false; } @@ -113,7 +111,7 @@ error: Failed checks: options.port = lib.mkOption {}; config._module.checks.portConflict = { - enable = config.port == 80; + check = config.port != 80; message = "Port ${toString config.port} defined using" + " ${options.port} is usually used for HTTP"; type = "warning"; @@ -143,8 +141,8 @@ trace: warning: [myServices.foo/portConflict] Port 80 defined using -{ lib, ... }: { - myServices.foo._module.checks.portConflict.enable = lib.mkForce false; +{ + myServices.foo._module.checks.portConflict.enable = false; } diff --git a/nixos/modules/misc/assertions.nix b/nixos/modules/misc/assertions.nix index 6a26a2332f2..d7cdb32491d 100644 --- a/nixos/modules/misc/assertions.nix +++ b/nixos/modules/misc/assertions.nix @@ -36,7 +36,7 @@ with lib; name = "_${toString n}"; isWarning = lib.isString value; result = { - enable = if isWarning then true else ! value.assertion; + check = if isWarning then false else value.assertion; type = if isWarning then "warning" else "error"; message = if isWarning then value else value.message; };