clan-cli: Refactor the API to use the Flake object

This commit is contained in:
Qubasa
2025-05-07 13:12:43 +02:00
committed by Mic92
parent 60a2246944
commit 89decdaf7e
22 changed files with 125 additions and 98 deletions

View File

@@ -107,7 +107,7 @@ def create_clan(opts: CreateOptions) -> CreateClanResponse:
response.flake_update = flake_update response.flake_update = flake_update
if opts.initial: if opts.initial:
init_inventory(str(opts.dest), init=opts.initial) init_inventory(Flake(str(opts.dest)), init=opts.initial)
return response return response

View File

@@ -2,20 +2,21 @@ from dataclasses import dataclass
from clan_lib.api import API from clan_lib.api import API
from clan_cli.flake import Flake
from clan_cli.inventory import Inventory, Meta, load_inventory_json, set_inventory from clan_cli.inventory import Inventory, Meta, load_inventory_json, set_inventory
@dataclass @dataclass
class UpdateOptions: class UpdateOptions:
directory: str flake: Flake
meta: Meta meta: Meta
@API.register @API.register
def update_clan_meta(options: UpdateOptions) -> Inventory: def update_clan_meta(options: UpdateOptions) -> Inventory:
inventory = load_inventory_json(options.directory) inventory = load_inventory_json(options.flake)
inventory["meta"] = options.meta inventory["meta"] = options.meta
set_inventory(inventory, options.directory, "Update clan metadata") set_inventory(inventory, options.flake, "Update clan metadata")
return inventory return inventory

View File

@@ -4,9 +4,13 @@ import sys
import urllib import urllib
from enum import Enum from enum import Enum
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING
from .errors import ClanError from .errors import ClanError
if TYPE_CHECKING:
from clan_cli.flake import Flake
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -131,12 +135,17 @@ def vm_state_dir(flake_url: str, vm_name: str) -> Path:
return user_data_dir() / "clan" / "vmstate" / clan_key / vm_name return user_data_dir() / "clan" / "vmstate" / clan_key / vm_name
def machines_dir(flake_dir: Path) -> Path: def machines_dir(flake: "Flake") -> Path:
return flake_dir / "machines" if flake.is_local:
return flake.path / "machines"
store_path = flake.store_path
assert store_path is not None, "Invalid flake object. Doesn't have a store path"
return Path(store_path) / "machines"
def specific_machine_dir(flake_dir: Path, machine: str) -> Path: def specific_machine_dir(flake: "Flake", machine: str) -> Path:
return machines_dir(flake_dir) / machine return machines_dir(flake) / machine
def module_root() -> Path: def module_root() -> Path:

View File

@@ -23,6 +23,7 @@ from clan_lib.api import API, dataclass_to_dict, from_dict
from clan_cli.cmd import run_no_stdout from clan_cli.cmd import run_no_stdout
from clan_cli.errors import ClanCmdError, ClanError from clan_cli.errors import ClanCmdError, ClanError
from clan_cli.flake import Flake
from clan_cli.git import commit_file from clan_cli.git import commit_file
from clan_cli.nix import nix_eval from clan_cli.nix import nix_eval
@@ -49,11 +50,11 @@ __all__ = [
] ]
def get_inventory_path(flake_dir: str | Path) -> Path: def get_inventory_path(flake: Flake) -> Path:
""" """
Get the path to the inventory file in the flake directory Get the path to the inventory file in the flake directory
""" """
inventory_file = (Path(flake_dir) / "inventory.json").resolve() inventory_file = (flake.path / "inventory.json").resolve()
return inventory_file return inventory_file
@@ -61,7 +62,7 @@ def get_inventory_path(flake_dir: str | Path) -> Path:
default_inventory: Inventory = {"meta": {"name": "New Clan"}} default_inventory: Inventory = {"meta": {"name": "New Clan"}}
def load_inventory_eval(flake_dir: str | Path) -> Inventory: def load_inventory_eval(flake_dir: Flake) -> Inventory:
""" """
Loads the evaluated inventory. Loads the evaluated inventory.
After all merge operations with eventual nix code in buildClan. After all merge operations with eventual nix code in buildClan.
@@ -354,7 +355,7 @@ def determine_writeability(
return results return results
def get_inventory_current_priority(flake_dir: str | Path) -> dict: def get_inventory_current_priority(flake: Flake) -> dict:
""" """
Returns the current priority of the inventory values Returns the current priority of the inventory values
@@ -374,7 +375,7 @@ def get_inventory_current_priority(flake_dir: str | Path) -> dict:
""" """
cmd = nix_eval( cmd = nix_eval(
[ [
f"{flake_dir}#clanInternals.inventoryClass.introspection", f"{flake}#clanInternals.inventoryClass.introspection",
"--json", "--json",
] ]
) )
@@ -392,7 +393,7 @@ def get_inventory_current_priority(flake_dir: str | Path) -> dict:
@API.register @API.register
def load_inventory_json(flake_dir: str | Path) -> Inventory: def load_inventory_json(flake: Flake) -> Inventory:
""" """
Load the inventory FILE from the flake directory Load the inventory FILE from the flake directory
If no file is found, returns an empty dictionary If no file is found, returns an empty dictionary
@@ -402,7 +403,7 @@ def load_inventory_json(flake_dir: str | Path) -> Inventory:
Use load_inventory_eval instead Use load_inventory_eval instead
""" """
inventory_file = get_inventory_path(flake_dir) inventory_file = get_inventory_path(flake)
if not inventory_file.exists(): if not inventory_file.exists():
return {} return {}
@@ -472,14 +473,14 @@ def patch(d: dict[str, Any], path: str, content: Any) -> None:
@API.register @API.register
def patch_inventory_with(base_dir: Path, section: str, content: dict[str, Any]) -> None: def patch_inventory_with(flake: Flake, section: str, content: dict[str, Any]) -> None:
""" """
Pass only the section to update and the content to update with. Pass only the section to update and the content to update with.
Make sure you pass only attributes that you would like to persist. Make sure you pass only attributes that you would like to persist.
ATTENTION: Don't pass nix eval values unintentionally. ATTENTION: Don't pass nix eval values unintentionally.
""" """
inventory_file = get_inventory_path(base_dir) inventory_file = get_inventory_path(flake)
curr_inventory = {} curr_inventory = {}
if inventory_file.exists(): if inventory_file.exists():
@@ -491,7 +492,9 @@ def patch_inventory_with(base_dir: Path, section: str, content: dict[str, Any])
with inventory_file.open("w") as f: with inventory_file.open("w") as f:
json.dump(curr_inventory, f, indent=2) json.dump(curr_inventory, f, indent=2)
commit_file(inventory_file, base_dir, commit_message=f"inventory.{section}: Update") commit_file(
inventory_file, flake.path, commit_message=f"inventory.{section}: Update"
)
@dataclass @dataclass
@@ -503,16 +506,16 @@ class WriteInfo:
@API.register @API.register
def get_inventory_with_writeable_keys( def get_inventory_with_writeable_keys(
flake_dir: str | Path, flake: Flake,
) -> WriteInfo: ) -> WriteInfo:
""" """
Load the inventory and determine the writeable keys Load the inventory and determine the writeable keys
Performs 2 nix evaluations to get the current priority and the inventory Performs 2 nix evaluations to get the current priority and the inventory
""" """
current_priority = get_inventory_current_priority(flake_dir) current_priority = get_inventory_current_priority(flake)
data_eval: Inventory = load_inventory_eval(flake_dir) data_eval: Inventory = load_inventory_eval(flake)
data_disk: Inventory = load_inventory_json(flake_dir) data_disk: Inventory = load_inventory_json(flake)
writeables = determine_writeability( writeables = determine_writeability(
current_priority, dict(data_eval), dict(data_disk) current_priority, dict(data_eval), dict(data_disk)
@@ -524,14 +527,14 @@ def get_inventory_with_writeable_keys(
# TODO: remove this function in favor of a proper read/write API # TODO: remove this function in favor of a proper read/write API
@API.register @API.register
def set_inventory( def set_inventory(
inventory: Inventory, flake_dir: str | Path, message: str, commit: bool = True inventory: Inventory, flake: Flake, message: str, commit: bool = True
) -> None: ) -> None:
""" """
Write the inventory to the flake directory Write the inventory to the flake directory
and commit it to git with the given message and commit it to git with the given message
""" """
write_info = get_inventory_with_writeable_keys(flake_dir) write_info = get_inventory_with_writeable_keys(flake)
# Remove internals from the inventory # Remove internals from the inventory
inventory.pop("tags", None) # type: ignore inventory.pop("tags", None) # type: ignore
@@ -552,43 +555,43 @@ def set_inventory(
for delete_path in delete_set: for delete_path in delete_set:
delete_by_path(persisted, delete_path) delete_by_path(persisted, delete_path)
inventory_file = get_inventory_path(flake_dir) inventory_file = get_inventory_path(flake)
with inventory_file.open("w") as f: with inventory_file.open("w") as f:
json.dump(persisted, f, indent=2) json.dump(persisted, f, indent=2)
if commit: if commit:
commit_file(inventory_file, Path(flake_dir), commit_message=message) commit_file(inventory_file, flake.path, commit_message=message)
# TODO: wrap this in a proper persistence API # TODO: wrap this in a proper persistence API
def delete(directory: str | Path, delete_set: set[str]) -> None: def delete(flake: Flake, delete_set: set[str]) -> None:
""" """
Delete keys from the inventory Delete keys from the inventory
""" """
write_info = get_inventory_with_writeable_keys(directory) write_info = get_inventory_with_writeable_keys(flake)
data_disk = dict(write_info.data_disk) data_disk = dict(write_info.data_disk)
for delete_path in delete_set: for delete_path in delete_set:
delete_by_path(data_disk, delete_path) delete_by_path(data_disk, delete_path)
inventory_file = get_inventory_path(directory) inventory_file = get_inventory_path(flake)
with inventory_file.open("w") as f: with inventory_file.open("w") as f:
json.dump(data_disk, f, indent=2) json.dump(data_disk, f, indent=2)
commit_file( commit_file(
inventory_file, inventory_file,
Path(directory), flake.path,
commit_message=f"Delete inventory keys {delete_set}", commit_message=f"Delete inventory keys {delete_set}",
) )
def init_inventory(directory: str, init: Inventory | None = None) -> None: def init_inventory(flake: Flake, init: Inventory | None = None) -> None:
inventory = None inventory = None
# Try reading the current flake # Try reading the current flake
if init is None: if init is None:
with contextlib.suppress(ClanCmdError): with contextlib.suppress(ClanCmdError):
inventory = load_inventory_eval(directory) inventory = load_inventory_eval(flake)
if init is not None: if init is not None:
inventory = init inventory = init
@@ -596,9 +599,9 @@ def init_inventory(directory: str, init: Inventory | None = None) -> None:
# Write inventory.json file # Write inventory.json file
if inventory is not None: if inventory is not None:
# Persist creates a commit message for each change # Persist creates a commit message for each change
set_inventory(inventory, directory, "Init inventory") set_inventory(inventory, flake, "Init inventory")
@API.register @API.register
def get_inventory(base_path: str | Path) -> Inventory: def get_inventory(flake: Flake) -> Inventory:
return load_inventory_eval(base_path) return load_inventory_eval(flake)

View File

@@ -110,7 +110,7 @@ def create_machine(opts: CreateOptions, commit: bool = True) -> None:
new_machine["deploy"] = {"targetHost": target_host} new_machine["deploy"] = {"targetHost": target_host}
patch_inventory_with( patch_inventory_with(
clan_dir, f"machines.{machine_name}", dataclass_to_dict(new_machine) Flake(str(clan_dir)), f"machines.{machine_name}", dataclass_to_dict(new_machine)
) )
# Commit at the end in that order to avoid committing halve-baked machines # Commit at the end in that order to avoid committing halve-baked machines

View File

@@ -23,7 +23,7 @@ log = logging.getLogger(__name__)
@API.register @API.register
def delete_machine(flake: Flake, name: str) -> None: def delete_machine(flake: Flake, name: str) -> None:
try: try:
inventory.delete(str(flake.path), {f"machines.{name}"}) inventory.delete(flake, {f"machines.{name}"})
except KeyError as exc: except KeyError as exc:
# louis@(2025-03-09): test infrastructure does not seem to set the # louis@(2025-03-09): test infrastructure does not seem to set the
# inventory properly, but more importantly only one machine in my # 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] = [] changed_paths: list[Path] = []
folder = specific_machine_dir(flake.path, name) folder = specific_machine_dir(flake, name)
if folder.exists(): if folder.exists():
changed_paths.append(folder) changed_paths.append(folder)
shutil.rmtree(folder) shutil.rmtree(folder)

View File

@@ -26,39 +26,39 @@ class HardwareConfig(Enum):
NIXOS_GENERATE_CONFIG = "nixos-generate-config" NIXOS_GENERATE_CONFIG = "nixos-generate-config"
NONE = "none" NONE = "none"
def config_path(self, clan_dir: Path, machine_name: str) -> Path: def config_path(self, flake: Flake, machine_name: str) -> Path:
machine_dir = specific_machine_dir(clan_dir, machine_name) machine_dir = specific_machine_dir(flake, machine_name)
if self == HardwareConfig.NIXOS_FACTER: if self == HardwareConfig.NIXOS_FACTER:
return machine_dir / "facter.json" return machine_dir / "facter.json"
return machine_dir / "hardware-configuration.nix" return machine_dir / "hardware-configuration.nix"
@classmethod @classmethod
def detect_type( def detect_type(
cls: type["HardwareConfig"], clan_dir: Path, machine_name: str cls: type["HardwareConfig"], flake: Flake, machine_name: str
) -> "HardwareConfig": ) -> "HardwareConfig":
hardware_config = HardwareConfig.NIXOS_GENERATE_CONFIG.config_path( hardware_config = HardwareConfig.NIXOS_GENERATE_CONFIG.config_path(
clan_dir, machine_name flake, machine_name
) )
if hardware_config.exists() and "throw" not in hardware_config.read_text(): if hardware_config.exists() and "throw" not in hardware_config.read_text():
return HardwareConfig.NIXOS_GENERATE_CONFIG return HardwareConfig.NIXOS_GENERATE_CONFIG
if HardwareConfig.NIXOS_FACTER.config_path(clan_dir, machine_name).exists(): if HardwareConfig.NIXOS_FACTER.config_path(flake, machine_name).exists():
return HardwareConfig.NIXOS_FACTER return HardwareConfig.NIXOS_FACTER
return HardwareConfig.NONE return HardwareConfig.NONE
@API.register @API.register
def show_machine_hardware_config(clan_dir: Path, machine_name: str) -> HardwareConfig: def show_machine_hardware_config(flake: Flake, machine_name: str) -> HardwareConfig:
""" """
Show hardware information for a machine returns None if none exist. Show hardware information for a machine returns None if none exist.
""" """
return HardwareConfig.detect_type(clan_dir, machine_name) return HardwareConfig.detect_type(flake, machine_name)
@API.register @API.register
def show_machine_hardware_platform(clan_dir: Path, machine_name: str) -> str | None: def show_machine_hardware_platform(flake: Flake, machine_name: str) -> str | None:
""" """
Show hardware information for a machine returns None if none exist. Show hardware information for a machine returns None if none exist.
""" """
@@ -66,7 +66,7 @@ def show_machine_hardware_platform(clan_dir: Path, machine_name: str) -> str | N
system = config["system"] system = config["system"]
cmd = nix_eval( cmd = nix_eval(
[ [
f"{clan_dir}#clanInternals.machines.{system}.{machine_name}", f"{flake}#clanInternals.machines.{system}.{machine_name}",
"--apply", "--apply",
"machine: { inherit (machine.pkgs) system; }", "machine: { inherit (machine.pkgs) system; }",
"--json", "--json",
@@ -103,7 +103,7 @@ def generate_machine_hardware_info(opts: HardwareGenerateOptions) -> HardwareCon
override_target_host=opts.target_host, override_target_host=opts.target_host,
) )
hw_file = opts.backend.config_path(opts.flake.path, opts.machine) hw_file = opts.backend.config_path(opts.flake, opts.machine)
hw_file.parent.mkdir(parents=True, exist_ok=True) hw_file.parent.mkdir(parents=True, exist_ok=True)
if opts.backend == HardwareConfig.NIXOS_FACTER: 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", f"machines/{opts.machine}/{hw_file.name}: update hardware configuration",
) )
try: try:
show_machine_hardware_platform(opts.flake.path, opts.machine) show_machine_hardware_platform(opts.flake, opts.machine)
if backup_file: if backup_file:
backup_file.unlink(missing_ok=True) backup_file.unlink(missing_ok=True)
except ClanCmdError as e: except ClanCmdError as e:

View File

@@ -113,7 +113,7 @@ def install_machine(opts: InstallOptions) -> None:
str(opts.update_hardware_config.value), str(opts.update_hardware_config.value),
str( str(
opts.update_hardware_config.config_path( opts.update_hardware_config.config_path(
machine.flake.path, machine.name machine.flake, machine.name
) )
), ),
] ]

View File

@@ -16,12 +16,14 @@ from clan_cli.cmd import RunOpts, run
from clan_cli.completions import add_dynamic_completer, complete_tags from clan_cli.completions import add_dynamic_completer, complete_tags
from clan_cli.dirs import specific_machine_dir from clan_cli.dirs import specific_machine_dir
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.flake import Flake
from clan_cli.inventory import ( from clan_cli.inventory import (
load_inventory_eval, load_inventory_eval,
patch_inventory_with, patch_inventory_with,
) )
from clan_cli.inventory.classes import Machine as InventoryMachine from clan_cli.inventory.classes import Machine as InventoryMachine
from clan_cli.machines.hardware import HardwareConfig from clan_cli.machines.hardware import HardwareConfig
from clan_cli.machines.machines import Machine
from clan_cli.nix import nix_eval from clan_cli.nix import nix_eval
from clan_cli.tags import list_nixos_machines_by_tags from clan_cli.tags import list_nixos_machines_by_tags
@@ -29,15 +31,13 @@ log = logging.getLogger(__name__)
@API.register @API.register
def set_machine(flake_url: Path, machine_name: str, machine: InventoryMachine) -> None: def set_machine(flake: Flake, machine_name: str, machine: InventoryMachine) -> None:
patch_inventory_with( patch_inventory_with(flake, f"machines.{machine_name}", dataclass_to_dict(machine))
flake_url, f"machines.{machine_name}", dataclass_to_dict(machine)
)
@API.register @API.register
def list_machines(flake_url: str | Path) -> dict[str, InventoryMachine]: def list_machines(flake: Flake) -> dict[str, InventoryMachine]:
inventory = load_inventory_eval(flake_url) inventory = load_inventory_eval(flake)
return inventory.get("machines", {}) return inventory.get("machines", {})
@@ -60,16 +60,16 @@ def extract_header(c: str) -> str:
@API.register @API.register
def get_machine_details(flake_url: Path, machine_name: str) -> MachineDetails: def get_machine_details(machine: Machine) -> MachineDetails:
inventory = load_inventory_eval(flake_url) inventory = load_inventory_eval(machine.flake)
machine = inventory.get("machines", {}).get(machine_name) machine_inv = inventory.get("machines", {}).get(machine.name)
if machine is None: if machine_inv is None:
msg = f"Machine {machine_name} not found in inventory" msg = f"Machine {machine.name} not found in inventory"
raise ClanError(msg) raise ClanError(msg)
hw_config = HardwareConfig.detect_type(flake_url, machine_name) hw_config = HardwareConfig.detect_type(machine.flake, machine.name)
machine_dir = specific_machine_dir(flake_url, machine_name) machine_dir = specific_machine_dir(machine.flake, machine.name)
disk_schema: MachineDiskMatter | None = None disk_schema: MachineDiskMatter | None = None
disk_path = machine_dir / "disko.nix" disk_path = machine_dir / "disko.nix"
if disk_path.exists(): if disk_path.exists():
@@ -80,7 +80,9 @@ def get_machine_details(flake_url: Path, machine_name: str) -> MachineDetails:
if data: if data:
disk_schema = data # type: ignore disk_schema = data # type: ignore
return MachineDetails(machine=machine, hw_config=hw_config, disk_schema=disk_schema) return MachineDetails(
machine=machine_inv, hw_config=hw_config, disk_schema=disk_schema
)
def list_nixos_machines(flake_url: str | Path) -> list[str]: def list_nixos_machines(flake_url: str | Path) -> list[str]:

View File

@@ -1,4 +1,5 @@
import pytest import pytest
from clan_cli.flake import Flake
from clan_cli.inventory import load_inventory_json from clan_cli.inventory import load_inventory_json
from clan_cli.secrets.folders import sops_machines_folder from clan_cli.secrets.folders import sops_machines_folder
from clan_cli.tests import fixtures_flakes from clan_cli.tests import fixtures_flakes
@@ -24,7 +25,7 @@ def test_machine_subcommands(
] ]
) )
inventory: dict = dict(load_inventory_json(str(test_flake_with_core.path))) inventory: dict = dict(load_inventory_json(Flake(str(test_flake_with_core.path))))
assert "machine1" in inventory["machines"] assert "machine1" in inventory["machines"]
assert "service" not in inventory assert "service" not in inventory
@@ -40,7 +41,7 @@ def test_machine_subcommands(
["machines", "delete", "--flake", str(test_flake_with_core.path), "machine1"] ["machines", "delete", "--flake", str(test_flake_with_core.path), "machine1"]
) )
inventory_2: dict = dict(load_inventory_json(str(test_flake_with_core.path))) inventory_2: dict = dict(load_inventory_json(Flake(str(test_flake_with_core.path))))
assert "machine1" not in inventory_2["machines"] assert "machine1" not in inventory_2["machines"]
assert "service" not in inventory_2 assert "service" not in inventory_2

View File

@@ -86,7 +86,7 @@ def test_add_module_to_inventory(
} }
} }
set_inventory(inventory, base_path, "Add borgbackup service") set_inventory(inventory, Flake(str(base_path)), "Add borgbackup service")
# cmd = ["facts", "generate", "--flake", str(test_flake_with_core.path), "machine1"] # cmd = ["facts", "generate", "--flake", str(test_flake_with_core.path), "machine1"]
cmd = [ cmd = [

View File

@@ -2,12 +2,12 @@ import json
import logging import logging
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path
from typing import Any, TypedDict from typing import Any, TypedDict
from uuid import uuid4 from uuid import uuid4
from clan_cli.dirs import TemplateType, clan_templates from clan_cli.dirs import TemplateType, clan_templates
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.flake import Flake
from clan_cli.git import commit_file from clan_cli.git import commit_file
from clan_cli.machines.hardware import HardwareConfig, show_machine_hardware_config 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 @API.register
def get_disk_schemas( def get_disk_schemas(
base_path: Path, machine_name: str | None = None flake: Flake, machine_name: str | None = None
) -> dict[str, DiskSchema]: ) -> dict[str, DiskSchema]:
""" """
Get the available disk schemas Get the available disk schemas
@@ -85,9 +85,7 @@ def get_disk_schemas(
hw_report = {} hw_report = {}
if machine_name is not None: if machine_name is not None:
hw_report_path = HardwareConfig.NIXOS_FACTER.config_path( hw_report_path = HardwareConfig.NIXOS_FACTER.config_path(flake, machine_name)
base_path, machine_name
)
if not hw_report_path.exists(): if not hw_report_path.exists():
msg = "Hardware configuration missing" msg = "Hardware configuration missing"
raise ClanError(msg) raise ClanError(msg)
@@ -132,7 +130,7 @@ class MachineDiskMatter(TypedDict):
@API.register @API.register
def set_machine_disk_schema( def set_machine_disk_schema(
base_path: Path, flake: Flake,
machine_name: str, machine_name: str,
schema_name: str, schema_name: str,
# Placeholders are used to fill in the disk schema # Placeholders are used to fill in the disk schema
@@ -144,8 +142,8 @@ def set_machine_disk_schema(
Set the disk placeholders of the template Set the disk placeholders of the template
""" """
# Assert the hw-config must exist before setting the disk # Assert the hw-config must exist before setting the disk
hw_config = show_machine_hardware_config(base_path, machine_name) hw_config = show_machine_hardware_config(flake, machine_name)
hw_config_path = hw_config.config_path(base_path, machine_name) hw_config_path = hw_config.config_path(flake, machine_name)
if not hw_config_path.exists(): if not hw_config_path.exists():
msg = "Hardware configuration must exist before applying disk schema" msg = "Hardware configuration must exist before applying disk schema"
@@ -162,7 +160,7 @@ def set_machine_disk_schema(
raise ClanError(msg) raise ClanError(msg)
# Check that the placeholders are valid # Check that the placeholders are valid
disk_schema = get_disk_schemas(base_path, machine_name)[schema_name] disk_schema = get_disk_schemas(flake, machine_name)[schema_name]
# check that all required placeholders are present # check that all required placeholders are present
for placeholder_name, schema_placeholder in disk_schema.placeholders.items(): for placeholder_name, schema_placeholder in disk_schema.placeholders.items():
if schema_placeholder.required and placeholder_name not in placeholders: if schema_placeholder.required and placeholder_name not in placeholders:
@@ -223,6 +221,6 @@ def set_machine_disk_schema(
commit_file( commit_file(
disko_file_path, disko_file_path,
base_path, flake.path,
commit_message=f"Set disk schema of machine: {machine_name} to {schema_name}", commit_message=f"Set disk schema of machine: {machine_name} to {schema_name}",
) )

View File

@@ -209,7 +209,7 @@ def test_clan_create_api(
# ===== CREATE BASE INVENTORY ====== # ===== CREATE BASE INVENTORY ======
inventory = create_base_inventory(ssh_keys) inventory = create_base_inventory(ssh_keys)
patch_inventory_with(dest_clan_dir, "services", inventory.services) patch_inventory_with(Flake(str(dest_clan_dir)), "services", inventory.services)
# Invalidate cache because of new inventory # Invalidate cache because of new inventory
clan_dir_flake.invalidate_cache() clan_dir_flake.invalidate_cache()
@@ -240,7 +240,7 @@ def test_clan_create_api(
facter_json = test_lib_root / "assets" / "facter.json" facter_json = test_lib_root / "assets" / "facter.json"
assert facter_json.exists(), f"Source facter file not found: {facter_json}" assert facter_json.exists(), f"Source facter file not found: {facter_json}"
dest_dir = specific_machine_dir(clan_dir_flake.path, machine.name) dest_dir = specific_machine_dir(Flake(str(clan_dir_flake.path)), machine.name)
# specific_machine_dir should create the directory, but ensure it exists just in case # specific_machine_dir should create the directory, but ensure it exists just in case
dest_dir.mkdir(parents=True, exist_ok=True) dest_dir.mkdir(parents=True, exist_ok=True)
@@ -254,7 +254,8 @@ def test_clan_create_api(
# ===== Create Disko Config ====== # ===== Create Disko Config ======
facter_path = ( facter_path = (
specific_machine_dir(clan_dir_flake.path, machine.name) / "facter.json" specific_machine_dir(Flake(str(clan_dir_flake.path)), machine.name)
/ "facter.json"
) )
with facter_path.open("r") as f: with facter_path.open("r") as f:
facter_report = json.load(f) facter_report = json.load(f)
@@ -264,9 +265,7 @@ def test_clan_create_api(
assert disk_devs is not None assert disk_devs is not None
placeholders = {"mainDisk": disk_devs[0]} placeholders = {"mainDisk": disk_devs[0]}
set_machine_disk_schema( set_machine_disk_schema(clan_dir_flake, machine.name, "single-disk", placeholders)
clan_dir_flake.path, machine.name, "single-disk", placeholders
)
clan_dir_flake.invalidate_cache() clan_dir_flake.invalidate_cache()
with pytest.raises(ClanError) as exc_info: with pytest.raises(ClanError) as exc_info:

View File

@@ -13,9 +13,9 @@ export async function get_inventory(client: QueryClient, base_path: string) {
queryKey: [base_path, "inventory"], queryKey: [base_path, "inventory"],
queryFn: () => { queryFn: () => {
console.log("Refreshing inventory"); console.log("Refreshing inventory");
return callApi("get_inventory", { base_path }) as Promise< return callApi("get_inventory", {
ApiEnvelope<Inventory> flake: { identifier: base_path },
>; }) as Promise<ApiEnvelope<Inventory>>;
}, },
revalidateIfStale: true, revalidateIfStale: true,
staleTime: 60 * 1000, staleTime: 60 * 1000,

View File

@@ -6,7 +6,7 @@ export const instance_name = (machine_name: string) =>
export async function get_iwd_service(base_path: string, machine_name: string) { export async function get_iwd_service(base_path: string, machine_name: string) {
const r = await callApi("get_inventory", { const r = await callApi("get_inventory", {
base_path, flake: { identifier: base_path },
}); });
if (r.status == "error") { if (r.status == "error") {
return null; return null;

View File

@@ -44,7 +44,7 @@ export const tagsQuery = (uri: string | null) =>
if (!uri) return []; if (!uri) return [];
const response = await callApi("get_inventory", { const response = await callApi("get_inventory", {
base_path: uri, flake: { identifier: uri },
}); });
if (response.status === "error") { if (response.status === "error") {
toast.error("Failed to fetch data"); toast.error("Failed to fetch data");
@@ -65,7 +65,7 @@ export const machinesQuery = (uri: string | null) =>
if (!uri) return []; if (!uri) return [];
const response = await callApi("get_inventory", { const response = await callApi("get_inventory", {
base_path: uri, flake: { identifier: uri },
}); });
if (response.status === "error") { if (response.status === "error") {
toast.error("Failed to fetch data"); toast.error("Failed to fetch data");

View File

@@ -42,7 +42,7 @@ const EditClanForm = (props: EditClanFormProps) => {
(async () => { (async () => {
await callApi("update_clan_meta", { await callApi("update_clan_meta", {
options: { options: {
directory: props.directory, flake: { identifier: props.directory },
meta: values, meta: values,
}, },
}); });

View File

@@ -10,7 +10,9 @@ export function DiskView() {
const currUri = activeURI(); const currUri = activeURI();
if (currUri) { if (currUri) {
// Example of calling an API // Example of calling an API
const result = await callApi("get_inventory", { base_path: currUri }); const result = await callApi("get_inventory", {
flake: { identifier: currUri },
});
if (result.status === "error") throw new Error("Failed to fetch data"); if (result.status === "error") throw new Error("Failed to fetch data");
return result.data; return result.data;
} }

View File

@@ -117,7 +117,7 @@ const InstallMachine = (props: InstallMachineProps) => {
if (shouldRunDisk) { if (shouldRunDisk) {
setProgressText("Setting up disk ... (1/5)"); setProgressText("Setting up disk ... (1/5)");
const disk_response = await callApi("set_machine_disk_schema", { const disk_response = await callApi("set_machine_disk_schema", {
base_path: curr_uri, flake: { identifier: curr_uri },
machine_name: props.name, machine_name: props.name,
placeholders: diskValues.placeholders, placeholders: diskValues.placeholders,
schema_name: diskValues.schema, schema_name: diskValues.schema,
@@ -417,7 +417,9 @@ const MachineForm = (props: MachineDetailsProps) => {
} }
const machine_response = await callApi("set_machine", { const machine_response = await callApi("set_machine", {
flake_url: curr_uri, flake: {
identifier: curr_uri,
},
machine_name: props.initialData.machine.name || "My machine", machine_name: props.initialData.machine.name || "My machine",
machine: { machine: {
...values.machine, ...values.machine,
@@ -703,8 +705,12 @@ export const MachineDetails = () => {
const curr = activeURI(); const curr = activeURI();
if (curr) { if (curr) {
const result = await callApi("get_machine_details", { const result = await callApi("get_machine_details", {
flake_url: curr, machine: {
machine_name: params.id, flake: {
identifier: curr,
},
name: params.id,
},
}); });
if (result.status === "error") throw new Error("Failed to fetch data"); if (result.status === "error") throw new Error("Failed to fetch data");
return result.data; return result.data;

View File

@@ -37,7 +37,9 @@ export const DiskStep = (props: StepProps<DiskValues>) => {
queryKey: [props.dir, props.machine_id, "disk_schemas"], queryKey: [props.dir, props.machine_id, "disk_schemas"],
queryFn: async () => { queryFn: async () => {
const result = await callApi("get_disk_schemas", { const result = await callApi("get_disk_schemas", {
base_path: props.dir, flake: {
identifier: props.dir,
},
machine_name: props.machine_id, machine_name: props.machine_id,
}); });
if (result.status === "error") throw new Error("Failed to fetch data"); if (result.status === "error") throw new Error("Failed to fetch data");

View File

@@ -52,7 +52,9 @@ export const HWStep = (props: StepProps<HardwareValues>) => {
queryKey: [props.dir, props.machine_id, "hw_report"], queryKey: [props.dir, props.machine_id, "hw_report"],
queryFn: async () => { queryFn: async () => {
const result = await callApi("show_machine_hardware_config", { const result = await callApi("show_machine_hardware_config", {
clan_dir: props.dir, flake: {
identifier: props.dir,
},
machine_name: props.machine_id, machine_name: props.machine_id,
}); });
if (result.status === "error") throw new Error("Failed to fetch data"); if (result.status === "error") throw new Error("Failed to fetch data");

View File

@@ -39,7 +39,9 @@ export const MachineListView: Component = () => {
const uri = activeURI(); const uri = activeURI();
if (uri) { if (uri) {
const response = await callApi("list_machines", { const response = await callApi("list_machines", {
flake_url: uri, flake: {
identifier: uri,
},
}); });
if (response.status === "error") { if (response.status === "error") {
toast.error("Failed to fetch data"); toast.error("Failed to fetch data");