From 679fd914e03b94ecdeeaf8129a8162f72ce3e6d6 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Tue, 23 Jul 2024 11:31:34 +0200 Subject: [PATCH] Inventory: add load_eval method --- pkgs/clan-cli/clan_cli/api/modules.py | 4 +- pkgs/clan-cli/clan_cli/clan/create.py | 4 +- pkgs/clan-cli/clan_cli/clan/update.py | 4 +- pkgs/clan-cli/clan_cli/inventory/__init__.py | 46 +++++++++++++++++++- pkgs/clan-cli/clan_cli/machines/delete.py | 4 +- pkgs/clan-cli/clan_cli/machines/list.py | 20 ++------- pkgs/clan-cli/tests/test_modules.py | 4 +- 7 files changed, 58 insertions(+), 28 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/api/modules.py b/pkgs/clan-cli/clan_cli/api/modules.py index 6c02e8ac2..290689bf4 100644 --- a/pkgs/clan-cli/clan_cli/api/modules.py +++ b/pkgs/clan-cli/clan_cli/api/modules.py @@ -6,7 +6,7 @@ from pathlib import Path from clan_cli.cmd import run_no_stdout from clan_cli.errors import ClanCmdError, ClanError -from clan_cli.inventory import Inventory, load_inventory +from clan_cli.inventory import Inventory, load_inventory_json from clan_cli.nix import nix_eval from . import API @@ -152,4 +152,4 @@ def get_module_info( @API.register def get_inventory(base_path: str) -> Inventory: - return load_inventory(base_path) + return load_inventory_json(base_path) diff --git a/pkgs/clan-cli/clan_cli/clan/create.py b/pkgs/clan-cli/clan_cli/clan/create.py index 75921131d..a669e4a95 100644 --- a/pkgs/clan-cli/clan_cli/clan/create.py +++ b/pkgs/clan-cli/clan_cli/clan/create.py @@ -6,7 +6,7 @@ from pathlib import Path from clan_cli.api import API from clan_cli.arg_actions import AppendOptionAction -from clan_cli.inventory import Meta, load_inventory, save_inventory +from clan_cli.inventory import Meta, load_inventory_json, save_inventory from ..cmd import CmdOut, run from ..errors import ClanError @@ -89,7 +89,7 @@ def create_clan(options: CreateOptions) -> CreateClanResponse: ) # Write inventory.json file - inventory = load_inventory(directory) + inventory = load_inventory_json(directory) if options.meta is not None: inventory.meta = options.meta # Persist creates a commit message for each change diff --git a/pkgs/clan-cli/clan_cli/clan/update.py b/pkgs/clan-cli/clan_cli/clan/update.py index 595b7f125..65ff096f6 100644 --- a/pkgs/clan-cli/clan_cli/clan/update.py +++ b/pkgs/clan-cli/clan_cli/clan/update.py @@ -1,7 +1,7 @@ from dataclasses import dataclass from clan_cli.api import API -from clan_cli.inventory import Meta, load_inventory, save_inventory +from clan_cli.inventory import Meta, load_inventory_json, save_inventory @dataclass @@ -12,7 +12,7 @@ class UpdateOptions: @API.register def update_clan_meta(options: UpdateOptions) -> Meta: - inventory = load_inventory(options.directory) + inventory = load_inventory_json(options.directory) inventory.meta = options.meta save_inventory(inventory, options.directory, "Update clan metadata") diff --git a/pkgs/clan-cli/clan_cli/inventory/__init__.py b/pkgs/clan-cli/clan_cli/inventory/__init__.py index 31ffecc00..0f409316d 100644 --- a/pkgs/clan-cli/clan_cli/inventory/__init__.py +++ b/pkgs/clan-cli/clan_cli/inventory/__init__.py @@ -1,3 +1,17 @@ +""" +All read/write operations MUST use the inventory. + +Machine data, clan data or service data can be accessed in a performant way. + +This file exports stable classnames for static & dynamic type safety. + +Utilize: + +- load_inventory_eval: To load the actual inventory with nix declarations merged. +Operate on the returned inventory to make changes +- save_inventory: To persist changes. +""" + import dataclasses import json from dataclasses import fields, is_dataclass @@ -8,6 +22,8 @@ from typing import Any, get_args, get_origin from clan_cli.errors import ClanError from clan_cli.git import commit_file +from ..cmd import run_no_stdout +from ..nix import nix_eval from .classes import ( Inventory, Machine, @@ -165,7 +181,35 @@ default_inventory = Inventory( ) -def load_inventory( +def load_inventory_eval(flake_dir: str | Path) -> Inventory: + """ + Loads the actual inventory. + After all merge operations with eventual nix code in buildClan. + + Evaluates clanInternals.inventory with nix. Which is performant. + + - Contains all clan metadata + - Contains all machines + - and more + """ + cmd = nix_eval( + [ + f"{flake_dir}#clanInternals.inventory", + "--json", + ] + ) + proc = run_no_stdout(cmd) + + try: + res = proc.stdout.strip() + data = json.loads(res) + inventory = from_dict(Inventory, data) + return inventory + except json.JSONDecodeError as e: + raise ClanError(f"Error decoding inventory from flake: {e}") + + +def load_inventory_json( flake_dir: str | Path, default: Inventory = default_inventory ) -> Inventory: """ diff --git a/pkgs/clan-cli/clan_cli/machines/delete.py b/pkgs/clan-cli/clan_cli/machines/delete.py index e9d64e785..228dba9f1 100644 --- a/pkgs/clan-cli/clan_cli/machines/delete.py +++ b/pkgs/clan-cli/clan_cli/machines/delete.py @@ -6,12 +6,12 @@ from ..clan_uri import FlakeId from ..completions import add_dynamic_completer, complete_machines from ..dirs import specific_machine_dir from ..errors import ClanError -from ..inventory import load_inventory, save_inventory +from ..inventory import load_inventory_json, save_inventory @API.register def delete_machine(flake: FlakeId, name: str) -> None: - inventory = load_inventory(flake.path) + inventory = load_inventory_json(flake.path) machine = inventory.machines.pop(name, None) if machine is None: diff --git a/pkgs/clan-cli/clan_cli/machines/list.py b/pkgs/clan-cli/clan_cli/machines/list.py index e2943cf57..f8df311d5 100644 --- a/pkgs/clan-cli/clan_cli/machines/list.py +++ b/pkgs/clan-cli/clan_cli/machines/list.py @@ -1,31 +1,17 @@ import argparse -import json import logging from pathlib import Path from clan_cli.api import API -from clan_cli.inventory import Machine, from_dict - -from ..cmd import run_no_stdout -from ..nix import nix_eval +from clan_cli.inventory import Machine, load_inventory_eval log = logging.getLogger(__name__) @API.register def list_machines(flake_url: str | Path, debug: bool = False) -> dict[str, Machine]: - cmd = nix_eval( - [ - f"{flake_url}#clanInternals.inventory.machines", - "--json", - ] - ) - - proc = run_no_stdout(cmd) - - res = proc.stdout.strip() - data = {name: from_dict(Machine, v) for name, v in json.loads(res).items()} - return data + inventory = load_inventory_eval(flake_url) + return inventory.machines def list_command(args: argparse.Namespace) -> None: diff --git a/pkgs/clan-cli/tests/test_modules.py b/pkgs/clan-cli/tests/test_modules.py index b637ba147..4b45f7a6f 100644 --- a/pkgs/clan-cli/tests/test_modules.py +++ b/pkgs/clan-cli/tests/test_modules.py @@ -14,7 +14,7 @@ from clan_cli.inventory import ( ServiceBorgbackupRoleClient, ServiceBorgbackupRoleServer, ServiceMeta, - load_inventory, + load_inventory_json, save_inventory, ) from clan_cli.machines.create import create_machine @@ -67,7 +67,7 @@ def test_add_module_to_inventory( ), ) - inventory = load_inventory(base_path) + inventory = load_inventory_json(base_path) inventory.services.borgbackup = { "borg1": ServiceBorgbackup(