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
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/3531
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ from dataclasses import dataclass
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from clan_cli.clan_dirs import machine_gcroot
|
||||||
from clan_cli.cmd import run
|
from clan_cli.cmd import run
|
||||||
from clan_cli.dirs import machine_gcroot
|
|
||||||
from clan_cli.errors import ClanError
|
from clan_cli.errors import ClanError
|
||||||
from clan_cli.flake import Flake
|
from clan_cli.flake import Flake
|
||||||
from clan_cli.machines.list import list_nixos_machines
|
from clan_cli.machines.list import list_nixos_machines
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
43
pkgs/clan-cli/clan_cli/clan_dirs.py
Normal file
43
pkgs/clan-cli/clan_cli/clan_dirs.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
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
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import urllib
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -24,14 +23,6 @@ def find_git_repo_root() -> Path | None:
|
|||||||
return find_toplevel([".git"])
|
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:
|
def find_toplevel(top_level_files: list[str]) -> Path | None:
|
||||||
"""Returns the path to the toplevel of the clan flake"""
|
"""Returns the path to the toplevel of the clan flake"""
|
||||||
for project_file in top_level_files:
|
for project_file in top_level_files:
|
||||||
@@ -114,31 +105,10 @@ def user_gcroot_dir() -> Path:
|
|||||||
return p
|
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:
|
def user_history_file() -> Path:
|
||||||
return user_config_dir() / "clan" / "history"
|
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:
|
def module_root() -> Path:
|
||||||
return Path(__file__).parent
|
return Path(__file__).parent
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from clan_cli.dirs import vm_state_dir
|
from clan_cli.clan_dirs import vm_state_dir
|
||||||
from clan_cli.errors import ClanError
|
from clan_cli.errors import ClanError
|
||||||
from clan_cli.machines.machines import Machine
|
from clan_cli.machines.machines import Machine
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import shutil
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import override
|
from typing import override
|
||||||
|
|
||||||
from clan_cli.dirs import vm_state_dir
|
from clan_cli.clan_dirs import vm_state_dir
|
||||||
from clan_cli.machines.machines import Machine
|
from clan_cli.machines.machines import Machine
|
||||||
|
|
||||||
from . import SecretStoreBase
|
from . import SecretStoreBase
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ from pathlib import Path
|
|||||||
from clan_lib.api import API
|
from clan_lib.api import API
|
||||||
|
|
||||||
from clan_cli import Flake, inventory
|
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.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.folders import sops_secrets_folder
|
||||||
from clan_cli.secrets.machines import has_machine as secrets_has_machine
|
from clan_cli.secrets.machines import has_machine as secrets_has_machine
|
||||||
from clan_cli.secrets.machines import remove_machine as secrets_machine_remove
|
from clan_cli.secrets.machines import remove_machine as secrets_machine_remove
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ from pathlib import Path
|
|||||||
|
|
||||||
from clan_lib.api import API
|
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.cmd import RunOpts, run_no_stdout
|
||||||
from clan_cli.completions import add_dynamic_completer, complete_machines
|
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.errors import ClanCmdError, ClanError
|
||||||
from clan_cli.flake import Flake
|
from clan_cli.flake import Flake
|
||||||
from clan_cli.git import commit_file
|
from clan_cli.git import commit_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:
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -12,16 +12,18 @@ from clan_lib.api.disk import MachineDiskMatter
|
|||||||
from clan_lib.api.modules import parse_frontmatter
|
from clan_lib.api.modules import parse_frontmatter
|
||||||
from clan_lib.api.serde import dataclass_to_dict
|
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.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.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]:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
# (subdir / ".clan-flake").touch()
|
# (subdir / ".clan-flake").touch()
|
||||||
# assert _get_clan_flake_toplevel() == subdir
|
# assert _get_clan_flake_toplevel() == subdir
|
||||||
|
|
||||||
from clan_cli.dirs import clan_key_safe, vm_state_dir
|
from clan_cli.clan_dirs import clan_key_safe, vm_state_dir
|
||||||
|
|
||||||
|
|
||||||
def test_clan_key_safe() -> None:
|
def test_clan_key_safe() -> None:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,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 = [
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import shutil
|
|||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from clan_cli.dirs import vm_state_dir
|
from clan_cli.clan_dirs import vm_state_dir
|
||||||
from clan_cli.errors import ClanError
|
from clan_cli.errors import ClanError
|
||||||
from clan_cli.machines.machines import Machine
|
from clan_cli.machines.machines import Machine
|
||||||
from clan_cli.ssh.host import Host
|
from clan_cli.ssh.host import Host
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import shutil
|
|||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from clan_cli.dirs import vm_state_dir
|
from clan_cli.clan_dirs import vm_state_dir
|
||||||
from clan_cli.machines.machines import Machine
|
from clan_cli.machines.machines import Machine
|
||||||
from clan_cli.ssh.host import Host
|
from clan_cli.ssh.host import Host
|
||||||
from clan_cli.vars._types import StoreBase
|
from clan_cli.vars._types import StoreBase
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ from dataclasses import dataclass
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
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.cmd import CmdOut, Log, RunOpts, handle_io, run
|
||||||
from clan_cli.completions import add_dynamic_completer, complete_machines
|
from clan_cli.completions import add_dynamic_completer, complete_machines
|
||||||
from clan_cli.dirs import module_root, user_cache_dir, vm_state_dir
|
from clan_cli.dirs import module_root, user_cache_dir
|
||||||
from clan_cli.errors import ClanCmdError, ClanError
|
from clan_cli.errors import ClanCmdError, ClanError
|
||||||
from clan_cli.facts.generate import generate_facts
|
from clan_cli.facts.generate import generate_facts
|
||||||
from clan_cli.machines.machines import Machine
|
from clan_cli.machines.machines import Machine
|
||||||
|
|||||||
@@ -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}",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ from typing import Any
|
|||||||
|
|
||||||
import clan_cli.clan.create
|
import clan_cli.clan.create
|
||||||
import pytest
|
import pytest
|
||||||
|
from clan_cli.clan_dirs import specific_machine_dir
|
||||||
from clan_cli.cmd import RunOpts, run
|
from clan_cli.cmd import RunOpts, run
|
||||||
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.flake import Flake
|
||||||
from clan_cli.inventory import patch_inventory_with
|
from clan_cli.inventory import patch_inventory_with
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from typing import IO, ClassVar
|
|||||||
|
|
||||||
import gi
|
import gi
|
||||||
from clan_cli import vms
|
from clan_cli import vms
|
||||||
from clan_cli.dirs import vm_state_dir
|
from clan_cli.clan_dirs import vm_state_dir
|
||||||
from clan_cli.machines.machines import Machine
|
from clan_cli.machines.machines import Machine
|
||||||
from clan_cli.vms.inspect import inspect_vm
|
from clan_cli.vms.inspect import inspect_vm
|
||||||
from clan_cli.vms.qemu import QMPWrapper
|
from clan_cli.vms.qemu import QMPWrapper
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -43,7 +43,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");
|
||||||
@@ -64,7 +64,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");
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,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,
|
||||||
@@ -415,7 +415,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,
|
||||||
@@ -680,8 +682,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;
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user