Conversation
teto
left a comment
There was a problem hiding this comment.
glad we won't have to filter _module anymore
lib/modules.nix
Outdated
There was a problem hiding this comment.
Because foldl' is the strict version, it would evaluate res (which is type.merge loc defsFinal) before checking all the types, so a merging of the types would be attempted even before they are all checked to be of the correct type, which would lead to an error Conflicting definitions rather than is not of type. See the test I added for this, which would not succeed without this change.
There was a problem hiding this comment.
res didn't need to go through the fold. I've contributed a fix for the potential stack overflow resulting from foldl.
There was a problem hiding this comment.
res
I mean the merge result. That didn't have to be the fold "accumulator".
There was a problem hiding this comment.
@roberth your commit was force-pushed, it seems. I've noticed it existed but now it is gone.
There was a problem hiding this comment.
@roberth (I accidentally force pushed over your commit, but I squashed it back together with my fix for this now)
The _module option is added as an internal option set, and it messes up the results of module evaluations, requiring people to manually filter _modules out. If people depend on this, they can still use config._module from inside the modules, exposing _module as an explicitly declared user option. Or alternatively with the _module attribute now returned by evalModules.
b498b1d to
62ad9c1
Compare
lib/modules.nix
Outdated
There was a problem hiding this comment.
@roberth I don't think this needs to be exposed though, it can be a let in within the mergedValue instead
There was a problem hiding this comment.
Didn't realize it was exposed. Yes, that is ok.
There was a problem hiding this comment.
Found an imo better way to do this instead, which I'm now using for both changes here
Co-Authored-By: Robert Hensing <robert@roberthensing.nl>
62ad9c1 to
e931de5
Compare
| mergeModules' loc decls defns | ||
| if all (def: isAttrs def.value) defns' then mergeModules' loc decls defns | ||
| else let firstInvalid = findFirst (def: ! isAttrs def.value) null defns'; | ||
| in throw "The option path `${showOption loc}' is an attribute set of options, but it is defined to not be an attribute set in `${firstInvalid.file}'. Did you define its value at the correct and complete path?" |
There was a problem hiding this comment.
Only in the case of a failure does this code actually try to figure out which element is problematic. For the successful case only the all builtin is used.
|
This ends up breaking the Mobile NixOS eval. The current implementation has a copy of that file, but I just tried locally with an import re-using the one from Nixpkgs and it fails the same way: I'll be coming soon with better repro info and a deeper investigation. |
Though with *our* modules. With NixOS/nixpkgs#82960 this fixes the regression from NixOS/nixpkgs#82751.
And adapt for change in NixOS/nixpkgs#82751
|
@infinisil I used the following as an evaluation-time check against accidentally mis-spelling systemd service names, like so: # Example:
# serviceUnitOf cfg.systemd.services.myservice == "myservice.service"
serviceUnitOf = service: "${service._module.args.name}.service";It errored nicely when you tried to refer to a service that wasn't actually available. This now no longer works in the places I use it (e.g. top-level nixops deployments). Do you happen to know what I can use instead? |
|
@nh2 The systemd module could be improved to provide this. Something like: Proof of concept: diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
index 4154389b2ce..69c764df719 100644
--- a/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -43,6 +43,16 @@ in rec {
'';
};
+ unitName = mkOption {
+ type = types.str;
+ description = ''
+ The name of the unit, such as <literal>foo.service</literal>, <literal>bar.socket</literal>, etc.
+
+ This value is derived from the name of the attribute that contains the unit. It can not be set. If you need to base the name on a variable, use a dynamic attribute. See <link xlink:href="https://nixos.org/manual/nix/stable/expressions/language-values.html#sets">https://nixos.org/manual/nix/stable/expressions/language-values.html#sets</link>
+ '';
+ readOnly = true;
+ };
+
requiredBy = mkOption {
default = [];
type = types.listOf types.str;
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 8fcf62d7fbf..9427f9988b6 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -262,7 +262,9 @@ let
serviceConfig = { name, config, ... }: {
config = mkMerge
- [ { # Default path for systemd services. Should be quite minimal.
+ [ {
+ unitName = "${name}.service";
+ # Default path for systemd services. Should be quite minimal.
path = mkAfter
[ pkgs.coreutils
pkgs.findutils
@@ -345,7 +347,7 @@ let
"Environment=${builtins.toJSON "${n}=${env.${n}}"}\n";
# systemd max line length is now 1MiB
# https://github.com/systemd/systemd/commit/e6dde451a51dc5aaa7f4d98d39b8fe735f73d2af
- in if stringLength s >= 1048576 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${name}.service’ is too long." else s) (attrNames env)}
+ in if stringLength s >= 1048576 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${def.unitName}’ is too long." else s) (attrNames env)}
${if def.reloadIfChanged then ''
X-ReloadIfChanged=true
'' else if !def.restartIfChanged then ''
@@ -1114,7 +1116,7 @@ in
systemd.units =
mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths
- // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
+ // mapAttrs' (n: v: nameValuePair v.unitName (serviceToUnit n v)) cfg.services
// mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices
// mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
// mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets
@@ -1128,7 +1130,7 @@ in
systemd.user.units =
mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.user.paths
- // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services
+ // mapAttrs' (n: v: nameValuePair v.unitName (serviceToUnit n v)) cfg.user.services
// mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.user.slices
// mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.user.sockets
// mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.user.targets
|
|
Actually the systemd module can be cleaned up a lot and be more useful, but I have my hands full with improvements in other areas. |
And adapt for change in NixOS/nixpkgs#82751
|
I have extracted the above discussion on evaluation-time checking of systemd units into its own issue for discussion: #153369 |
Motivation for this change
Some tiny improvements to the module system:
_modulefrom the evaluation result, this will be very useful for Freeform modules #82743, as it means that people won't have to filter out that key manually from every submodule that's used.Ping @roberth
Previous discussion regarding the removal of
_moduleis in #82461 (comment), where @roberth mentioned that he uses it for debugging sometimes. This could still be exposed via an explicit and non-module-internal option specifically for NixOS modules though.Things done
_modulechange