# Adapter function between the inventory.instances and the clan.service module # # Data flow: # - inventory.instances -> Adapter -> clan.service module -> Service Resources (i.e. NixosModules per Machine, Vars per Service, etc.) # # What this file does: # # - Resolves the [Module] to an actual module-path and imports it. # - Groups together all the same modules into a single import and creates all instances for it. # - Resolves the inventory tags into machines. Tags don't exist at the service level. # Also combines the settings for 'machines' and 'tags'. { lib, clanLib, ... }: { mapInstances = { # This is used to resolve the module imports from 'flake.inputs' flakeInputs, # The clan inventory inventory, directory, clanCoreModules, prefix ? [ ], exportsModule, }: let # machineHasTag = machineName: tagName: lib.elem tagName inventory.machines.${machineName}.tags; # map the instances into the module importedModuleWithInstances = lib.mapAttrs ( instanceName: instance: let resolvedModule = clanLib.resolveModule { moduleSpec = instance.module; inherit flakeInputs clanCoreModules; }; # Every instance includes machines via roles # :: { client :: ... } instanceRoles = lib.mapAttrs ( roleName: role: let resolvedMachines = clanLib.inventory.resolveTags { members = { # Explicit members machines = lib.attrNames role.machines; # Resolved Members tags = lib.attrNames role.tags; }; inherit (inventory) machines; inherit instanceName roleName; }; in # instances..roles. = # Remove "tags", they are resolved into "machines" (removeAttrs role [ "tags" ]) // { machines = lib.genAttrs resolvedMachines.machines ( machineName: let machineSettings = instance.roles.${roleName}.machines.${machineName}.settings or { }; in # TODO: tag settings # Wait for this feature until option introspection for 'settings' is done. # This might get too complex to handle otherwise. # settingsViaTags = lib.filterAttrs ( # tagName: _: machineHasTag machineName tagName # ) instance.roles.${roleName}.tags; { # TODO: Do we want to wrap settings with # setDefaultModuleLocation "inventory.instances.${instanceName}.roles.${roleName}.tags.${tagName}"; settings = { imports = [ machineSettings ]; # ++ lib.attrValues (lib.mapAttrs (_tagName: v: v.settings) settingsViaTags); }; } ); } ) instance.roles; in { inherit (instance) module; inherit resolvedModule instanceRoles; } ) inventory.instances or { }; # Group the instances by the module they resolve to # This is necessary to evaluate the module in a single pass # :: { _ :: [ { name, value } ] } # Since 'perMachine' needs access to all the instances we should include them as a whole grouped = lib.foldlAttrs ( acc: instanceName: instance: let inputName = if instance.module.input == null then "" else instance.module.input; id = inputName + "-" + instance.module.name; in acc // { ${id} = acc.${id} or [ ] ++ [ { inherit instanceName instance; } ]; } ) { } importedModuleWithInstances; # servicesEval.config.mappedServices.self-A.result.final.jon.nixosModule allMachines = lib.mapAttrs (machineName: _: { # This is the list of nixosModules for each machine machineImports = lib.foldlAttrs ( acc: _module_ident: serviceModule: acc ++ [ serviceModule.result.final.${machineName}.nixosModule or { } ] ) [ ] servicesEval.config.mappedServices; }) inventory.machines or { }; evalServices = { modules, prefix }: lib.evalModules { class = "clan"; specialArgs = { inherit clanLib; _ctx = prefix; }; modules = [ (import ./all-services-wrapper.nix { inherit directory; }) ] ++ modules; }; servicesEval = evalServices { inherit prefix; modules = [ { inherit exportsModule; mappedServices = lib.mapAttrs (_module_ident: instances: { imports = [ # Import the resolved module. # i.e. clan.modules.admin { options.module = lib.mkOption { type = lib.types.raw; default = (builtins.head instances).instance.module; }; } (builtins.head instances).instance.resolvedModule ] # Include all the instances that correlate to the resolved module ++ (builtins.map (v: { instances.${v.instanceName}.roles = v.instance.instanceRoles; }) instances); }) grouped; } ]; }; in { inherit servicesEval importedModuleWithInstances # Exposed for testing grouped allMachines ; }; }