Skip to content

Commit

Permalink
Merge pull request 'inventory: refactor role resolution into submodul…
Browse files Browse the repository at this point in the history
…e' (#2826) from hsjobeki/clan-core:hsjobeki-main into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/2826
  • Loading branch information
hsjobeki committed Feb 8, 2025
2 parents 32748c1 + e75b50e commit 934d8fc
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 91 deletions.
1 change: 1 addition & 0 deletions checks/backups/flake-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
"lib/flake-module.nix"
"lib/frontmatter"
"lib/inventory"
"lib/constraints"
"nixosModules"
];
};
Expand Down
140 changes: 51 additions & 89 deletions lib/inventory/build-inventory/builder/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ let
legacyResolveImports =
{
supportedRoles,
resolvedRolesPerInstance,
serviceConfigs,
serviceName,
machineName,
Expand All @@ -69,18 +70,7 @@ let
# : [ Modules ] -> String -> ServiceConfig -> [ Modules ]
acc2: instanceName: serviceConfig:
let
resolvedRoles = lib.genAttrs supportedRoles (
roleName:
resolveTags {
members = serviceConfig.roles.${roleName} or { };
inherit
serviceName
instanceName
roleName
inventory
;
}
);
resolvedRoles = resolvedRolesPerInstance.${instanceName};

isInService = builtins.any (members: builtins.elem machineName members.machines) (
builtins.attrValues resolvedRoles
Expand All @@ -90,6 +80,7 @@ let
machineRoles = builtins.attrNames (
lib.filterAttrs (_role: roleConfig: builtins.elem machineName roleConfig.machines) resolvedRoles
);

machineServiceConfig = (serviceConfig.machines.${machineName} or { }).config or { };
globalConfig = serviceConfig.config or { };

Expand All @@ -99,7 +90,7 @@ let
acc: role: acc ++ serviceConfig.roles.${role}.extraModules or [ ]
) [ ] machineRoles;

# TODO: maybe optimize this dont lookup the role in inverse roles. Imports are not lazy
# TODO: maybe optimize this don't lookup the role in inverse roles. Imports are not lazy
roleModules = builtins.map (
role:
if builtins.elem role supportedRoles && inventory.modules ? ${serviceName} then
Expand All @@ -117,28 +108,14 @@ let
extraModules = map (s: if builtins.typeOf s == "string" then "${directory}/${s}" else s) (
globalExtraModules ++ machineExtraModules ++ roleServiceExtraModules
);

nonExistingRoles = builtins.filter (role: !(builtins.elem role supportedRoles)) (
builtins.attrNames (serviceConfig.roles or { })
);

constraintAssertions = clan-core.lib.modules.checkConstraints {
moduleName = serviceName;
allModules = inventory.modules;
inherit resolvedRoles instanceName;
};
in
if (nonExistingRoles != [ ]) then
throw "Roles ${builtins.toString nonExistingRoles} are not defined in the service ${serviceName}."
else if !(serviceConfig.enabled or true) then
if !(serviceConfig.enabled or true) then
acc2
else if isInService then
acc2
++ [
{
imports = roleModules ++ extraModules;

clan.inventory.assertions = constraintAssertions;
clan.inventory.services.${serviceName}.${instanceName} = {
roles = resolvedRoles;
# TODO: Add inverseRoles to the service config if needed
Expand All @@ -162,7 +139,9 @@ let
) [ ] (serviceConfigs));
in
{
imports = [ ./interface.nix ];
imports = [
./interface.nix
];
config = {
machines = builtins.mapAttrs (
machineName: machineConfig: m:
Expand All @@ -173,75 +152,46 @@ in
{ config, ... }:
let
serviceName = config.serviceName;
loadModuleForClassCheck =
m:
if lib.isFunction m then
let
args = lib.functionArgs m;
in
m args
else
m;
firstRole = import (getRoleFile (builtins.head config.supportedRoles));
getRoleFile = role: builtins.seq role inventory.modules.${serviceName} + "/roles/${role}.nix";

resolvedRolesPerInstance = lib.mapAttrs (
instanceName: instanceConfig:
let
resolvedRoles = lib.genAttrs config.supportedRoles (
roleName:
resolveTags {
members = instanceConfig.roles.${roleName} or { };
inherit
instanceName
serviceName
roleName
inventory
;
}
);
usedRoles = builtins.attrNames instanceConfig.roles;
unmatchedRoles = builtins.filter (role: !builtins.elem role config.supportedRoles) usedRoles;
in
if unmatchedRoles != [ ] then
throw ''
Service: '${serviceName}' Instance: '${instanceName}'
The following roles do not exist: ${builtins.toJSON unmatchedRoles}
Please use one of available roles: ${builtins.toJSON config.supportedRoles}
''
else
resolvedRoles
) serviceConfigs;
getRoleFile = role: builtins.seq role inventory.modules.${serviceName} + "/roles/${role}.nix";
in
{
# Roles resolution
# : List String
supportedRoles = clan-core.lib.modules.getRoles inventory.modules serviceName;
matchedRoles = builtins.attrNames (
lib.filterAttrs (_: ms: builtins.elem machineName ms) config.machinesRoles
);
inherit resolvedRolesPerInstance;
isClanModule =
let
module = loadModuleForClassCheck firstRole;
in
if module ? _class then module._class == "clan" else false;
_module.args = {
inherit
resolveTags
inventory
clan-core
machineName
serviceConfigs
;
};
imports = [
./roles.nix
];

machinesRoles = builtins.zipAttrsWith (
_n: vs:
isClanModule =
let
flat = builtins.foldl' (acc: s: acc ++ s.machines) [ ] vs;
firstRole = import (getRoleFile (builtins.head config.supportedRoles));
loadModuleForClassCheck =
m:
if lib.isFunction m then
let
args = lib.functionArgs m;
in
m args
else
m;
module = loadModuleForClassCheck (firstRole);
in
lib.unique flat
) (builtins.attrValues resolvedRolesPerInstance);

if (module) ? _class then module._class == "clan" else false;
# The actual result
machineImports =
if config.isClanModule then
throw "Clan modules are not supported yet."
else
legacyResolveImports {
supportedRoles = config.supportedRoles;
resolvedRolesPerInstance = config.resolvedRolesPerInstance;
inherit
serviceConfigs
serviceName
Expand Down Expand Up @@ -280,7 +230,7 @@ in
;
};

machineImports =
machineImports = (
compiledMachine.machineImports
++ builtins.foldl' (
acc: service:
Expand All @@ -294,14 +244,26 @@ in
}
]
else
[ ];
[
{
clan.inventory.assertions = {
"alive.assertion.inventory" = {
assertion = true;
message = ''
No failed assertions found for machine ${machineName}. This will never be displayed.
It is here for testing purposes.
'';
};
};
}
];
in
acc
++ service.machineImports
# Import failed assertions
++ failedAssertionsImports
) [ ] (builtins.attrValues m.config.compiledServices);

) [ ] (builtins.attrValues m.config.compiledServices)
);
in
{
inherit machineImports compiledServices compiledMachine;
Expand Down
65 changes: 65 additions & 0 deletions lib/inventory/build-inventory/builder/roles.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
lib,
config,
resolveTags,
inventory,
clan-core,
machineName,
serviceConfigs,
...
}:
let
serviceName = config.serviceName;
in
{
# Roles resolution
# : List String
supportedRoles = clan-core.lib.modules.getRoles inventory.modules serviceName;
matchedRoles = builtins.attrNames (
lib.filterAttrs (_: ms: builtins.elem machineName ms) config.machinesRoles
);
resolvedRolesPerInstance = lib.mapAttrs (
instanceName: instanceConfig:
let
resolvedRoles = lib.genAttrs config.supportedRoles (
roleName:
resolveTags {
members = instanceConfig.roles.${roleName} or { };
inherit
instanceName
serviceName
roleName
inventory
;
}
);
usedRoles = builtins.attrNames instanceConfig.roles;
unmatchedRoles = builtins.filter (role: !builtins.elem role config.supportedRoles) usedRoles;
in
if unmatchedRoles != [ ] then
throw ''
Roles ${builtins.toJSON unmatchedRoles} are not defined in the service ${serviceName}.
Instance: '${instanceName}'
Please use one of available roles: ${builtins.toJSON config.supportedRoles}
''
else
resolvedRoles
) serviceConfigs;

machinesRoles = builtins.zipAttrsWith (
_n: vs:
let
flat = builtins.foldl' (acc: s: acc ++ s.machines) [ ] vs;
in
lib.unique flat
) (builtins.attrValues config.resolvedRolesPerInstance);

assertions = lib.concatMapAttrs (
instanceName: resolvedRoles:
clan-core.lib.modules.checkConstraints {
moduleName = serviceName;
allModules = inventory.modules;
inherit resolvedRoles instanceName;
}
) config.resolvedRolesPerInstance;
}
1 change: 1 addition & 0 deletions lib/inventory/flake-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ in
"lib/default.nix"
"lib/flake-module.nix"
"lib/inventory"
"lib/constraints"
"lib/frontmatter"
"clanModules/flake-module.nix"
"clanModules/borgbackup"
Expand Down
6 changes: 4 additions & 2 deletions lib/inventory/tests/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ in
expr = configs.machines.machine_1.machineImports;
expectedError = {
type = "ThrownError";
msg = "Roles roleXYZ are not defined in the service borgbackup.";
msg = ''Roles \["roleXYZ"\] are not defined in the service borgbackup'';
};
};
# Needs NIX_ABORT_ON_WARN=1
Expand Down Expand Up @@ -286,7 +286,9 @@ in
in
{
inherit configs;
expr = builtins.filter (v: v != { }) configs.machines.machine_1.machineImports;
expr = builtins.filter (
v: v != { } && !v.clan.inventory.assertions ? "alive.assertion.inventory"
) configs.machines.machine_1.machineImports;
expected = [ ];
};
}

0 comments on commit 934d8fc

Please sign in to comment.