clan-cli: Use machine object everywhere instead of name + flake
This commit is contained in:
@@ -6,8 +6,9 @@ from urllib.parse import urlparse
|
|||||||
|
|
||||||
from clan_lib.api import API
|
from clan_lib.api import API
|
||||||
|
|
||||||
from clan_cli.cmd import run_no_stdout
|
from clan_cli.cmd import run
|
||||||
from clan_cli.errors import ClanCmdError, ClanError
|
from clan_cli.errors import ClanCmdError, ClanError
|
||||||
|
from clan_cli.flake import Flake
|
||||||
from clan_cli.inventory import Meta
|
from clan_cli.inventory import Meta
|
||||||
from clan_cli.nix import nix_eval
|
from clan_cli.nix import nix_eval
|
||||||
|
|
||||||
@@ -15,26 +16,26 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def show_clan_meta(uri: str) -> Meta:
|
def show_clan_meta(flake: Flake) -> Meta:
|
||||||
if uri.startswith("/") and not Path(uri).exists():
|
if flake.is_local and not flake.path.exists():
|
||||||
msg = f"Path {uri} does not exist"
|
msg = f"Path {flake} does not exist"
|
||||||
raise ClanError(msg, description="clan directory does not exist")
|
raise ClanError(msg, description="clan directory does not exist")
|
||||||
cmd = nix_eval(
|
cmd = nix_eval(
|
||||||
[
|
[
|
||||||
f"{uri}#clanInternals.inventory.meta",
|
f"{flake}#clanInternals.inventory.meta",
|
||||||
"--json",
|
"--json",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
res = "{}"
|
res = "{}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
proc = run_no_stdout(cmd)
|
proc = run(cmd)
|
||||||
res = proc.stdout.strip()
|
res = proc.stdout.strip()
|
||||||
except ClanCmdError as e:
|
except ClanCmdError as e:
|
||||||
msg = "Evaluation failed on meta attribute"
|
msg = "Evaluation failed on meta attribute"
|
||||||
raise ClanError(
|
raise ClanError(
|
||||||
msg,
|
msg,
|
||||||
location=f"show_clan {uri}",
|
location=f"show_clan {flake}",
|
||||||
description=str(e.cmd),
|
description=str(e.cmd),
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
@@ -53,16 +54,16 @@ def show_clan_meta(uri: str) -> Meta:
|
|||||||
msg = "Invalid absolute path"
|
msg = "Invalid absolute path"
|
||||||
raise ClanError(
|
raise ClanError(
|
||||||
msg,
|
msg,
|
||||||
location=f"show_clan {uri}",
|
location=f"show_clan {flake}",
|
||||||
description="Icon path must be a URL or a relative path",
|
description="Icon path must be a URL or a relative path",
|
||||||
)
|
)
|
||||||
|
|
||||||
icon_path = str((Path(uri) / meta_icon).resolve())
|
icon_path = str((flake.path / meta_icon).resolve())
|
||||||
else:
|
else:
|
||||||
msg = "Invalid schema"
|
msg = "Invalid schema"
|
||||||
raise ClanError(
|
raise ClanError(
|
||||||
msg,
|
msg,
|
||||||
location=f"show_clan {uri}",
|
location=f"show_clan {flake}",
|
||||||
description="Icon path must be a URL or a relative path",
|
description="Icon path must be a URL or a relative path",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from .errors import ClanError
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from clan_cli.flake import Flake
|
from clan_cli.flake import Flake
|
||||||
|
from clan_cli.machines.machines import Machine
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -144,8 +145,8 @@ def machines_dir(flake: "Flake") -> Path:
|
|||||||
return Path(store_path) / "machines"
|
return Path(store_path) / "machines"
|
||||||
|
|
||||||
|
|
||||||
def specific_machine_dir(flake: "Flake", machine: str) -> Path:
|
def specific_machine_dir(machine: "Machine") -> Path:
|
||||||
return machines_dir(flake) / machine
|
return machines_dir(machine.flake) / machine.name
|
||||||
|
|
||||||
|
|
||||||
def module_root() -> Path:
|
def module_root() -> Path:
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ from tempfile import TemporaryDirectory
|
|||||||
|
|
||||||
from clan_cli.completions import add_dynamic_completer, complete_machines
|
from clan_cli.completions import add_dynamic_completer, complete_machines
|
||||||
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.upload import upload
|
from clan_cli.ssh.upload import upload
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def upload_secrets(machine: Machine, host: Host) -> None:
|
def upload_secrets(machine: Machine) -> None:
|
||||||
|
with machine.target_host() as host:
|
||||||
if not machine.secret_facts_store.needs_upload(host):
|
if not machine.secret_facts_store.needs_upload(host):
|
||||||
machine.info("Secrets already uploaded")
|
machine.info("Secrets already uploaded")
|
||||||
return
|
return
|
||||||
@@ -25,8 +25,7 @@ def upload_secrets(machine: Machine, host: Host) -> None:
|
|||||||
|
|
||||||
def upload_command(args: argparse.Namespace) -> None:
|
def upload_command(args: argparse.Namespace) -> None:
|
||||||
machine = Machine(name=args.machine, flake=args.flake)
|
machine = Machine(name=args.machine, flake=args.flake)
|
||||||
with machine.target_host() as host:
|
upload_secrets(machine)
|
||||||
upload_secrets(machine, host)
|
|
||||||
|
|
||||||
|
|
||||||
def register_upload_parser(parser: argparse.ArgumentParser) -> None:
|
def register_upload_parser(parser: argparse.ArgumentParser) -> None:
|
||||||
|
|||||||
@@ -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(
|
||||||
Flake(str(clan_dir)), f"machines.{machine_name}", dataclass_to_dict(new_machine)
|
opts.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
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ 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 inventory
|
||||||
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.dirs import specific_machine_dir
|
||||||
|
from clan_cli.machines.machines import Machine
|
||||||
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
|
||||||
@@ -15,49 +16,46 @@ from clan_cli.secrets.secrets import (
|
|||||||
list_secrets,
|
list_secrets,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .machines import Machine
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def delete_machine(flake: Flake, name: str) -> None:
|
def delete_machine(machine: Machine) -> None:
|
||||||
try:
|
try:
|
||||||
inventory.delete(flake, {f"machines.{name}"})
|
inventory.delete(machine.flake, {f"machines.{machine.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
|
||||||
# personal clan ended up in the inventory for some reason, so I think
|
# personal clan ended up in the inventory for some reason, so I think
|
||||||
# it makes sense to eat the exception here.
|
# it makes sense to eat the exception here.
|
||||||
log.warning(
|
log.warning(
|
||||||
f"{name} was missing or already deleted from the machines inventory: {exc}"
|
f"{machine.name} was missing or already deleted from the machines inventory: {exc}"
|
||||||
)
|
)
|
||||||
|
|
||||||
changed_paths: list[Path] = []
|
changed_paths: list[Path] = []
|
||||||
|
|
||||||
folder = specific_machine_dir(flake, name)
|
folder = specific_machine_dir(machine)
|
||||||
if folder.exists():
|
if folder.exists():
|
||||||
changed_paths.append(folder)
|
changed_paths.append(folder)
|
||||||
shutil.rmtree(folder)
|
shutil.rmtree(folder)
|
||||||
|
|
||||||
# louis@(2025-02-04): clean-up legacy (pre-vars) secrets:
|
# louis@(2025-02-04): clean-up legacy (pre-vars) secrets:
|
||||||
sops_folder = sops_secrets_folder(flake.path)
|
sops_folder = sops_secrets_folder(machine.flake.path)
|
||||||
filter_fn = lambda secret_name: secret_name.startswith(f"{name}-")
|
filter_fn = lambda secret_name: secret_name.startswith(f"{machine.name}-")
|
||||||
for secret_name in list_secrets(flake.path, filter_fn):
|
for secret_name in list_secrets(machine.flake.path, filter_fn):
|
||||||
secret_path = sops_folder / secret_name
|
secret_path = sops_folder / secret_name
|
||||||
changed_paths.append(secret_path)
|
changed_paths.append(secret_path)
|
||||||
shutil.rmtree(secret_path)
|
shutil.rmtree(secret_path)
|
||||||
|
|
||||||
machine = Machine(name, flake)
|
|
||||||
changed_paths.extend(machine.public_vars_store.delete_store())
|
changed_paths.extend(machine.public_vars_store.delete_store())
|
||||||
changed_paths.extend(machine.secret_vars_store.delete_store())
|
changed_paths.extend(machine.secret_vars_store.delete_store())
|
||||||
# Remove the machine's key, and update secrets & vars that referenced it:
|
# Remove the machine's key, and update secrets & vars that referenced it:
|
||||||
if secrets_has_machine(flake.path, name):
|
if secrets_has_machine(machine.flake.path, machine.name):
|
||||||
secrets_machine_remove(flake.path, name)
|
secrets_machine_remove(machine.flake.path, machine.name)
|
||||||
|
|
||||||
|
|
||||||
def delete_command(args: argparse.Namespace) -> None:
|
def delete_command(args: argparse.Namespace) -> None:
|
||||||
delete_machine(args.flake, args.name)
|
delete_machine(Machine(flake=args.flake, name=args.name))
|
||||||
|
|
||||||
|
|
||||||
def register_delete_parser(parser: argparse.ArgumentParser) -> None:
|
def register_delete_parser(parser: argparse.ArgumentParser) -> None:
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ from pathlib import Path
|
|||||||
|
|
||||||
from clan_lib.api import API
|
from clan_lib.api import API
|
||||||
|
|
||||||
from clan_cli.cmd import RunOpts, run_no_stdout
|
from clan_cli.cmd import RunOpts, 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 specific_machine_dir
|
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.git import commit_file
|
from clan_cli.git import commit_file
|
||||||
from clan_cli.machines.machines import Machine
|
from clan_cli.machines.machines import Machine
|
||||||
from clan_cli.nix import nix_config, nix_eval
|
from clan_cli.nix import nix_config, nix_eval
|
||||||
@@ -26,39 +25,35 @@ class HardwareConfig(Enum):
|
|||||||
NIXOS_GENERATE_CONFIG = "nixos-generate-config"
|
NIXOS_GENERATE_CONFIG = "nixos-generate-config"
|
||||||
NONE = "none"
|
NONE = "none"
|
||||||
|
|
||||||
def config_path(self, flake: Flake, machine_name: str) -> Path:
|
def config_path(self, machine: Machine) -> Path:
|
||||||
machine_dir = specific_machine_dir(flake, machine_name)
|
machine_dir = specific_machine_dir(machine)
|
||||||
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"], machine: Machine) -> "HardwareConfig":
|
||||||
cls: type["HardwareConfig"], flake: Flake, machine_name: str
|
hardware_config = HardwareConfig.NIXOS_GENERATE_CONFIG.config_path(machine)
|
||||||
) -> "HardwareConfig":
|
|
||||||
hardware_config = HardwareConfig.NIXOS_GENERATE_CONFIG.config_path(
|
|
||||||
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(flake, machine_name).exists():
|
if HardwareConfig.NIXOS_FACTER.config_path(machine).exists():
|
||||||
return HardwareConfig.NIXOS_FACTER
|
return HardwareConfig.NIXOS_FACTER
|
||||||
|
|
||||||
return HardwareConfig.NONE
|
return HardwareConfig.NONE
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def show_machine_hardware_config(flake: Flake, machine_name: str) -> HardwareConfig:
|
def show_machine_hardware_config(machine: Machine) -> 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(flake, machine_name)
|
return HardwareConfig.detect_type(machine)
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def show_machine_hardware_platform(flake: Flake, machine_name: str) -> str | None:
|
def show_machine_hardware_platform(machine: Machine) -> 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,13 +61,13 @@ def show_machine_hardware_platform(flake: Flake, machine_name: str) -> str | Non
|
|||||||
system = config["system"]
|
system = config["system"]
|
||||||
cmd = nix_eval(
|
cmd = nix_eval(
|
||||||
[
|
[
|
||||||
f"{flake}#clanInternals.machines.{system}.{machine_name}",
|
f"{machine.flake}#clanInternals.machines.{system}.{machine.name}",
|
||||||
"--apply",
|
"--apply",
|
||||||
"machine: { inherit (machine.pkgs) system; }",
|
"machine: { inherit (machine.pkgs) system; }",
|
||||||
"--json",
|
"--json",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
proc = run_no_stdout(cmd, RunOpts(prefix=machine_name))
|
proc = run(cmd, RunOpts(prefix=machine.name))
|
||||||
res = proc.stdout.strip()
|
res = proc.stdout.strip()
|
||||||
|
|
||||||
host_platform = json.loads(res)
|
host_platform = json.loads(res)
|
||||||
@@ -81,11 +76,8 @@ def show_machine_hardware_platform(flake: Flake, machine_name: str) -> str | Non
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class HardwareGenerateOptions:
|
class HardwareGenerateOptions:
|
||||||
flake: Flake
|
machine: Machine
|
||||||
machine: str
|
|
||||||
backend: HardwareConfig
|
backend: HardwareConfig
|
||||||
target_host: str | None = None
|
|
||||||
keyfile: str | None = None
|
|
||||||
password: str | None = None
|
password: str | None = None
|
||||||
|
|
||||||
|
|
||||||
@@ -96,14 +88,9 @@ def generate_machine_hardware_info(opts: HardwareGenerateOptions) -> HardwareCon
|
|||||||
and place the resulting *.nix file in the machine's directory.
|
and place the resulting *.nix file in the machine's directory.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
machine = Machine(
|
machine = opts.machine
|
||||||
opts.machine,
|
|
||||||
flake=opts.flake,
|
|
||||||
private_key=Path(opts.keyfile) if opts.keyfile else None,
|
|
||||||
override_target_host=opts.target_host,
|
|
||||||
)
|
|
||||||
|
|
||||||
hw_file = opts.backend.config_path(opts.flake, opts.machine)
|
hw_file = opts.backend.config_path(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:
|
||||||
@@ -148,11 +135,11 @@ def generate_machine_hardware_info(opts: HardwareGenerateOptions) -> HardwareCon
|
|||||||
|
|
||||||
commit_file(
|
commit_file(
|
||||||
hw_file,
|
hw_file,
|
||||||
opts.flake.path,
|
opts.machine.flake.path,
|
||||||
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, opts.machine)
|
show_machine_hardware_platform(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:
|
||||||
@@ -173,10 +160,13 @@ def generate_machine_hardware_info(opts: HardwareGenerateOptions) -> HardwareCon
|
|||||||
|
|
||||||
|
|
||||||
def update_hardware_config_command(args: argparse.Namespace) -> None:
|
def update_hardware_config_command(args: argparse.Namespace) -> None:
|
||||||
opts = HardwareGenerateOptions(
|
machine = Machine(
|
||||||
flake=args.flake,
|
flake=args.flake,
|
||||||
machine=args.machine,
|
name=args.machine,
|
||||||
target_host=args.target_host,
|
override_target_host=args.target_host,
|
||||||
|
)
|
||||||
|
opts = HardwareGenerateOptions(
|
||||||
|
machine=machine,
|
||||||
password=args.password,
|
password=args.password,
|
||||||
backend=HardwareConfig(args.backend),
|
backend=HardwareConfig(args.backend),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -111,11 +111,7 @@ def install_machine(opts: InstallOptions) -> None:
|
|||||||
[
|
[
|
||||||
"--generate-hardware-config",
|
"--generate-hardware-config",
|
||||||
str(opts.update_hardware_config.value),
|
str(opts.update_hardware_config.value),
|
||||||
str(
|
str(opts.update_hardware_config.config_path(machine)),
|
||||||
opts.update_hardware_config.config_path(
|
|
||||||
machine.flake, machine.name
|
|
||||||
)
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -67,9 +67,9 @@ def get_machine_details(machine: Machine) -> MachineDetails:
|
|||||||
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(machine.flake, machine.name)
|
hw_config = HardwareConfig.detect_type(machine)
|
||||||
|
|
||||||
machine_dir = specific_machine_dir(machine.flake, machine.name)
|
machine_dir = specific_machine_dir(machine)
|
||||||
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():
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ def deploy_machine(machine: Machine) -> None:
|
|||||||
generate_facts([machine], service=None, regenerate=False)
|
generate_facts([machine], service=None, regenerate=False)
|
||||||
generate_vars([machine], generator_name=None, regenerate=False)
|
generate_vars([machine], generator_name=None, regenerate=False)
|
||||||
|
|
||||||
upload_secrets(machine, target_host)
|
upload_secrets(machine)
|
||||||
upload_secret_vars(machine, target_host)
|
upload_secret_vars(machine, target_host)
|
||||||
|
|
||||||
path = upload_sources(machine, host)
|
path = upload_sources(machine, host)
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ def upload(
|
|||||||
for mdir in dirs:
|
for mdir in dirs:
|
||||||
dir_path = Path(root) / mdir
|
dir_path = Path(root) / mdir
|
||||||
tarinfo = tar.gettarinfo(
|
tarinfo = tar.gettarinfo(
|
||||||
dir_path, arcname=str(dir_path.relative_to(str(local_src)))
|
dir_path,
|
||||||
|
arcname=str(dir_path.relative_to(str(local_src))),
|
||||||
)
|
)
|
||||||
tarinfo.mode = dir_mode
|
tarinfo.mode = dir_mode
|
||||||
tarinfo.uname = file_user
|
tarinfo.uname = file_user
|
||||||
|
|||||||
@@ -10,8 +10,15 @@ from pathlib import Path
|
|||||||
from typing import Any, NamedTuple
|
from typing import Any, NamedTuple
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from clan_cli.dirs import TemplateType, clan_templates, nixpkgs_source
|
from clan_cli.dirs import (
|
||||||
|
TemplateType,
|
||||||
|
clan_templates,
|
||||||
|
nixpkgs_source,
|
||||||
|
specific_machine_dir,
|
||||||
|
)
|
||||||
|
from clan_cli.flake import Flake
|
||||||
from clan_cli.locked_open import locked_open
|
from clan_cli.locked_open import locked_open
|
||||||
|
from clan_cli.machines.machines import Machine
|
||||||
from clan_cli.nix import nix_test_store
|
from clan_cli.nix import nix_test_store
|
||||||
from clan_cli.tests import age_keys
|
from clan_cli.tests import age_keys
|
||||||
from clan_cli.tests.fixture_error import FixtureError
|
from clan_cli.tests.fixture_error import FixtureError
|
||||||
@@ -70,11 +77,10 @@ class FlakeForTest(NamedTuple):
|
|||||||
|
|
||||||
|
|
||||||
def set_machine_settings(
|
def set_machine_settings(
|
||||||
flake: Path,
|
machine: Machine,
|
||||||
machine_name: str,
|
|
||||||
machine_settings: dict,
|
machine_settings: dict,
|
||||||
) -> None:
|
) -> None:
|
||||||
config_path = flake / "machines" / machine_name / "configuration.json"
|
config_path = specific_machine_dir(machine) / "configuration.json"
|
||||||
config_path.write_text(json.dumps(machine_settings, indent=2))
|
config_path.write_text(json.dumps(machine_settings, indent=2))
|
||||||
|
|
||||||
|
|
||||||
@@ -202,7 +208,8 @@ class ClanFlake:
|
|||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
set_machine_settings(self.path, machine_name, machine_config)
|
machine = Machine(name=machine_name, flake=Flake(str(self.path)))
|
||||||
|
set_machine_settings(machine, machine_config)
|
||||||
sp.run(["git", "add", "."], cwd=self.path, check=True)
|
sp.run(["git", "add", "."], cwd=self.path, check=True)
|
||||||
sp.run(
|
sp.run(
|
||||||
["git", "commit", "-a", "-m", "Update by flake generator"],
|
["git", "commit", "-a", "-m", "Update by flake generator"],
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ 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
|
||||||
|
from clan_cli.machines.machines import Machine
|
||||||
|
|
||||||
from clan_lib.api import API
|
from clan_lib.api import API
|
||||||
from clan_lib.api.modules import Frontmatter, extract_frontmatter
|
from clan_lib.api.modules import Frontmatter, extract_frontmatter
|
||||||
@@ -74,9 +74,7 @@ templates: dict[str, dict[str, Callable[[dict[str, Any]], Placeholder]]] = {
|
|||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def get_disk_schemas(
|
def get_disk_schemas(machine: Machine) -> dict[str, DiskSchema]:
|
||||||
flake: Flake, machine_name: str | None = None
|
|
||||||
) -> dict[str, DiskSchema]:
|
|
||||||
"""
|
"""
|
||||||
Get the available disk schemas
|
Get the available disk schemas
|
||||||
"""
|
"""
|
||||||
@@ -84,8 +82,7 @@ def get_disk_schemas(
|
|||||||
disk_schemas = {}
|
disk_schemas = {}
|
||||||
hw_report = {}
|
hw_report = {}
|
||||||
|
|
||||||
if machine_name is not None:
|
hw_report_path = HardwareConfig.NIXOS_FACTER.config_path(machine)
|
||||||
hw_report_path = HardwareConfig.NIXOS_FACTER.config_path(flake, 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)
|
||||||
@@ -130,8 +127,7 @@ class MachineDiskMatter(TypedDict):
|
|||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def set_machine_disk_schema(
|
def set_machine_disk_schema(
|
||||||
flake: Flake,
|
machine: Machine,
|
||||||
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
|
||||||
# Use get disk schemas to get the placeholders and their options
|
# Use get disk schemas to get the placeholders and their options
|
||||||
@@ -142,8 +138,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(flake, machine_name)
|
hw_config = show_machine_hardware_config(machine)
|
||||||
hw_config_path = hw_config.config_path(flake, machine_name)
|
hw_config_path = hw_config.config_path(machine)
|
||||||
|
|
||||||
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"
|
||||||
@@ -160,7 +156,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(flake, machine_name)[schema_name]
|
disk_schema = get_disk_schemas(machine)[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:
|
||||||
@@ -221,6 +217,6 @@ def set_machine_disk_schema(
|
|||||||
|
|
||||||
commit_file(
|
commit_file(
|
||||||
disko_file_path,
|
disko_file_path,
|
||||||
flake.path,
|
machine.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}",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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(Flake(str(clan_dir_flake.path)), machine.name)
|
dest_dir = specific_machine_dir(machine)
|
||||||
# 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)
|
||||||
|
|
||||||
@@ -253,10 +253,7 @@ def test_clan_create_api(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# ===== Create Disko Config ======
|
# ===== Create Disko Config ======
|
||||||
facter_path = (
|
facter_path = specific_machine_dir(machine) / "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)
|
||||||
|
|
||||||
@@ -265,7 +262,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(clan_dir_flake, machine.name, "single-disk", placeholders)
|
set_machine_disk_schema(machine, "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:
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ export { clanList, setClanList };
|
|||||||
(async function () {
|
(async function () {
|
||||||
const curr = activeURI();
|
const curr = activeURI();
|
||||||
if (curr) {
|
if (curr) {
|
||||||
const result = await callApi("show_clan_meta", { uri: curr });
|
const result = await callApi("show_clan_meta", {
|
||||||
|
flake: { identifier: curr },
|
||||||
|
});
|
||||||
console.log("refetched meta for ", curr);
|
console.log("refetched meta for ", curr);
|
||||||
if (result.status === "error") {
|
if (result.status === "error") {
|
||||||
result.errors.forEach((error) => {
|
result.errors.forEach((error) => {
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ export const Sidebar = (props: RouteSectionProps) => {
|
|||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const curr = activeURI();
|
const curr = activeURI();
|
||||||
if (curr) {
|
if (curr) {
|
||||||
const result = await callApi("show_clan_meta", { uri: curr });
|
const result = await callApi("show_clan_meta", {
|
||||||
|
flake: { identifier: curr },
|
||||||
|
});
|
||||||
console.log("refetched meta for ", curr);
|
console.log("refetched meta for ", curr);
|
||||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||||
|
|
||||||
|
|||||||
@@ -329,7 +329,9 @@ export const ClanDetails = () => {
|
|||||||
const clanQuery = createQuery(() => ({
|
const clanQuery = createQuery(() => ({
|
||||||
queryKey: [clan_dir, "inventory", "meta"],
|
queryKey: [clan_dir, "inventory", "meta"],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const result = await callApi("show_clan_meta", { uri: clan_dir });
|
const result = await callApi("show_clan_meta", {
|
||||||
|
flake: { identifier: clan_dir },
|
||||||
|
});
|
||||||
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;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ const ClanItem = (props: ClanItemProps) => {
|
|||||||
const details = createQuery(() => ({
|
const details = createQuery(() => ({
|
||||||
queryKey: [clan_dir, "meta"],
|
queryKey: [clan_dir, "meta"],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const result = await callApi("show_clan_meta", { uri: clan_dir });
|
const result = await callApi("show_clan_meta", {
|
||||||
|
flake: { identifier: clan_dir },
|
||||||
|
});
|
||||||
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;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -117,8 +117,10 @@ 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", {
|
||||||
|
machine: {
|
||||||
flake: { identifier: curr_uri },
|
flake: { identifier: curr_uri },
|
||||||
machine_name: props.name,
|
name: props.name,
|
||||||
|
},
|
||||||
placeholders: diskValues.placeholders,
|
placeholders: diskValues.placeholders,
|
||||||
schema_name: diskValues.schema,
|
schema_name: diskValues.schema,
|
||||||
force: true,
|
force: true,
|
||||||
|
|||||||
@@ -37,10 +37,12 @@ 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", {
|
||||||
|
machine: {
|
||||||
flake: {
|
flake: {
|
||||||
identifier: props.dir,
|
identifier: props.dir,
|
||||||
},
|
},
|
||||||
machine_name: props.machine_id,
|
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");
|
||||||
return result.data;
|
return result.data;
|
||||||
|
|||||||
@@ -52,10 +52,12 @@ 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", {
|
||||||
|
machine: {
|
||||||
flake: {
|
flake: {
|
||||||
identifier: props.dir,
|
identifier: props.dir,
|
||||||
},
|
},
|
||||||
machine_name: props.machine_id,
|
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");
|
||||||
return result.data;
|
return result.data;
|
||||||
@@ -85,9 +87,13 @@ export const HWStep = (props: StepProps<HardwareValues>) => {
|
|||||||
setIsGenerating(true);
|
setIsGenerating(true);
|
||||||
const r = await callApi("generate_machine_hardware_info", {
|
const r = await callApi("generate_machine_hardware_info", {
|
||||||
opts: {
|
opts: {
|
||||||
flake: { identifier: curr_uri },
|
machine: {
|
||||||
machine: props.machine_id,
|
name: props.machine_id,
|
||||||
target_host: target,
|
override_target_host: target,
|
||||||
|
flake: {
|
||||||
|
identifier: curr_uri,
|
||||||
|
},
|
||||||
|
},
|
||||||
backend: "nixos-facter",
|
backend: "nixos-facter",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user