From d472f0a1740203886b2fd5e4996c48fcbd2f8e69 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Fri, 4 Apr 2025 18:19:49 +0200 Subject: [PATCH 1/4] doc(inventory): document experimental settings vendoring --- docs/site/manual/distributed-services.md | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/site/manual/distributed-services.md b/docs/site/manual/distributed-services.md index a9c91569b..5396737cd 100644 --- a/docs/site/manual/distributed-services.md +++ b/docs/site/manual/distributed-services.md @@ -280,3 +280,35 @@ Next we need to define the settings and the behavior of these distinct roles. # ... } ``` + +## Vendoring settings for a machine + +!!! Example "Experimental Status" + This feature is experimental and should be used with care. + +Sometimes the *default* value depends on something within a machines `config`. + +Since the `interface` is defined completely machine-agnostic this means default values from a machine cannot be set in the traditional way. + +The following example shows how to create a local instance of machine specific settings. + +```nix title="someservice.nix" +{ + # Maps over all instances and produces one result per instance. + perInstance = { instanceName, settings, machine, roles, ... }: { + nixosModule = { config, ... }: + let + # Calling settings via function application + # will extend the underlying module + localSettings = settings { ipRanges = lib.mkDefault config.network.ip.range; }; + in + { + # ... + }; + }; +} +``` + +!!! Danger + `localSettings` are a local attribute. Other machines cannot access it. + If settings vendoring is done. Accessing that settings attribute of another machine via the `settings` argument is considered **unsafe**. From 29fbf361a721bb0c46f9b02b35af60c962e1cf11 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Fri, 4 Apr 2025 18:50:56 +0200 Subject: [PATCH 2/4] chore(lib/modulesDocs): move dependency on clan-core and pkgs into function args This is a good idea, to make lib agnostic from clan-core The next step could be to rename the clan-core argument name Or to explizitly pass the actual dependencies, instead of everything in clan-core --- docs/nix/flake-module.nix | 8 ++++++- docs/nix/get-module-docs.nix | 16 +++++++++++-- lib/default.nix | 22 ++++++++---------- lib/inventory/eval-clan-modules/default.nix | 25 ++++++++++++++------- 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/docs/nix/flake-module.nix b/docs/nix/flake-module.nix index ec079d0a7..0efb47bbb 100644 --- a/docs/nix/flake-module.nix +++ b/docs/nix/flake-module.nix @@ -13,8 +13,14 @@ # { clanCore = «derivation JSON»; clanModules = { ${name} = «derivation JSON» }; } jsonDocs = pkgs.callPackage ./get-module-docs.nix { inherit (self) clanModules; + clan-core = self; + inherit pkgs; evalClanModules = self.lib.evalClan.evalClanModules; - modulesRolesOptions = self.lib.evalClan.evalClanModulesWithRoles self.clanModules; + modulesRolesOptions = self.lib.evalClan.evalClanModulesWithRoles { + allModules = self.clanModules; + inherit pkgs; + clan-core = self; + }; }; # Frontmatter for clanModules diff --git a/docs/nix/get-module-docs.nix b/docs/nix/get-module-docs.nix index 3f4972e85..076a7f650 100644 --- a/docs/nix/get-module-docs.nix +++ b/docs/nix/get-module-docs.nix @@ -4,6 +4,8 @@ clanModules, evalClanModules, lib, + pkgs, + clan-core, }: { # clanModules docs @@ -11,7 +13,12 @@ name: module: if builtins.pathExists (module + "/default.nix") then (nixosOptionsDoc { - options = ((evalClanModules [ module ]).options).clan.${name} or { }; + options = + ((evalClanModules { + modules = [ module ]; + inherit pkgs clan-core; + }).options + ).clan.${name} or { }; warningsAreErrors = true; }).optionsJSON else @@ -31,7 +38,12 @@ clanCore = (nixosOptionsDoc { - options = ((evalClanModules [ ]).options).clan.core or { }; + options = + ((evalClanModules { + modules = [ ]; + inherit pkgs clan-core; + }).options + ).clan.core or { }; warningsAreErrors = true; }).optionsJSON; } diff --git a/lib/default.nix b/lib/default.nix index ada137e8b..fc01e3ffe 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -9,25 +9,21 @@ # 'clanLib' attribute set # Wrapped with fix, so we can depend on other clanLib functions without passing the whole flake lib.fix (clanLib: { - # TODO: - # SSome bad lib functions that depend on something in 'self'. - # We should reduce the dependency on 'self' aka the 'flake' object - # This makes it easier to test - # most of the time passing the whole flake is unnecessary + /** + Like callPackage, but doesn't try to automatically detect arguments + 'lib' and 'clanLib' are always passed, plus the additional arguments + */ callLib = file: args: import file ({ inherit lib clanLib; } // args); - evalClan = import ./inventory/eval-clan-modules { - inherit lib; - clan-core = self; - pkgs = nixpkgs.legacyPackages.x86_64-linux; - }; - buildClanModule = clanLib.callLib ./build-clan { inherit nixpkgs nix-darwin; }; - buildClan = clanLib.buildClanModule.buildClanWith { clan-core = self; }; # ------------------------------------ - # Lib functions that don't depend on 'self' + # ClanLib functions + evalClan = clanLib.callLib ./inventory/eval-clan-modules { }; + buildClanModule = clanLib.callLib ./build-clan { inherit nixpkgs nix-darwin; }; inventory = clanLib.callLib ./inventory { }; modules = clanLib.callLib ./inventory/frontmatter { }; + + # Plain imports. values = import ./introspection { inherit lib; }; jsonschema = import ./jsonschema { inherit lib; }; select = import select/default.nix; diff --git a/lib/inventory/eval-clan-modules/default.nix b/lib/inventory/eval-clan-modules/default.nix index 04354e8db..2aed71757 100644 --- a/lib/inventory/eval-clan-modules/default.nix +++ b/lib/inventory/eval-clan-modules/default.nix @@ -1,10 +1,11 @@ { - clan-core, lib, - pkgs, + clanLib, }: let baseModule = + { pkgs }: + # Module { config, ... }: { imports = (import (pkgs.path + "/nixos/modules/module-list.nix")); @@ -20,11 +21,15 @@ let # This function takes a list of module names and evaluates them # [ module ] -> { config, options, ... } evalClanModulesLegacy = - modules: + { + modules, + pkgs, + clan-core, + }: let evaled = lib.evalModules { modules = [ - baseModule + (baseModule { inherit pkgs; }) { clan.core.settings.directory = clan-core; } @@ -56,16 +61,20 @@ let } */ evalClanModulesWithRoles = - allModules: + { + allModules, + clan-core, + pkgs, + }: let res = builtins.mapAttrs ( moduleName: module: let - frontmatter = clan-core.lib.modules.getFrontmatter allModules.${moduleName} moduleName; + frontmatter = clanLib.modules.getFrontmatter allModules.${moduleName} moduleName; roles = if builtins.elem "inventory" frontmatter.features or [ ] then assert lib.isPath module; - clan-core.lib.modules.getRoles allModules moduleName + clanLib.modules.getRoles allModules moduleName else [ ]; in @@ -75,7 +84,7 @@ let value = (lib.evalModules { modules = [ - baseModule + (baseModule { inherit pkgs; }) clan-core.nixosModules.clanCore { clan.core.settings.directory = clan-core; From e6312601a5f935a0f357a669b569c8b54d622061 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Tue, 8 Apr 2025 15:46:13 +0200 Subject: [PATCH 3/4] docs(inventory): improve extendSettings docs --- docs/site/manual/distributed-services.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/site/manual/distributed-services.md b/docs/site/manual/distributed-services.md index 5396737cd..d8867e6cd 100644 --- a/docs/site/manual/distributed-services.md +++ b/docs/site/manual/distributed-services.md @@ -281,26 +281,29 @@ Next we need to define the settings and the behavior of these distinct roles. } ``` -## Vendoring settings for a machine +## Using values from a NixOS machine inside the module !!! Example "Experimental Status" This feature is experimental and should be used with care. -Sometimes the *default* value depends on something within a machines `config`. +Sometimes a settings value depends on something within a machines `config`. -Since the `interface` is defined completely machine-agnostic this means default values from a machine cannot be set in the traditional way. +Since the `interface` is defined completely machine-agnostic this means values from a machine cannot be set in the traditional way. The following example shows how to create a local instance of machine specific settings. ```nix title="someservice.nix" { # Maps over all instances and produces one result per instance. - perInstance = { instanceName, settings, machine, roles, ... }: { + perInstance = { instanceName, extendSettings, machine, roles, ... }: { nixosModule = { config, ... }: let - # Calling settings via function application - # will extend the underlying module - localSettings = settings { ipRanges = lib.mkDefault config.network.ip.range; }; + # Create new settings with + # 'ipRanges' defaulting to 'config.network.ip.range' from this machine + # This only works if there is no 'default' already. + localSettings = extendSettings { + ipRanges = lib.mkDefault config.network.ip.range; + }; in { # ... @@ -311,4 +314,6 @@ The following example shows how to create a local instance of machine specific s !!! Danger `localSettings` are a local attribute. Other machines cannot access it. - If settings vendoring is done. Accessing that settings attribute of another machine via the `settings` argument is considered **unsafe**. + If calling extendSettings is done that doesn't change the original `settings` this means if a different machine tries to access i.e `roles.client.settings` it would *NOT* contain your changes. + + Exposing the changed settings to other machines would come with a huge performance penalty, thats why we don't want to offer it. From be8b86153cb7bae2293c0c0f3d4e9197389f5cfb Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Tue, 8 Apr 2025 15:55:03 +0200 Subject: [PATCH 4/4] fix(getModulesSchema): forward argument to evalClanModules --- lib/inventory/frontmatter/default.nix | 21 ++++++++++++++++----- lib/inventory/schemas/default.nix | 6 +++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/inventory/frontmatter/default.nix b/lib/inventory/frontmatter/default.nix index 1beeb21ac..fee0e29ed 100644 --- a/lib/inventory/frontmatter/default.nix +++ b/lib/inventory/frontmatter/default.nix @@ -9,11 +9,22 @@ let }; getModulesSchema = - modules: - lib.mapAttrs ( - _moduleName: rolesOptions: - lib.mapAttrs (_roleName: options: jsonWithoutHeader.parseOptions options { }) rolesOptions - ) (clanLib.evalClan.evalClanModulesWithRoles modules); + { + modules, + clan-core, + pkgs, + }: + lib.mapAttrs + ( + _moduleName: rolesOptions: + lib.mapAttrs (_roleName: options: jsonWithoutHeader.parseOptions options { }) rolesOptions + ) + ( + clanLib.evalClan.evalClanModulesWithRoles { + allModules = modules; + inherit pkgs clan-core; + } + ); evalFrontmatter = { diff --git a/lib/inventory/schemas/default.nix b/lib/inventory/schemas/default.nix index f6a6660a1..4ebde390e 100644 --- a/lib/inventory/schemas/default.nix +++ b/lib/inventory/schemas/default.nix @@ -6,7 +6,11 @@ }: let - modulesSchema = self.lib.modules.getModulesSchema self.clanModules; + modulesSchema = self.lib.modules.getModulesSchema { + modules = self.clanModules; + inherit pkgs; + clan-core = self; + }; jsonLib = self.lib.jsonschema { inherit includeDefaults; }; includeDefaults = true;