From ae5efd9e2fe63009e534b853be5b041f0bb9db80 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Thu, 23 Oct 2025 18:26:22 +0200 Subject: [PATCH 1/3] inventory: fix path filter wrong length --- modules/inventoryClass/default.nix | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/modules/inventoryClass/default.nix b/modules/inventoryClass/default.nix index f0d65e5b8..f767b9dc2 100644 --- a/modules/inventoryClass/default.nix +++ b/modules/inventoryClass/default.nix @@ -40,12 +40,11 @@ let name: let v = set.${name}; + loc = path ++ [ name ]; in - if pred path v then + if pred loc v then [ - (lib.nameValuePair name ( - if lib.isAttrs v then filterAttrsRecursive' (path ++ [ name ]) pred v else v - )) + (lib.nameValuePair name (if lib.isAttrs v then filterAttrsRecursive' loc pred v else v)) ] else [ ] @@ -56,8 +55,7 @@ let # Remove extraModules from serialization, # identified by: prefix + pathLength + name # inventory.instances.*.roles.*.extraModules - path: _value: - lib.length path <= 5 || lib.head path != "instances" || (lib.elemAt path 5) != "extraModules" + path: _value: !(lib.length path == 5 && ((lib.last path)) == "extraModules") ) exposedInventory; in { From f50475fcfd633761a5c9780779b67f6a29b436c7 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Thu, 23 Oct 2025 18:26:57 +0200 Subject: [PATCH 2/3] services: allow inline modules --- .../distributed-service/service-module.nix | 2 +- modules/inventoryClass/role.nix | 9 +----- .../clan_lib/services/modules_test.py | 31 +++++++++++++++++++ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/inventory/distributed-service/service-module.nix b/lib/inventory/distributed-service/service-module.nix index 55000e89d..28c8d9434 100644 --- a/lib/inventory/distributed-service/service-module.nix +++ b/lib/inventory/distributed-service/service-module.nix @@ -216,7 +216,7 @@ in options.extraModules = lib.mkOption { default = [ ]; - type = types.listOf (types.either types.deferredModule types.str); + type = types.listOf types.deferredModule; }; } ) diff --git a/modules/inventoryClass/role.nix b/modules/inventoryClass/role.nix index 0f3a437f4..650cc56d1 100644 --- a/modules/inventoryClass/role.nix +++ b/modules/inventoryClass/role.nix @@ -73,15 +73,8 @@ in } ``` ''; - apply = value: if lib.isString value then value else builtins.seq (builtins.toJSON value) value; default = [ ]; - type = types.listOf ( - types.oneOf [ - types.str - types.path - (types.attrsOf types.anything) - ] - ); + type = types.listOf types.deferredModule; }; }; } diff --git a/pkgs/clan-cli/clan_lib/services/modules_test.py b/pkgs/clan-cli/clan_lib/services/modules_test.py index 0c3221f28..07e01b99b 100644 --- a/pkgs/clan-cli/clan_lib/services/modules_test.py +++ b/pkgs/clan-cli/clan_lib/services/modules_test.py @@ -316,3 +316,34 @@ def test_delete_static_service_instance( # TODO: improve error message assert "Cannot delete path 'instances.static" in str(excinfo.value) + + +@pytest.mark.with_core +def test_inline_extra_modules(clan_flake: Callable[..., Flake]) -> None: + """ExtraModules are excluded from serialization to allow arbitrary inlining""" + # Data that can be mutated via API calls + mutable_inventory_json: Inventory = { + "instances": { + "static": {"module": {"name": "admin"}}, + } + } + nix = r""" + { + inventory.instances.static = { + roles.default.extraModules = [ + (_: { }) # non-serializable inline module + ]; + }; + } + """ + + flake = clan_flake( + {}, + raw=nix, + mutable_inventory_json=mutable_inventory_json, + ) + + # Ensure preconditions + instances = list_service_instances(flake) + + assert set(instances.keys()) == {"static"} From d015218226c71f7c420648f394d7b111f07ce6dd Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Thu, 23 Oct 2025 18:27:11 +0200 Subject: [PATCH 3/3] api: update models --- pkgs/clan-cli/clan_lib/nix_models/clan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/clan-cli/clan_lib/nix_models/clan.py b/pkgs/clan-cli/clan_lib/nix_models/clan.py index 9277044b2..5cf42e02a 100644 --- a/pkgs/clan-cli/clan_lib/nix_models/clan.py +++ b/pkgs/clan-cli/clan_lib/nix_models/clan.py @@ -28,7 +28,7 @@ class InventoryInstanceRoleMachine(TypedDict): -InventoryInstanceRoleExtramodulesType = list[dict[str, Any] | str] +InventoryInstanceRoleExtramodulesType = list[Unknown] InventoryInstanceRoleMachinesType = dict[str, InventoryInstanceRoleMachine] InventoryInstanceRoleSettingsType = Unknown InventoryInstanceRoleTagsType = dict[str, Any] | list[str]