From 109a6a9d1e13013a4d3e4220d79d0f599a10ac97 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 20 Jul 2025 01:35:14 +0200 Subject: [PATCH] Add assertions and warnings to modular services --- nixos/modules/misc/assertions.nix | 4 +- nixos/modules/system/service/portable/lib.nix | 33 +++++++ .../system/service/portable/service.nix | 5 ++ .../modules/system/service/portable/test.nix | 89 +++++++++++++++++-- .../modules/system/service/systemd/system.nix | 24 ++++- 5 files changed, 147 insertions(+), 8 deletions(-) create mode 100644 nixos/modules/system/service/portable/lib.nix diff --git a/nixos/modules/misc/assertions.nix b/nixos/modules/misc/assertions.nix index 5853f64329c1..ae8fa710b2b8 100644 --- a/nixos/modules/misc/assertions.nix +++ b/nixos/modules/misc/assertions.nix @@ -32,5 +32,7 @@ }; }; - # impl of assertions is in + # impl of assertions is in + # - + # - } diff --git a/nixos/modules/system/service/portable/lib.nix b/nixos/modules/system/service/portable/lib.nix new file mode 100644 index 000000000000..0529f81f8561 --- /dev/null +++ b/nixos/modules/system/service/portable/lib.nix @@ -0,0 +1,33 @@ +{ lib, ... }: +let + inherit (lib) concatLists mapAttrsToList showOption; +in +rec { + flattenMapServicesConfigToList = + f: loc: config: + f loc config + ++ concatLists ( + mapAttrsToList ( + k: v: + flattenMapServicesConfigToList f ( + loc + ++ [ + "services" + k + ] + ) v + ) config.services + ); + + getWarnings = flattenMapServicesConfigToList ( + loc: config: map (msg: "in ${showOption loc}: ${msg}") config.warnings + ); + + getAssertions = flattenMapServicesConfigToList ( + loc: config: + map (ass: { + message = "in ${showOption loc}: ${ass.message}"; + assertion = ass.assertion; + }) config.assertions + ); +} diff --git a/nixos/modules/system/service/portable/service.nix b/nixos/modules/system/service/portable/service.nix index 58772320abb6..d000398b8a6a 100644 --- a/nixos/modules/system/service/portable/service.nix +++ b/nixos/modules/system/service/portable/service.nix @@ -21,6 +21,11 @@ let }; in { + # https://nixos.org/manual/nixos/unstable/#modular-services + _class = "service"; + imports = [ + ../../../misc/assertions.nix + ]; options = { services = mkOption { type = types.attrsOf ( diff --git a/nixos/modules/system/service/portable/test.nix b/nixos/modules/system/service/portable/test.nix index b4a29122fca7..7901ea6965e9 100644 --- a/nixos/modules/system/service/portable/test.nix +++ b/nixos/modules/system/service/portable/test.nix @@ -5,6 +5,8 @@ let inherit (lib) mkOption types; + portable-lib = import ./lib.nix { inherit lib; }; + dummyPkg = name: derivation { @@ -21,6 +23,15 @@ let executable = "/usr/bin/echo"; # *giggles* args = [ "hello" ]; }; + assertions = [ + { + assertion = false; + message = "you can't enable this for that reason"; + } + ]; + warnings = [ + "The `foo' service is deprecated and will go away soon!" + ]; }; service2 = { process = { @@ -32,10 +43,25 @@ let }; service3 = { process = { - executable = dummyPkg "cowsay-ng" // { - meta.mainProgram = "cowsay"; + executable = "/bin/false"; + args = [ ]; + }; + services.exclacow = { + process = { + executable = dummyPkg "cowsay-ng" // { + meta.mainProgram = "cowsay"; + }; + args = [ "!" ]; }; - args = [ "!" ]; + assertions = [ + { + assertion = false; + message = "you can't enable this for such reason"; + } + ]; + warnings = [ + "The `bar' service is deprecated and will go away soon!" + ]; }; }; }; @@ -69,6 +95,15 @@ let args = [ "hello" ]; }; services = { }; + assertions = [ + { + assertion = false; + message = "you can't enable this for that reason"; + } + ]; + warnings = [ + "The `foo' service is deprecated and will go away soon!" + ]; }; service2 = { process = { @@ -76,17 +111,59 @@ let args = [ "world" ]; }; services = { }; + assertions = [ ]; + warnings = [ ]; }; service3 = { process = { - executable = "${dummyPkg "cowsay-ng"}/bin/cowsay"; - args = [ "!" ]; + executable = "/bin/false"; + args = [ ]; }; - services = { }; + services.exclacow = { + process = { + executable = "${dummyPkg "cowsay-ng"}/bin/cowsay"; + args = [ "!" ]; + }; + services = { }; + assertions = [ + { + assertion = false; + message = "you can't enable this for such reason"; + } + ]; + warnings = [ "The `bar' service is deprecated and will go away soon!" ]; + }; + assertions = [ ]; + warnings = [ ]; }; }; }; + assert + portable-lib.getWarnings [ "service1" ] exampleEval.config.services.service1 == [ + "in service1: The `foo' service is deprecated and will go away soon!" + ]; + + assert + portable-lib.getAssertions [ "service1" ] exampleEval.config.services.service1 == [ + { + message = "in service1: you can't enable this for that reason"; + assertion = false; + } + ]; + + assert + portable-lib.getWarnings [ "service3" ] exampleEval.config.services.service3 == [ + "in service3.services.exclacow: The `bar' service is deprecated and will go away soon!" + ]; + assert + portable-lib.getAssertions [ "service3" ] exampleEval.config.services.service3 == [ + { + message = "in service3.services.exclacow: you can't enable this for such reason"; + assertion = false; + } + ]; + "ok"; in diff --git a/nixos/modules/system/service/systemd/system.nix b/nixos/modules/system/service/systemd/system.nix index 9e639921b93a..92bf4d41abe8 100644 --- a/nixos/modules/system/service/systemd/system.nix +++ b/nixos/modules/system/service/systemd/system.nix @@ -1,12 +1,21 @@ { lib, config, + options, pkgs, ... }: let - inherit (lib) concatMapAttrs mkOption types; + inherit (lib) + concatMapAttrs + mkOption + types + concatLists + mapAttrsToList + ; + + portable-lib = import ../portable/lib.nix { inherit lib; }; dash = before: after: @@ -57,6 +66,19 @@ in # Second half of the magic: siphon units that were defined in isolation to the system config = { + + assertions = concatLists ( + mapAttrsToList ( + name: cfg: portable-lib.getAssertions (options.system.services.loc ++ [ name ]) cfg + ) config.system.services + ); + + warnings = concatLists ( + mapAttrsToList ( + name: cfg: portable-lib.getWarnings (options.system.services.loc ++ [ name ]) cfg + ) config.system.services + ); + systemd.services = concatMapAttrs ( serviceName: topLevelService: makeUnits "services" serviceName topLevelService ) config.system.services;