From d99dfbcedd085ade0f73347bcc5d2344b1627327 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Mon, 15 Jul 2024 16:38:54 +0200 Subject: [PATCH 1/7] Inventory: add global imports --- lib/build-clan/default.nix | 5 ++++- lib/inventory/build-inventory/default.nix | 16 ++++++++++++++-- lib/inventory/build-inventory/interface.nix | 15 ++++++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/build-clan/default.nix b/lib/build-clan/default.nix index 35401dd1c..82c8f9c36 100644 --- a/lib/build-clan/default.nix +++ b/lib/build-clan/default.nix @@ -106,7 +106,10 @@ let # map from machine name to service configuration # { ${machineName} :: Config } - serviceConfigs = buildInventory mergedInventory; + serviceConfigs = buildInventory { + inventory = mergedInventory; + inherit directory; + }; machinesDirs = lib.optionalAttrs (builtins.pathExists "${directory}/machines") ( builtins.readDir (directory + /machines) diff --git a/lib/inventory/build-inventory/default.nix b/lib/inventory/build-inventory/default.nix index c0e9852da..88fe066df 100644 --- a/lib/inventory/build-inventory/default.nix +++ b/lib/inventory/build-inventory/default.nix @@ -1,7 +1,7 @@ # Generate partial NixOS configurations for every machine in the inventory # This function is responsible for generating the module configuration for every machine in the inventory. { lib, clan-core }: -inventory: +{ inventory, directory }: let machines = machinesFromInventory inventory; @@ -72,6 +72,12 @@ let machineServiceConfig = (serviceConfig.machines.${machineName} or { }).config or { }; globalConfig = serviceConfig.config or { }; + globalImports = serviceConfig.imports or [ ]; + machineImports = serviceConfig.machines.${machineName}.imports or [ ]; + roleServiceImports = builtins.foldl' ( + acc: role: acc ++ serviceConfig.roles.${role}.imports or [ ] + ) [ ] inverseRoles.${machineName} or [ ]; + # TODO: maybe optimize this dont lookup the role in inverse roles. Imports are not lazy roleModules = builtins.map ( role: @@ -87,12 +93,18 @@ let roleServiceConfigs = builtins.map ( role: serviceConfig.roles.${role}.config or { } ) inverseRoles.${machineName} or [ ]; + dbg = v: lib.traceSeq v v; + + customImports = map (s: "${directory}/${s}") ( + globalImports ++ machineImports ++ roleServiceImports + ); in + if isInService then acc2 ++ [ { - imports = [ clan-core.clanModules.${moduleName} ] ++ roleModules; + imports = dbg ([ clan-core.clanModules.${moduleName} ] ++ roleModules ++ customImports); config.clan.${moduleName} = lib.mkMerge ( [ globalConfig diff --git a/lib/inventory/build-inventory/interface.nix b/lib/inventory/build-inventory/interface.nix index 817e380cd..34a3c1e89 100644 --- a/lib/inventory/build-inventory/interface.nix +++ b/lib/inventory/build-inventory/interface.nix @@ -39,6 +39,12 @@ let default = { }; type = t.attrsOf t.anything; }; + + importsOption = lib.mkOption { + default = [ ]; + type = t.listOf t.str; + # apply = map (pathOrString: "${pathOrString}"); + }; in { options = { @@ -76,10 +82,16 @@ in t.attrsOf ( t.submodule { options.meta = metaOptions; + options.imports = importsOption; options.config = moduleConfig; options.machines = lib.mkOption { default = { }; - type = t.attrsOf (t.submodule { options.config = moduleConfig; }); + type = t.attrsOf ( + t.submodule { + options.imports = importsOption; + options.config = moduleConfig; + } + ); }; options.roles = lib.mkOption { default = { }; @@ -95,6 +107,7 @@ in type = t.listOf tagRef; }; options.config = moduleConfig; + options.imports = importsOption; } ); }; From ec5fb47b3281e8988c206717e849e5b028516ff2 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Mon, 15 Jul 2024 16:52:24 +0200 Subject: [PATCH 2/7] Inventory: fix tests --- lib/inventory/build-inventory/default.nix | 3 +- lib/inventory/build-inventory/interface.nix | 1 - lib/inventory/tests/default.nix | 108 ++++++++++++-------- 3 files changed, 64 insertions(+), 48 deletions(-) diff --git a/lib/inventory/build-inventory/default.nix b/lib/inventory/build-inventory/default.nix index 88fe066df..fce8f5e67 100644 --- a/lib/inventory/build-inventory/default.nix +++ b/lib/inventory/build-inventory/default.nix @@ -93,7 +93,6 @@ let roleServiceConfigs = builtins.map ( role: serviceConfig.roles.${role}.config or { } ) inverseRoles.${machineName} or [ ]; - dbg = v: lib.traceSeq v v; customImports = map (s: "${directory}/${s}") ( globalImports ++ machineImports ++ roleServiceImports @@ -104,7 +103,7 @@ let acc2 ++ [ { - imports = dbg ([ clan-core.clanModules.${moduleName} ] ++ roleModules ++ customImports); + imports = [ clan-core.clanModules.${moduleName} ] ++ roleModules ++ customImports; config.clan.${moduleName} = lib.mkMerge ( [ globalConfig diff --git a/lib/inventory/build-inventory/interface.nix b/lib/inventory/build-inventory/interface.nix index 34a3c1e89..0ceb62e72 100644 --- a/lib/inventory/build-inventory/interface.nix +++ b/lib/inventory/build-inventory/interface.nix @@ -43,7 +43,6 @@ let importsOption = lib.mkOption { default = [ ]; type = t.listOf t.str; - # apply = map (pathOrString: "${pathOrString}"); }; in { diff --git a/lib/inventory/tests/default.nix b/lib/inventory/tests/default.nix index 14eaa8acd..bfd070e70 100644 --- a/lib/inventory/tests/default.nix +++ b/lib/inventory/tests/default.nix @@ -2,25 +2,31 @@ { test_inventory_empty = { # Empty inventory should return an empty module - expr = buildInventory { }; + expr = buildInventory { + inventory = { }; + directory = ./.; + }; expected = { }; }; test_inventory_role_imports = let configs = buildInventory { - services = { - borgbackup.instance_1 = { - roles.server.machines = [ "backup_server" ]; - roles.client.machines = [ - "client_1_machine" - "client_2_machine" - ]; + directory = ./.; + inventory = { + services = { + borgbackup.instance_1 = { + roles.server.machines = [ "backup_server" ]; + roles.client.machines = [ + "client_1_machine" + "client_2_machine" + ]; + }; + }; + machines = { + "backup_server" = { }; + "client_1_machine" = { }; + "client_2_machine" = { }; }; - }; - machines = { - "backup_server" = { }; - "client_1_machine" = { }; - "client_2_machine" = { }; }; }; in @@ -49,18 +55,21 @@ test_inventory_tag_resolve = let configs = buildInventory { - services = { - borgbackup.instance_1 = { - roles.client.tags = [ "backup" ]; + directory = ./.; + inventory = { + services = { + borgbackup.instance_1 = { + roles.client.tags = [ "backup" ]; + }; }; - }; - machines = { - "not_used_machine" = { }; - "client_1_machine" = { - tags = [ "backup" ]; - }; - "client_2_machine" = { - tags = [ "backup" ]; + machines = { + "not_used_machine" = { }; + "client_1_machine" = { + tags = [ "backup" ]; + }; + "client_2_machine" = { + tags = [ "backup" ]; + }; }; }; }; @@ -85,14 +94,17 @@ test_inventory_multiple_roles = let configs = buildInventory { - services = { - borgbackup.instance_1 = { - roles.client.machines = [ "machine_1" ]; - roles.server.machines = [ "machine_1" ]; + directory = ./.; + inventory = { + services = { + borgbackup.instance_1 = { + roles.client.machines = [ "machine_1" ]; + roles.server.machines = [ "machine_1" ]; + }; + }; + machines = { + "machine_1" = { }; }; - }; - machines = { - "machine_1" = { }; }; }; in @@ -112,13 +124,16 @@ test_inventory_role_doesnt_exist = let configs = buildInventory { - services = { - borgbackup.instance_1 = { - roles.roleXYZ.machines = [ "machine_1" ]; + directory = ./.; + inventory = { + services = { + borgbackup.instance_1 = { + roles.roleXYZ.machines = [ "machine_1" ]; + }; + }; + machines = { + "machine_1" = { }; }; - }; - machines = { - "machine_1" = { }; }; }; in @@ -132,15 +147,18 @@ test_inventory_tag_doesnt_exist = let configs = buildInventory { - services = { - borgbackup.instance_1 = { - roles.client.machines = [ "machine_1" ]; - roles.client.tags = [ "tagXYZ" ]; + directory = ./.; + inventory = { + services = { + borgbackup.instance_1 = { + roles.client.machines = [ "machine_1" ]; + roles.client.tags = [ "tagXYZ" ]; + }; }; - }; - machines = { - "machine_1" = { - tags = [ "tagABC" ]; + machines = { + "machine_1" = { + tags = [ "tagABC" ]; + }; }; }; }; From 7d028790d9689a20d65793e75929045a0d69cad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 15 Jul 2024 17:20:40 +0200 Subject: [PATCH 3/7] vars: remove deprecated nixos options --- nixosModules/clanCore/vars/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nixosModules/clanCore/vars/default.nix b/nixosModules/clanCore/vars/default.nix index 34ee569b9..aff6be588 100644 --- a/nixosModules/clanCore/vars/default.nix +++ b/nixosModules/clanCore/vars/default.nix @@ -45,7 +45,7 @@ in ); inherit (config.clan.core.vars.settings) secretUploadDirectory secretModule publicModule; }; - inherit (config.clan.networking) targetHost buildHost; - inherit (config.clan.deployment) requireExplicitUpdate; + inherit (config.clan.core.networking) targetHost buildHost; + inherit (config.clan.core.deployment) requireExplicitUpdate; }; } From cc5c027d92a4986c687679066153388b15214b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 15 Jul 2024 17:29:24 +0200 Subject: [PATCH 4/7] inventory: don't check if tag is used in inventory this doesn't work with flake-parts --- lib/inventory/build-inventory/interface.nix | 54 ++++++++------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/lib/inventory/build-inventory/interface.nix b/lib/inventory/build-inventory/interface.nix index 0ceb62e72..42663ccc1 100644 --- a/lib/inventory/build-inventory/interface.nix +++ b/lib/inventory/build-inventory/interface.nix @@ -1,16 +1,16 @@ { config, lib, ... }: let - t = lib.types; + types = lib.types; metaOptions = { - name = lib.mkOption { type = t.str; }; + name = lib.mkOption { type = types.str; }; description = lib.mkOption { default = null; - type = t.nullOr t.str; + type = types.nullOr types.str; }; icon = lib.mkOption { default = null; - type = t.nullOr t.str; + type = types.nullOr types.str; }; }; @@ -21,34 +21,20 @@ let merge = lib.mergeEqualOption; }; - allTags = lib.unique ( - lib.foldlAttrs ( - tags: _: m: - tags ++ m.tags or [ ] - ) [ ] config.machines - ); - - tagRef = lib.mkOptionType { - name = "str"; - description = "Tags :: [${builtins.concatStringsSep " | " allTags}]"; - check = v: lib.isString v && builtins.elem v allTags; - merge = lib.mergeEqualOption; - }; - moduleConfig = lib.mkOption { default = { }; - type = t.attrsOf t.anything; + type = types.attrsOf types.anything; }; importsOption = lib.mkOption { default = [ ]; - type = t.listOf t.str; + type = types.listOf types.str; }; in { options = { assertions = lib.mkOption { - type = t.listOf t.unspecified; + type = types.listOf types.unspecified; internal = true; visible = false; default = [ ]; @@ -57,18 +43,18 @@ in machines = lib.mkOption { default = { }; - type = t.attrsOf ( - t.submodule { + type = types.attrsOf ( + types.submodule { options = { inherit (metaOptions) name description icon; tags = lib.mkOption { default = [ ]; apply = lib.unique; - type = t.listOf t.str; + type = types.listOf types.str; }; system = lib.mkOption { default = null; - type = t.nullOr t.str; + type = types.nullOr types.str; }; }; } @@ -77,16 +63,16 @@ in services = lib.mkOption { default = { }; - type = t.attrsOf ( - t.attrsOf ( - t.submodule { + type = types.attrsOf ( + types.attrsOf ( + types.submodule { options.meta = metaOptions; options.imports = importsOption; options.config = moduleConfig; options.machines = lib.mkOption { default = { }; - type = t.attrsOf ( - t.submodule { + type = types.attrsOf ( + types.submodule { options.imports = importsOption; options.config = moduleConfig; } @@ -94,16 +80,16 @@ in }; options.roles = lib.mkOption { default = { }; - type = t.attrsOf ( - t.submodule { + type = types.attrsOf ( + types.submodule { options.machines = lib.mkOption { default = [ ]; - type = t.listOf machineRef; + type = types.listOf types.str; }; options.tags = lib.mkOption { default = [ ]; apply = lib.unique; - type = t.listOf tagRef; + type = types.listOf types.str; }; options.config = moduleConfig; options.imports = importsOption; From ef4ef00d0fcc4c6c567027ee12620b40ff91e6f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 15 Jul 2024 17:38:00 +0200 Subject: [PATCH 5/7] inventory: better error message if tags are not found --- lib/inventory/build-inventory/default.nix | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/inventory/build-inventory/default.nix b/lib/inventory/build-inventory/default.nix index fce8f5e67..e11c1af31 100644 --- a/lib/inventory/build-inventory/default.nix +++ b/lib/inventory/build-inventory/default.nix @@ -7,7 +7,7 @@ let resolveTags = # Inventory, { machines :: [string], tags :: [string] } - inventory: members: { + { serviceName, instanceName, roleName, inventory, members}: { machines = members.machines or [ ] ++ (builtins.foldl' ( @@ -24,7 +24,10 @@ let ); in if tagMembers == [ ] then - throw "Tag: '${tag}' not found. Available tags: ${builtins.toJSON (lib.unique availableTags)}" + throw '' + inventory.services.${serviceName}.${instanceName}: - ${roleName} tags: no machine with '${tag}' found. + Available tags: ${builtins.toJSON (lib.unique availableTags)} + '' else acc ++ tagMembers ) [ ] members.tags or [ ]); @@ -43,7 +46,7 @@ let machineName: machineConfig: lib.foldlAttrs ( # [ Modules ], String, { ${instance_name} :: ServiceConfig } - acc: moduleName: serviceConfigs: + acc: serviceName: serviceConfigs: acc # Collect service config ++ (lib.foldlAttrs ( @@ -51,7 +54,9 @@ let acc2: instanceName: serviceConfig: let resolvedRoles = builtins.mapAttrs ( - _roleName: members: resolveTags inventory members + roleName: members: resolveTags { + inherit serviceName instanceName roleName inventory members; + } ) serviceConfig.roles; isInService = builtins.any (members: builtins.elem machineName members.machines) ( @@ -82,7 +87,7 @@ let roleModules = builtins.map ( role: let - path = "${clan-core.clanModules.${moduleName}}/roles/${role}.nix"; + path = "${clan-core.clanModules.${serviceName}}/roles/${role}.nix"; in if builtins.pathExists path then path @@ -103,8 +108,8 @@ let acc2 ++ [ { - imports = [ clan-core.clanModules.${moduleName} ] ++ roleModules ++ customImports; - config.clan.${moduleName} = lib.mkMerge ( + imports = [ clan-core.clanModules.${serviceName} ] ++ roleModules ++ customImports; + config.clan.${serviceName} = lib.mkMerge ( [ globalConfig machineServiceConfig @@ -113,7 +118,7 @@ let ); } { - config.clan.inventory.services.${moduleName}.${instanceName} = { + config.clan.inventory.services.${serviceName}.${instanceName} = { roles = resolvedRoles; # TODO: Add inverseRoles to the service config if needed # inherit inverseRoles; From d9932b3b814f90f7b6805a6b9f59ad19111bc02a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 15 Jul 2024 17:42:06 +0200 Subject: [PATCH 6/7] wip: debug --- flakeModules/clan.nix | 5 +++-- lib/inventory/build-inventory/default.nix | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/flakeModules/clan.nix b/flakeModules/clan.nix index 554ef2ec8..0149201ed 100644 --- a/flakeModules/clan.nix +++ b/flakeModules/clan.nix @@ -60,7 +60,8 @@ in description = "Allows to include machine-specific modules i.e. machines.\${name} = { ... }"; }; inventory = mkOption { - type = types.submodule { imports = [ ../lib/inventory/build-inventory/interface.nix ]; }; + #type = types.submodule { imports = [ ../lib/inventory/build-inventory/interface.nix ]; }; + type = types.attrsOf types.raw; default = { }; description = '' An abstract service layer for consistently configuring distributed services across machine boundaries. @@ -117,10 +118,10 @@ in directory specialArgs machines - inventory pkgsForSystem meta ; + inventory = (lib.traceValSeq cfg.inventory); }; }; _file = __curPos.file; diff --git a/lib/inventory/build-inventory/default.nix b/lib/inventory/build-inventory/default.nix index e11c1af31..8067edef6 100644 --- a/lib/inventory/build-inventory/default.nix +++ b/lib/inventory/build-inventory/default.nix @@ -17,7 +17,7 @@ let availableTags = lib.foldlAttrs ( acc: _: v: v.tags or [ ] ++ acc - ) [ ] inventory.machines; + ) [ ] (lib.traceValSeq inventory.machines); tagMembers = builtins.attrNames ( lib.filterAttrs (_n: v: builtins.elem tag v.tags or [ ]) inventory.machines From 5ec952d29d69788be58db7b1f370bd52cab0b44d Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Tue, 16 Jul 2024 10:33:24 +0200 Subject: [PATCH 7/7] Inventory: migrate failure test message --- flakeModules/clan.nix | 2 +- lib/inventory/build-inventory/default.nix | 22 +++++++++++++++++---- lib/inventory/build-inventory/interface.nix | 7 ------- lib/inventory/tests/default.nix | 2 +- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/flakeModules/clan.nix b/flakeModules/clan.nix index 0149201ed..01739305a 100644 --- a/flakeModules/clan.nix +++ b/flakeModules/clan.nix @@ -121,7 +121,7 @@ in pkgsForSystem meta ; - inventory = (lib.traceValSeq cfg.inventory); + inventory = (lib.traceValSeq cfg.inventory); }; }; _file = __curPos.file; diff --git a/lib/inventory/build-inventory/default.nix b/lib/inventory/build-inventory/default.nix index 8067edef6..a24eaa053 100644 --- a/lib/inventory/build-inventory/default.nix +++ b/lib/inventory/build-inventory/default.nix @@ -7,7 +7,14 @@ let resolveTags = # Inventory, { machines :: [string], tags :: [string] } - { serviceName, instanceName, roleName, inventory, members}: { + { + serviceName, + instanceName, + roleName, + inventory, + members, + }: + { machines = members.machines or [ ] ++ (builtins.foldl' ( @@ -25,7 +32,7 @@ let in if tagMembers == [ ] then throw '' - inventory.services.${serviceName}.${instanceName}: - ${roleName} tags: no machine with '${tag}' found. + inventory.services.${serviceName}.${instanceName}: - ${roleName} tags: no machine with tag '${tag}' found. Available tags: ${builtins.toJSON (lib.unique availableTags)} '' else @@ -54,8 +61,15 @@ let acc2: instanceName: serviceConfig: let resolvedRoles = builtins.mapAttrs ( - roleName: members: resolveTags { - inherit serviceName instanceName roleName inventory members; + roleName: members: + resolveTags { + inherit + serviceName + instanceName + roleName + inventory + members + ; } ) serviceConfig.roles; diff --git a/lib/inventory/build-inventory/interface.nix b/lib/inventory/build-inventory/interface.nix index 42663ccc1..a64b376e4 100644 --- a/lib/inventory/build-inventory/interface.nix +++ b/lib/inventory/build-inventory/interface.nix @@ -14,13 +14,6 @@ let }; }; - machineRef = lib.mkOptionType { - name = "str"; - description = "Machine :: [${builtins.concatStringsSep " | " (builtins.attrNames config.machines)}]"; - check = v: lib.isString v && builtins.elem v (builtins.attrNames config.machines); - merge = lib.mergeEqualOption; - }; - moduleConfig = lib.mkOption { default = { }; type = types.attrsOf types.anything; diff --git a/lib/inventory/tests/default.nix b/lib/inventory/tests/default.nix index bfd070e70..2fda0e321 100644 --- a/lib/inventory/tests/default.nix +++ b/lib/inventory/tests/default.nix @@ -167,7 +167,7 @@ expr = configs; expectedError = { type = "ThrownError"; - msg = "Tag: '\\w+' not found"; + msg = "no machine with tag '\\w+' found"; }; }; }