diff --git a/checks/flake-module.nix b/checks/flake-module.nix
index a3dedf0ec..c8d703060 100644
--- a/checks/flake-module.nix
+++ b/checks/flake-module.nix
@@ -139,33 +139,6 @@ in
nixosTests
// flakeOutputs
// {
- # TODO: Automatically provide this check to downstream users to check their modules
- clan-modules-json-compatible =
- let
- allSchemas = lib.mapAttrs (
- _n: m:
- let
- schema =
- (self.clanLib.evalService {
- modules = [ m ];
- prefix = [
- "checks"
- system
- ];
- }).config.result.api.schema;
- in
- schema
- ) self.clan.modules;
- in
- pkgs.runCommand "combined-result"
- {
- schemaFile = builtins.toFile "schemas.json" (builtins.toJSON allSchemas);
- }
- ''
- mkdir -p $out
- cat $schemaFile > $out/allSchemas.json
- '';
-
clan-core-for-checks = pkgs.runCommand "clan-core-for-checks" { } ''
cp -r ${privateInputs.clan-core-for-checks} $out
chmod -R +w $out
diff --git a/docs/nix/flake-module.nix b/docs/nix/flake-module.nix
index 17467463a..22c6c07af 100644
--- a/docs/nix/flake-module.nix
+++ b/docs/nix/flake-module.nix
@@ -19,11 +19,6 @@
clan-core = self;
inherit pkgs;
evalClanModules = self.clanLib.evalClan.evalClanModules;
- modulesRolesOptions = self.clanLib.evalClan.evalClanModulesWithRoles {
- allModules = self.clanModules;
- inherit pkgs;
- clan-core = self;
- };
};
# Frontmatter for clanModules
@@ -35,10 +30,6 @@
};
in
docs.optionsJSON;
-
- # Options available when imported via ` inventory.${moduleName}....${rolesName} `
- clanModulesViaRoles = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModulesViaRoles);
-
# clan service options
clanModulesViaService = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModulesViaService);
@@ -89,7 +80,6 @@
}
export CLAN_CORE_DOCS=${jsonDocs.clanCore}/share/doc/nixos/options.json
# A file that contains the links to all clanModule docs
- export CLAN_MODULES_VIA_ROLES=${clanModulesViaRoles}
export CLAN_MODULES_VIA_SERVICE=${clanModulesViaService}
export CLAN_SERVICE_INTERFACE=${self'.legacyPackages.clan-service-module-interface}/share/doc/nixos/options.json
# Frontmatter format for clanModules
@@ -107,7 +97,6 @@
legacyPackages = {
inherit
jsonDocs
- clanModulesViaRoles
clanModulesViaService
;
};
diff --git a/docs/nix/get-module-docs.nix b/docs/nix/get-module-docs.nix
index a58ce69ff..d736744f7 100644
--- a/docs/nix/get-module-docs.nix
+++ b/docs/nix/get-module-docs.nix
@@ -1,5 +1,4 @@
{
- modulesRolesOptions,
nixosOptionsDoc,
evalClanModules,
lib,
@@ -12,19 +11,6 @@ let
transformOptions = stripStorePathsFromDeclarations;
in
{
-
- clanModulesViaRoles = lib.mapAttrs (
- _moduleName: rolesOptions:
- lib.mapAttrs (
- _roleName: options:
- (nixosOptionsDoc {
- inherit options;
- warningsAreErrors = true;
- inherit transformOptions;
- }).optionsJSON
- ) rolesOptions
- ) modulesRolesOptions;
-
# Test with:
# nix build .\#legacyPackages.x86_64-linux.clanModulesViaService
clanModulesViaService = lib.mapAttrs (
diff --git a/docs/nix/render_options/__init__.py b/docs/nix/render_options/__init__.py
index d0327461f..acbad9ed5 100644
--- a/docs/nix/render_options/__init__.py
+++ b/docs/nix/render_options/__init__.py
@@ -33,8 +33,6 @@ from clan_lib.errors import ClanError
from clan_lib.services.modules import (
CategoryInfo,
Frontmatter,
- extract_frontmatter,
- get_roles,
)
# Get environment variables
@@ -43,12 +41,6 @@ CLAN_CORE_DOCS = Path(os.environ["CLAN_CORE_DOCS"])
CLAN_MODULES_FRONTMATTER_DOCS = os.environ.get("CLAN_MODULES_FRONTMATTER_DOCS")
BUILD_CLAN_PATH = os.environ.get("BUILD_CLAN_PATH")
-## Clan modules ##
-# Some modules can be imported via nix natively
-CLAN_MODULES_VIA_NIX = os.environ.get("CLAN_MODULES_VIA_NIX")
-# Some modules can be imported via inventory
-CLAN_MODULES_VIA_ROLES = os.environ.get("CLAN_MODULES_VIA_ROLES")
-
# Options how to author clan.modules
# perInstance, perMachine, ...
CLAN_SERVICE_INTERFACE = os.environ.get("CLAN_SERVICE_INTERFACE")
@@ -505,154 +497,6 @@ Learn how to use `clanServices` in practice in the [Using clanServices guide](..
of.write(output)
-def produce_clan_modules_docs() -> None:
- if not CLAN_MODULES_VIA_NIX:
- msg = f"Environment variables are not set correctly: $CLAN_MODULES_VIA_NIX={CLAN_MODULES_VIA_NIX}"
- raise ClanError(msg)
-
- if not CLAN_MODULES_VIA_ROLES:
- msg = f"Environment variables are not set correctly: $CLAN_MODULES_VIA_ROLES={CLAN_MODULES_VIA_ROLES}"
- raise ClanError(msg)
-
- if not CLAN_CORE_PATH:
- msg = f"Environment variables are not set correctly: $CLAN_CORE_PATH={CLAN_CORE_PATH}"
- raise ClanError(msg)
-
- if not OUT:
- msg = f"Environment variables are not set correctly: $out={OUT}"
- raise ClanError(msg)
-
- modules_index = "# Modules Overview\n\n"
- modules_index += clan_modules_descr
- modules_index += "## Overview\n\n"
- modules_index += '
\n\n'
-
- with Path(CLAN_MODULES_VIA_ROLES).open() as f2:
- role_links: dict[str, dict[str, str]] = json.load(f2)
-
- with Path(CLAN_MODULES_VIA_NIX).open() as f:
- links: dict[str, str] = json.load(f)
-
- for module_name, options_file in links.items():
- print(f"Rendering ClanModule: {module_name}")
- readme_file = CLAN_CORE_PATH / "clanModules" / module_name / "README.md"
- with readme_file.open() as f:
- readme = f.read()
- frontmatter: Frontmatter
- frontmatter, readme_content = extract_frontmatter(readme, str(readme_file))
-
- # skip if experimental feature enabled
- if "experimental" in frontmatter.features:
- print(f"Skipping {module_name}: Experimental feature")
- continue
-
- modules_index += build_option_card(module_name, frontmatter)
-
- ##### Print module documentation #####
-
- # 1. Header
- output = module_header(module_name, "inventory" in frontmatter.features)
-
- # 2. Description from README.md
- if frontmatter.description:
- output += f"*{frontmatter.description}*\n\n"
-
- # 2. Deprecation note if the module is deprecated
- if "deprecated" in frontmatter.features:
- output += f"""
-!!! Warning "Deprecated"
- The `{module_name}` module is deprecated.*
-
- Use 'clanServices/{module_name}' or a similar successor instead
-"""
- else:
- output += f"""
-!!! Warning "Will be deprecated"
- The `{module_name}` module might eventually be migrated to 'clanServices'*
-
- See: [clanServices](../../guides/clanServices.md)
-"""
-
- # 3. Categories from README.md
- output += "## Categories\n\n"
- output += render_categories(frontmatter.categories, frontmatter.categories_info)
- output += "\n---\n\n"
-
- # 3. README.md content
- output += f"{readme_content}\n"
-
- # 4. Usage
- ##### Print usage via Inventory #####
-
- # get_roles(str) -> list[str] | None
- # if not isinstance(options_file, str):
- roles = get_roles(CLAN_CORE_PATH / "clanModules" / module_name)
- if roles:
- # Render inventory usage
- output += """## Usage via Inventory\n\n"""
- output += render_roles(roles, module_name)
- for role in roles:
- role_options_file = role_links[module_name][role]
- # Abort if the options file is not found
- if not isinstance(role_options_file, str):
- print(
- f"Error: module: {module_name} in role: {role} - options file not found, Got {role_options_file}"
- )
- exit(1)
-
- no_options = f"""### Options of `{role}` role
-
-**The `{module_name}` `{role}` doesnt offer / require any options to be set.**
-"""
-
- heading = f"""### Options of `{role}` role
-
-The following options are available when using the `{role}` role.
-"""
- output += print_options(
- role_options_file,
- heading,
- no_options,
- replace_prefix=f"clan.{module_name}",
- )
- else:
- # No roles means no inventory usage
- output += """## Usage via Inventory
-
-**This module cannot be used via the inventory interface.**
-"""
-
- ##### Print usage via Nix / nixos #####
- if not isinstance(options_file, str):
- print(
- f"Skipping {module_name}: Cannot be used via import clanModules.{module_name}"
- )
- output += """## Usage via Nix
-
-**This module cannot be imported directly in your nixos configuration.**
-
-"""
- else:
- output += module_nix_usage(module_name)
- no_options = "** This module doesnt require any options to be set.**"
- output += print_options(options_file, options_head, no_options)
-
- outfile = Path(OUT) / f"clanModules/{module_name}.md"
- outfile.parent.mkdir(
- parents=True,
- exist_ok=True,
- )
- with outfile.open("w") as of:
- of.write(output)
-
- modules_index += "
"
- modules_index += "\n"
- modules_outfile = Path(OUT) / "clanModules/index.md"
-
- with modules_outfile.open("w") as of:
- of.write(modules_index)
-
-
def build_option_card(module_name: str, frontmatter: Frontmatter) -> str:
"""
Build the overview index card for each reference target option.
@@ -863,8 +707,6 @@ if __name__ == "__main__": #
produce_clan_core_docs()
produce_clan_service_author_docs()
-
- # produce_clan_modules_docs()
produce_clan_service_docs()
# produce_clan_modules_frontmatter_docs()
diff --git a/flake.nix b/flake.nix
index 15406c1cd..7e92e3ec1 100644
--- a/flake.nix
+++ b/flake.nix
@@ -67,7 +67,6 @@
clan = {
meta.name = "clan-core";
inventory = {
- services = { };
machines = {
"test-darwin-machine" = {
machineClass = "darwin";
diff --git a/lib/modules/inventory/eval-clan-modules/default.nix b/lib/modules/inventory/eval-clan-modules/default.nix
index bc836761c..f356ddc98 100644
--- a/lib/modules/inventory/eval-clan-modules/default.nix
+++ b/lib/modules/inventory/eval-clan-modules/default.nix
@@ -52,57 +52,8 @@ let
# ''
evaled;
- /*
- This function takes a list of module names and evaluates them
- Returns a set of interfaces as described below:
- Fn :: { ${moduleName} = Module; } -> {
- ${moduleName} :: {
- ${roleName}: JSONSchema
- }
- }
- */
- evalClanModulesWithRoles =
- {
- allModules,
- clan-core,
- pkgs,
- }:
- let
- res = builtins.mapAttrs (
- moduleName: module:
- let
- frontmatter = clanLib.modules.getFrontmatter allModules.${moduleName} moduleName;
- roles =
- if builtins.elem "inventory" frontmatter.features or [ ] then
- assert lib.isPath module;
- clan-core.clanLib.modules.getRoles "Documentation: inventory.modules" allModules moduleName
- else
- [ ];
- in
- lib.listToAttrs (
- lib.map (role: {
- name = role;
- value =
- (lib.evalModules {
- class = "nixos";
- modules = [
- (baseModule { inherit pkgs; })
- clan-core.nixosModules.clanCore
- {
- clan.core.settings.directory = clan-core;
- }
- # Role interface
- (module + "/roles/${role}.nix")
- ];
- }).options.clan.${moduleName} or { };
- }) roles
- )
- ) allModules;
- in
- res;
in
{
evalClanModules = evalClanModulesLegacy;
- inherit evalClanModulesWithRoles;
}
diff --git a/lib/modules/inventory/frontmatter/default.nix b/lib/modules/inventory/frontmatter/default.nix
index 1ce0c656a..9084bbef1 100644
--- a/lib/modules/inventory/frontmatter/default.nix
+++ b/lib/modules/inventory/frontmatter/default.nix
@@ -3,51 +3,6 @@ let
# Trim the .nix extension from a filename
trimExtension = name: builtins.substring 0 (builtins.stringLength name - 4) name;
- jsonWithoutHeader = clanLib.jsonschema {
- includeDefaults = true;
- header = { };
- };
-
- getModulesSchema =
- {
- 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 =
- {
- moduleName,
- instanceName,
- resolvedRoles,
- allModules,
- }:
- lib.evalModules {
- modules = [
- (getFrontmatter allModules.${moduleName} moduleName)
- ./interface.nix
- {
- constraints.imports = [
- (lib.modules.importApply ../constraints {
- inherit moduleName resolvedRoles instanceName;
- allRoles = getRoles "inventory.modules" allModules moduleName;
- })
- ];
- }
- ];
- };
-
# For Documentation purposes only
frontmatterOptions =
(lib.evalModules {
@@ -119,17 +74,12 @@ let
builtins.readDir (checkedPath)
)
);
-
- checkConstraints = args: (evalFrontmatter args).config.constraints.assertions;
getFrontmatter = _modulepath: _modulename: "clanModules are removed!";
in
{
inherit
frontmatterOptions
- getModulesSchema
getFrontmatter
-
- checkConstraints
getRoles
;
}
diff --git a/lib/modules/inventory/schemas/default.nix b/lib/modules/inventory/schemas/default.nix
index a7e92f589..946e64f7e 100644
--- a/lib/modules/inventory/schemas/default.nix
+++ b/lib/modules/inventory/schemas/default.nix
@@ -1,19 +1,11 @@
{
self,
- self',
lib,
pkgs,
flakeOptions,
...
}:
let
-
- modulesSchema = self.clanLib.modules.getModulesSchema {
- modules = self.clanModules;
- inherit pkgs;
- clan-core = self;
- };
-
jsonLib = self.clanLib.jsonschema { inherit includeDefaults; };
includeDefaults = true;
@@ -38,13 +30,6 @@ let
];
clanSchema = jsonLib.parseOptions (lib.filterAttrs (n: _v: lib.elem n include) clanOpts) { };
- renderSchema = pkgs.writers.writePython3Bin "render-schema" {
- flakeIgnore = [
- "F401"
- "E501"
- ];
- } ./render_schema.py;
-
clan-schema-abstract = pkgs.stdenv.mkDerivation {
name = "clan-schema-files";
buildInputs = [ pkgs.cue ];
@@ -66,26 +51,6 @@ in
frontMatterSchema
clanSchema
inventorySchema
- modulesSchema
- renderSchema
clan-schema-abstract
;
-
- # Inventory schema, with the modules schema added per role
- inventory =
- pkgs.runCommand "rendered"
- {
- buildInputs = [
- pkgs.python3
- self'.packages.clan-cli
- ];
- }
- ''
- export INVENTORY_SCHEMA_PATH=${builtins.toFile "inventory-schema.json" (builtins.toJSON inventorySchema)}
- export MODULES_SCHEMA_PATH=${builtins.toFile "modules-schema.json" (builtins.toJSON modulesSchema)}
-
- mkdir $out
- # The python script will place the schemas in the output directory
- exec python3 ${renderSchema}/bin/render-schema
- '';
}
diff --git a/lib/modules/inventory/schemas/render_schema.py b/lib/modules/inventory/schemas/render_schema.py
deleted file mode 100644
index de544049a..000000000
--- a/lib/modules/inventory/schemas/render_schema.py
+++ /dev/null
@@ -1,162 +0,0 @@
-"""
-Python script to join the abstract inventory schema, with the concrete clan modules
-Inventory has slots which are 'Any' type.
-We dont want to evaluate the clanModules interface in nix, when evaluating the inventory
-"""
-
-import json
-import os
-from pathlib import Path
-from typing import Any
-
-from clan_lib.errors import ClanError
-
-# Get environment variables
-INVENTORY_SCHEMA_PATH = Path(os.environ["INVENTORY_SCHEMA_PATH"])
-
-# { [moduleName] :: { [roleName] :: SCHEMA }}
-MODULES_SCHEMA_PATH = Path(os.environ["MODULES_SCHEMA_PATH"])
-
-OUT = os.environ.get("out")
-
-if not INVENTORY_SCHEMA_PATH:
- msg = f"Environment variables are not set correctly: INVENTORY_SCHEMA_PATH={INVENTORY_SCHEMA_PATH}."
- raise ClanError(msg)
-
-if not MODULES_SCHEMA_PATH:
- msg = f"Environment variables are not set correctly: MODULES_SCHEMA_PATH={MODULES_SCHEMA_PATH}."
- raise ClanError(msg)
-
-if not OUT:
- msg = f"Environment variables are not set correctly: OUT={OUT}."
- raise ClanError(msg)
-
-
-def service_roles_to_schema(
- schema: dict[str, Any],
- service_name: str,
- roles: list[str],
- roles_schemas: dict[str, dict[str, Any]],
- # Original service properties: {'config': Schema, 'machines': Schema, 'meta': Schema, 'extraModules': Schema, ...?}
- orig: dict[str, Any],
-) -> dict[str, Any]:
- """
- Add roles to the service schema
- """
- # collect all the roles for the service, to form a type union
- all_roles_schema: list[dict[str, Any]] = []
- for role_name, role_schema in roles_schemas.items():
- role_schema["title"] = f"{module_name}-config-role-{role_name}"
- all_roles_schema.append(role_schema)
-
- role_schema = {}
- for role in roles:
- role_schema[role] = {
- "type": "object",
- "additionalProperties": False,
- "properties": {
- **orig["roles"]["additionalProperties"]["properties"],
- "config": {
- **roles_schemas.get(role, {}),
- "title": f"{service_name}-config-role-{role}",
- "type": "object",
- "default": {},
- "additionalProperties": False,
- },
- },
- }
-
- machines_schema = {
- "type": "object",
- "additionalProperties": {
- "type": "object",
- "properties": {
- **orig["machines"]["additionalProperties"]["properties"],
- "config": {
- "title": f"{service_name}-config",
- "oneOf": all_roles_schema,
- "type": "object",
- "default": {},
- "additionalProperties": False,
- },
- },
- },
- }
-
- services["properties"][service_name] = {
- "type": "object",
- "additionalProperties": {
- "type": "object",
- "additionalProperties": False,
- "properties": {
- # Original inventory schema
- **orig,
- # Inject the roles schemas
- "roles": {
- "title": f"{service_name}-roles",
- "type": "object",
- "properties": role_schema,
- "additionalProperties": False,
- },
- "machines": machines_schema,
- "config": {
- "title": f"{service_name}-config",
- "oneOf": all_roles_schema,
- "type": "object",
- "default": {},
- "additionalProperties": False,
- },
- },
- },
- }
-
- return schema
-
-
-if __name__ == "__main__":
- print("Joining inventory schema with modules schema")
- print(f"Inventory schema path: {INVENTORY_SCHEMA_PATH}")
- print(f"Modules schema path: {MODULES_SCHEMA_PATH}")
-
- modules_schema = {}
- with Path.open(MODULES_SCHEMA_PATH) as f:
- modules_schema = json.load(f)
-
- inventory_schema = {}
- with Path.open(INVENTORY_SCHEMA_PATH) as f:
- inventory_schema = json.load(f)
-
- services = inventory_schema["properties"]["services"]
- original_service_props = services["additionalProperties"]["additionalProperties"][
- "properties"
- ].copy()
- # Init the outer services schema
- # Properties (service names) will be filled in the next step
- services = {
- "type": "object",
- "properties": {
- # Service names
- },
- "additionalProperties": False,
- }
-
- for module_name, roles_schemas in modules_schema.items():
- # Add the roles schemas to the service schema
- roles = list(roles_schemas.keys())
- if roles:
- services = service_roles_to_schema(
- services,
- module_name,
- roles,
- roles_schemas,
- original_service_props,
- )
-
- inventory_schema["properties"]["services"] = services
-
- outpath = Path(OUT)
- with (outpath / "schema.json").open("w") as f:
- json.dump(inventory_schema, f, indent=2)
-
- with (outpath / "modules_schemas.json").open("w") as f:
- json.dump(modules_schema, f, indent=2)
diff --git a/lib/modules/inventoryClass/assertions.nix b/lib/modules/inventoryClass/assertions.nix
deleted file mode 100644
index 5326f4a74..000000000
--- a/lib/modules/inventoryClass/assertions.nix
+++ /dev/null
@@ -1,78 +0,0 @@
-# Integrity validation of the inventory
-{ config, lib, ... }:
-{
- # Assertion must be of type
- # { assertion :: bool, message :: string, severity :: "error" | "warning" }
- imports = [
- # Check that each machine used in a service is defined in the top-level machines
- {
- assertions = lib.foldlAttrs (
- ass1: serviceName: c:
- ass1
- ++ lib.foldlAttrs (
- ass2: instanceName: instanceConfig:
- let
- topLevelMachines = lib.attrNames config.machines;
- # All machines must be defined in the top-level machines
- assertions = lib.foldlAttrs (
- assertions: roleName: role:
- assertions
- ++ builtins.filter (a: !a.assertion) (
- builtins.map (m: {
- assertion = builtins.elem m topLevelMachines;
- message = ''
- Machine '${m}' is not defined in the inventory. This might still work, if the machine is defined via nix.
-
- Defined in service: '${serviceName}' instance: '${instanceName}' role: '${roleName}'.
-
- Inventory machines:
- ${builtins.concatStringsSep "\n" (map (n: "'${n}'") topLevelMachines)}
- '';
- severity = "warning";
- }) role.machines
- )
- ) [ ] instanceConfig.roles;
- in
- ass2 ++ assertions
- ) [ ] c
- ) [ ] config.services;
- }
- # Check that each tag used in a role is defined in at least one machines tags
- {
- assertions = lib.foldlAttrs (
- ass1: serviceName: c:
- ass1
- ++ lib.foldlAttrs (
- ass2: instanceName: instanceConfig:
- let
- allTags = lib.foldlAttrs (
- tags: _machineName: machine:
- tags ++ machine.tags
- ) [ ] config.machines;
- # All machines must be defined in the top-level machines
- assertions = lib.foldlAttrs (
- assertions: roleName: role:
- assertions
- ++ builtins.filter (a: !a.assertion) (
- builtins.map (m: {
- assertion = builtins.elem m allTags;
- message = ''
- Tag '${m}' is not defined in the inventory.
-
- Defined in service: '${serviceName}' instance: '${instanceName}' role: '${roleName}'.
-
- Available tags:
- ${builtins.concatStringsSep "\n" (map (n: "'${n}'") allTags)}
- '';
- severity = "error";
- }) role.tags
- )
- ) [ ] instanceConfig.roles;
- in
- ass2 ++ assertions
- ) [ ] c
- ) [ ] config.services;
- }
- ];
-
-}
diff --git a/lib/modules/inventoryClass/builder/default.nix b/lib/modules/inventoryClass/builder/default.nix
index c25ea7057..bd4ca0b59 100644
--- a/lib/modules/inventoryClass/builder/default.nix
+++ b/lib/modules/inventoryClass/builder/default.nix
@@ -1,268 +1,5 @@
-{
- lib,
- config,
- clanLib,
- ...
-}:
-let
- inherit (config) inventory directory;
- resolveTags =
- # Inventory, { machines :: [string], tags :: [string] }
- {
- serviceName,
- instanceName,
- roleName,
- inventory,
- members,
- }:
- {
- machines =
- members.machines or [ ]
- ++ (builtins.foldl' (
- acc: tag:
- let
- # For error printing
- availableTags = lib.foldlAttrs (
- acc: _: v:
- v.tags or [ ] ++ acc
- ) [ ] (inventory.machines);
-
- tagMembers = builtins.attrNames (
- lib.filterAttrs (_n: v: builtins.elem tag v.tags or [ ]) inventory.machines
- );
- in
- if tagMembers == [ ] then
- lib.warn ''
- inventory.services.${serviceName}.${instanceName}: - ${roleName} tags: no machine with tag '${tag}' found.
- Available tags: ${builtins.toJSON (lib.unique availableTags)}
- '' [ ]
- else
- acc ++ tagMembers
- ) [ ] members.tags or [ ]);
- };
-
- checkService =
- modulepath: serviceName:
- builtins.elem "inventory" (clanLib.modules.getFrontmatter modulepath serviceName).features or [ ];
-
- compileMachine =
- { machineConfig }:
- {
- machineImports = [
- (lib.optionalAttrs (machineConfig.deploy.targetHost or null != null) {
- config.clan.core.networking.targetHost = lib.mkForce machineConfig.deploy.targetHost;
- })
- (lib.optionalAttrs (machineConfig.deploy.buildHost or null != null) {
- config.clan.core.networking.buildHost = lib.mkForce machineConfig.deploy.buildHost;
- })
- ];
- assertions = { };
- };
-
- resolveImports =
- {
- supportedRoles,
- resolvedRolesPerInstance,
- serviceConfigs,
- serviceName,
- machineName,
- getRoleFile,
- }:
- (lib.foldlAttrs (
- # : [ Modules ] -> String -> ServiceConfig -> [ Modules ]
- acc2: instanceName: serviceConfig:
- let
- resolvedRoles = resolvedRolesPerInstance.${instanceName};
-
- isInService = builtins.any (members: builtins.elem machineName members.machines) (
- builtins.attrValues resolvedRoles
- );
-
- # all roles where the machine is present
- machineRoles = builtins.attrNames (
- lib.filterAttrs (_role: roleConfig: builtins.elem machineName roleConfig.machines) resolvedRoles
- );
-
- machineServiceConfig = (serviceConfig.machines.${machineName} or { }).config or { };
- globalConfig = serviceConfig.config or { };
-
- globalExtraModules = serviceConfig.extraModules or [ ];
- machineExtraModules = serviceConfig.machines.${machineName}.extraModules or [ ];
- roleServiceExtraModules = builtins.foldl' (
- acc: role: acc ++ serviceConfig.roles.${role}.extraModules or [ ]
- ) [ ] machineRoles;
-
- # 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
- getRoleFile role
- else
- throw "Module ${serviceName} doesn't have role: '${role}'. Role: ${
- inventory.modules.${serviceName}
- }/roles/${role}.nix not found."
- ) machineRoles;
-
- roleServiceConfigs = builtins.filter (m: m != { }) (
- builtins.map (role: serviceConfig.roles.${role}.config or { }) machineRoles
- );
-
- extraModules = map (s: if builtins.typeOf s == "string" then "${directory}/${s}" else s) (
- globalExtraModules ++ machineExtraModules ++ roleServiceExtraModules
- );
-
- features =
- (clanLib.modules.getFrontmatter inventory.modules.${serviceName} serviceName).features or [ ];
- deprecationWarning = lib.optionalAttrs (builtins.elem "deprecated" features) {
- warnings = [
- ''
- The '${serviceName}' module has been migrated from `inventory.services` to `inventory.instances`
- See https://docs.clan.lol/guides/clanServices/ for usage.
- ''
- ];
- };
- in
- if !(serviceConfig.enabled or true) then
- acc2
- else if isInService then
- acc2
- ++ [
- deprecationWarning
- {
- imports = roleModules ++ extraModules;
- clan.inventory.services.${serviceName}.${instanceName} = {
- roles = resolvedRoles;
- # TODO: Add inverseRoles to the service config if needed
- # inherit inverseRoles;
- };
- }
- (lib.optionalAttrs (globalConfig != { } || machineServiceConfig != { } || roleServiceConfigs != [ ])
- {
- clan.${serviceName} = lib.mkMerge (
- [
- globalConfig
- machineServiceConfig
- ]
- ++ roleServiceConfigs
- );
- }
- )
- ]
- else
- acc2
- ) [ ] (serviceConfigs));
-in
{
imports = [
./interface.nix
];
- config = {
- machines = builtins.mapAttrs (
- machineName: machineConfig: m:
- let
- compiledServices = lib.mapAttrs (
- _: serviceConfigs:
- (
- { config, ... }:
- let
- serviceName = config.serviceName;
-
- getRoleFile = role: builtins.seq role inventory.modules.${serviceName} + "/roles/${role}.nix";
- in
- {
- _file = "inventory/builder.nix";
- _module.args = {
- inherit
- resolveTags
- inventory
- clanLib
- machineName
- serviceConfigs
- ;
- };
- imports = [
- ./roles.nix
- ];
-
- machineImports = resolveImports {
- supportedRoles = config.supportedRoles;
- resolvedRolesPerInstance = config.resolvedRolesPerInstance;
- inherit
- serviceConfigs
- serviceName
- machineName
- getRoleFile
- ;
- };
-
- # Assertions
- assertions = {
- "checkservice.${serviceName}" = {
- assertion = checkService inventory.modules.${serviceName} serviceName;
- message = ''
- Service ${serviceName} cannot be used in inventory. It does not declare the 'inventory' feature.
-
- To allow it add the following to the beginning of the README.md of the module:
-
- ---
- ...
-
- features = [ "inventory" ]
- ---
-
- Also make sure to test the module with the 'inventory' feature enabled.
-
- '';
- };
- };
- }
- )
- ) (config.inventory.services or { });
-
- compiledMachine = compileMachine {
- inherit
- machineConfig
- ;
- };
-
- machineImports = (
- compiledMachine.machineImports
- ++ builtins.foldl' (
- acc: service:
- let
- failedAssertions = (lib.filterAttrs (_: v: !v.assertion) service.assertions);
- failedAssertionsImports =
- if failedAssertions != { } then
- [
- {
- clan.inventory.assertions = failedAssertions;
- }
- ]
- 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)
- );
- in
- {
- inherit machineImports compiledServices compiledMachine;
- }
- ) (inventory.machines or { });
- };
}
diff --git a/lib/modules/inventoryClass/builder/roles.nix b/lib/modules/inventoryClass/builder/roles.nix
deleted file mode 100644
index 364b72d62..000000000
--- a/lib/modules/inventoryClass/builder/roles.nix
+++ /dev/null
@@ -1,65 +0,0 @@
-{
- lib,
- config,
- resolveTags,
- inventory,
- clanLib,
- machineName,
- serviceConfigs,
- ...
-}:
-let
- serviceName = config.serviceName;
-in
-{
- # Roles resolution
- # : List String
- supportedRoles = clanLib.modules.getRoles "inventory.modules" 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:
- clanLib.modules.checkConstraints {
- moduleName = serviceName;
- allModules = inventory.modules;
- inherit resolvedRoles instanceName;
- }
- ) config.resolvedRolesPerInstance;
-}
diff --git a/lib/modules/inventoryClass/interface.nix b/lib/modules/inventoryClass/interface.nix
index b66c216de..25fd4a08e 100644
--- a/lib/modules/inventoryClass/interface.nix
+++ b/lib/modules/inventoryClass/interface.nix
@@ -94,7 +94,10 @@ let
in
{
imports = [
- ./assertions.nix
+ (lib.mkRemovedOptionModule [ "services" ] ''
+ The `inventory.services` option has been removed. Use `inventory.instances` instead.
+ See: https://docs.clan.lol/concepts/inventory/#services
+ '')
];
options = {
# Internal things
@@ -415,160 +418,5 @@ in
);
default = { };
};
-
- services = lib.mkOption {
- # TODO: deprecate these options
- # services are deprecated in favor of `instances`
- # visible = false;
- description = ''
- Services of the inventory.
-
- - The first `` is the moduleName. It must be a valid clanModule name.
- - The second `` is an arbitrary instance name.
-
- ???+ Example
- ```nix
- # ClanModule name. See the module documentation for the available modules.
- # ↓ ↓ Instance name, can be anything, some services might use it as a unique identifier.
- services.borgbackup."instance_1" = {
- roles.client.machines = ["machineA"];
- };
- ```
-
- !!! Note
- Services MUST be added to machines via `roles` exclusively.
- See [`roles..machines`](#inventory.services.roles.machines) or [`roles..tags`](#inventory.services.roles.tags) for more information.
- '';
- default = { };
- type = types.attrsOf (
- types.attrsOf (
- types.submodule (
- # instance name
- { name, ... }:
- {
- options.enabled = lib.mkOption {
- type = lib.types.bool;
- default = true;
- description = ''
- Enable or disable the complete service.
-
- If the service is disabled, it will not be added to any machine.
-
- !!! Note
- This flag is primarily used to temporarily disable a service.
- I.e. A 'backup service' without any 'server' might be incomplete and would cause failure if enabled.
- '';
- };
- options.meta = metaOptionsWith name;
- options.extraModules = extraModulesOption;
- options.config = moduleConfig // {
- description = ''
- Configuration of the specific clanModule.
-
- !!! Note
- Configuration is passed to the nixos configuration scoped to the module.
-
- ```nix
- clan. = { ... # Config }
- ```
-
- ???+ Example
-
- For `services.borgbackup` the config is the passed to the machine with the prefix of `clan.borgbackup`.
- This means all config values are mapped to the `borgbackup` clanModule exclusively (`config.clan.borgbackup`).
-
- ```nix
- {
- services.borgbackup."instance_1".config = {
- destinations = [ ... ];
- # See the 'borgbackup' module docs for all options
- };
- }
- ```
-
- !!! Note
- The module author is responsible for supporting multiple instance configurations in different roles.
- See each clanModule's documentation for more information.
- '';
- };
- options.machines = lib.mkOption {
- description = ''
- Attribute set of machines specific config for the service.
-
- Will be merged with other service configs, such as the role config and the global config.
- For machine specific overrides use `mkForce` or other higher priority methods.
-
- ???+ Example
-
- ```{.nix hl_lines="4-7"}
- services.borgbackup."instance_1" = {
- roles.client.machines = ["machineA"];
-
- machines.machineA.config = {
- # Additional specific config for the machine
- # This is merged with all other config places
- };
- };
- ```
- '';
- default = { };
- type = types.attrsOf (
- types.submodule {
- options.extraModules = extraModulesOption;
- options.config = moduleConfig // {
- description = ''
- Additional configuration of the specific machine.
-
- See how [`service...config`](#inventory.services.config) works in general for further information.
- '';
- };
- }
- );
- };
- options.roles = lib.mkOption {
- default = { };
- type = types.attrsOf (
- types.submodule {
- options.machines = lib.mkOption {
- default = [ ];
- type = types.listOf types.str;
- example = [ "machineA" ];
- description = ''
- List of machines which are part of the role.
-
- The machines are referenced by their `attributeName` in the `inventory.machines` attribute set.
-
- Memberships are declared here to determine which machines are part of the service.
-
- Alternatively, `tags` can be used to determine the membership, more dynamically.
- '';
- };
- options.tags = lib.mkOption {
- default = [ ];
- apply = lib.unique;
- type = types.listOf types.str;
- description = ''
- List of tags which are used to determine the membership of the role.
-
- The tags are matched against the `inventory.machines..tags` attribute set.
- If a machine has at least one tag of the role, it is part of the role.
- '';
- };
- options.config = moduleConfig // {
- description = ''
- Additional configuration of the specific role.
-
- See how [`service...config`](#inventory.services.config) works in general for further information.
- '';
- };
- options.extraModules = extraModulesOption;
- }
- );
- };
- }
- )
- )
- );
- };
};
}
diff --git a/lib/modules/inventoryClass/inventory-introspection.nix b/lib/modules/inventoryClass/inventory-introspection.nix
index 7ff95af12..a9b77bd32 100644
--- a/lib/modules/inventoryClass/inventory-introspection.nix
+++ b/lib/modules/inventoryClass/inventory-introspection.nix
@@ -11,6 +11,7 @@
default =
builtins.removeAttrs (clanLib.introspection.getPrios { options = config.inventory.options; })
# tags are freeformType which is not supported yet.
- [ "tags" ];
+ # services is removed and throws an error if accessed.
+ [ "tags" "services"];
};
}