Merge pull request 'Init: inventory schema for role dependant interfaces' (#2225) from hsjobeki/clan-core:hsjobeki-main into main
This commit is contained in:
@@ -97,6 +97,18 @@ in
|
|||||||
cp schema.json $out
|
cp schema.json $out
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
legacyPackages.schemas = (
|
||||||
|
import ./schemas {
|
||||||
|
inherit
|
||||||
|
pkgs
|
||||||
|
self
|
||||||
|
lib
|
||||||
|
self'
|
||||||
|
;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
packages.inventory-schema-pretty = pkgs.stdenv.mkDerivation {
|
packages.inventory-schema-pretty = pkgs.stdenv.mkDerivation {
|
||||||
name = "inventory-schema-pretty";
|
name = "inventory-schema-pretty";
|
||||||
buildInputs = [ pkgs.cue ];
|
buildInputs = [ pkgs.cue ];
|
||||||
|
|||||||
52
lib/inventory/schemas/default.nix
Normal file
52
lib/inventory/schemas/default.nix
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
jsonLib = self.lib.jsonschema { inherit includeDefaults; };
|
||||||
|
|
||||||
|
jsonWithoutHeader = self.lib.jsonschema {
|
||||||
|
inherit includeDefaults;
|
||||||
|
header = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
inventorySchema = jsonLib.parseModule (import ../build-inventory/interface.nix);
|
||||||
|
renderSchema = pkgs.writers.writePython3Bin "render-schema" {
|
||||||
|
flakeIgnore = [
|
||||||
|
"F401"
|
||||||
|
"E501"
|
||||||
|
];
|
||||||
|
} ./render_schema.py;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit inventorySchema modulesSchema renderSchema;
|
||||||
|
|
||||||
|
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
|
||||||
|
'';
|
||||||
|
}
|
||||||
163
lib/inventory/schemas/render_schema.py
Normal file
163
lib/inventory/schemas/render_schema.py
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
"""
|
||||||
|
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_cli.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: list[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
|
||||||
|
"""
|
||||||
|
role_schema = {}
|
||||||
|
for role in roles:
|
||||||
|
role_schema[role] = {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": False,
|
||||||
|
"properties": {
|
||||||
|
**orig["roles"]["additionalProperties"]["properties"],
|
||||||
|
"config": {
|
||||||
|
"title": f"{service_name}-config-role-{role}",
|
||||||
|
"oneOf": roles_schemas,
|
||||||
|
"type": "object",
|
||||||
|
"default": {},
|
||||||
|
"additionalProperties": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
machines_schema = {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
**orig["machines"]["additionalProperties"]["properties"],
|
||||||
|
"config": {
|
||||||
|
"title": f"{service_name}-config",
|
||||||
|
"oneOf": roles_schemas,
|
||||||
|
"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": roles_schemas,
|
||||||
|
"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_schema in modules_schema.items():
|
||||||
|
# collect all the roles for the service
|
||||||
|
roles_schemas = []
|
||||||
|
for role_name, role_schema in roles_schema.items():
|
||||||
|
role_schema["title"] = f"{module_name}-config-role-{role_name}"
|
||||||
|
roles_schemas.append(role_schema)
|
||||||
|
|
||||||
|
# Add the roles schemas to the service schema
|
||||||
|
if roles_schemas:
|
||||||
|
roles = list(roles_schema.keys())
|
||||||
|
|
||||||
|
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)
|
||||||
Reference in New Issue
Block a user