From f96a487bc3a2928b5bd7507dce1fc47bfcd101ea Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Sun, 19 Oct 2025 18:01:50 +0200 Subject: [PATCH] inventory: expose finalSettings of every machine --- .../distributed-service/service-module.nix | 134 ++++++++---------- .../distributed-service/tests/default.nix | 1 + .../distributed-service/tests/settings.nix | 81 +++++++++++ 3 files changed, 145 insertions(+), 71 deletions(-) create mode 100644 lib/modules/inventory/distributed-service/tests/settings.nix diff --git a/lib/modules/inventory/distributed-service/service-module.nix b/lib/modules/inventory/distributed-service/service-module.nix index 87ec23b0d..4dd244efb 100644 --- a/lib/modules/inventory/distributed-service/service-module.nix +++ b/lib/modules/inventory/distributed-service/service-module.nix @@ -22,19 +22,15 @@ let - roleName: The name of the role - instanceName: The name of the instance - - settings: The settings of the machine. Leave empty to get the role settings + - : The settings of the machine. Leave empty to get the role settings + - modules: The settings of the machine. Leave empty to get the role settings Returns: evalModules result The caller is responsible to use .config or .extendModules */ evalMachineSettings = - { - roleName, - instanceName, - machineName ? null, - settings, - }: + instanceName: roleName: machineName: roleSettings: machineSettings: lib.evalModules { # Prefix for better error reporting # This prints the path where the option should be defined rather than the plain path within settings @@ -66,13 +62,8 @@ let (lib.setDefaultModuleLocation "Via clan.service module: roles.${roleName}.interface" config.roles.${roleName}.interface ) - (lib.setDefaultModuleLocation "instances.${instanceName}.roles.${roleName}.settings" - config.instances.${instanceName}.roles.${roleName}.settings - ) - settings - # Dont set the module location here - # This should already be set by the tags resolver - # config.instances.${instanceName}.roles.${roleName}.machines.${machineName}.settings + (lib.setDefaultModuleLocation "Via clan.service module: instances.${instanceName}.roles.${roleName}.settings" roleSettings) + machineSettings ]; }; @@ -90,12 +81,9 @@ let applySettings = instanceName: instance: lib.mapAttrs (roleName: role: { - machines = lib.mapAttrs (machineName: v: { + machines = lib.mapAttrs (machineName: _v: { settings = - (evalMachineSettings { - inherit roleName instanceName machineName; - inherit (v) settings; - }).config; + config.instances.${instanceName}.roles.${roleName}.machines.${machineName}.finalSettings.config; }) role.machines; }) instance.roles; in @@ -137,7 +125,7 @@ in elemType = submoduleWith { modules = [ ( - { name, ... }: + { name, ... }@instance: { options.roles = mkOption { description = '' @@ -167,51 +155,63 @@ in placeholder = "roleName"; elemType = submoduleWith { modules = [ - ({ - # instances.{instanceName}.roles.{roleName}.machines - options.machines = mkOption { - description = '' - Machines of the role. + ( + { name, ... }@role: + { + # instances.{instanceName}.roles.{roleName}.machines + options.machines = mkOption { + description = '' + Machines of the role. - A machine is a physical or virtual machine that is part of the instance. - The `` must match the name of any machine defined in the clan. + A machine is a physical or virtual machine that is part of the instance. + The `` must match the name of any machine defined in the clan. - For example: + For example: - - 'machines.my-machine = { ...; }' for a machine that is part of the instance - - 'machines.my-other-machine = { ...; }' for another machine that is part of the instance - ''; - type = attrsWith { - placeholder = "machineName"; - elemType = submoduleWith { - modules = [ - (m: { - options.settings = mkOption { - type = types.raw; - description = "Settings of '${name}-machine': ${m.name or ""}."; - default = { }; - }; - }) - ]; + - 'machines.my-machine = { ...; }' for a machine that is part of the instance + - 'machines.my-other-machine = { ...; }' for another machine that is part of the instance + ''; + type = attrsWith { + placeholder = "machineName"; + elemType = submoduleWith { + modules = [ + ( + { name, ... }@machine: + { + options.settings = mkOption { + type = types.raw; + description = "Settings of '${name}-machine': ${machine.name or ""}."; + default = { }; + }; + options.finalSettings = mkOption { + default = + evalMachineSettings instance.name role.name machine.name role.config.settings + machine.config.settings; + type = types.raw; + }; + } + ) + ]; + }; }; }; - }; - # instances.{instanceName}.roles.{roleName}.settings - # options._settings = mkOption { }; - # options._settingsViaTags = mkOption { }; - # A deferred module that combines _settingsViaTags with _settings - options.settings = mkOption { - type = types.raw; - description = "Settings of 'role': ${name}"; - default = { }; - }; + # instances.{instanceName}.roles.{roleName}.settings + # options._settings = mkOption { }; + # options._settingsViaTags = mkOption { }; + # A deferred module that combines _settingsViaTags with _settings + options.settings = mkOption { + type = types.raw; + description = "Settings of 'role': ${name}"; + default = { }; + }; - options.extraModules = lib.mkOption { - default = [ ]; - type = types.listOf (types.either types.deferredModule types.str); - }; - }) + options.extraModules = lib.mkOption { + default = [ ]; + type = types.listOf (types.either types.deferredModule types.str); + }; + } + ) ]; }; }; @@ -549,16 +549,10 @@ in roles = lib.attrNames (lib.filterAttrs (_n: v: v.machines ? ${machineName}) roles); }; settings = - (evalMachineSettings { - inherit roleName instanceName machineName; - settings = - config.instances.${instanceName}.roles.${roleName}.machines.${machineName}.settings or { }; - }).config; - extendSettings = extendEval (evalMachineSettings { - inherit roleName instanceName machineName; - settings = - config.instances.${instanceName}.roles.${roleName}.machines.${machineName}.settings or { }; - }); + config.instances.${instanceName}.roles.${roleName}.machines.${machineName}.finalSettings.config; + extendSettings = + extendEval + config.instances.${instanceName}.roles.${roleName}.machines.${machineName}.finalSettings; }; modules = [ v ]; }).config; @@ -589,10 +583,8 @@ in = { roles = { = { - # Per-machine settings + # Resolved per-machine settings machines = { = { settings = { ... }; }; }; }; - # Per-role settings - settings = { ... }; }; }; }; diff --git a/lib/modules/inventory/distributed-service/tests/default.nix b/lib/modules/inventory/distributed-service/tests/default.nix index 426351c8b..a58d8390e 100644 --- a/lib/modules/inventory/distributed-service/tests/default.nix +++ b/lib/modules/inventory/distributed-service/tests/default.nix @@ -55,6 +55,7 @@ in { extraModules = import ./extraModules.nix { inherit clanLib; }; exports = import ./exports.nix { inherit lib clanLib; }; + settings = import ./settings.nix { inherit lib callInventoryAdapter; }; resolve_module_spec = import ./import_module_spec.nix { inherit lib callInventoryAdapter; }; test_simple = let diff --git a/lib/modules/inventory/distributed-service/tests/settings.nix b/lib/modules/inventory/distributed-service/tests/settings.nix new file mode 100644 index 000000000..f9214abda --- /dev/null +++ b/lib/modules/inventory/distributed-service/tests/settings.nix @@ -0,0 +1,81 @@ +{ callInventoryAdapter, lib, ... }: +let + res = callInventoryAdapter { + modules."A" = { + _class = "clan.service"; + manifest = { + name = "network"; + }; + roles.peer.interface = + { lib, ... }: + { + options.timeout = lib.mkOption { + type = lib.types.int; + }; + }; + roles.controller.interface = + { lib, ... }: + { + options.maxPeers = lib.mkOption { + type = lib.types.int; + }; + }; + }; + machines = { + jon = { }; + sara = { }; + }; + instances."instance_foo" = { + module = { + name = "A"; + input = "self"; + }; + # Settings for both jon and sara + roles.peer.settings = { + timeout = 40; + }; + # Jon overrides timeout + roles.peer.machines.jon = { + settings.timeout = lib.mkForce 42; + }; + roles.peer.machines.sara = { }; + }; + }; + + config = res.servicesEval.config.mappedServices.self-A; + + # + applySettings = + instanceName: instance: + lib.mapAttrs (roleName: role: { + machines = lib.mapAttrs (machineName: _v: { + settings = + config.instances.${instanceName}.roles.${roleName}.machines.${machineName}.finalSettings.config; + }) role.machines; + }) instance.roles; + + mapSettings = lib.mapAttrs applySettings config.instances; +in +{ + test_simple = { + expr = mapSettings; + expected = { + instance_foo = { + peer = { + machines = { + jon = { + settings = { + timeout = 42; + }; + }; + sara = { + settings = { + timeout = 40; + }; + }; + }; + }; + }; + }; + }; +}