Merge pull request 'inventory modules: expose module schemas at runtime' (#2469) from hsjobeki/clan-core:inventory-modules into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/2469
This commit is contained in:
@@ -41,6 +41,8 @@ clanModules/borgbackup
|
||||
|
||||
The `roles` folder is strictly required for `features = [ "inventory" ]`.
|
||||
|
||||
## Registering the module
|
||||
|
||||
=== "User module"
|
||||
|
||||
If the module should be ad-hoc loaded.
|
||||
@@ -134,6 +136,56 @@ Adds the roles: `client` and `server`
|
||||
}
|
||||
```
|
||||
|
||||
## Adding configuration options
|
||||
|
||||
While we recommend to keep the interface as minimal as possible and deriving all required information from the `roles` model it might sometimes be required or convinient to expose customization options beyond `roles`.
|
||||
|
||||
The following shows how to add options to your module.
|
||||
|
||||
**It is important to understand that every module has its own namespace where it should declare options**
|
||||
|
||||
**`clan.{moduleName}`**
|
||||
|
||||
???+ Example
|
||||
The following example shows how to register options in the module interface
|
||||
|
||||
and how it can be set via the inventory
|
||||
|
||||
|
||||
```nix title="/default.nix"
|
||||
custom-module = ./modules/custom-module;
|
||||
```
|
||||
|
||||
Since the module is called `custom-module` all of its exposed options should be added to `options.clan.custom-module.*...*`
|
||||
|
||||
```nix title="custom-module/roles/default.nix"
|
||||
{
|
||||
options = {
|
||||
clan.custom-module.foo = mkOption {
|
||||
type = types.str;
|
||||
default = "bar";
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
If the module is [registered](#registering-the-module).
|
||||
Configuration can be set as follows.
|
||||
|
||||
```nix title="flake.nix"
|
||||
buildClan {
|
||||
inventory.services = {
|
||||
custom-module.instance_1 = {
|
||||
roles.default.machines = [ "machineA" ];
|
||||
roles.default.config = {
|
||||
# All configuration here is scoped to `clan.custom-module`
|
||||
foo = "foobar";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Organizing the ClanModule
|
||||
|
||||
Each `{role}.nix` is included into the machine if the machine is declared to have the role.
|
||||
|
||||
@@ -101,8 +101,12 @@ in
|
||||
# Those options are interfaced by the CLI
|
||||
# We don't specify the type here, for better performance.
|
||||
inventory = lib.mkOption { type = lib.types.raw; };
|
||||
# all inventory module schemas
|
||||
moduleSchemas = lib.mkOption { type = lib.types.raw; };
|
||||
inventoryFile = lib.mkOption { type = lib.types.raw; };
|
||||
# The machine 'imports' generated by the inventory per machine
|
||||
serviceConfigs = lib.mkOption { type = lib.types.raw; };
|
||||
# clan-core's modules
|
||||
clanModules = lib.mkOption { type = lib.types.raw; };
|
||||
source = lib.mkOption { type = lib.types.raw; };
|
||||
meta = lib.mkOption { type = lib.types.raw; };
|
||||
|
||||
@@ -169,6 +169,7 @@ in
|
||||
inherit nixosConfigurations;
|
||||
|
||||
clanInternals = {
|
||||
moduleSchemas = clan-core.lib.modules.getModulesSchema config.inventory.modules;
|
||||
inherit serviceConfigs;
|
||||
inherit (clan-core) clanModules;
|
||||
inherit inventoryFile;
|
||||
|
||||
@@ -16,5 +16,8 @@ in
|
||||
facts = import ./facts.nix { inherit lib; };
|
||||
inventory = import ./inventory { inherit lib clan-core; };
|
||||
jsonschema = import ./jsonschema { inherit lib; };
|
||||
modules = import ./frontmatter { inherit lib; };
|
||||
modules = import ./frontmatter {
|
||||
inherit lib;
|
||||
self = clan-core;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
{ lib }:
|
||||
{ lib, self }:
|
||||
let
|
||||
# Trim the .nix extension from a filename
|
||||
trimExtension = name: builtins.substring 0 (builtins.stringLength name - 4) name;
|
||||
|
||||
jsonWithoutHeader = self.lib.jsonschema {
|
||||
includeDefaults = true;
|
||||
header = { };
|
||||
};
|
||||
|
||||
getModulesSchema =
|
||||
modules:
|
||||
lib.mapAttrs (
|
||||
_moduleName: rolesOptions:
|
||||
lib.mapAttrs (_roleName: options: jsonWithoutHeader.parseOptions options { }) rolesOptions
|
||||
) (self.lib.evalClanModulesWithRoles modules);
|
||||
|
||||
evalFrontmatter =
|
||||
{
|
||||
moduleName,
|
||||
@@ -90,7 +102,7 @@ in
|
||||
{
|
||||
inherit
|
||||
frontmatterOptions
|
||||
|
||||
getModulesSchema
|
||||
getFrontmatter
|
||||
|
||||
checkConstraints
|
||||
|
||||
@@ -2,25 +2,14 @@
|
||||
self,
|
||||
self',
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
includeDefaults = true;
|
||||
|
||||
# { mName :: { roleName :: Options } }
|
||||
modulesRolesOptions = self.lib.evalClanModulesWithRoles self.clanModules;
|
||||
modulesSchema = lib.mapAttrs (
|
||||
_moduleName: rolesOptions:
|
||||
lib.mapAttrs (_roleName: options: jsonWithoutHeader.parseOptions options { }) rolesOptions
|
||||
) modulesRolesOptions;
|
||||
modulesSchema = self.lib.modules.getModulesSchema self.clanModules;
|
||||
|
||||
jsonLib = self.lib.jsonschema { inherit includeDefaults; };
|
||||
|
||||
jsonWithoutHeader = self.lib.jsonschema {
|
||||
inherit includeDefaults;
|
||||
header = { };
|
||||
};
|
||||
includeDefaults = true;
|
||||
|
||||
frontMatterSchema = jsonLib.parseOptions self.lib.modules.frontmatterOptions { };
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ class ModuleInfo:
|
||||
def get_modules(base_path: str) -> dict[str, str]:
|
||||
cmd = nix_eval(
|
||||
[
|
||||
f"{base_path}#clanInternals.clanModules",
|
||||
f"{base_path}#clanInternals.inventory.modules",
|
||||
"--json",
|
||||
]
|
||||
)
|
||||
@@ -153,11 +153,11 @@ def get_modules(base_path: str) -> dict[str, str]:
|
||||
proc = run_no_stdout(cmd)
|
||||
res = proc.stdout.strip()
|
||||
except ClanCmdError as e:
|
||||
msg = "clanInternals might not have clanModules attributes"
|
||||
msg = "clanInternals might not have inventory.modules attributes"
|
||||
raise ClanError(
|
||||
msg,
|
||||
location=f"list_modules {base_path}",
|
||||
description="Evaluation failed on clanInternals.clanModules attribute",
|
||||
description="Evaluation failed on clanInternals.inventory.modules attribute",
|
||||
) from e
|
||||
modules: dict[str, str] = json.loads(res)
|
||||
return modules
|
||||
|
||||
Reference in New Issue
Block a user