From e236dba1c9bbbe2179cce4f5df89877c2352d455 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Wed, 17 Jul 2024 10:06:46 +0200 Subject: [PATCH] Inventory: extend python dataclasses by schema changes --- inventory.json | 69 +++++++++++++------- lib/inventory/flake-module.nix | 17 +++-- lib/inventory/interface-to-schema.nix | 37 +++++++++-- pkgs/clan-cli/clan_cli/inventory/__init__.py | 7 ++ 4 files changed, 97 insertions(+), 33 deletions(-) diff --git a/inventory.json b/inventory.json index fae406ab1..021f9ab05 100644 --- a/inventory.json +++ b/inventory.json @@ -1,16 +1,18 @@ { "meta": { - "name": "clan-core" + "name": "clan-core", + "description": null, + "icon": null }, "machines": { - "minimal-inventory-machine": { + "test-inventory-machine": { "name": "foo", - "system": "x86_64-linux", "description": "A nice thing", "icon": "./path/to/icon.png", "tags": ["1", "2", "3"], - "deploymentInfo": { - "targetHost": "root@remote.com" + "system": "x86_64-linux", + "deployment_info": { + "target_host": "root@remote.com" } } }, @@ -18,65 +20,82 @@ "packages": { "editors": { "meta": { - "name": "Some editor packages" + "name": "Some editor packages", + "description": null, + "icon": null }, "roles": { "default": { - "machines": ["minimal-inventory-machine"], + "machines": ["test-inventory-machine"], + "tags": [], "config": { "packages": ["vim"] - } + }, + "imports": [] } }, "machines": { - "minimal-inventory-machine": { + "test-inventory-machine": { "config": { "packages": ["zed-editor"] - } + }, + "imports": [] } }, - "config": { - "packages": ["vim"] - } + "config": null, + "imports": [] }, "browsing": { "meta": { - "name": "Web browsing packages" + "name": "Web browsing packages", + "description": null, + "icon": null }, "roles": { "default": { - "machines": ["minimal-inventory-machine"] + "machines": ["test-inventory-machine"], + "tags": [], + "config": null, + "imports": [] } }, "machines": { - "minimal-inventory-machine": { + "test-inventory-machine": { "config": { "packages": ["chromium"] - } + }, + "imports": [] } }, - "config": { - "packages": ["firefox"] - } + "config": null, + "imports": [] } }, "single-disk": { "default": { "meta": { - "name": "single-disk" + "name": "single-disk", + "description": null, + "icon": null }, "roles": { "default": { - "machines": ["minimal-inventory-machine"] + "machines": ["test-inventory-machine"], + "tags": [], + "config": null, + "imports": [] } }, "machines": { - "minimal-inventory-machine": { + "test-inventory-machine": { "config": { "device": "/dev/null" - } + }, + "imports": [] } - } + }, + "config": null, + "imports": [] } } } diff --git a/lib/inventory/flake-module.nix b/lib/inventory/flake-module.nix index 5ca9385cc..07ad18f5c 100644 --- a/lib/inventory/flake-module.nix +++ b/lib/inventory/flake-module.nix @@ -23,10 +23,19 @@ in }; getSchema = import ./interface-to-schema.nix { inherit lib self; }; + + # The schema for the inventory, without default values, from the module system. + # This is better suited for human reading and for generating code. + bareSchema = getSchema { includeDefaults = false; }; + # The schema for the inventory with default values, from the module system. + # This is better suited for validation, since default values are included. + fullSchema = getSchema { }; in { - legacyPackages.inventorySchema = getSchema { }; - legacyPackages.inventorySchemaPretty = getSchema { includeDefaults = false; }; + legacyPackages.inventory = { + inherit fullSchema; + inherit bareSchema; + }; devShells.inventory-schema = pkgs.mkShell { inputsFrom = with config.checks; [ @@ -42,7 +51,7 @@ in buildInputs = [ pkgs.cue ]; src = ./.; buildPhase = '' - export SCHEMA=${builtins.toFile "inventory-schema.json" (builtins.toJSON self'.legacyPackages.inventorySchema)} + export SCHEMA=${builtins.toFile "inventory-schema.json" (builtins.toJSON fullSchema.schemaWithModules)} cp $SCHEMA schema.json cue import -f -p compose -l '#Root:' schema.json mkdir $out @@ -55,7 +64,7 @@ in buildInputs = [ pkgs.cue ]; src = ./.; buildPhase = '' - export SCHEMA=${builtins.toFile "inventory-schema.json" (builtins.toJSON self'.legacyPackages.inventorySchemaPretty)} + export SCHEMA=${builtins.toFile "inventory-schema.json" (builtins.toJSON bareSchema.schemaWithModules)} cp $SCHEMA schema.json cue import -f -p compose -l '#Root:' schema.json mkdir $out diff --git a/lib/inventory/interface-to-schema.nix b/lib/inventory/interface-to-schema.nix index 0daa5c639..b9ee02808 100644 --- a/lib/inventory/interface-to-schema.nix +++ b/lib/inventory/interface-to-schema.nix @@ -53,7 +53,9 @@ let properties = { meta = inventorySchema.properties.services.additionalProperties.additionalProperties.properties.meta; - config = moduleSchema; + config = { + title = "${moduleName}-config"; + } // moduleSchema; roles = { type = "object"; additionalProperties = false; @@ -62,14 +64,24 @@ let map (role: { name = role; value = - inventorySchema.properties.services.additionalProperties.additionalProperties.properties.roles.additionalProperties; + lib.recursiveUpdate + inventorySchema.properties.services.additionalProperties.additionalProperties.properties.roles.additionalProperties + { + properties.config = { + title = "${moduleName}-config"; + } // moduleSchema; + }; }) (rolesOf moduleName) ); }; machines = lib.recursiveUpdate inventorySchema.properties.services.additionalProperties.additionalProperties.properties.machines - { additionalProperties.properties.config = moduleSchema; }; + { + additionalProperties.properties.config = { + title = "${moduleName}-config"; + } // moduleSchema; + }; }; }; }; @@ -95,4 +107,21 @@ let }; }; in -schema +{ + /* + The abstract inventory without the exact schema for each module filled + + InventorySchema :: { + serviceConfig :: dict[str, T]; + } + */ + abstractSchema = inventorySchema; + /* + The inventory with each module schema filled. + + InventorySchema :: { + ${serviceConfig} :: T; # <- each concrete module name is filled + } + */ + schemaWithModules = schema; +} diff --git a/pkgs/clan-cli/clan_cli/inventory/__init__.py b/pkgs/clan-cli/clan_cli/inventory/__init__.py index bb621e1d5..021efc70d 100644 --- a/pkgs/clan-cli/clan_cli/inventory/__init__.py +++ b/pkgs/clan-cli/clan_cli/inventory/__init__.py @@ -82,6 +82,7 @@ class Machine: @dataclass class MachineServiceConfig: config: dict[str, Any] | None = None + imports: list[str] = field(default_factory=list) @dataclass @@ -93,6 +94,8 @@ class ServiceMeta: @dataclass class Role: + config: dict[str, Any] | None = None + imports: list[str] = field(default_factory=list) machines: list[str] = field(default_factory=list) tags: list[str] = field(default_factory=list) @@ -101,6 +104,8 @@ class Role: class Service: meta: ServiceMeta roles: dict[str, Role] + config: dict[str, Any] | None = None + imports: list[str] = field(default_factory=list) machines: dict[str, MachineServiceConfig] = field(default_factory=dict) @staticmethod @@ -116,6 +121,8 @@ class Service: if d.get("machines") else {} ), + config=d.get("config", None), + imports=d.get("imports", []), )