make machine class now a dataclass
Some checks failed
buildbot/nix-build .#checks.aarch64-darwin.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-minimal-inventory-machine Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-flash-installer Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-minimal-inventory-machine Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.check-for-breakpoints Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-flash-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-cli Build done.
buildbot/nix-build .#checks.x86_64-linux.package-default Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-cli-docs Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-apk Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-archlinux Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-deb Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-bash Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-test-backup Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-ts-api Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-rpm Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-git Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-test-backup Build done.
buildbot/nix-build .#checks.x86_64-linux.renderClanOptions Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-sshpass Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-clan-cli Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-app-no-breakpoints Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-test-backup Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-webview-ui Build done.
buildbot/nix-build .#checks.x86_64-linux.package-module-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-nix Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-openssh Build done.
buildbot/nix-build .#checks.x86_64-linux."clan-dep-python3.11-mypy" Build done.
buildbot/nix-build .#checks.x86_64-linux."clan-dep-python3.11-qemu" Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-rsync Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-sops Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-tor Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-zbar Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-e2fsprogs Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-age Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-app-pytest Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-default Build done.
buildbot/nix-build .#checks.x86_64-linux.borgbackup Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-jsonschema-example-valid Build done.
buildbot/nix-build .#checks.x86_64-linux.container Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-inventory-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-inventory-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-jsonschema-nix-unit-tests Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-minimal-inventory-machine Build done.
buildbot/nix-build .#checks.x86_64-linux.package-editor Build done.
buildbot/nix-build .#checks.x86_64-linux.package-impure-checks Build done.
buildbot/nix-build .#checks.x86_64-linux.package-merge-after-ci Build done.
buildbot/nix-build .#checks.x86_64-linux.package-pending-reviews Build done.
buildbot/nix-build .#checks.x86_64-linux.package-tea-create-pr Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zerotier-members Build done.
buildbot/nix-build .#checks.x86_64-linux.package-deploy-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.matrix-synapse Build done.
buildbot/nix-build .#checks.x86_64-linux.deltachat Build done.
buildbot/nix-build .#checks.x86_64-linux.module-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.package-function-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-flash-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.package-module-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.postgresql Build done.
buildbot/nix-build .#checks.x86_64-linux.secrets Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-clan-app Build done.
buildbot/nix-build .#checks.x86_64-linux.template-minimal Build done.
buildbot/nix-build .#checks.x86_64-linux.package-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.syncthing Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zt-tcp-relay Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zerotierone Build done.
buildbot/nix-build .#checks.x86_64-linux.package-moonlight-sunshine-accept Build done.
buildbot/nix-build .#checks.x86_64-linux.package-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.zt-tcp-relay Build done.
buildbot/nix-build .#checks.x86_64-linux.wayland-proxy-virtwl Build done.
buildbot/nix-build .#checks.x86_64-linux.treefmt Build done.
buildbot/nix-build .#checks.x86_64-linux.package-webview-ui Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-inventory-eval Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-app Build done.
buildbot/nix-build .#checks.x86_64-linux.module-clan-vars-eval Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-install-test-ubuntu-22-04 Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-pytest-without-core Build done.
checks / checks-impure (pull_request) Failing after 1m36s
buildbot/nix-build .#checks.x86_64-linux.clan-pytest-with-core Build done.
buildbot/nix-build .#checks.x86_64-linux.test-backups Build done.
buildbot/nix-build .#checks.x86_64-linux.flash Build done.
buildbot/nix-build .#checks.x86_64-linux.test-installation Build done.
buildbot/nix-eval Build done.

This commit is contained in:
Jörg Thalheim 2024-07-02 14:06:31 +02:00
parent cc583dd79e
commit d0f159192c
19 changed files with 107 additions and 110 deletions

View File

@ -2,6 +2,7 @@ import argparse
import json import json
import logging import logging
from ..clan_uri import FlakeId
from ..completions import ( from ..completions import (
add_dynamic_completer, add_dynamic_completer,
complete_backup_providers_for_machine, complete_backup_providers_for_machine,
@ -40,7 +41,7 @@ def create_backup(machine: Machine, provider: str | None = None) -> None:
def create_command(args: argparse.Namespace) -> None: def create_command(args: argparse.Namespace) -> None:
if args.flake is None: if args.flake is None:
raise ClanError("Could not find clan flake toplevel directory") raise ClanError("Could not find clan flake toplevel directory")
machine = Machine(name=args.machine, flake=args.flake) machine = Machine(name=args.machine, flake=FlakeId(args.flake))
create_backup(machine=machine, provider=args.provider) create_backup(machine=machine, provider=args.provider)

View File

@ -3,11 +3,10 @@ import json
import subprocess import subprocess
from dataclasses import dataclass from dataclasses import dataclass
from ..completions import ( from ..clan_uri import FlakeId
add_dynamic_completer, from ..completions import (add_dynamic_completer,
complete_backup_providers_for_machine, complete_backup_providers_for_machine,
complete_machines, complete_machines)
)
from ..errors import ClanError from ..errors import ClanError
from ..machines.machines import Machine from ..machines.machines import Machine
@ -55,7 +54,7 @@ def list_backups(machine: Machine, provider: str | None = None) -> list[Backup]:
def list_command(args: argparse.Namespace) -> None: def list_command(args: argparse.Namespace) -> None:
if args.flake is None: if args.flake is None:
raise ClanError("Could not find clan flake toplevel directory") raise ClanError("Could not find clan flake toplevel directory")
machine = Machine(name=args.machine, flake=args.flake) machine = Machine(name=args.machine, flake=FlakeId(args.flake))
backups = list_backups(machine=machine, provider=args.provider) backups = list_backups(machine=machine, provider=args.provider)
for backup in backups: for backup in backups:
print(backup.name) print(backup.name)

View File

@ -2,6 +2,7 @@ import argparse
import json import json
import subprocess import subprocess
from ..clan_uri import FlakeId
from ..completions import ( from ..completions import (
add_dynamic_completer, add_dynamic_completer,
complete_backup_providers_for_machine, complete_backup_providers_for_machine,
@ -86,7 +87,7 @@ def restore_backup(
def restore_command(args: argparse.Namespace) -> None: def restore_command(args: argparse.Namespace) -> None:
if args.flake is None: if args.flake is None:
raise ClanError("Could not find clan flake toplevel directory") raise ClanError("Could not find clan flake toplevel directory")
machine = Machine(name=args.machine, flake=args.flake) machine = Machine(name=args.machine, flake=FlakeId(args.flake))
restore_backup( restore_backup(
machine=machine, machine=machine,
provider=args.provider, provider=args.provider,

View File

@ -2,6 +2,7 @@ import argparse
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from ..clan_uri import FlakeId
from ..cmd import run from ..cmd import run
from ..dirs import machine_gcroot from ..dirs import machine_gcroot
from ..errors import ClanError from ..errors import ClanError
@ -45,7 +46,7 @@ def inspect_flake(flake_url: str | Path, machine_name: str) -> FlakeConfig:
f"Machine {machine_name} not found in {flake_url}. Available machines: {', '.join(machines)}" f"Machine {machine_name} not found in {flake_url}. Available machines: {', '.join(machines)}"
) )
machine = Machine(machine_name, flake_url) machine = Machine(machine_name, FlakeId(flake_url))
vm = inspect_vm(machine) vm = inspect_vm(machine)
# Make symlink to gcroots from vm.machine_icon # Make symlink to gcroots from vm.machine_icon
@ -102,13 +103,13 @@ def inspect_flake(flake_url: str | Path, machine_name: str) -> FlakeConfig:
@dataclass @dataclass
class InspectOptions: class InspectOptions:
machine: str machine: str
flake: Path flake: FlakeId
def inspect_command(args: argparse.Namespace) -> None: def inspect_command(args: argparse.Namespace) -> None:
inspect_options = InspectOptions( inspect_options = InspectOptions(
machine=args.machine, machine=args.machine,
flake=args.flake or Path.cwd(), flake=FlakeId(args.flake or Path.cwd()),
) )
res = inspect_flake( res = inspect_flake(
flake_url=inspect_options.flake, machine_name=inspect_options.machine flake_url=inspect_options.flake, machine_name=inspect_options.machine

View File

@ -37,8 +37,9 @@ class FlakeId:
# Define the ClanURI class # Define the ClanURI class
@dataclass
class ClanURI: class ClanURI:
flake_id: FlakeId flake: FlakeId
machine_name: str machine_name: str
# Initialize the class with a clan:// URI # Initialize the class with a clan:// URI
@ -62,7 +63,7 @@ class ClanURI:
clean_comps = components._replace(query=components.query, fragment="") clean_comps = components._replace(query=components.query, fragment="")
# Parse the URL into a ClanUrl object # Parse the URL into a ClanUrl object
self.flake_id = self._parse_url(clean_comps) self.flake = self._parse_url(clean_comps)
self.machine_name = "defaultVM" self.machine_name = "defaultVM"
if components.fragment: if components.fragment:
self.machine_name = components.fragment self.machine_name = components.fragment
@ -85,7 +86,7 @@ class ClanURI:
return flake_id return flake_id
def get_url(self) -> str: def get_url(self) -> str:
return str(self.flake_id) return str(self.flake)
@classmethod @classmethod
def from_str( def from_str(

View File

@ -2,6 +2,7 @@ import argparse
import importlib import importlib
import logging import logging
from ..clan_uri import FlakeId
from ..completions import add_dynamic_completer, complete_machines from ..completions import add_dynamic_completer, complete_machines
from ..machines.machines import Machine from ..machines.machines import Machine
@ -49,7 +50,7 @@ def check_secrets(machine: Machine, service: None | str = None) -> bool:
def check_command(args: argparse.Namespace) -> None: def check_command(args: argparse.Namespace) -> None:
machine = Machine( machine = Machine(
name=args.machine, name=args.machine,
flake=args.flake, flake=FlakeId(args.flake),
) )
check_secrets(machine, service=args.service) check_secrets(machine, service=args.service)

View File

@ -3,6 +3,7 @@ import importlib
import json import json
import logging import logging
from ..clan_uri import FlakeId
from ..completions import add_dynamic_completer, complete_machines from ..completions import add_dynamic_completer, complete_machines
from ..machines.machines import Machine from ..machines.machines import Machine
@ -26,7 +27,7 @@ def get_all_facts(machine: Machine) -> dict:
def get_command(args: argparse.Namespace) -> None: def get_command(args: argparse.Namespace) -> None:
machine = Machine(name=args.machine, flake=args.flake) machine = Machine(name=args.machine, flake=FlakeId(args.flake))
# the raw_facts are bytestrings making them not json serializable # the raw_facts are bytestrings making them not json serializable
raw_facts = get_all_facts(machine) raw_facts = get_all_facts(machine)

View File

@ -4,6 +4,7 @@ import logging
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from ..clan_uri import FlakeId
from ..cmd import Log, run from ..cmd import Log, run
from ..completions import add_dynamic_completer, complete_machines from ..completions import add_dynamic_completer, complete_machines
from ..machines.machines import Machine from ..machines.machines import Machine
@ -44,7 +45,7 @@ def upload_secrets(machine: Machine) -> 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=FlakeId(args.flake))
upload_secrets(machine) upload_secrets(machine)

View File

@ -11,6 +11,7 @@ from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from typing import Any from typing import Any
from .clan_uri import FlakeId
from .cmd import Log, run from .cmd import Log, run
from .completions import add_dynamic_completer, complete_machines from .completions import add_dynamic_completer, complete_machines
from .errors import ClanError from .errors import ClanError
@ -87,7 +88,7 @@ def flash_machine(
@dataclass @dataclass
class FlashOptions: class FlashOptions:
flake: Path flake: FlakeId
machine: str machine: str
disks: dict[str, str] disks: dict[str, str]
ssh_keys_path: list[Path] ssh_keys_path: list[Path]
@ -119,7 +120,7 @@ class AppendDiskAction(argparse.Action):
def flash_command(args: argparse.Namespace) -> None: def flash_command(args: argparse.Namespace) -> None:
opts = FlashOptions( opts = FlashOptions(
flake=args.flake, flake=FlakeId(args.flake),
machine=args.machine, machine=args.machine,
disks=args.disk, disks=args.disk,
ssh_keys_path=args.ssh_pubkey, ssh_keys_path=args.ssh_pubkey,

View File

@ -7,6 +7,7 @@ from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from ..clan_uri import FlakeId
from ..cmd import Log, run from ..cmd import Log, run
from ..completions import add_dynamic_completer, complete_machines from ..completions import add_dynamic_completer, complete_machines
from ..facts.generate import generate_facts from ..facts.generate import generate_facts
@ -90,7 +91,7 @@ def install_nixos(
@dataclass @dataclass
class InstallOptions: class InstallOptions:
flake: Path flake: FlakeId
machine: str machine: str
target_host: str target_host: str
kexec: str | None kexec: str | None
@ -123,7 +124,7 @@ def install_command(args: argparse.Namespace) -> None:
password = None password = None
opts = InstallOptions( opts = InstallOptions(
flake=args.flake, flake=FlakeId(args.flake),
machine=args.machine, machine=args.machine,
target_host=target_host, target_host=target_host,
kexec=args.kexec, kexec=args.kexec,

View File

@ -1,6 +1,7 @@
import json import json
from pathlib import Path from pathlib import Path
from ..clan_uri import FlakeId
from ..cmd import run from ..cmd import run
from ..nix import nix_build, nix_config from ..nix import nix_build, nix_config
from .machines import Machine from .machines import Machine
@ -21,8 +22,8 @@ def get_all_machines(flake_dir: Path, nix_options: list[str]) -> list[Machine]:
machines.append( machines.append(
Machine( Machine(
name=name, name=name,
flake=flake_dir, flake=FlakeId(flake_dir),
deployment_info=machine_data, cached_deployment=machine_data,
nix_options=nix_options, nix_options=nix_options,
) )
) )
@ -34,5 +35,7 @@ def get_selected_machines(
) -> list[Machine]: ) -> list[Machine]:
machines = [] machines = []
for name in machine_names: for name in machine_names:
machines.append(Machine(name=name, flake=flake_dir, nix_options=nix_options)) machines.append(
Machine(name=name, flake=FlakeId(flake_dir), nix_options=nix_options)
)
return machines return machines

View File

@ -1,10 +1,11 @@
import json import json
import logging import logging
from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from typing import Any from typing import Any
from clan_cli.clan_uri import ClanURI, FlakeId from clan_cli.clan_uri import FlakeId
from ..cmd import run_no_stdout from ..cmd import run_no_stdout
from ..errors import ClanError from ..errors import ClanError
@ -14,63 +15,41 @@ from ..ssh import Host, parse_deployment_address
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@dataclass
class Machine: class Machine:
name: str name: str
flake: FlakeId flake: FlakeId
nix_options: list[str] nix_options: list[str] = field(default_factory=list)
eval_cache: dict[str, str] cached_deployment: None | dict = None
build_cache: dict[str, Path]
_flake_path: Path | None
_deployment_info: None | dict
def __init__( _eval_cache: dict[str, str] = field(default_factory=dict)
self, _build_cache: dict[str, Path] = field(default_factory=dict)
name: str,
flake: Path | str,
deployment_info: dict | None = None,
nix_options: list[str] = [],
) -> None:
"""
Creates a Machine
@name: the name of the machine
@clan_dir: the directory of the clan, optional, if not set it will be determined from the current working directory
@machine_json: can be optionally used to skip evaluation of the machine, location of the json file with machine data
"""
uri = ClanURI.from_str(str(flake), name)
self.flake_id = uri.flake_id
self.name: str = uri.machine_name
self.eval_cache: dict[str, str] = {}
self.build_cache: dict[str, Path] = {}
self._flake_path: Path | None = None
self._deployment_info: None | dict = deployment_info
self.nix_options = nix_options
def flush_caches(self) -> None: def flush_caches(self) -> None:
self._deployment_info = None self.cached_deployment = None
self._flake_path = None self._build_cache.clear()
self.build_cache.clear() self._eval_cache.clear()
self.eval_cache.clear()
def __str__(self) -> str: def __str__(self) -> str:
return f"Machine(name={self.name}, flake={self.flake_id})" return f"Machine(name={self.name}, flake={self.flake})"
def __repr__(self) -> str: def __repr__(self) -> str:
return str(self) return str(self)
@property @property
def deployment_info(self) -> dict: def deployment(self) -> dict:
if self._deployment_info is not None: if self.cached_deployment is not None:
return self._deployment_info return self.cached_deployment
self._deployment_info = json.loads( deployment = json.loads(
self.build_nix("config.system.clan.deployment.file").read_text() self.build_nix("config.system.clan.deployment.file").read_text()
) )
return self._deployment_info self.cached_deployment = deployment
return deployment
@property @property
def target_host_address(self) -> str: def target_host_address(self) -> str:
# deploymentAddress is deprecated. # deploymentAddress is deprecated.
val = self.deployment_info.get("targetHost") or self.deployment_info.get( val = self.deployment.get("targetHost") or self.deployment.get(
"deploymentAddress" "deploymentAddress"
) )
if val is None: if val is None:
@ -80,40 +59,34 @@ class Machine:
@target_host_address.setter @target_host_address.setter
def target_host_address(self, value: str) -> None: def target_host_address(self, value: str) -> None:
self.deployment_info["targetHost"] = value self.deployment["targetHost"] = value
@property @property
def secret_facts_module(self) -> str: def secret_facts_module(self) -> str:
return self.deployment_info["facts"]["secretModule"] return self.deployment["facts"]["secretModule"]
@property @property
def public_facts_module(self) -> str: def public_facts_module(self) -> str:
return self.deployment_info["facts"]["publicModule"] return self.deployment["facts"]["publicModule"]
@property @property
def facts_data(self) -> dict[str, dict[str, Any]]: def facts_data(self) -> dict[str, dict[str, Any]]:
if self.deployment_info["facts"]["services"]: if self.deployment["facts"]["services"]:
return self.deployment_info["facts"]["services"] return self.deployment["facts"]["services"]
return {} return {}
@property @property
def secrets_upload_directory(self) -> str: def secrets_upload_directory(self) -> str:
return self.deployment_info["facts"]["secretUploadDirectory"] return self.deployment["facts"]["secretUploadDirectory"]
@property @property
def flake_dir(self) -> Path: def flake_dir(self) -> Path:
if self._flake_path: if self.flake.is_local():
return self._flake_path return self.flake.path
elif self.flake.is_remote():
if self.flake_id.is_local(): return Path(nix_metadata(self.flake.url)["path"])
self._flake_path = self.flake_id.path
elif self.flake_id.is_remote():
self._flake_path = Path(nix_metadata(self.flake_id.url)["path"])
else: else:
raise ClanError(f"Unsupported flake url: {self.flake_id}") raise ClanError(f"Unsupported flake url: {self.flake}")
assert self._flake_path is not None
return self._flake_path
@property @property
def target_host(self) -> Host: def target_host(self) -> Host:
@ -127,7 +100,7 @@ class Machine:
The host where the machine is built and deployed from. The host where the machine is built and deployed from.
Can be the same as the target host. Can be the same as the target host.
""" """
build_host = self.deployment_info.get("buildHost") build_host = self.deployment.get("buildHost")
if build_host is None: if build_host is None:
return self.target_host return self.target_host
# enable ssh agent forwarding to allow the build host to access the target host # enable ssh agent forwarding to allow the build host to access the target host
@ -230,12 +203,12 @@ class Machine:
eval a nix attribute of the machine eval a nix attribute of the machine
@attr: the attribute to get @attr: the attribute to get
""" """
if attr in self.eval_cache and not refresh and extra_config is None: if attr in self._eval_cache and not refresh and extra_config is None:
return self.eval_cache[attr] return self._eval_cache[attr]
output = self.nix("eval", attr, extra_config, impure, nix_options) output = self.nix("eval", attr, extra_config, impure, nix_options)
if isinstance(output, str): if isinstance(output, str):
self.eval_cache[attr] = output self._eval_cache[attr] = output
return output return output
else: else:
raise ClanError("eval_nix returned not a string") raise ClanError("eval_nix returned not a string")
@ -253,12 +226,12 @@ class Machine:
@attr: the attribute to get @attr: the attribute to get
""" """
if attr in self.build_cache and not refresh and extra_config is None: if attr in self._build_cache and not refresh and extra_config is None:
return self.build_cache[attr] return self._build_cache[attr]
output = self.nix("build", attr, extra_config, impure, nix_options) output = self.nix("build", attr, extra_config, impure, nix_options)
if isinstance(output, Path): if isinstance(output, Path):
self.build_cache[attr] = output self._build_cache[attr] = output
return output return output
else: else:
raise ClanError("build_nix returned not a Path") raise ClanError("build_nix returned not a Path")

View File

@ -5,6 +5,7 @@ import os
import shlex import shlex
import sys import sys
from ..clan_uri import FlakeId
from ..cmd import run from ..cmd import run
from ..completions import add_dynamic_completer, complete_machines from ..completions import add_dynamic_completer, complete_machines
from ..errors import ClanError from ..errors import ClanError
@ -80,7 +81,7 @@ def upload_sources(
) )
def deploy_nixos(machines: MachineGroup) -> None: def deploy_machine(machines: MachineGroup) -> None:
""" """
Deploy to all hosts in parallel Deploy to all hosts in parallel
""" """
@ -137,7 +138,7 @@ def update(args: argparse.Namespace) -> None:
machines = [] machines = []
if len(args.machines) == 1 and args.target_host is not None: if len(args.machines) == 1 and args.target_host is not None:
machine = Machine( machine = Machine(
name=args.machines[0], flake=args.flake, nix_options=args.option name=args.machines[0], flake=FlakeId(args.flake), nix_options=args.option
) )
machine.target_host_address = args.target_host machine.target_host_address = args.target_host
machines.append(machine) machines.append(machine)
@ -149,7 +150,7 @@ def update(args: argparse.Namespace) -> None:
if len(args.machines) == 0: if len(args.machines) == 0:
ignored_machines = [] ignored_machines = []
for machine in get_all_machines(args.flake, args.option): for machine in get_all_machines(args.flake, args.option):
if machine.deployment_info.get("requireExplicitUpdate", False): if machine.deployment.get("requireExplicitUpdate", False):
continue continue
try: try:
machine.build_host machine.build_host
@ -170,7 +171,7 @@ def update(args: argparse.Namespace) -> None:
else: else:
machines = get_selected_machines(args.flake, args.option, args.machines) machines = get_selected_machines(args.flake, args.option, args.machines)
deploy_nixos(MachineGroup(machines)) deploy_machine(MachineGroup(machines))
def register_update_parser(parser: argparse.ArgumentParser) -> None: def register_update_parser(parser: argparse.ArgumentParser) -> None:
@ -190,4 +191,9 @@ def register_update_parser(parser: argparse.ArgumentParser) -> None:
type=str, type=str,
help="address of the machine to update, in the format of user@host:1234", help="address of the machine to update, in the format of user@host:1234",
) )
parser.add_argument(
"--darwin",
type=str,
help="Hack to deploy darwin machines. This will be removed in the future when we have full darwin integration.",
)
parser.set_defaults(func=update) parser.set_defaults(func=update)

View File

@ -9,6 +9,7 @@ from pathlib import Path
from typing import IO from typing import IO
from .. import tty from .. import tty
from ..clan_uri import FlakeId
from ..completions import ( from ..completions import (
add_dynamic_completer, add_dynamic_completer,
complete_groups, complete_groups,
@ -263,13 +264,13 @@ def list_secrets(flake_dir: Path, pattern: str | None = None) -> list[str]:
@dataclass @dataclass
class ListSecretsOptions: class ListSecretsOptions:
flake: Path flake: FlakeId
pattern: str | None pattern: str | None
def list_command(args: argparse.Namespace) -> None: def list_command(args: argparse.Namespace) -> None:
options = ListSecretsOptions( options = ListSecretsOptions(
flake=args.flake, flake=FlakeId(args.flake),
pattern=args.pattern, pattern=args.pattern,
) )
lst = list_secrets(options.flake, options.pattern) lst = list_secrets(options.flake, options.pattern)

View File

@ -3,6 +3,7 @@ import json
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from ..clan_uri import FlakeId
from ..completions import add_dynamic_completer, complete_machines from ..completions import add_dynamic_completer, complete_machines
from ..machines.machines import Machine from ..machines.machines import Machine
@ -29,13 +30,13 @@ def inspect_vm(machine: Machine) -> VmConfig:
@dataclass @dataclass
class InspectOptions: class InspectOptions:
machine: str machine: str
flake: Path flake: FlakeId
def inspect_command(args: argparse.Namespace) -> None: def inspect_command(args: argparse.Namespace) -> None:
inspect_options = InspectOptions( inspect_options = InspectOptions(
machine=args.machine, machine=args.machine,
flake=args.flake or Path.cwd(), flake=FlakeId(args.flake or Path.cwd()),
) )
machine = Machine(inspect_options.machine, inspect_options.flake) machine = Machine(inspect_options.machine, inspect_options.flake)

View File

@ -7,6 +7,7 @@ from contextlib import ExitStack
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from ..clan_uri import FlakeId
from ..cmd import Log, run from ..cmd import Log, run
from ..completions import add_dynamic_completer, complete_machines from ..completions import add_dynamic_completer, complete_machines
from ..dirs import module_root, user_cache_dir, vm_state_dir from ..dirs import module_root, user_cache_dir, vm_state_dir
@ -110,7 +111,7 @@ def run_vm(
nix_options: list[str] = [], nix_options: list[str] = [],
) -> None: ) -> None:
with ExitStack() as stack: with ExitStack() as stack:
machine = Machine(vm.machine_name, vm.flake_url) machine = Machine(vm.machine_name, FlakeId(vm.flake_url))
log.debug(f"Creating VM for {machine}") log.debug(f"Creating VM for {machine}")
# store the temporary rootfs inside XDG_CACHE_HOME on the host # store the temporary rootfs inside XDG_CACHE_HOME on the host
@ -198,7 +199,7 @@ def run_command(
option: list[str] = [], option: list[str] = [],
**kwargs: dict[str, str], **kwargs: dict[str, str],
) -> None: ) -> None:
machine_obj: Machine = Machine(machine, flake) machine_obj: Machine = Machine(machine, FlakeId(flake))
vm: VmConfig = inspect_vm(machine=machine_obj) vm: VmConfig = inspect_vm(machine=machine_obj)

View File

@ -21,13 +21,13 @@ def test_get_url() -> None:
def test_local_uri() -> None: def test_local_uri() -> None:
# Create a ClanURI object from a local URI # Create a ClanURI object from a local URI
uri = ClanURI("clan://file:///home/user/Downloads") uri = ClanURI("clan://file:///home/user/Downloads")
assert uri.flake_id.path == Path("/home/user/Downloads") assert uri.flake.path == Path("/home/user/Downloads")
def test_is_remote() -> None: def test_is_remote() -> None:
# Create a ClanURI object from a remote URI # Create a ClanURI object from a remote URI
uri = ClanURI("clan://https://example.com") uri = ClanURI("clan://https://example.com")
assert uri.flake_id.url == "https://example.com" assert uri.flake.url == "https://example.com"
def test_direct_local_path() -> None: def test_direct_local_path() -> None:
@ -47,35 +47,35 @@ def test_remote_with_clanparams() -> None:
uri = ClanURI("clan://https://example.com") uri = ClanURI("clan://https://example.com")
assert uri.machine_name == "defaultVM" assert uri.machine_name == "defaultVM"
assert uri.flake_id.url == "https://example.com" assert uri.flake.url == "https://example.com"
def test_from_str_remote() -> None: def test_from_str_remote() -> None:
uri = ClanURI.from_str(url="https://example.com", machine_name="myVM") uri = ClanURI.from_str(url="https://example.com", machine_name="myVM")
assert uri.get_url() == "https://example.com" assert uri.get_url() == "https://example.com"
assert uri.machine_name == "myVM" assert uri.machine_name == "myVM"
assert uri.flake_id.url == "https://example.com" assert uri.flake.url == "https://example.com"
def test_from_str_local() -> None: def test_from_str_local() -> None:
uri = ClanURI.from_str(url="~/Projects/democlan", machine_name="myVM") uri = ClanURI.from_str(url="~/Projects/democlan", machine_name="myVM")
assert uri.get_url().endswith("/Projects/democlan") assert uri.get_url().endswith("/Projects/democlan")
assert uri.machine_name == "myVM" assert uri.machine_name == "myVM"
assert uri.flake_id.is_local() assert uri.flake.is_local()
assert str(uri.flake_id).endswith("/Projects/democlan") # type: ignore assert str(uri.flake).endswith("/Projects/democlan") # type: ignore
def test_from_str_local_no_machine() -> None: def test_from_str_local_no_machine() -> None:
uri = ClanURI.from_str("~/Projects/democlan") uri = ClanURI.from_str("~/Projects/democlan")
assert uri.get_url().endswith("/Projects/democlan") assert uri.get_url().endswith("/Projects/democlan")
assert uri.machine_name == "defaultVM" assert uri.machine_name == "defaultVM"
assert uri.flake_id.is_local() assert uri.flake.is_local()
assert str(uri.flake_id).endswith("/Projects/democlan") # type: ignore assert str(uri.flake).endswith("/Projects/democlan") # type: ignore
def test_from_str_local_no_machine2() -> None: def test_from_str_local_no_machine2() -> None:
uri = ClanURI.from_str("~/Projects/democlan#syncthing-peer1") uri = ClanURI.from_str("~/Projects/democlan#syncthing-peer1")
assert uri.get_url().endswith("/Projects/democlan") assert uri.get_url().endswith("/Projects/democlan")
assert uri.machine_name == "syncthing-peer1" assert uri.machine_name == "syncthing-peer1"
assert uri.flake_id.is_local() assert uri.flake.is_local()
assert str(uri.flake_id).endswith("/Projects/democlan") # type: ignore assert str(uri.flake).endswith("/Projects/democlan") # type: ignore

View File

@ -6,6 +6,7 @@ from cli import Cli
from fixtures_flakes import FlakeForTest from fixtures_flakes import FlakeForTest
from validator import is_valid_age_key, is_valid_ssh_key from validator import is_valid_age_key, is_valid_ssh_key
from clan_cli.clan_uri import FlakeId
from clan_cli.facts.secret_modules.sops import SecretStore from clan_cli.facts.secret_modules.sops import SecretStore
from clan_cli.machines.facts import machine_get_fact from clan_cli.machines.facts import machine_get_fact
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
@ -48,7 +49,7 @@ def test_generate_secret(
) )
cmd = ["facts", "generate", "--flake", str(test_flake_with_core.path), "vm1"] cmd = ["facts", "generate", "--flake", str(test_flake_with_core.path), "vm1"]
cli.run(cmd) cli.run(cmd)
store1 = SecretStore(Machine(name="vm1", flake=test_flake_with_core.path)) store1 = SecretStore(Machine(name="vm1", flake=FlakeId(test_flake_with_core.path)))
assert store1.exists("", "age.key") assert store1.exists("", "age.key")
assert store1.exists("", "zerotier-identity-secret") assert store1.exists("", "zerotier-identity-secret")
@ -76,7 +77,7 @@ def test_generate_secret(
secrets_folder / "vm1-zerotier-identity-secret" / "machines" / "vm1" secrets_folder / "vm1-zerotier-identity-secret" / "machines" / "vm1"
).exists() ).exists()
store2 = SecretStore(Machine(name="vm2", flake=test_flake_with_core.path)) store2 = SecretStore(Machine(name="vm2", flake=FlakeId(test_flake_with_core.path)))
assert store2.exists("", "password") assert store2.exists("", "password")
assert store2.exists("", "password-hash") assert store2.exists("", "password-hash")

View File

@ -6,6 +6,7 @@ from cli import Cli
from fixtures_flakes import FlakeForTest from fixtures_flakes import FlakeForTest
from validator import is_valid_ssh_key from validator import is_valid_ssh_key
from clan_cli.clan_uri import FlakeId
from clan_cli.facts.secret_modules.password_store import SecretStore from clan_cli.facts.secret_modules.password_store import SecretStore
from clan_cli.machines.facts import machine_get_fact from clan_cli.machines.facts import machine_get_fact
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
@ -48,7 +49,9 @@ def test_upload_secret(
) )
cli.run(["facts", "generate", "vm1"]) cli.run(["facts", "generate", "vm1"])
store = SecretStore(Machine(name="vm1", flake=test_flake_with_core_and_pass.path)) store = SecretStore(
Machine(name="vm1", flake=FlakeId(test_flake_with_core_and_pass.path))
)
network_id = machine_get_fact( network_id = machine_get_fact(
test_flake_with_core_and_pass.path, "vm1", "zerotier-network-id" test_flake_with_core_and_pass.path, "vm1", "zerotier-network-id"