From 1037bd115b53a3aaa49b57b2d32cd523c05bb8f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 7 May 2025 15:24:57 +0200 Subject: [PATCH] Revert "Merge pull request 'clan-cli: Refactor the API to use the Flake object' (#3531) from Qubasa/clan-core:replace_machine_name_with_machine_obj into main" This reverts commit c09618b3235d2bdad635bdb80feac21cdea126c9, reversing changes made to 45b2539455499a3d620b893e92dcf7cdec60e921. --- pkgs/clan-cli/clan_cli/clan/create.py | 2 +- pkgs/clan-cli/clan_cli/clan/inspect.py | 2 +- pkgs/clan-cli/clan_cli/clan/update.py | 7 +-- pkgs/clan-cli/clan_cli/clan_dirs.py | 43 -------------- pkgs/clan-cli/clan_cli/dirs.py | 30 ++++++++++ .../clan_cli/facts/public_modules/vm.py | 2 +- .../clan_cli/facts/secret_modules/vm.py | 2 +- pkgs/clan-cli/clan_cli/inventory/__init__.py | 57 +++++++++---------- pkgs/clan-cli/clan_cli/machines/create.py | 2 +- pkgs/clan-cli/clan_cli/machines/delete.py | 6 +- pkgs/clan-cli/clan_cli/machines/hardware.py | 24 ++++---- pkgs/clan-cli/clan_cli/machines/install.py | 2 +- pkgs/clan-cli/clan_cli/machines/list.py | 32 +++++------ pkgs/clan-cli/clan_cli/tests/test_dirs.py | 2 +- .../clan_cli/tests/test_machines_cli.py | 5 +- pkgs/clan-cli/clan_cli/tests/test_modules.py | 2 +- .../clan_cli/vars/public_modules/vm.py | 2 +- .../clan_cli/vars/secret_modules/vm.py | 2 +- pkgs/clan-cli/clan_cli/vms/run.py | 3 +- pkgs/clan-cli/clan_lib/api/disk.py | 18 +++--- pkgs/clan-cli/clan_lib/tests/test_create.py | 13 +++-- .../clan_vm_manager/components/vmobj.py | 2 +- pkgs/webview-ui/app/src/api/inventory.ts | 6 +- pkgs/webview-ui/app/src/api/wifi.ts | 2 +- pkgs/webview-ui/app/src/queries/index.ts | 4 +- .../app/src/routes/clans/details.tsx | 2 +- pkgs/webview-ui/app/src/routes/disk/view.tsx | 4 +- .../app/src/routes/machines/details.tsx | 14 ++--- .../src/routes/machines/install/disk-step.tsx | 4 +- .../routes/machines/install/hardware-step.tsx | 4 +- .../app/src/routes/machines/list.tsx | 4 +- 31 files changed, 136 insertions(+), 168 deletions(-) delete mode 100644 pkgs/clan-cli/clan_cli/clan_dirs.py diff --git a/pkgs/clan-cli/clan_cli/clan/create.py b/pkgs/clan-cli/clan_cli/clan/create.py index fc63a6574..861206790 100644 --- a/pkgs/clan-cli/clan_cli/clan/create.py +++ b/pkgs/clan-cli/clan_cli/clan/create.py @@ -107,7 +107,7 @@ def create_clan(opts: CreateOptions) -> CreateClanResponse: response.flake_update = flake_update if opts.initial: - init_inventory(Flake(str(opts.dest)), init=opts.initial) + init_inventory(str(opts.dest), init=opts.initial) return response diff --git a/pkgs/clan-cli/clan_cli/clan/inspect.py b/pkgs/clan-cli/clan_cli/clan/inspect.py index 92ef3e8ca..32e36299d 100644 --- a/pkgs/clan-cli/clan_cli/clan/inspect.py +++ b/pkgs/clan-cli/clan_cli/clan/inspect.py @@ -3,8 +3,8 @@ from dataclasses import dataclass from pathlib import Path from typing import Any -from clan_cli.clan_dirs import machine_gcroot from clan_cli.cmd import run +from clan_cli.dirs import machine_gcroot from clan_cli.errors import ClanError from clan_cli.flake import Flake from clan_cli.machines.list import list_nixos_machines diff --git a/pkgs/clan-cli/clan_cli/clan/update.py b/pkgs/clan-cli/clan_cli/clan/update.py index b1d87cf01..4955ac0cc 100644 --- a/pkgs/clan-cli/clan_cli/clan/update.py +++ b/pkgs/clan-cli/clan_cli/clan/update.py @@ -2,21 +2,20 @@ from dataclasses import dataclass from clan_lib.api import API -from clan_cli.flake import Flake from clan_cli.inventory import Inventory, Meta, load_inventory_json, set_inventory @dataclass class UpdateOptions: - flake: Flake + directory: str meta: Meta @API.register def update_clan_meta(options: UpdateOptions) -> Inventory: - inventory = load_inventory_json(options.flake) + inventory = load_inventory_json(options.directory) inventory["meta"] = options.meta - set_inventory(inventory, options.flake, "Update clan metadata") + set_inventory(inventory, options.directory, "Update clan metadata") return inventory diff --git a/pkgs/clan-cli/clan_cli/clan_dirs.py b/pkgs/clan-cli/clan_cli/clan_dirs.py deleted file mode 100644 index c2b05deeb..000000000 --- a/pkgs/clan-cli/clan_cli/clan_dirs.py +++ /dev/null @@ -1,43 +0,0 @@ -import logging -import urllib -from pathlib import Path - -from clan_cli.flake import Flake - -from .dirs import user_data_dir, user_gcroot_dir - -log = logging.getLogger(__name__) - - -def clan_key_safe(flake_url: str) -> str: - """ - only embed the url in the path, not the clan name, as it would involve eval. - """ - quoted_url = urllib.parse.quote_plus(flake_url) - return f"{quoted_url}" - - -def machine_gcroot(flake_url: str) -> Path: - # Always build icon so that we can symlink it to the gcroot - gcroot_dir = user_gcroot_dir() - clan_gcroot = gcroot_dir / clan_key_safe(flake_url) - clan_gcroot.mkdir(parents=True, exist_ok=True) - return clan_gcroot - - -def vm_state_dir(flake_url: str, vm_name: str) -> Path: - clan_key = clan_key_safe(str(flake_url)) - return user_data_dir() / "clan" / "vmstate" / clan_key / vm_name - - -def machines_dir(flake: Flake) -> Path: - if flake.is_local: - return flake.path / "machines" - - store_path = flake.store_path - assert store_path is not None, "Invalid flake object" - return Path(store_path) / "machines" - - -def specific_machine_dir(flake: Flake, machine: str) -> Path: - return machines_dir(flake) / machine diff --git a/pkgs/clan-cli/clan_cli/dirs.py b/pkgs/clan-cli/clan_cli/dirs.py index 8ff3e94e1..defe64fe5 100644 --- a/pkgs/clan-cli/clan_cli/dirs.py +++ b/pkgs/clan-cli/clan_cli/dirs.py @@ -1,6 +1,7 @@ import logging import os import sys +import urllib from enum import Enum from pathlib import Path @@ -23,6 +24,14 @@ def find_git_repo_root() -> Path | None: return find_toplevel([".git"]) +def clan_key_safe(flake_url: str) -> str: + """ + only embed the url in the path, not the clan name, as it would involve eval. + """ + quoted_url = urllib.parse.quote_plus(flake_url) + return f"{quoted_url}" + + def find_toplevel(top_level_files: list[str]) -> Path | None: """Returns the path to the toplevel of the clan flake""" for project_file in top_level_files: @@ -105,10 +114,31 @@ def user_gcroot_dir() -> Path: return p +def machine_gcroot(flake_url: str) -> Path: + # Always build icon so that we can symlink it to the gcroot + gcroot_dir = user_gcroot_dir() + clan_gcroot = gcroot_dir / clan_key_safe(flake_url) + clan_gcroot.mkdir(parents=True, exist_ok=True) + return clan_gcroot + + def user_history_file() -> Path: return user_config_dir() / "clan" / "history" +def vm_state_dir(flake_url: str, vm_name: str) -> Path: + clan_key = clan_key_safe(str(flake_url)) + return user_data_dir() / "clan" / "vmstate" / clan_key / vm_name + + +def machines_dir(flake_dir: Path) -> Path: + return flake_dir / "machines" + + +def specific_machine_dir(flake_dir: Path, machine: str) -> Path: + return machines_dir(flake_dir) / machine + + def module_root() -> Path: return Path(__file__).parent diff --git a/pkgs/clan-cli/clan_cli/facts/public_modules/vm.py b/pkgs/clan-cli/clan_cli/facts/public_modules/vm.py index 7bfe43184..8788a199d 100644 --- a/pkgs/clan-cli/clan_cli/facts/public_modules/vm.py +++ b/pkgs/clan-cli/clan_cli/facts/public_modules/vm.py @@ -1,7 +1,7 @@ import logging from pathlib import Path -from clan_cli.clan_dirs import vm_state_dir +from clan_cli.dirs import vm_state_dir from clan_cli.errors import ClanError from clan_cli.machines.machines import Machine diff --git a/pkgs/clan-cli/clan_cli/facts/secret_modules/vm.py b/pkgs/clan-cli/clan_cli/facts/secret_modules/vm.py index f153e78a4..29209536e 100644 --- a/pkgs/clan-cli/clan_cli/facts/secret_modules/vm.py +++ b/pkgs/clan-cli/clan_cli/facts/secret_modules/vm.py @@ -2,7 +2,7 @@ import shutil from pathlib import Path from typing import override -from clan_cli.clan_dirs import vm_state_dir +from clan_cli.dirs import vm_state_dir from clan_cli.machines.machines import Machine from . import SecretStoreBase diff --git a/pkgs/clan-cli/clan_cli/inventory/__init__.py b/pkgs/clan-cli/clan_cli/inventory/__init__.py index d5a1a6135..fe3d0715f 100644 --- a/pkgs/clan-cli/clan_cli/inventory/__init__.py +++ b/pkgs/clan-cli/clan_cli/inventory/__init__.py @@ -23,7 +23,6 @@ from clan_lib.api import API, dataclass_to_dict, from_dict from clan_cli.cmd import run_no_stdout from clan_cli.errors import ClanCmdError, ClanError -from clan_cli.flake import Flake from clan_cli.git import commit_file from clan_cli.nix import nix_eval @@ -50,11 +49,11 @@ __all__ = [ ] -def get_inventory_path(flake: Flake) -> Path: +def get_inventory_path(flake_dir: str | Path) -> Path: """ Get the path to the inventory file in the flake directory """ - inventory_file = (flake.path / "inventory.json").resolve() + inventory_file = (Path(flake_dir) / "inventory.json").resolve() return inventory_file @@ -62,7 +61,7 @@ def get_inventory_path(flake: Flake) -> Path: default_inventory: Inventory = {"meta": {"name": "New Clan"}} -def load_inventory_eval(flake_dir: Flake) -> Inventory: +def load_inventory_eval(flake_dir: str | Path) -> Inventory: """ Loads the evaluated inventory. After all merge operations with eventual nix code in buildClan. @@ -355,7 +354,7 @@ def determine_writeability( return results -def get_inventory_current_priority(flake: Flake) -> dict: +def get_inventory_current_priority(flake_dir: str | Path) -> dict: """ Returns the current priority of the inventory values @@ -375,7 +374,7 @@ def get_inventory_current_priority(flake: Flake) -> dict: """ cmd = nix_eval( [ - f"{flake}#clanInternals.inventoryClass.introspection", + f"{flake_dir}#clanInternals.inventoryClass.introspection", "--json", ] ) @@ -393,7 +392,7 @@ def get_inventory_current_priority(flake: Flake) -> dict: @API.register -def load_inventory_json(flake: Flake) -> Inventory: +def load_inventory_json(flake_dir: str | Path) -> Inventory: """ Load the inventory FILE from the flake directory If no file is found, returns an empty dictionary @@ -403,7 +402,7 @@ def load_inventory_json(flake: Flake) -> Inventory: Use load_inventory_eval instead """ - inventory_file = get_inventory_path(flake) + inventory_file = get_inventory_path(flake_dir) if not inventory_file.exists(): return {} @@ -473,14 +472,14 @@ def patch(d: dict[str, Any], path: str, content: Any) -> None: @API.register -def patch_inventory_with(flake: Flake, section: str, content: dict[str, Any]) -> None: +def patch_inventory_with(base_dir: Path, section: str, content: dict[str, Any]) -> None: """ Pass only the section to update and the content to update with. Make sure you pass only attributes that you would like to persist. ATTENTION: Don't pass nix eval values unintentionally. """ - inventory_file = get_inventory_path(flake) + inventory_file = get_inventory_path(base_dir) curr_inventory = {} if inventory_file.exists(): @@ -492,9 +491,7 @@ def patch_inventory_with(flake: Flake, section: str, content: dict[str, Any]) -> with inventory_file.open("w") as f: json.dump(curr_inventory, f, indent=2) - commit_file( - inventory_file, flake.path, commit_message=f"inventory.{section}: Update" - ) + commit_file(inventory_file, base_dir, commit_message=f"inventory.{section}: Update") @dataclass @@ -506,16 +503,16 @@ class WriteInfo: @API.register def get_inventory_with_writeable_keys( - flake: Flake, + flake_dir: str | Path, ) -> WriteInfo: """ Load the inventory and determine the writeable keys Performs 2 nix evaluations to get the current priority and the inventory """ - current_priority = get_inventory_current_priority(flake) + current_priority = get_inventory_current_priority(flake_dir) - data_eval: Inventory = load_inventory_eval(flake) - data_disk: Inventory = load_inventory_json(flake) + data_eval: Inventory = load_inventory_eval(flake_dir) + data_disk: Inventory = load_inventory_json(flake_dir) writeables = determine_writeability( current_priority, dict(data_eval), dict(data_disk) @@ -527,14 +524,14 @@ def get_inventory_with_writeable_keys( # TODO: remove this function in favor of a proper read/write API @API.register def set_inventory( - inventory: Inventory, flake: Flake, message: str, commit: bool = True + inventory: Inventory, flake_dir: str | Path, message: str, commit: bool = True ) -> None: """ Write the inventory to the flake directory and commit it to git with the given message """ - write_info = get_inventory_with_writeable_keys(flake) + write_info = get_inventory_with_writeable_keys(flake_dir) # Remove internals from the inventory inventory.pop("tags", None) # type: ignore @@ -555,43 +552,43 @@ def set_inventory( for delete_path in delete_set: delete_by_path(persisted, delete_path) - inventory_file = get_inventory_path(flake) + inventory_file = get_inventory_path(flake_dir) with inventory_file.open("w") as f: json.dump(persisted, f, indent=2) if commit: - commit_file(inventory_file, flake.path, commit_message=message) + commit_file(inventory_file, Path(flake_dir), commit_message=message) # TODO: wrap this in a proper persistence API -def delete(flake: Flake, delete_set: set[str]) -> None: +def delete(directory: str | Path, delete_set: set[str]) -> None: """ Delete keys from the inventory """ - write_info = get_inventory_with_writeable_keys(flake) + write_info = get_inventory_with_writeable_keys(directory) data_disk = dict(write_info.data_disk) for delete_path in delete_set: delete_by_path(data_disk, delete_path) - inventory_file = get_inventory_path(flake) + inventory_file = get_inventory_path(directory) with inventory_file.open("w") as f: json.dump(data_disk, f, indent=2) commit_file( inventory_file, - flake.path, + Path(directory), commit_message=f"Delete inventory keys {delete_set}", ) -def init_inventory(flake: Flake, init: Inventory | None = None) -> None: +def init_inventory(directory: str, init: Inventory | None = None) -> None: inventory = None # Try reading the current flake if init is None: with contextlib.suppress(ClanCmdError): - inventory = load_inventory_eval(flake) + inventory = load_inventory_eval(directory) if init is not None: inventory = init @@ -599,9 +596,9 @@ def init_inventory(flake: Flake, init: Inventory | None = None) -> None: # Write inventory.json file if inventory is not None: # Persist creates a commit message for each change - set_inventory(inventory, flake, "Init inventory") + set_inventory(inventory, directory, "Init inventory") @API.register -def get_inventory(flake: Flake) -> Inventory: - return load_inventory_eval(flake) +def get_inventory(base_path: str | Path) -> Inventory: + return load_inventory_eval(base_path) diff --git a/pkgs/clan-cli/clan_cli/machines/create.py b/pkgs/clan-cli/clan_cli/machines/create.py index 3f9ef22fd..8ea74c9ad 100644 --- a/pkgs/clan-cli/clan_cli/machines/create.py +++ b/pkgs/clan-cli/clan_cli/machines/create.py @@ -110,7 +110,7 @@ def create_machine(opts: CreateOptions, commit: bool = True) -> None: new_machine["deploy"] = {"targetHost": target_host} patch_inventory_with( - Flake(str(clan_dir)), f"machines.{machine_name}", dataclass_to_dict(new_machine) + clan_dir, f"machines.{machine_name}", dataclass_to_dict(new_machine) ) # Commit at the end in that order to avoid committing halve-baked machines diff --git a/pkgs/clan-cli/clan_cli/machines/delete.py b/pkgs/clan-cli/clan_cli/machines/delete.py index 96b721b7e..4f0698798 100644 --- a/pkgs/clan-cli/clan_cli/machines/delete.py +++ b/pkgs/clan-cli/clan_cli/machines/delete.py @@ -6,8 +6,8 @@ from pathlib import Path from clan_lib.api import API from clan_cli import Flake, inventory -from clan_cli.clan_dirs import specific_machine_dir from clan_cli.completions import add_dynamic_completer, complete_machines +from clan_cli.dirs import specific_machine_dir from clan_cli.secrets.folders import sops_secrets_folder from clan_cli.secrets.machines import has_machine as secrets_has_machine from clan_cli.secrets.machines import remove_machine as secrets_machine_remove @@ -23,7 +23,7 @@ log = logging.getLogger(__name__) @API.register def delete_machine(flake: Flake, name: str) -> None: try: - inventory.delete(flake, {f"machines.{name}"}) + inventory.delete(str(flake.path), {f"machines.{name}"}) except KeyError as exc: # louis@(2025-03-09): test infrastructure does not seem to set the # inventory properly, but more importantly only one machine in my @@ -35,7 +35,7 @@ def delete_machine(flake: Flake, name: str) -> None: changed_paths: list[Path] = [] - folder = specific_machine_dir(flake, name) + folder = specific_machine_dir(flake.path, name) if folder.exists(): changed_paths.append(folder) shutil.rmtree(folder) diff --git a/pkgs/clan-cli/clan_cli/machines/hardware.py b/pkgs/clan-cli/clan_cli/machines/hardware.py index 77b3b7d5a..ac31f98d7 100644 --- a/pkgs/clan-cli/clan_cli/machines/hardware.py +++ b/pkgs/clan-cli/clan_cli/machines/hardware.py @@ -7,9 +7,9 @@ from pathlib import Path from clan_lib.api import API -from clan_cli.clan_dirs import specific_machine_dir from clan_cli.cmd import RunOpts, run_no_stdout from clan_cli.completions import add_dynamic_completer, complete_machines +from clan_cli.dirs import specific_machine_dir from clan_cli.errors import ClanCmdError, ClanError from clan_cli.flake import Flake from clan_cli.git import commit_file @@ -26,39 +26,39 @@ class HardwareConfig(Enum): NIXOS_GENERATE_CONFIG = "nixos-generate-config" NONE = "none" - def config_path(self, flake: Flake, machine_name: str) -> Path: - machine_dir = specific_machine_dir(flake, machine_name) + def config_path(self, clan_dir: Path, machine_name: str) -> Path: + machine_dir = specific_machine_dir(clan_dir, machine_name) if self == HardwareConfig.NIXOS_FACTER: return machine_dir / "facter.json" return machine_dir / "hardware-configuration.nix" @classmethod def detect_type( - cls: type["HardwareConfig"], flake: Flake, machine_name: str + cls: type["HardwareConfig"], clan_dir: Path, machine_name: str ) -> "HardwareConfig": hardware_config = HardwareConfig.NIXOS_GENERATE_CONFIG.config_path( - flake, machine_name + clan_dir, machine_name ) if hardware_config.exists() and "throw" not in hardware_config.read_text(): return HardwareConfig.NIXOS_GENERATE_CONFIG - if HardwareConfig.NIXOS_FACTER.config_path(flake, machine_name).exists(): + if HardwareConfig.NIXOS_FACTER.config_path(clan_dir, machine_name).exists(): return HardwareConfig.NIXOS_FACTER return HardwareConfig.NONE @API.register -def show_machine_hardware_config(flake: Flake, machine_name: str) -> HardwareConfig: +def show_machine_hardware_config(clan_dir: Path, machine_name: str) -> HardwareConfig: """ Show hardware information for a machine returns None if none exist. """ - return HardwareConfig.detect_type(flake, machine_name) + return HardwareConfig.detect_type(clan_dir, machine_name) @API.register -def show_machine_hardware_platform(flake: Flake, machine_name: str) -> str | None: +def show_machine_hardware_platform(clan_dir: Path, machine_name: str) -> str | None: """ Show hardware information for a machine returns None if none exist. """ @@ -66,7 +66,7 @@ def show_machine_hardware_platform(flake: Flake, machine_name: str) -> str | Non system = config["system"] cmd = nix_eval( [ - f"{flake}#clanInternals.machines.{system}.{machine_name}", + f"{clan_dir}#clanInternals.machines.{system}.{machine_name}", "--apply", "machine: { inherit (machine.pkgs) system; }", "--json", @@ -103,7 +103,7 @@ def generate_machine_hardware_info(opts: HardwareGenerateOptions) -> HardwareCon override_target_host=opts.target_host, ) - hw_file = opts.backend.config_path(opts.flake, opts.machine) + hw_file = opts.backend.config_path(opts.flake.path, opts.machine) hw_file.parent.mkdir(parents=True, exist_ok=True) if opts.backend == HardwareConfig.NIXOS_FACTER: @@ -152,7 +152,7 @@ def generate_machine_hardware_info(opts: HardwareGenerateOptions) -> HardwareCon f"machines/{opts.machine}/{hw_file.name}: update hardware configuration", ) try: - show_machine_hardware_platform(opts.flake, opts.machine) + show_machine_hardware_platform(opts.flake.path, opts.machine) if backup_file: backup_file.unlink(missing_ok=True) except ClanCmdError as e: diff --git a/pkgs/clan-cli/clan_cli/machines/install.py b/pkgs/clan-cli/clan_cli/machines/install.py index 707b8b06d..2c7b28ac1 100644 --- a/pkgs/clan-cli/clan_cli/machines/install.py +++ b/pkgs/clan-cli/clan_cli/machines/install.py @@ -113,7 +113,7 @@ def install_machine(opts: InstallOptions) -> None: str(opts.update_hardware_config.value), str( opts.update_hardware_config.config_path( - machine.flake, machine.name + machine.flake.path, machine.name ) ), ] diff --git a/pkgs/clan-cli/clan_cli/machines/list.py b/pkgs/clan-cli/clan_cli/machines/list.py index 81a30ce0e..263df33c1 100644 --- a/pkgs/clan-cli/clan_cli/machines/list.py +++ b/pkgs/clan-cli/clan_cli/machines/list.py @@ -12,18 +12,16 @@ from clan_lib.api.disk import MachineDiskMatter from clan_lib.api.modules import parse_frontmatter from clan_lib.api.serde import dataclass_to_dict -from clan_cli.clan_dirs import specific_machine_dir from clan_cli.cmd import RunOpts, run from clan_cli.completions import add_dynamic_completer, complete_tags +from clan_cli.dirs import specific_machine_dir from clan_cli.errors import ClanError -from clan_cli.flake import Flake from clan_cli.inventory import ( load_inventory_eval, patch_inventory_with, ) from clan_cli.inventory.classes import Machine as InventoryMachine from clan_cli.machines.hardware import HardwareConfig -from clan_cli.machines.machines import Machine from clan_cli.nix import nix_eval from clan_cli.tags import list_nixos_machines_by_tags @@ -31,13 +29,15 @@ log = logging.getLogger(__name__) @API.register -def set_machine(flake: Flake, machine_name: str, machine: InventoryMachine) -> None: - patch_inventory_with(flake, f"machines.{machine_name}", dataclass_to_dict(machine)) +def set_machine(flake_url: Path, machine_name: str, machine: InventoryMachine) -> None: + patch_inventory_with( + flake_url, f"machines.{machine_name}", dataclass_to_dict(machine) + ) @API.register -def list_machines(flake: Flake) -> dict[str, InventoryMachine]: - inventory = load_inventory_eval(flake) +def list_machines(flake_url: str | Path) -> dict[str, InventoryMachine]: + inventory = load_inventory_eval(flake_url) return inventory.get("machines", {}) @@ -60,16 +60,16 @@ def extract_header(c: str) -> str: @API.register -def get_machine_details(machine: Machine) -> MachineDetails: - inventory = load_inventory_eval(machine.flake) - machine_inv = inventory.get("machines", {}).get(machine.name) - if machine_inv is None: - msg = f"Machine {machine.name} not found in inventory" +def get_machine_details(flake_url: Path, machine_name: str) -> MachineDetails: + inventory = load_inventory_eval(flake_url) + machine = inventory.get("machines", {}).get(machine_name) + if machine is None: + msg = f"Machine {machine_name} not found in inventory" raise ClanError(msg) - hw_config = HardwareConfig.detect_type(machine.flake, machine.name) + hw_config = HardwareConfig.detect_type(flake_url, machine_name) - machine_dir = specific_machine_dir(machine.flake, machine.name) + machine_dir = specific_machine_dir(flake_url, machine_name) disk_schema: MachineDiskMatter | None = None disk_path = machine_dir / "disko.nix" if disk_path.exists(): @@ -80,9 +80,7 @@ def get_machine_details(machine: Machine) -> MachineDetails: if data: disk_schema = data # type: ignore - return MachineDetails( - machine=machine_inv, hw_config=hw_config, disk_schema=disk_schema - ) + return MachineDetails(machine=machine, hw_config=hw_config, disk_schema=disk_schema) def list_nixos_machines(flake_url: str | Path) -> list[str]: diff --git a/pkgs/clan-cli/clan_cli/tests/test_dirs.py b/pkgs/clan-cli/clan_cli/tests/test_dirs.py index e4528c59d..ac2626834 100644 --- a/pkgs/clan-cli/clan_cli/tests/test_dirs.py +++ b/pkgs/clan-cli/clan_cli/tests/test_dirs.py @@ -16,7 +16,7 @@ # (subdir / ".clan-flake").touch() # assert _get_clan_flake_toplevel() == subdir -from clan_cli.clan_dirs import clan_key_safe, vm_state_dir +from clan_cli.dirs import clan_key_safe, vm_state_dir def test_clan_key_safe() -> None: diff --git a/pkgs/clan-cli/clan_cli/tests/test_machines_cli.py b/pkgs/clan-cli/clan_cli/tests/test_machines_cli.py index e8b2a0383..db708bce9 100644 --- a/pkgs/clan-cli/clan_cli/tests/test_machines_cli.py +++ b/pkgs/clan-cli/clan_cli/tests/test_machines_cli.py @@ -1,5 +1,4 @@ import pytest -from clan_cli.flake import Flake from clan_cli.inventory import load_inventory_json from clan_cli.secrets.folders import sops_machines_folder from clan_cli.tests import fixtures_flakes @@ -25,7 +24,7 @@ def test_machine_subcommands( ] ) - inventory: dict = dict(load_inventory_json(Flake(str(test_flake_with_core.path)))) + inventory: dict = dict(load_inventory_json(str(test_flake_with_core.path))) assert "machine1" in inventory["machines"] assert "service" not in inventory @@ -41,7 +40,7 @@ def test_machine_subcommands( ["machines", "delete", "--flake", str(test_flake_with_core.path), "machine1"] ) - inventory_2: dict = dict(load_inventory_json(Flake(str(test_flake_with_core.path)))) + inventory_2: dict = dict(load_inventory_json(str(test_flake_with_core.path))) assert "machine1" not in inventory_2["machines"] assert "service" not in inventory_2 diff --git a/pkgs/clan-cli/clan_cli/tests/test_modules.py b/pkgs/clan-cli/clan_cli/tests/test_modules.py index dff0751ab..bb1412f27 100644 --- a/pkgs/clan-cli/clan_cli/tests/test_modules.py +++ b/pkgs/clan-cli/clan_cli/tests/test_modules.py @@ -88,7 +88,7 @@ def test_add_module_to_inventory( } } - set_inventory(inventory, Flake(str(base_path)), "Add borgbackup service") + set_inventory(inventory, base_path, "Add borgbackup service") # cmd = ["facts", "generate", "--flake", str(test_flake_with_core.path), "machine1"] cmd = [ diff --git a/pkgs/clan-cli/clan_cli/vars/public_modules/vm.py b/pkgs/clan-cli/clan_cli/vars/public_modules/vm.py index 0d1913338..01eeecd73 100644 --- a/pkgs/clan-cli/clan_cli/vars/public_modules/vm.py +++ b/pkgs/clan-cli/clan_cli/vars/public_modules/vm.py @@ -3,7 +3,7 @@ import shutil from collections.abc import Iterable from pathlib import Path -from clan_cli.clan_dirs import vm_state_dir +from clan_cli.dirs import vm_state_dir from clan_cli.errors import ClanError from clan_cli.machines.machines import Machine from clan_cli.ssh.host import Host diff --git a/pkgs/clan-cli/clan_cli/vars/secret_modules/vm.py b/pkgs/clan-cli/clan_cli/vars/secret_modules/vm.py index 138f2ab34..37818fcd5 100644 --- a/pkgs/clan-cli/clan_cli/vars/secret_modules/vm.py +++ b/pkgs/clan-cli/clan_cli/vars/secret_modules/vm.py @@ -2,7 +2,7 @@ import shutil from collections.abc import Iterable from pathlib import Path -from clan_cli.clan_dirs import vm_state_dir +from clan_cli.dirs import vm_state_dir from clan_cli.machines.machines import Machine from clan_cli.ssh.host import Host from clan_cli.vars._types import StoreBase diff --git a/pkgs/clan-cli/clan_cli/vms/run.py b/pkgs/clan-cli/clan_cli/vms/run.py index dc4ebf9de..a65886737 100644 --- a/pkgs/clan-cli/clan_cli/vms/run.py +++ b/pkgs/clan-cli/clan_cli/vms/run.py @@ -12,10 +12,9 @@ from dataclasses import dataclass from pathlib import Path from tempfile import TemporaryDirectory -from clan_cli.clan_dirs import vm_state_dir from clan_cli.cmd import CmdOut, Log, RunOpts, handle_io, run from clan_cli.completions import add_dynamic_completer, complete_machines -from clan_cli.dirs import module_root, user_cache_dir +from clan_cli.dirs import module_root, user_cache_dir, vm_state_dir from clan_cli.errors import ClanCmdError, ClanError from clan_cli.facts.generate import generate_facts from clan_cli.machines.machines import Machine diff --git a/pkgs/clan-cli/clan_lib/api/disk.py b/pkgs/clan-cli/clan_lib/api/disk.py index 2ff98ea9e..a524a6b6b 100644 --- a/pkgs/clan-cli/clan_lib/api/disk.py +++ b/pkgs/clan-cli/clan_lib/api/disk.py @@ -2,12 +2,12 @@ import json import logging from collections.abc import Callable from dataclasses import dataclass +from pathlib import Path from typing import Any, TypedDict from uuid import uuid4 from clan_cli.dirs import TemplateType, clan_templates from clan_cli.errors import ClanError -from clan_cli.flake import Flake from clan_cli.git import commit_file from clan_cli.machines.hardware import HardwareConfig, show_machine_hardware_config @@ -75,7 +75,7 @@ templates: dict[str, dict[str, Callable[[dict[str, Any]], Placeholder]]] = { @API.register def get_disk_schemas( - flake: Flake, machine_name: str | None = None + base_path: Path, machine_name: str | None = None ) -> dict[str, DiskSchema]: """ Get the available disk schemas @@ -85,7 +85,9 @@ def get_disk_schemas( hw_report = {} if machine_name is not None: - hw_report_path = HardwareConfig.NIXOS_FACTER.config_path(flake, machine_name) + hw_report_path = HardwareConfig.NIXOS_FACTER.config_path( + base_path, machine_name + ) if not hw_report_path.exists(): msg = "Hardware configuration missing" raise ClanError(msg) @@ -130,7 +132,7 @@ class MachineDiskMatter(TypedDict): @API.register def set_machine_disk_schema( - flake: Flake, + base_path: Path, machine_name: str, schema_name: str, # Placeholders are used to fill in the disk schema @@ -142,8 +144,8 @@ def set_machine_disk_schema( Set the disk placeholders of the template """ # Assert the hw-config must exist before setting the disk - hw_config = show_machine_hardware_config(flake, machine_name) - hw_config_path = hw_config.config_path(flake, machine_name) + hw_config = show_machine_hardware_config(base_path, machine_name) + hw_config_path = hw_config.config_path(base_path, machine_name) if not hw_config_path.exists(): msg = "Hardware configuration must exist before applying disk schema" @@ -160,7 +162,7 @@ def set_machine_disk_schema( raise ClanError(msg) # Check that the placeholders are valid - disk_schema = get_disk_schemas(flake, machine_name)[schema_name] + disk_schema = get_disk_schemas(base_path, machine_name)[schema_name] # check that all required placeholders are present for placeholder_name, schema_placeholder in disk_schema.placeholders.items(): if schema_placeholder.required and placeholder_name not in placeholders: @@ -221,6 +223,6 @@ def set_machine_disk_schema( commit_file( disko_file_path, - flake.path, + base_path, commit_message=f"Set disk schema of machine: {machine_name} to {schema_name}", ) diff --git a/pkgs/clan-cli/clan_lib/tests/test_create.py b/pkgs/clan-cli/clan_lib/tests/test_create.py index dbee211bf..05f6522a7 100644 --- a/pkgs/clan-cli/clan_lib/tests/test_create.py +++ b/pkgs/clan-cli/clan_lib/tests/test_create.py @@ -8,8 +8,8 @@ from typing import Any import clan_cli.clan.create import pytest -from clan_cli.clan_dirs import specific_machine_dir from clan_cli.cmd import RunOpts, run +from clan_cli.dirs import specific_machine_dir from clan_cli.errors import ClanError from clan_cli.flake import Flake from clan_cli.inventory import patch_inventory_with @@ -209,7 +209,7 @@ def test_clan_create_api( # ===== CREATE BASE INVENTORY ====== inventory = create_base_inventory(ssh_keys) - patch_inventory_with(Flake(str(dest_clan_dir)), "services", inventory.services) + patch_inventory_with(dest_clan_dir, "services", inventory.services) # Invalidate cache because of new inventory clan_dir_flake.invalidate_cache() @@ -240,7 +240,7 @@ def test_clan_create_api( facter_json = test_lib_root / "assets" / "facter.json" assert facter_json.exists(), f"Source facter file not found: {facter_json}" - dest_dir = specific_machine_dir(Flake(str(clan_dir_flake.path)), machine.name) + dest_dir = specific_machine_dir(clan_dir_flake.path, machine.name) # specific_machine_dir should create the directory, but ensure it exists just in case dest_dir.mkdir(parents=True, exist_ok=True) @@ -254,8 +254,7 @@ def test_clan_create_api( # ===== Create Disko Config ====== facter_path = ( - specific_machine_dir(Flake(str(clan_dir_flake.path)), machine.name) - / "facter.json" + specific_machine_dir(clan_dir_flake.path, machine.name) / "facter.json" ) with facter_path.open("r") as f: facter_report = json.load(f) @@ -265,7 +264,9 @@ def test_clan_create_api( assert disk_devs is not None placeholders = {"mainDisk": disk_devs[0]} - set_machine_disk_schema(clan_dir_flake, machine.name, "single-disk", placeholders) + set_machine_disk_schema( + clan_dir_flake.path, machine.name, "single-disk", placeholders + ) clan_dir_flake.invalidate_cache() with pytest.raises(ClanError) as exc_info: diff --git a/pkgs/clan-vm-manager/clan_vm_manager/components/vmobj.py b/pkgs/clan-vm-manager/clan_vm_manager/components/vmobj.py index 3568fb74f..bceba2fed 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/components/vmobj.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/components/vmobj.py @@ -13,7 +13,7 @@ from typing import IO, ClassVar import gi from clan_cli import vms -from clan_cli.clan_dirs import vm_state_dir +from clan_cli.dirs import vm_state_dir from clan_cli.machines.machines import Machine from clan_cli.vms.inspect import inspect_vm from clan_cli.vms.qemu import QMPWrapper diff --git a/pkgs/webview-ui/app/src/api/inventory.ts b/pkgs/webview-ui/app/src/api/inventory.ts index b77f47c89..ade59652d 100644 --- a/pkgs/webview-ui/app/src/api/inventory.ts +++ b/pkgs/webview-ui/app/src/api/inventory.ts @@ -13,9 +13,9 @@ export async function get_inventory(client: QueryClient, base_path: string) { queryKey: [base_path, "inventory"], queryFn: () => { console.log("Refreshing inventory"); - return callApi("get_inventory", { - flake: { identifier: base_path }, - }) as Promise>; + return callApi("get_inventory", { base_path }) as Promise< + ApiEnvelope + >; }, revalidateIfStale: true, staleTime: 60 * 1000, diff --git a/pkgs/webview-ui/app/src/api/wifi.ts b/pkgs/webview-ui/app/src/api/wifi.ts index 0de237e8d..6cb284554 100644 --- a/pkgs/webview-ui/app/src/api/wifi.ts +++ b/pkgs/webview-ui/app/src/api/wifi.ts @@ -6,7 +6,7 @@ export const instance_name = (machine_name: string) => export async function get_iwd_service(base_path: string, machine_name: string) { const r = await callApi("get_inventory", { - flake: { identifier: base_path }, + base_path, }); if (r.status == "error") { return null; diff --git a/pkgs/webview-ui/app/src/queries/index.ts b/pkgs/webview-ui/app/src/queries/index.ts index 4baa550aa..e7f0e8909 100644 --- a/pkgs/webview-ui/app/src/queries/index.ts +++ b/pkgs/webview-ui/app/src/queries/index.ts @@ -43,7 +43,7 @@ export const tagsQuery = (uri: string | null) => if (!uri) return []; const response = await callApi("get_inventory", { - flake: { identifier: uri }, + base_path: uri, }); if (response.status === "error") { toast.error("Failed to fetch data"); @@ -64,7 +64,7 @@ export const machinesQuery = (uri: string | null) => if (!uri) return []; const response = await callApi("get_inventory", { - flake: { identifier: uri }, + base_path: uri, }); if (response.status === "error") { toast.error("Failed to fetch data"); diff --git a/pkgs/webview-ui/app/src/routes/clans/details.tsx b/pkgs/webview-ui/app/src/routes/clans/details.tsx index 7f0b4945e..7696e4872 100644 --- a/pkgs/webview-ui/app/src/routes/clans/details.tsx +++ b/pkgs/webview-ui/app/src/routes/clans/details.tsx @@ -42,7 +42,7 @@ const EditClanForm = (props: EditClanFormProps) => { (async () => { await callApi("update_clan_meta", { options: { - flake: { identifier: props.directory }, + directory: props.directory, meta: values, }, }); diff --git a/pkgs/webview-ui/app/src/routes/disk/view.tsx b/pkgs/webview-ui/app/src/routes/disk/view.tsx index 5f90d0b9e..02eaf143b 100644 --- a/pkgs/webview-ui/app/src/routes/disk/view.tsx +++ b/pkgs/webview-ui/app/src/routes/disk/view.tsx @@ -10,9 +10,7 @@ export function DiskView() { const currUri = activeURI(); if (currUri) { // Example of calling an API - const result = await callApi("get_inventory", { - flake: { identifier: currUri }, - }); + const result = await callApi("get_inventory", { base_path: currUri }); if (result.status === "error") throw new Error("Failed to fetch data"); return result.data; } diff --git a/pkgs/webview-ui/app/src/routes/machines/details.tsx b/pkgs/webview-ui/app/src/routes/machines/details.tsx index e09aec051..9b64691a1 100644 --- a/pkgs/webview-ui/app/src/routes/machines/details.tsx +++ b/pkgs/webview-ui/app/src/routes/machines/details.tsx @@ -115,7 +115,7 @@ const InstallMachine = (props: InstallMachineProps) => { if (shouldRunDisk) { setProgressText("Setting up disk ... (1/5)"); const disk_response = await callApi("set_machine_disk_schema", { - flake: { identifier: curr_uri }, + base_path: curr_uri, machine_name: props.name, placeholders: diskValues.placeholders, schema_name: diskValues.schema, @@ -415,9 +415,7 @@ const MachineForm = (props: MachineDetailsProps) => { } const machine_response = await callApi("set_machine", { - flake: { - identifier: curr_uri, - }, + flake_url: curr_uri, machine_name: props.initialData.machine.name || "My machine", machine: { ...values.machine, @@ -682,12 +680,8 @@ export const MachineDetails = () => { const curr = activeURI(); if (curr) { const result = await callApi("get_machine_details", { - machine: { - flake: { - identifier: curr, - }, - name: params.id, - }, + flake_url: curr, + machine_name: params.id, }); if (result.status === "error") throw new Error("Failed to fetch data"); return result.data; diff --git a/pkgs/webview-ui/app/src/routes/machines/install/disk-step.tsx b/pkgs/webview-ui/app/src/routes/machines/install/disk-step.tsx index a12570506..29b07c959 100644 --- a/pkgs/webview-ui/app/src/routes/machines/install/disk-step.tsx +++ b/pkgs/webview-ui/app/src/routes/machines/install/disk-step.tsx @@ -37,9 +37,7 @@ export const DiskStep = (props: StepProps) => { queryKey: [props.dir, props.machine_id, "disk_schemas"], queryFn: async () => { const result = await callApi("get_disk_schemas", { - flake: { - identifier: props.dir, - }, + base_path: props.dir, machine_name: props.machine_id, }); if (result.status === "error") throw new Error("Failed to fetch data"); diff --git a/pkgs/webview-ui/app/src/routes/machines/install/hardware-step.tsx b/pkgs/webview-ui/app/src/routes/machines/install/hardware-step.tsx index daf6be0af..c413ff899 100644 --- a/pkgs/webview-ui/app/src/routes/machines/install/hardware-step.tsx +++ b/pkgs/webview-ui/app/src/routes/machines/install/hardware-step.tsx @@ -52,9 +52,7 @@ export const HWStep = (props: StepProps) => { queryKey: [props.dir, props.machine_id, "hw_report"], queryFn: async () => { const result = await callApi("show_machine_hardware_config", { - flake: { - identifier: props.dir, - }, + clan_dir: props.dir, machine_name: props.machine_id, }); if (result.status === "error") throw new Error("Failed to fetch data"); diff --git a/pkgs/webview-ui/app/src/routes/machines/list.tsx b/pkgs/webview-ui/app/src/routes/machines/list.tsx index 903954fd0..35b90ac71 100644 --- a/pkgs/webview-ui/app/src/routes/machines/list.tsx +++ b/pkgs/webview-ui/app/src/routes/machines/list.tsx @@ -39,9 +39,7 @@ export const MachineListView: Component = () => { const uri = activeURI(); if (uri) { const response = await callApi("list_machines", { - flake: { - identifier: uri, - }, + flake_url: uri, }); if (response.status === "error") { toast.error("Failed to fetch data");