clan_cli machines: use Flake instead of FlakeId

This commit is contained in:
lassulus
2025-02-06 04:51:28 +01:00
parent 6d2845c645
commit 32748c14f4
47 changed files with 242 additions and 251 deletions

View File

@@ -22,11 +22,11 @@ from . import (
state, state,
vms, vms,
) )
from .clan_uri import FlakeId
from .custom_logger import setup_logging from .custom_logger import setup_logging
from .dirs import get_clan_flake_toplevel_or_env from .dirs import get_clan_flake_toplevel_or_env
from .errors import ClanError from .errors import ClanError
from .facts import cli as facts from .facts import cli as facts
from .flake import Flake
from .flash import cli as flash_cli from .flash import cli as flash_cli
from .hyperlink import help_hyperlink from .hyperlink import help_hyperlink
from .machines import cli as machines from .machines import cli as machines
@@ -41,17 +41,17 @@ with contextlib.suppress(ImportError):
import argcomplete # type: ignore[no-redef] import argcomplete # type: ignore[no-redef]
def flake_path(arg: str) -> FlakeId: def flake_path(arg: str) -> Flake:
flake_dir = Path(arg).resolve() flake_dir = Path(arg).resolve()
if flake_dir.exists() and flake_dir.is_dir(): if flake_dir.exists() and flake_dir.is_dir():
return FlakeId(str(flake_dir)) return Flake(str(flake_dir))
return FlakeId(arg) return Flake(arg)
def default_flake() -> FlakeId | None: def default_flake() -> Flake | None:
val = get_clan_flake_toplevel_or_env() val = get_clan_flake_toplevel_or_env()
if val: if val:
return FlakeId(str(val)) return Flake(str(val))
return None return None

View File

@@ -5,9 +5,9 @@ from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from clan_cli.api import API from clan_cli.api import API
from clan_cli.clan_uri import FlakeId
from clan_cli.cmd import CmdOut, RunOpts, run from clan_cli.cmd import CmdOut, RunOpts, run
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.flake import Flake
from clan_cli.inventory import Inventory, init_inventory from clan_cli.inventory import Inventory, init_inventory
from clan_cli.nix import nix_shell from clan_cli.nix import nix_shell
from clan_cli.templates import ( from clan_cli.templates import (
@@ -33,7 +33,7 @@ class CreateClanResponse:
class CreateOptions: class CreateOptions:
dest: Path dest: Path
template_name: str template_name: str
src_flake: FlakeId | None = None src_flake: Flake | None = None
input_prio: InputPrio | None = None input_prio: InputPrio | None = None
setup_git: bool = True setup_git: bool = True
initial: Inventory | None = None initial: Inventory | None = None

View File

@@ -3,10 +3,10 @@ from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from clan_cli.clan_uri import FlakeId
from clan_cli.cmd import run from clan_cli.cmd import run
from clan_cli.dirs import machine_gcroot 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.machines.list import list_nixos_machines from clan_cli.machines.list import list_nixos_machines
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.nix import ( from clan_cli.nix import (
@@ -21,7 +21,7 @@ from clan_cli.vms.inspect import VmConfig, inspect_vm
@dataclass @dataclass
class FlakeConfig: class FlakeConfig:
flake_url: FlakeId flake_url: Flake
flake_attr: str flake_attr: str
clan_name: str clan_name: str
@@ -35,7 +35,7 @@ class FlakeConfig:
@classmethod @classmethod
def from_json(cls: type["FlakeConfig"], data: dict[str, Any]) -> "FlakeConfig": def from_json(cls: type["FlakeConfig"], data: dict[str, Any]) -> "FlakeConfig":
return cls( return cls(
flake_url=FlakeId.from_json(data["flake_url"]), flake_url=Flake.from_json(data["flake_url"]),
flake_attr=data["flake_attr"], flake_attr=data["flake_attr"],
clan_name=data["clan_name"], clan_name=data["clan_name"],
nar_hash=data["nar_hash"], nar_hash=data["nar_hash"],
@@ -62,7 +62,7 @@ def inspect_flake(flake_url: str | Path, machine_name: str) -> FlakeConfig:
msg = f"Machine {machine_name} not found in {flake_url}. Available machines: {', '.join(machines)}" msg = f"Machine {machine_name} not found in {flake_url}. Available machines: {', '.join(machines)}"
raise ClanError(msg) raise ClanError(msg)
machine = Machine(machine_name, FlakeId(str(flake_url))) machine = Machine(machine_name, Flake(str(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
@@ -105,7 +105,7 @@ def inspect_flake(flake_url: str | Path, machine_name: str) -> FlakeConfig:
meta = nix_metadata(flake_url) meta = nix_metadata(flake_url)
return FlakeConfig( return FlakeConfig(
vm=vm, vm=vm,
flake_url=FlakeId(str(flake_url)), flake_url=Flake(str(flake_url)),
clan_name=clan_name, clan_name=clan_name,
flake_attr=machine_name, flake_attr=machine_name,
nar_hash=meta["locked"]["narHash"], nar_hash=meta["locked"]["narHash"],
@@ -119,13 +119,13 @@ def inspect_flake(flake_url: str | Path, machine_name: str) -> FlakeConfig:
@dataclass @dataclass
class InspectOptions: class InspectOptions:
machine: str machine: str
flake: FlakeId flake: Flake
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 FlakeId(str(Path.cwd())), flake=args.flake or Flake(str(Path.cwd())),
) )
res = inspect_flake( res = inspect_flake(
flake_url=str(inspect_options.flake), machine_name=inspect_options.machine flake_url=str(inspect_options.flake), machine_name=inspect_options.machine

View File

@@ -3,68 +3,14 @@ import urllib.parse
import urllib.request import urllib.request
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Any
from clan_cli.flake import Flake
@dataclass
class FlakeId:
loc: str
@classmethod
def from_json(cls: type["FlakeId"], data: dict[str, Any]) -> "FlakeId":
return cls(loc=data["loc"])
def __str__(self) -> str:
return str(self.loc)
def __hash__(self) -> int:
return hash(str(self.loc))
@property
def path(self) -> Path:
assert self.is_local(), f"Flake {self.loc} is not a local path"
return Path(self.loc)
@property
def url(self) -> str:
assert self.is_remote(), f"Flake {self.loc} is not a remote url"
return str(self.loc)
def is_local(self) -> bool:
"""
https://nix.dev/manual/nix/2.22/language/builtins.html?highlight=urlS#source-types
Examples:
- file:///home/eelco/nix/README.md file LOCAL
- git+file://git:github.com:NixOS/nixpkgs git+file LOCAL
- https://example.com/index.html https REMOTE
- github:nixos/nixpkgs github REMOTE
- ftp://serv.file ftp REMOTE
- ./. '' LOCAL
"""
x = urllib.parse.urlparse(str(self.loc))
# See above *file* or empty are the only local schemas
return x.scheme == "" or "file" in x.scheme
def is_remote(self) -> bool:
return not self.is_local()
def _parse_url(comps: urllib.parse.ParseResult) -> FlakeId:
if comps.scheme == "" or "file" in comps.scheme:
res_p = Path(comps.path).expanduser().resolve()
flake_id = FlakeId(str(res_p))
else:
flake_id = FlakeId(comps.geturl())
return flake_id
# Define the ClanURI class # Define the ClanURI class
@dataclass @dataclass
class ClanURI: class ClanURI:
flake: FlakeId flake: Flake
machine_name: str machine_name: str
def get_url(self) -> str: def get_url(self) -> str:
@@ -104,7 +50,10 @@ 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
flake = _parse_url(clean_comps) if clean_comps.path and Path(clean_comps.path).exists():
flake = Flake(clean_comps.path)
else:
flake = Flake(clean_comps.geturl())
machine_name = "defaultVM" machine_name = "defaultVM"
if components.fragment: if components.fragment:
machine_name = components.fragment machine_name = components.fragment

View File

@@ -207,13 +207,13 @@ def complete_secrets(
""" """
Provides completion functionality for clan secrets Provides completion functionality for clan secrets
""" """
from .clan_uri import FlakeId from .clan_uri import Flake
from .secrets.secrets import ListSecretsOptions, list_secrets from .secrets.secrets import ListSecretsOptions, list_secrets
flake = clan_dir_result if (clan_dir_result := clan_dir(None)) is not None else "." flake = clan_dir_result if (clan_dir_result := clan_dir(None)) is not None else "."
options = ListSecretsOptions( options = ListSecretsOptions(
flake=FlakeId(flake), flake=Flake(flake),
pattern=None, pattern=None,
) )

View File

@@ -5,8 +5,6 @@ import urllib
from enum import Enum from enum import Enum
from pathlib import Path from pathlib import Path
from clan_cli.clan_uri import FlakeId
from .errors import ClanError from .errors import ClanError
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -110,7 +108,7 @@ def user_history_file() -> Path:
return user_config_dir() / "clan" / "history" return user_config_dir() / "clan" / "history"
def vm_state_dir(flake_url: FlakeId, vm_name: str) -> Path: def vm_state_dir(flake_url: str, vm_name: str) -> Path:
clan_key = clan_key_safe(str(flake_url)) clan_key = clan_key_safe(str(flake_url))
return user_data_dir() / "clan" / "vmstate" / clan_key / vm_name return user_data_dir() / "clan" / "vmstate" / clan_key / vm_name

View File

@@ -12,7 +12,7 @@ class FactStore(FactStoreBase):
self.works_remotely = False self.works_remotely = False
def set(self, service: str, name: str, value: bytes) -> Path | None: def set(self, service: str, name: str, value: bytes) -> Path | None:
if self.machine.flake.is_local(): if self.machine.flake.is_local:
fact_path = ( fact_path = (
self.machine.flake.path self.machine.flake.path
/ "machines" / "machines"

View File

@@ -14,7 +14,7 @@ class FactStore(FactStoreBase):
def __init__(self, machine: Machine) -> None: def __init__(self, machine: Machine) -> None:
self.machine = machine self.machine = machine
self.works_remotely = False self.works_remotely = False
self.dir = vm_state_dir(machine.flake, machine.name) / "facts" self.dir = vm_state_dir(machine.flake.identifier, machine.name) / "facts"
machine.debug(f"FactStore initialized with dir {self.dir}") machine.debug(f"FactStore initialized with dir {self.dir}")
def exists(self, service: str, name: str) -> bool: def exists(self, service: str, name: str) -> bool:

View File

@@ -10,7 +10,7 @@ from . import SecretStoreBase
class SecretStore(SecretStoreBase): class SecretStore(SecretStoreBase):
def __init__(self, machine: Machine) -> None: def __init__(self, machine: Machine) -> None:
self.machine = machine self.machine = machine
self.dir = vm_state_dir(machine.flake, machine.name) / "secrets" self.dir = vm_state_dir(machine.flake.identifier, machine.name) / "secrets"
self.dir.mkdir(parents=True, exist_ok=True) self.dir.mkdir(parents=True, exist_ok=True)
def set( def set(

View File

@@ -1,7 +1,7 @@
import json import json
import logging import logging
import re import re
from dataclasses import dataclass, field from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -275,9 +275,43 @@ class Flake:
""" """
identifier: str identifier: str
cache: FlakeCache = field(default_factory=FlakeCache)
def __post_init__(self) -> None: def __post_init__(self) -> None:
self._cache: FlakeCache | None = None
self._path: Path | None = None
self._is_local: bool | None = None
@classmethod
def from_json(cls: type["Flake"], data: dict[str, Any]) -> "Flake":
return cls(data["identifier"])
def __str__(self) -> str:
return self.identifier
def __hash__(self) -> int:
return hash(self.identifier)
def __eq__(self, other: object) -> bool:
if not isinstance(other, Flake):
return NotImplemented
return self.identifier == other.identifier
@property
def is_local(self) -> bool:
if self._is_local is None:
self.prefetch()
assert isinstance(self._is_local, bool)
return self._is_local
@property
def path(self) -> Path:
if self._path is None:
self.prefetch()
assert isinstance(self._path, Path)
return self._path
def prefetch(self) -> None:
self._cache = FlakeCache()
flake_prefetch = run( flake_prefetch = run(
nix_command( nix_command(
[ [
@@ -294,9 +328,23 @@ class Flake:
flake_metadata = json.loads(flake_prefetch.stdout) flake_metadata = json.loads(flake_prefetch.stdout)
self.store_path = flake_metadata["storePath"] self.store_path = flake_metadata["storePath"]
self.hash = flake_metadata["hash"] self.hash = flake_metadata["hash"]
self.cache = FlakeCache() if flake_metadata["original"].get("url", "").startswith("file:"):
self._is_local = True
path = flake_metadata["original"]["url"].removeprefix("file://")
path = path.removeprefix("file:")
self._path = Path(path)
elif flake_metadata["original"].get("path"):
self._is_local = True
self._path = Path(flake_metadata["original"]["path"])
else:
self._is_local = False
self._path = Path(self.store_path)
def prepare_cache(self, selectors: list[str]) -> None: def prepare_cache(self, selectors: list[str]) -> None:
if self._cache is None:
self.prefetch()
assert self._cache is not None
config = nix_config() config = nix_config()
nix_code = f""" nix_code = f"""
let let
@@ -312,10 +360,14 @@ class Flake:
msg = f"flake_prepare_cache: Expected {len(outputs)} outputs, got {len(outputs)}" msg = f"flake_prepare_cache: Expected {len(outputs)} outputs, got {len(outputs)}"
raise ClanError(msg) raise ClanError(msg)
for i, selector in enumerate(selectors): for i, selector in enumerate(selectors):
self.cache.insert(outputs[i], selector) self._cache.insert(outputs[i], selector)
def select(self, selector: str) -> Any: def select(self, selector: str) -> Any:
if not self.cache.is_cached(selector): if self._cache is None:
self.prefetch()
assert self._cache is not None
if not self._cache.is_cached(selector):
log.info(f"Cache miss for {selector}") log.info(f"Cache miss for {selector}")
self.prepare_cache([selector]) self.prepare_cache([selector])
return self.cache.select(selector) return self._cache.select(selector)

View File

@@ -6,8 +6,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_uri import FlakeId
from clan_cli.completions import add_dynamic_completer, complete_machines from clan_cli.completions import add_dynamic_completer, complete_machines
from clan_cli.flake import Flake
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from .flash import Disk, SystemConfig, flash_machine from .flash import Disk, SystemConfig, flash_machine
@@ -17,7 +17,7 @@ log = logging.getLogger(__name__)
@dataclass @dataclass
class FlashOptions: class FlashOptions:
flake: FlakeId flake: Flake
machine: str machine: str
disks: list[Disk] disks: list[Disk]
dry_run: bool dry_run: bool

View File

@@ -5,10 +5,10 @@ from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from clan_cli.api import API from clan_cli.api import API
from clan_cli.clan_uri import FlakeId
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 get_clan_flake_toplevel_or_env from clan_cli.dirs import get_clan_flake_toplevel_or_env
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.inventory import Machine as InventoryMachine from clan_cli.inventory import Machine as InventoryMachine
from clan_cli.inventory import ( from clan_cli.inventory import (
@@ -29,7 +29,7 @@ log = logging.getLogger(__name__)
@dataclass @dataclass
class CreateOptions: class CreateOptions:
clan_dir: FlakeId clan_dir: Flake
machine: InventoryMachine machine: InventoryMachine
target_host: str | None = None target_host: str | None = None
input_prio: InputPrio | None = None input_prio: InputPrio | None = None
@@ -38,7 +38,7 @@ class CreateOptions:
@API.register @API.register
def create_machine(opts: CreateOptions) -> None: def create_machine(opts: CreateOptions) -> None:
if not opts.clan_dir.is_local(): if not opts.clan_dir.is_local:
msg = f"Clan {opts.clan_dir} is not a local clan." msg = f"Clan {opts.clan_dir} is not a local clan."
description = "Import machine only works on local clans" description = "Import machine only works on local clans"
raise ClanError(msg, description=description) raise ClanError(msg, description=description)
@@ -127,7 +127,7 @@ def create_command(args: argparse.Namespace) -> None:
clan_dir = args.flake clan_dir = args.flake
else: else:
tmp = get_clan_flake_toplevel_or_env() tmp = get_clan_flake_toplevel_or_env()
clan_dir = FlakeId(str(tmp)) if tmp else None clan_dir = Flake(str(tmp)) if tmp else None
if not clan_dir: if not clan_dir:
msg = "No clan found." msg = "No clan found."

View File

@@ -3,7 +3,7 @@ import logging
import shutil import shutil
from clan_cli.api import API from clan_cli.api import API
from clan_cli.clan_uri import FlakeId from clan_cli.clan_uri import Flake
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 ClanError from clan_cli.errors import ClanError
@@ -13,7 +13,7 @@ log = logging.getLogger(__name__)
@API.register @API.register
def delete_machine(flake: FlakeId, name: str) -> None: def delete_machine(flake: Flake, name: str) -> None:
inventory = get_inventory(flake.path) inventory = get_inventory(flake.path)
if "machines" not in inventory: if "machines" not in inventory:

View File

@@ -6,11 +6,11 @@ from enum import Enum
from pathlib import Path from pathlib import Path
from clan_cli.api import API from clan_cli.api import API
from clan_cli.clan_uri import FlakeId
from clan_cli.cmd import RunOpts, run, run_no_stdout from clan_cli.cmd import RunOpts, run, 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.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, nix_shell from clan_cli.nix import nix_config, nix_eval, nix_shell
@@ -102,7 +102,7 @@ def show_machine_hardware_platform(clan_dir: Path, machine_name: str) -> str | N
@dataclass @dataclass
class HardwareGenerateOptions: class HardwareGenerateOptions:
flake: FlakeId flake: Flake
machine: str machine: str
backend: HardwareConfig backend: HardwareConfig
target_host: str | None = None target_host: str | None = None

View File

@@ -1,15 +1,15 @@
import json import json
from pathlib import Path from pathlib import Path
from clan_cli.clan_uri import FlakeId
from clan_cli.cmd import run from clan_cli.cmd import run
from clan_cli.flake import Flake
from clan_cli.nix import nix_build, nix_config, nix_test_store from clan_cli.nix import nix_build, nix_config, nix_test_store
from .machines import Machine from .machines import Machine
# function to speedup eval if we want to evaluate all machines # function to speedup eval if we want to evaluate all machines
def get_all_machines(flake: FlakeId, nix_options: list[str]) -> list[Machine]: def get_all_machines(flake: Flake, nix_options: list[str]) -> list[Machine]:
config = nix_config() config = nix_config()
system = config["system"] system = config["system"]
json_path = Path( json_path = Path(
@@ -37,7 +37,7 @@ def get_all_machines(flake: FlakeId, nix_options: list[str]) -> list[Machine]:
def get_selected_machines( def get_selected_machines(
flake: FlakeId, nix_options: list[str], machine_names: list[str] flake: Flake, nix_options: list[str], machine_names: list[str]
) -> list[Machine]: ) -> list[Machine]:
machines = [] machines = []
for name in machine_names: for name in machine_names:

View File

@@ -8,11 +8,11 @@ from tempfile import NamedTemporaryFile
from time import time from time import time
from typing import TYPE_CHECKING, Any, Literal from typing import TYPE_CHECKING, Any, Literal
from clan_cli.clan_uri import FlakeId
from clan_cli.cmd import RunOpts, run_no_stdout from clan_cli.cmd import RunOpts, run_no_stdout
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.facts import public_modules as facts_public_modules from clan_cli.facts import public_modules as facts_public_modules
from clan_cli.facts import secret_modules as facts_secret_modules from clan_cli.facts import secret_modules as facts_secret_modules
from clan_cli.flake import Flake
from clan_cli.nix import nix_build, nix_config, nix_eval, nix_metadata, nix_test_store from clan_cli.nix import nix_build, nix_config, nix_eval, nix_metadata, nix_test_store
from clan_cli.ssh.host import Host from clan_cli.ssh.host import Host
from clan_cli.ssh.host_key import HostKeyCheck from clan_cli.ssh.host_key import HostKeyCheck
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
@dataclass @dataclass
class Machine: class Machine:
name: str name: str
flake: FlakeId flake: Flake
nix_options: list[str] = field(default_factory=list) nix_options: list[str] = field(default_factory=list)
cached_deployment: None | dict[str, Any] = None cached_deployment: None | dict[str, Any] = None
override_target_host: None | str = None override_target_host: None | str = None
@@ -201,12 +201,7 @@ class Machine:
@property @property
def flake_dir(self) -> Path: def flake_dir(self) -> Path:
if self.flake.is_local():
return self.flake.path return self.flake.path
if self.flake.is_remote():
return Path(nix_metadata(self.flake.url)["path"])
msg = f"Unsupported flake url: {self.flake}"
raise ClanError(msg)
@property @property
def target_host(self) -> Host: def target_host(self) -> Host:

View File

@@ -8,7 +8,6 @@ import sys
from clan_cli.api import API from clan_cli.api import API
from clan_cli.async_run import AsyncContext, AsyncOpts, AsyncRuntime, is_async_cancelled from clan_cli.async_run import AsyncContext, AsyncOpts, AsyncRuntime, is_async_cancelled
from clan_cli.clan_uri import FlakeId
from clan_cli.cmd import MsgColor, RunOpts, run from clan_cli.cmd import MsgColor, RunOpts, run
from clan_cli.colors import AnsiColor from clan_cli.colors import AnsiColor
from clan_cli.completions import ( from clan_cli.completions import (
@@ -18,6 +17,7 @@ from clan_cli.completions import (
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.facts.generate import generate_facts from clan_cli.facts.generate import generate_facts
from clan_cli.facts.upload import upload_secrets from clan_cli.facts.upload import upload_secrets
from clan_cli.flake import Flake
from clan_cli.inventory import Machine as InventoryMachine from clan_cli.inventory import Machine as InventoryMachine
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.nix import nix_command, nix_metadata from clan_cli.nix import nix_command, nix_metadata
@@ -46,7 +46,7 @@ def upload_sources(machine: Machine) -> str:
env = host.nix_ssh_env(os.environ.copy()) env = host.nix_ssh_env(os.environ.copy())
flake_url = ( flake_url = (
str(machine.flake.path) if machine.flake.is_local() else machine.flake.url str(machine.flake.path) if machine.flake.is_local else machine.flake.identifier
) )
flake_data = nix_metadata(flake_url) flake_data = nix_metadata(flake_url)
has_path_inputs = any( has_path_inputs = any(
@@ -96,6 +96,7 @@ def update_machines(base_path: str, machines: list[InventoryMachine]) -> None:
group_machines: list[Machine] = [] group_machines: list[Machine] = []
# Convert InventoryMachine to Machine # Convert InventoryMachine to Machine
flake = Flake(base_path)
for machine in machines: for machine in machines:
name = machine.get("name") name = machine.get("name")
if not name: if not name:
@@ -103,7 +104,7 @@ def update_machines(base_path: str, machines: list[InventoryMachine]) -> None:
raise ClanError(msg) raise ClanError(msg)
m = Machine( m = Machine(
name, name,
flake=FlakeId(base_path), flake=flake,
) )
if not machine.get("deploy", {}).get("targetHost"): if not machine.get("deploy", {}).get("targetHost"):
msg = f"'TargetHost' is not set for machine '{name}'" msg = f"'TargetHost' is not set for machine '{name}'"

View File

@@ -10,7 +10,6 @@ from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import IO from typing import IO
from clan_cli.clan_uri import FlakeId
from clan_cli.completions import ( from clan_cli.completions import (
add_dynamic_completer, add_dynamic_completer,
complete_groups, complete_groups,
@@ -19,6 +18,7 @@ from clan_cli.completions import (
complete_users, complete_users,
) )
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.flake import Flake
from clan_cli.git import commit_files from clan_cli.git import commit_files
from . import sops from . import sops
@@ -341,7 +341,7 @@ def list_secrets(flake_dir: Path, pattern: str | None = None) -> list[str]:
@dataclass @dataclass
class ListSecretsOptions: class ListSecretsOptions:
flake: FlakeId flake: Flake
pattern: str | None pattern: str | None

View File

@@ -15,3 +15,8 @@ def register_parser(parser: argparse.ArgumentParser) -> None:
"selector", "selector",
help="select from a flake", help="select from a flake",
) )
parser.add_argument(
"--impure",
action="store_true",
default=False,
)

View File

@@ -6,9 +6,9 @@ from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import Literal, NewType, TypedDict from typing import Literal, NewType, TypedDict
from clan_cli.clan_uri import FlakeId
from clan_cli.cmd import run from clan_cli.cmd import run
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.flake import Flake
from clan_cli.nix import nix_eval from clan_cli.nix import nix_eval
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -59,7 +59,7 @@ class ClanExports(TypedDict):
self: ClanAttrset self: ClanAttrset
def get_clan_nix_attrset(clan_dir: FlakeId | None = None) -> ClanExports: def get_clan_nix_attrset(clan_dir: Flake | None = None) -> ClanExports:
# Check if the clan directory is provided, otherwise use the environment variable # Check if the clan directory is provided, otherwise use the environment variable
if not clan_dir: if not clan_dir:
# TODO: Quickfix, templates dir seems to be missing in CLAN_CORE_PATH?? # TODO: Quickfix, templates dir seems to be missing in CLAN_CORE_PATH??
@@ -69,7 +69,7 @@ def get_clan_nix_attrset(clan_dir: FlakeId | None = None) -> ClanExports:
# msg = "Environment var CLAN_CORE_PATH is not set, this shouldn't happen" # msg = "Environment var CLAN_CORE_PATH is not set, this shouldn't happen"
# raise ClanError(msg) # raise ClanError(msg)
clan_dir = FlakeId(clan_core_path) clan_dir = Flake(clan_core_path)
log.debug(f"Evaluating flake {clan_dir} for Clan attrsets") log.debug(f"Evaluating flake {clan_dir} for Clan attrsets")
@@ -170,7 +170,7 @@ class TemplateList:
def list_templates( def list_templates(
template_type: TemplateType, clan_dir: FlakeId | None = None template_type: TemplateType, clan_dir: Flake | None = None
) -> TemplateList: ) -> TemplateList:
clan_exports = get_clan_nix_attrset(clan_dir) clan_exports = get_clan_nix_attrset(clan_dir)
result = TemplateList() result = TemplateList()
@@ -204,7 +204,7 @@ def get_template(
template_type: TemplateType, template_type: TemplateType,
*, *,
input_prio: InputPrio | None = None, input_prio: InputPrio | None = None,
clan_dir: FlakeId | None = None, clan_dir: Flake | None = None,
) -> FoundTemplate: ) -> FoundTemplate:
log.info(f"Searching for template '{template_name}' of type '{template_type}'") log.info(f"Searching for template '{template_name}' of type '{template_type}'")

View File

@@ -3,9 +3,9 @@ import logging
import sys import sys
from clan_cli.api import API from clan_cli.api import API
from clan_cli.clan_uri import FlakeId
from clan_cli.completions import add_dynamic_completer, complete_machines from clan_cli.completions import add_dynamic_completer, complete_machines
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.flake import Flake
from .generate import Var from .generate import Var
from .list import get_vars from .list import get_vars
@@ -42,7 +42,7 @@ def get_var(base_dir: str, machine_name: str, var_id: str) -> Var:
raise ClanError(msg) raise ClanError(msg)
def get_command(machine_name: str, var_id: str, flake: FlakeId) -> None: def get_command(machine_name: str, var_id: str, flake: Flake) -> None:
var = get_var(str(flake.path), machine_name, var_id) var = get_var(str(flake.path), machine_name, var_id)
if not var.exists: if not var.exists:
msg = f"Var {var.id} has not been generated yet" msg = f"Var {var.id} has not been generated yet"

View File

@@ -2,8 +2,8 @@ import argparse
import logging import logging
import os import os
from clan_cli.clan_uri import FlakeId
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.flake import Flake
from clan_cli.secrets.key import generate_key from clan_cli.secrets.key import generate_key
from clan_cli.secrets.sops import KeyType, maybe_get_admin_public_key from clan_cli.secrets.sops import KeyType, maybe_get_admin_public_key
from clan_cli.secrets.users import add_user from clan_cli.secrets.users import add_user
@@ -11,7 +11,7 @@ from clan_cli.secrets.users import add_user
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def keygen(user: str | None, flake: FlakeId, force: bool) -> None: def keygen(user: str | None, flake: Flake, force: bool) -> None:
if user is None: if user is None:
user = os.getenv("USER", None) user = os.getenv("USER", None)
if not user: if not user:

View File

@@ -3,9 +3,9 @@ import importlib
import logging import logging
from clan_cli.api import API from clan_cli.api import API
from clan_cli.clan_uri import FlakeId
from clan_cli.completions import add_dynamic_completer, complete_machines from clan_cli.completions import add_dynamic_completer, complete_machines
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.flake import Flake
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.vars._types import StoreBase from clan_cli.vars._types import StoreBase
@@ -27,7 +27,7 @@ def secret_store(machine: Machine) -> StoreBase:
@API.register @API.register
def get_vars(base_dir: str, machine_name: str) -> list[Var]: def get_vars(base_dir: str, machine_name: str) -> list[Var]:
machine = Machine(name=machine_name, flake=FlakeId(base_dir)) machine = Machine(name=machine_name, flake=Flake(base_dir))
pub_store = public_store(machine) pub_store = public_store(machine)
sec_store = secret_store(machine) sec_store = secret_store(machine)
all_vars = [] all_vars = []
@@ -61,7 +61,7 @@ def _get_previous_value(
@API.register @API.register
def get_generators(base_dir: str, machine_name: str) -> list[Generator]: def get_generators(base_dir: str, machine_name: str) -> list[Generator]:
machine = Machine(name=machine_name, flake=FlakeId(base_dir)) machine = Machine(name=machine_name, flake=Flake(base_dir))
generators: list[Generator] = machine.vars_generators generators: list[Generator] = machine.vars_generators
for generator in generators: for generator in generators:
for prompt in generator.prompts: for prompt in generator.prompts:
@@ -76,7 +76,7 @@ def get_generators(base_dir: str, machine_name: str) -> list[Generator]:
def set_prompts( def set_prompts(
base_dir: str, machine_name: str, updates: list[GeneratorUpdate] base_dir: str, machine_name: str, updates: list[GeneratorUpdate]
) -> None: ) -> None:
machine = Machine(name=machine_name, flake=FlakeId(base_dir)) machine = Machine(name=machine_name, flake=Flake(base_dir))
for update in updates: for update in updates:
for generator in machine.vars_generators: for generator in machine.vars_generators:
if generator.name == update.generator: if generator.name == update.generator:

View File

@@ -26,7 +26,7 @@ class FactStore(StoreBase):
var: Var, var: Var,
value: bytes, value: bytes,
) -> Path | None: ) -> Path | None:
if not self.machine.flake.is_local(): if not self.machine.flake.is_local:
msg = f"in_flake fact storage is only supported for local flakes: {self.machine.flake}" msg = f"in_flake fact storage is only supported for local flakes: {self.machine.flake}"
raise ClanError(msg) raise ClanError(msg)
folder = self.directory(generator, var.name) folder = self.directory(generator, var.name)

View File

@@ -18,7 +18,7 @@ class FactStore(StoreBase):
def __init__(self, machine: Machine) -> None: def __init__(self, machine: Machine) -> None:
self.machine = machine self.machine = machine
self.works_remotely = False self.works_remotely = False
self.dir = vm_state_dir(machine.flake, machine.name) / "facts" self.dir = vm_state_dir(machine.flake.identifier, machine.name) / "facts"
machine.debug(f"FactStore initialized with dir {self.dir}") machine.debug(f"FactStore initialized with dir {self.dir}")
@property @property

View File

@@ -14,7 +14,7 @@ class SecretStore(StoreBase):
def __init__(self, machine: Machine) -> None: def __init__(self, machine: Machine) -> None:
self.machine = machine self.machine = machine
self.dir = vm_state_dir(machine.flake, machine.name) / "secrets" self.dir = vm_state_dir(machine.flake.identifier, machine.name) / "secrets"
self.dir.mkdir(parents=True, exist_ok=True) self.dir.mkdir(parents=True, exist_ok=True)
@property @property

View File

@@ -2,8 +2,8 @@ import argparse
import logging import logging
import sys import sys
from clan_cli.clan_uri import FlakeId
from clan_cli.completions import add_dynamic_completer, complete_machines from clan_cli.completions import add_dynamic_completer, complete_machines
from clan_cli.flake import Flake
from clan_cli.git import commit_files from clan_cli.git import commit_files
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.vars.get import get_var from clan_cli.vars.get import get_var
@@ -15,9 +15,7 @@ from .prompt import ask
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def set_var( def set_var(machine: str | Machine, var: str | Var, value: bytes, flake: Flake) -> None:
machine: str | Machine, var: str | Var, value: bytes, flake: FlakeId
) -> None:
if isinstance(machine, str): if isinstance(machine, str):
_machine = Machine(name=machine, flake=flake) _machine = Machine(name=machine, flake=flake)
else: else:
@@ -35,7 +33,7 @@ def set_var(
) )
def set_via_stdin(machine: str, var_id: str, flake: FlakeId) -> None: def set_via_stdin(machine: str, var_id: str, flake: Flake) -> None:
var = get_var(str(flake.path), machine, var_id) var = get_var(str(flake.path), machine, var_id)
if sys.stdin.isatty(): if sys.stdin.isatty():
new_value = ask( new_value = ask(

View File

@@ -5,8 +5,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_uri import FlakeId
from clan_cli.completions import add_dynamic_completer, complete_machines from clan_cli.completions import add_dynamic_completer, complete_machines
from clan_cli.flake import Flake
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
@@ -26,7 +26,7 @@ class WaypipeConfig:
@dataclass @dataclass
class VmConfig: class VmConfig:
machine_name: str machine_name: str
flake_url: FlakeId flake_url: Flake
cores: int cores: int
memory_size: int memory_size: int
@@ -43,7 +43,7 @@ class VmConfig:
def from_json(cls: type["VmConfig"], data: dict[str, Any]) -> "VmConfig": def from_json(cls: type["VmConfig"], data: dict[str, Any]) -> "VmConfig":
return cls( return cls(
machine_name=data["machine_name"], machine_name=data["machine_name"],
flake_url=FlakeId.from_json(data["flake_url"]), flake_url=Flake.from_json(data["flake_url"]),
cores=data["cores"], cores=data["cores"],
memory_size=data["memory_size"], memory_size=data["memory_size"],
graphics=data["graphics"], graphics=data["graphics"],
@@ -64,13 +64,13 @@ def inspect_vm(machine: Machine) -> VmConfig:
@dataclass @dataclass
class InspectOptions: class InspectOptions:
machine: str machine: str
flake: FlakeId flake: Flake
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 FlakeId(str(Path.cwd())), flake=args.flake or Flake(str(Path.cwd())),
) )
machine = Machine(inspect_options.machine, inspect_options.flake) machine = Machine(inspect_options.machine, inspect_options.flake)

View File

@@ -236,7 +236,7 @@ def spawn_vm(
# TODO: We should get this from the vm argument # TODO: We should get this from the vm argument
nixos_config = build_vm(machine, cachedir, nix_options) nixos_config = build_vm(machine, cachedir, nix_options)
state_dir = vm_state_dir(vm.flake_url, machine.name) state_dir = vm_state_dir(vm.flake_url.identifier, machine.name)
state_dir.mkdir(parents=True, exist_ok=True) state_dir.mkdir(parents=True, exist_ok=True)
# specify socket files for qmp and qga # specify socket files for qmp and qga

View File

@@ -121,7 +121,6 @@ def test_all_dataclasses() -> None:
"api/__init__.py", "api/__init__.py",
"cmd.py", # We don't want the UI to have access to the cmd module anyway "cmd.py", # We don't want the UI to have access to the cmd module anyway
"async_run.py", # We don't want the UI to have access to the async_run module anyway "async_run.py", # We don't want the UI to have access to the async_run module anyway
"flake.py", # Not compatible yet with the UI, unclear semantics, maybe it's just an internal thing
] ]
cli_path = Path("clan_cli").resolve() cli_path = Path("clan_cli").resolve()

View File

@@ -5,7 +5,7 @@ from pathlib import Path
from typing import Any from typing import Any
import pytest import pytest
from clan_cli.clan_uri import FlakeId 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.templates import get_clan_nix_attrset from clan_cli.templates import get_clan_nix_attrset
from fixtures_flakes import FlakeForTest from fixtures_flakes import FlakeForTest
@@ -26,7 +26,7 @@ def nix_attr_tester(
test_number: int, test_number: int,
) -> None: ) -> None:
write_clan_attr(injected, test_flake) write_clan_attr(injected, test_flake)
nix_attrset = get_clan_nix_attrset(FlakeId(str(test_flake.path))) nix_attrset = get_clan_nix_attrset(Flake(str(test_flake.path)))
assert json.dumps(nix_attrset, indent=2) == json.dumps(expected, indent=2) assert json.dumps(nix_attrset, indent=2) == json.dumps(expected, indent=2)

View File

@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory
from clan_cli.clan_uri import ClanURI from clan_cli.clan_uri import ClanURI
@@ -15,26 +16,28 @@ def test_get_url() -> None:
assert uri.get_url() == "/home/user/Downloads" assert uri.get_url() == "/home/user/Downloads"
uri = ClanURI.from_str("clan://file:///home/user/Downloads") uri = ClanURI.from_str("clan://file:///home/user/Downloads")
assert uri.get_url() == "/home/user/Downloads" assert uri.get_url() == "file:///home/user/Downloads"
def test_firefox_strip_uri() -> None: def test_firefox_strip_uri() -> None:
uri = ClanURI.from_str("clan://https//git.clan.lol/clan/democlan")
assert uri.get_url() == "https://git.clan.lol/clan/democlan"
uri = ClanURI.from_str("clan://git+https//git.clan.lol/clan/democlan.git") uri = ClanURI.from_str("clan://git+https//git.clan.lol/clan/democlan.git")
assert uri.get_url() == "git+https://git.clan.lol/clan/democlan.git" assert uri.get_url() == "git+https://git.clan.lol/clan/democlan.git"
def test_local_uri() -> None: def test_local_uri() -> None:
with TemporaryDirectory(prefix="clan_test") as tempdir:
flake_nix = Path(tempdir) / "flake.nix"
flake_nix.write_text("outputs = _: {}")
# Create a ClanURI object from a local URI # Create a ClanURI object from a local URI
uri = ClanURI.from_str("clan://file:///home/user/Downloads") uri = ClanURI.from_str(f"clan://file://{tempdir}")
assert uri.flake.path == Path("/home/user/Downloads") assert uri.flake.path == Path(tempdir)
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.from_str("clan://https://example.com") uri = ClanURI.from_str("clan://https://example.com")
assert uri.flake.url == "https://example.com" assert uri.flake.identifier == "https://example.com"
def test_direct_local_path() -> None: def test_direct_local_path() -> None:
@@ -54,35 +57,35 @@ def test_remote_with_clanparams() -> None:
uri = ClanURI.from_str("clan://https://example.com") uri = ClanURI.from_str("clan://https://example.com")
assert uri.machine_name == "defaultVM" assert uri.machine_name == "defaultVM"
assert uri.flake.url == "https://example.com" assert uri.flake.identifier == "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.url == "https://example.com" assert uri.flake.identifier == "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") with TemporaryDirectory(prefix="clan_test") as tempdir:
assert uri.get_url().endswith("/Projects/democlan") flake_nix = Path(tempdir) / "flake.nix"
flake_nix.write_text("outputs = _: {}")
uri = ClanURI.from_str(url=tempdir, machine_name="myVM")
assert uri.get_url().endswith(tempdir)
assert uri.machine_name == "myVM" assert uri.machine_name == "myVM"
assert uri.flake.is_local() assert uri.flake.is_local
assert str(uri.flake).endswith("/Projects/democlan") # type: ignore assert str(uri.flake).endswith(tempdir) # type: ignore
def test_from_str_local_no_machine() -> None: def test_from_str_local_no_machine() -> None:
uri = ClanURI.from_str("~/Projects/democlan") with TemporaryDirectory(prefix="clan_test") as tempdir:
assert uri.get_url().endswith("/Projects/democlan") flake_nix = Path(tempdir) / "flake.nix"
flake_nix.write_text("outputs = _: {}")
uri = ClanURI.from_str(tempdir)
assert uri.get_url().endswith(tempdir)
assert uri.machine_name == "defaultVM" assert uri.machine_name == "defaultVM"
assert uri.flake.is_local() assert uri.flake.is_local
assert str(uri.flake).endswith("/Projects/democlan") # type: ignore assert str(uri.flake).endswith(tempdir) # type: ignore
def test_from_str_local_no_machine2() -> None:
uri = ClanURI.from_str("~/Projects/democlan#syncthing-peer1")
assert uri.get_url().endswith("/Projects/democlan")
assert uri.machine_name == "syncthing-peer1"
assert uri.flake.is_local()
assert str(uri.flake).endswith("/Projects/democlan") # type: ignore

View File

@@ -82,7 +82,7 @@ def test_nested_nullable() -> None:
data = { data = {
"machine": { "machine": {
"name": "flash-installer", "name": "flash-installer",
"flake": {"loc": "git+https://git.clan.lol/clan/clan-core"}, "flake": {"identifier": "git+https://git.clan.lol/clan/clan-core"},
}, },
"mode": "format", "mode": "format",
"disks": {"main": "/dev/sda"}, "disks": {"main": "/dev/sda"},
@@ -96,7 +96,7 @@ def test_nested_nullable() -> None:
expected = FlashOptions( expected = FlashOptions(
machine=machines.Machine( machine=machines.Machine(
name="flash-installer", name="flash-installer",
flake=machines.FlakeId("git+https://git.clan.lol/clan/clan-core"), flake=machines.Flake("git+https://git.clan.lol/clan/clan-core"),
), ),
mode="format", mode="format",
disks={"main": "/dev/sda"}, disks={"main": "/dev/sda"},

View File

@@ -16,7 +16,6 @@
# (subdir / ".clan-flake").touch() # (subdir / ".clan-flake").touch()
# assert _get_clan_flake_toplevel() == subdir # assert _get_clan_flake_toplevel() == subdir
from clan_cli.clan_uri import FlakeId
from clan_cli.dirs import clan_key_safe, vm_state_dir from clan_cli.dirs import clan_key_safe, vm_state_dir
@@ -25,12 +24,12 @@ def test_clan_key_safe() -> None:
def test_vm_state_dir_identity() -> None: def test_vm_state_dir_identity() -> None:
dir1 = vm_state_dir(FlakeId("https://some.clan"), "vm1") dir1 = vm_state_dir("https://some.clan", "vm1")
dir2 = vm_state_dir(FlakeId("https://some.clan"), "vm1") dir2 = vm_state_dir("https://some.clan", "vm1")
assert str(dir1) == str(dir2) assert str(dir1) == str(dir2)
def test_vm_state_dir_no_collision() -> None: def test_vm_state_dir_no_collision() -> None:
dir1 = vm_state_dir(FlakeId("/foo/bar"), "vm1") dir1 = vm_state_dir("/foo/bar", "vm1")
dir2 = vm_state_dir(FlakeId("https://some.clan"), "vm1") dir2 = vm_state_dir("https://some.clan", "vm1")
assert str(dir1) != str(dir2) assert str(dir1) != str(dir2)

View File

@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING
import pytest import pytest
from clan_cli.api.modules import list_modules from clan_cli.api.modules import list_modules
from clan_cli.clan_uri import FlakeId from clan_cli.flake import Flake
from clan_cli.inventory import ( from clan_cli.inventory import (
Inventory, Inventory,
Machine, Machine,
@@ -56,7 +56,7 @@ def test_add_module_to_inventory(
] ]
) )
opts = CreateOptions( opts = CreateOptions(
clan_dir=FlakeId(str(base_path)), clan_dir=Flake(str(base_path)),
machine=Machine(name="machine1", tags=[], deploy=MachineDeploy()), machine=Machine(name="machine1", tags=[], deploy=MachineDeploy()),
) )
@@ -93,7 +93,7 @@ def test_add_module_to_inventory(
cli.run(cmd) cli.run(cmd)
machine = MachineMachine( machine = MachineMachine(
name="machine1", flake=FlakeId(str(test_flake_with_core.path)) name="machine1", flake=Flake(str(test_flake_with_core.path))
) )
generator = None generator = None

View File

@@ -2,8 +2,8 @@ import ipaddress
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import pytest import pytest
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.flake import Flake
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
from clan_cli.secrets.folders import sops_secrets_folder from clan_cli.secrets.folders import sops_secrets_folder
@@ -48,7 +48,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( store1 = SecretStore(
Machine(name="vm1", flake=FlakeId(str(test_flake_with_core.path))) Machine(name="vm1", flake=Flake(str(test_flake_with_core.path)))
) )
assert store1.exists("", "age.key") assert store1.exists("", "age.key")
@@ -78,7 +78,7 @@ def test_generate_secret(
).exists() ).exists()
store2 = SecretStore( store2 = SecretStore(
Machine(name="vm2", flake=FlakeId(str(test_flake_with_core.path))) Machine(name="vm2", flake=Flake(str(test_flake_with_core.path)))
) )
# Should not exist clan facts generate # Should not exist clan facts generate

View File

@@ -2,8 +2,8 @@ import subprocess
from pathlib import Path from pathlib import Path
import pytest import pytest
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.flake import Flake
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
from clan_cli.nix import nix_shell from clan_cli.nix import nix_shell
@@ -68,7 +68,7 @@ def test_upload_secret(
) )
cli.run(["facts", "generate", "vm1", "--flake", str(flake.path)]) cli.run(["facts", "generate", "vm1", "--flake", str(flake.path)])
store = SecretStore(Machine(name="vm1", flake=FlakeId(str(flake.path)))) store = SecretStore(Machine(name="vm1", flake=Flake(str(flake.path))))
network_id = machine_get_fact(flake.path, "vm1", "zerotier-network-id") network_id = machine_get_fact(flake.path, "vm1", "zerotier-network-id")
assert len(network_id) == 16 assert len(network_id) == 16

View File

@@ -6,8 +6,8 @@ from typing import TYPE_CHECKING
import pytest import pytest
from age_keys import SopsSetup from age_keys import SopsSetup
from clan_cli.clan_uri import FlakeId
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.flake import Flake
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.nix import nix_eval, run from clan_cli.nix import nix_eval, run
from clan_cli.vars.check import check_vars from clan_cli.vars.check import check_vars
@@ -138,7 +138,7 @@ def test_generate_public_and_secret_vars(
monkeypatch.chdir(flake.path) monkeypatch.chdir(flake.path)
sops_setup.init() sops_setup.init()
machine = Machine(name="my_machine", flake=FlakeId(str(flake.path))) machine = Machine(name="my_machine", flake=Flake(str(flake.path)))
assert not check_vars(machine) assert not check_vars(machine)
vars_text = stringify_all_vars(machine) vars_text = stringify_all_vars(machine)
assert "my_generator/my_value: <not set>" in vars_text assert "my_generator/my_value: <not set>" in vars_text
@@ -184,11 +184,11 @@ def test_generate_public_and_secret_vars(
) )
vars_text = stringify_all_vars(machine) vars_text = stringify_all_vars(machine)
in_repo_store = in_repo.FactStore( in_repo_store = in_repo.FactStore(
Machine(name="my_machine", flake=FlakeId(str(flake.path))) Machine(name="my_machine", flake=Flake(str(flake.path)))
) )
assert not in_repo_store.exists(Generator("my_generator"), "my_secret") assert not in_repo_store.exists(Generator("my_generator"), "my_secret")
sops_store = sops.SecretStore( sops_store = sops.SecretStore(
Machine(name="my_machine", flake=FlakeId(str(flake.path))) Machine(name="my_machine", flake=Flake(str(flake.path)))
) )
assert sops_store.exists(Generator("my_generator"), "my_secret") assert sops_store.exists(Generator("my_generator"), "my_secret")
assert sops_store.get(Generator("my_generator"), "my_secret").decode() == "secret" assert sops_store.get(Generator("my_generator"), "my_secret").decode() == "secret"
@@ -246,11 +246,11 @@ def test_generate_secret_var_sops_with_default_group(
cli.run(["secrets", "groups", "add-user", "my_group", sops_setup.user]) cli.run(["secrets", "groups", "add-user", "my_group", sops_setup.user])
cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"]) cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"])
in_repo_store = in_repo.FactStore( in_repo_store = in_repo.FactStore(
Machine(name="my_machine", flake=FlakeId(str(flake.path))) Machine(name="my_machine", flake=Flake(str(flake.path)))
) )
assert not in_repo_store.exists(Generator("my_generator"), "my_secret") assert not in_repo_store.exists(Generator("my_generator"), "my_secret")
sops_store = sops.SecretStore( sops_store = sops.SecretStore(
Machine(name="my_machine", flake=FlakeId(str(flake.path))) Machine(name="my_machine", flake=Flake(str(flake.path)))
) )
assert sops_store.exists(Generator("my_generator"), "my_secret") assert sops_store.exists(Generator("my_generator"), "my_secret")
assert sops_store.get(Generator("my_generator"), "my_secret").decode() == "hello\n" assert sops_store.get(Generator("my_generator"), "my_secret").decode() == "hello\n"
@@ -317,8 +317,8 @@ def test_generated_shared_secret_sops(
flake.refresh() flake.refresh()
monkeypatch.chdir(flake.path) monkeypatch.chdir(flake.path)
sops_setup.init() sops_setup.init()
machine1 = Machine(name="machine1", flake=FlakeId(str(flake.path))) machine1 = Machine(name="machine1", flake=Flake(str(flake.path)))
machine2 = Machine(name="machine2", flake=FlakeId(str(flake.path))) machine2 = Machine(name="machine2", flake=Flake(str(flake.path)))
cli.run(["vars", "generate", "--flake", str(flake.path), "machine1"]) cli.run(["vars", "generate", "--flake", str(flake.path), "machine1"])
assert check_vars(machine1) assert check_vars(machine1)
cli.run(["vars", "generate", "--flake", str(flake.path), "machine2"]) cli.run(["vars", "generate", "--flake", str(flake.path), "machine2"])
@@ -368,12 +368,12 @@ def test_generate_secret_var_password_store(
shutil.copytree(test_root / "data" / "password-store", password_store_dir) shutil.copytree(test_root / "data" / "password-store", password_store_dir)
monkeypatch.setenv("PASSWORD_STORE_DIR", str(flake.path / "pass")) monkeypatch.setenv("PASSWORD_STORE_DIR", str(flake.path / "pass"))
machine = Machine(name="my_machine", flake=FlakeId(str(flake.path))) machine = Machine(name="my_machine", flake=Flake(str(flake.path)))
assert not check_vars(machine) assert not check_vars(machine)
cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"]) cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"])
assert check_vars(machine) assert check_vars(machine)
store = password_store.SecretStore( store = password_store.SecretStore(
Machine(name="my_machine", flake=FlakeId(str(flake.path))) Machine(name="my_machine", flake=Flake(str(flake.path)))
) )
assert store.exists(Generator("my_generator", share=False, files=[]), "my_secret") assert store.exists(Generator("my_generator", share=False, files=[]), "my_secret")
assert not store.exists( assert not store.exists(
@@ -432,10 +432,10 @@ def test_generate_secret_for_multiple_machines(
cli.run(["vars", "generate", "--flake", str(flake.path)]) cli.run(["vars", "generate", "--flake", str(flake.path)])
# check if public vars have been created correctly # check if public vars have been created correctly
in_repo_store1 = in_repo.FactStore( in_repo_store1 = in_repo.FactStore(
Machine(name="machine1", flake=FlakeId(str(flake.path))) Machine(name="machine1", flake=Flake(str(flake.path)))
) )
in_repo_store2 = in_repo.FactStore( in_repo_store2 = in_repo.FactStore(
Machine(name="machine2", flake=FlakeId(str(flake.path))) Machine(name="machine2", flake=Flake(str(flake.path)))
) )
assert in_repo_store1.exists(Generator("my_generator"), "my_value") assert in_repo_store1.exists(Generator("my_generator"), "my_value")
assert in_repo_store2.exists(Generator("my_generator"), "my_value") assert in_repo_store2.exists(Generator("my_generator"), "my_value")
@@ -449,10 +449,10 @@ def test_generate_secret_for_multiple_machines(
) )
# check if secret vars have been created correctly # check if secret vars have been created correctly
sops_store1 = sops.SecretStore( sops_store1 = sops.SecretStore(
Machine(name="machine1", flake=FlakeId(str(flake.path))) Machine(name="machine1", flake=Flake(str(flake.path)))
) )
sops_store2 = sops.SecretStore( sops_store2 = sops.SecretStore(
Machine(name="machine2", flake=FlakeId(str(flake.path))) Machine(name="machine2", flake=Flake(str(flake.path)))
) )
assert sops_store1.exists(Generator("my_generator"), "my_secret") assert sops_store1.exists(Generator("my_generator"), "my_secret")
assert sops_store2.exists(Generator("my_generator"), "my_secret") assert sops_store2.exists(Generator("my_generator"), "my_secret")
@@ -498,7 +498,7 @@ def test_prompt(
) )
cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"]) cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"])
in_repo_store = in_repo.FactStore( in_repo_store = in_repo.FactStore(
Machine(name="my_machine", flake=FlakeId(str(flake.path))) Machine(name="my_machine", flake=Flake(str(flake.path)))
) )
assert in_repo_store.exists(Generator("my_generator"), "line_value") assert in_repo_store.exists(Generator("my_generator"), "line_value")
assert ( assert (
@@ -512,7 +512,7 @@ def test_prompt(
== "my\nmultiline\ninput\n" == "my\nmultiline\ninput\n"
) )
sops_store = sops.SecretStore( sops_store = sops.SecretStore(
Machine(name="my_machine", flake=FlakeId(str(flake.path))) Machine(name="my_machine", flake=Flake(str(flake.path)))
) )
assert sops_store.exists( assert sops_store.exists(
Generator(name="my_generator", share=False, files=[]), "prompt_persist" Generator(name="my_generator", share=False, files=[]), "prompt_persist"
@@ -553,8 +553,8 @@ def test_multi_machine_shared_vars(
flake.refresh() flake.refresh()
monkeypatch.chdir(flake.path) monkeypatch.chdir(flake.path)
sops_setup.init() sops_setup.init()
machine1 = Machine(name="machine1", flake=FlakeId(str(flake.path))) machine1 = Machine(name="machine1", flake=Flake(str(flake.path)))
machine2 = Machine(name="machine2", flake=FlakeId(str(flake.path))) machine2 = Machine(name="machine2", flake=Flake(str(flake.path)))
sops_store_1 = sops.SecretStore(machine1) sops_store_1 = sops.SecretStore(machine1)
sops_store_2 = sops.SecretStore(machine2) sops_store_2 = sops.SecretStore(machine2)
in_repo_store_1 = in_repo.FactStore(machine1) in_repo_store_1 = in_repo.FactStore(machine1)
@@ -616,7 +616,7 @@ def test_api_set_prompts(
) )
], ],
) )
machine = Machine(name="my_machine", flake=FlakeId(str(flake.path))) machine = Machine(name="my_machine", flake=Flake(str(flake.path)))
store = in_repo.FactStore(machine) store = in_repo.FactStore(machine)
assert store.exists(Generator("my_generator"), "prompt1") assert store.exists(Generator("my_generator"), "prompt1")
assert store.get(Generator("my_generator"), "prompt1").decode() == "input1" assert store.get(Generator("my_generator"), "prompt1").decode() == "input1"
@@ -663,7 +663,7 @@ def test_stdout_of_generate(
# with capture_output as output: # with capture_output as output:
with caplog.at_level(logging.INFO): with caplog.at_level(logging.INFO):
generate_vars_for_machine( generate_vars_for_machine(
Machine(name="my_machine", flake=FlakeId(str(flake.path))), Machine(name="my_machine", flake=Flake(str(flake.path))),
"my_generator", "my_generator",
regenerate=False, regenerate=False,
) )
@@ -673,10 +673,10 @@ def test_stdout_of_generate(
assert "new: hello" in caplog.text assert "new: hello" in caplog.text
caplog.clear() caplog.clear()
set_var("my_machine", "my_generator/my_value", b"world", FlakeId(str(flake.path))) set_var("my_machine", "my_generator/my_value", b"world", Flake(str(flake.path)))
with caplog.at_level(logging.INFO): with caplog.at_level(logging.INFO):
generate_vars_for_machine( generate_vars_for_machine(
Machine(name="my_machine", flake=FlakeId(str(flake.path))), Machine(name="my_machine", flake=Flake(str(flake.path))),
"my_generator", "my_generator",
regenerate=True, regenerate=True,
) )
@@ -687,7 +687,7 @@ def test_stdout_of_generate(
# check the output when nothing gets regenerated # check the output when nothing gets regenerated
with caplog.at_level(logging.INFO): with caplog.at_level(logging.INFO):
generate_vars_for_machine( generate_vars_for_machine(
Machine(name="my_machine", flake=FlakeId(str(flake.path))), Machine(name="my_machine", flake=Flake(str(flake.path))),
"my_generator", "my_generator",
regenerate=True, regenerate=True,
) )
@@ -696,7 +696,7 @@ def test_stdout_of_generate(
caplog.clear() caplog.clear()
with caplog.at_level(logging.INFO): with caplog.at_level(logging.INFO):
generate_vars_for_machine( generate_vars_for_machine(
Machine(name="my_machine", flake=FlakeId(str(flake.path))), Machine(name="my_machine", flake=Flake(str(flake.path))),
"my_secret_generator", "my_secret_generator",
regenerate=False, regenerate=False,
) )
@@ -707,11 +707,11 @@ def test_stdout_of_generate(
"my_machine", "my_machine",
"my_secret_generator/my_secret", "my_secret_generator/my_secret",
b"world", b"world",
FlakeId(str(flake.path)), Flake(str(flake.path)),
) )
with caplog.at_level(logging.INFO): with caplog.at_level(logging.INFO):
generate_vars_for_machine( generate_vars_for_machine(
Machine(name="my_machine", flake=FlakeId(str(flake.path))), Machine(name="my_machine", flake=Flake(str(flake.path))),
"my_secret_generator", "my_secret_generator",
regenerate=True, regenerate=True,
) )
@@ -760,10 +760,10 @@ def test_migration(
assert "Migrated var my_generator/my_value" in caplog.text assert "Migrated var my_generator/my_value" in caplog.text
assert "Migrated secret var my_generator/my_secret" in caplog.text assert "Migrated secret var my_generator/my_secret" in caplog.text
in_repo_store = in_repo.FactStore( in_repo_store = in_repo.FactStore(
Machine(name="my_machine", flake=FlakeId(str(flake.path))) Machine(name="my_machine", flake=Flake(str(flake.path)))
) )
sops_store = sops.SecretStore( sops_store = sops.SecretStore(
Machine(name="my_machine", flake=FlakeId(str(flake.path))) Machine(name="my_machine", flake=Flake(str(flake.path)))
) )
assert in_repo_store.exists(Generator("my_generator"), "my_value") assert in_repo_store.exists(Generator("my_generator"), "my_value")
assert in_repo_store.get(Generator("my_generator"), "my_value").decode() == "hello" assert in_repo_store.get(Generator("my_generator"), "my_value").decode() == "hello"
@@ -800,7 +800,7 @@ def test_fails_when_files_are_left_from_other_backend(
sops_setup.init() sops_setup.init()
for generator in ["my_secret_generator", "my_value_generator"]: for generator in ["my_secret_generator", "my_value_generator"]:
generate_vars_for_machine( generate_vars_for_machine(
Machine(name="my_machine", flake=FlakeId(str(flake.path))), Machine(name="my_machine", flake=Flake(str(flake.path))),
generator, generator,
regenerate=False, regenerate=False,
) )
@@ -817,36 +817,33 @@ def test_fails_when_files_are_left_from_other_backend(
if generator == "my_secret_generator": if generator == "my_secret_generator":
with pytest.raises(ClanError): with pytest.raises(ClanError):
generate_vars_for_machine( generate_vars_for_machine(
Machine(name="my_machine", flake=FlakeId(str(flake.path))), Machine(name="my_machine", flake=Flake(str(flake.path))),
generator, generator,
regenerate=False, regenerate=False,
) )
else: else:
generate_vars_for_machine( generate_vars_for_machine(
Machine(name="my_machine", flake=FlakeId(str(flake.path))), Machine(name="my_machine", flake=Flake(str(flake.path))),
generator, generator,
regenerate=False, regenerate=False,
) )
@pytest.mark.with_core @pytest.mark.with_core
def test_keygen( def test_keygen(monkeypatch: pytest.MonkeyPatch, flake: ClanFlake) -> None:
monkeypatch: pytest.MonkeyPatch, monkeypatch.chdir(flake.path)
temporary_home: Path, cli.run(["vars", "keygen", "--flake", str(flake.path), "--user", "user"])
) -> None:
monkeypatch.chdir(temporary_home)
cli.run(["vars", "keygen", "--flake", str(temporary_home), "--user", "user"])
# check public key exists # check public key exists
assert (temporary_home / "sops" / "users" / "user").is_dir() assert (flake.path / "sops" / "users" / "user").is_dir()
# check private key exists # check private key exists
assert (temporary_home / ".config" / "sops" / "age" / "keys.txt").is_file() assert (flake.temporary_home / ".config" / "sops" / "age" / "keys.txt").is_file()
# it should still work, even if the keys already exist # it should still work, even if the keys already exist
import shutil import shutil
shutil.rmtree(temporary_home / "sops" / "users" / "user") shutil.rmtree(flake.path / "sops" / "users" / "user")
cli.run(["vars", "keygen", "--flake", str(temporary_home), "--user", "user"]) cli.run(["vars", "keygen", "--flake", str(flake.path), "--user", "user"])
# check public key exists # check public key exists
assert (temporary_home / "sops" / "users" / "user").is_dir() assert (flake.path / "sops" / "users" / "user").is_dir()
@pytest.mark.with_core @pytest.mark.with_core
@@ -862,7 +859,7 @@ def test_invalidation(
flake.refresh() flake.refresh()
monkeypatch.chdir(flake.path) monkeypatch.chdir(flake.path)
cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"]) cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"])
machine = Machine(name="my_machine", flake=FlakeId(str(flake.path))) machine = Machine(name="my_machine", flake=Flake(str(flake.path)))
value1 = get_var( value1 = get_var(
str(machine.flake.path), machine.name, "my_generator/my_value" str(machine.flake.path), machine.name, "my_generator/my_value"
).printable_value ).printable_value

View File

@@ -5,7 +5,7 @@ from contextlib import ExitStack
import pytest import pytest
from age_keys import SopsSetup from age_keys import SopsSetup
from clan_cli import cmd from clan_cli import cmd
from clan_cli.clan_uri import FlakeId from clan_cli.flake import Flake
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.nix import nix_eval, run from clan_cli.nix import nix_eval, run
from clan_cli.vms.run import inspect_vm, spawn_vm from clan_cli.vms.run import inspect_vm, spawn_vm
@@ -97,8 +97,8 @@ def test_vm_deployment(
# run nix flake lock # run nix flake lock
cmd.run(["nix", "flake", "lock"], cmd.RunOpts(cwd=flake.path)) cmd.run(["nix", "flake", "lock"], cmd.RunOpts(cwd=flake.path))
vm1_config = inspect_vm(machine=Machine("m1_machine", FlakeId(str(flake.path)))) vm1_config = inspect_vm(machine=Machine("m1_machine", Flake(str(flake.path))))
vm2_config = inspect_vm(machine=Machine("m2_machine", FlakeId(str(flake.path)))) vm2_config = inspect_vm(machine=Machine("m2_machine", Flake(str(flake.path))))
with ExitStack() as stack: with ExitStack() as stack:
vm1 = stack.enter_context(spawn_vm(vm1_config, stdin=subprocess.DEVNULL)) vm1 = stack.enter_context(spawn_vm(vm1_config, stdin=subprocess.DEVNULL))
vm2 = stack.enter_context(spawn_vm(vm2_config, stdin=subprocess.DEVNULL)) vm2 = stack.enter_context(spawn_vm(vm2_config, stdin=subprocess.DEVNULL))

View File

@@ -2,7 +2,7 @@ from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import pytest import pytest
from clan_cli.clan_uri import FlakeId from clan_cli.flake import Flake
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.vms.run import inspect_vm, spawn_vm from clan_cli.vms.run import inspect_vm, spawn_vm
from fixtures_flakes import ClanFlake, FlakeForTest from fixtures_flakes import ClanFlake, FlakeForTest
@@ -80,7 +80,7 @@ def test_vm_persistence(
flake.refresh() flake.refresh()
vm_config = inspect_vm(machine=Machine("my_machine", FlakeId(str(flake.path)))) vm_config = inspect_vm(machine=Machine("my_machine", Flake(str(flake.path))))
with spawn_vm(vm_config) as vm, vm.qga_connect() as qga: with spawn_vm(vm_config) as vm, vm.qga_connect() as qga:
# create state via qmp command instead of systemd service # create state via qmp command instead of systemd service

View File

@@ -147,12 +147,6 @@ class VMObject(GObject.Object):
uri = ClanURI.from_str( uri = ClanURI.from_str(
url=str(self.data.flake.flake_url), machine_name=self.data.flake.flake_attr url=str(self.data.flake.flake_url), machine_name=self.data.flake.flake_attr
) )
if uri.flake.is_local():
self.machine = Machine(
name=self.data.flake.flake_attr,
flake=uri.flake,
)
if uri.flake.is_remote():
self.machine = Machine( self.machine = Machine(
name=self.data.flake.flake_attr, name=self.data.flake.flake_attr,
flake=uri.flake, flake=uri.flake,
@@ -160,7 +154,7 @@ class VMObject(GObject.Object):
assert self.machine is not None assert self.machine is not None
state_dir = vm_state_dir( state_dir = vm_state_dir(
flake_url=self.machine.flake, vm_name=self.machine.name flake_url=self.machine.flake.identifier, vm_name=self.machine.name
) )
self.qmp_wrap = QMPWrapper(state_dir) self.qmp_wrap = QMPWrapper(state_dir)
assert self.machine is not None assert self.machine is not None

View File

@@ -4,7 +4,8 @@ from pathlib import Path
from typing import Any, ClassVar from typing import Any, ClassVar
import gi import gi
from clan_cli.clan_uri import ClanURI, FlakeId from clan_cli.clan_uri import ClanURI
from clan_cli.flake import Flake
from clan_cli.history.add import HistoryEntry from clan_cli.history.add import HistoryEntry
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
@@ -34,7 +35,7 @@ class Emitter(GObject.GObject):
class ClanStore: class ClanStore:
_instance: "None | ClanStore" = None _instance: "None | ClanStore" = None
_clan_store: GKVStore[FlakeId, VMStore] _clan_store: GKVStore[Flake, VMStore]
_emitter: Emitter _emitter: Emitter
@@ -94,7 +95,7 @@ class ClanStore:
self.clan_store.register_on_change(on_clanstore_change) self.clan_store.register_on_change(on_clanstore_change)
@property @property
def clan_store(self) -> GKVStore[FlakeId, VMStore]: def clan_store(self) -> GKVStore[Flake, VMStore]:
return self._clan_store return self._clan_store
def create_vm_task(self, vm: HistoryEntry) -> bool: def create_vm_task(self, vm: HistoryEntry) -> bool:

View File

@@ -51,7 +51,7 @@ export const MachineListItem = (props: MachineListItemProps) => {
machine: { machine: {
name: name, name: name,
flake: { flake: {
loc: active_clan, identifier: active_clan,
}, },
}, },
no_reboot: true, no_reboot: true,

View File

@@ -163,7 +163,7 @@ export const Flash = () => {
machine: { machine: {
name: values.machine.devicePath, name: values.machine.devicePath,
flake: { flake: {
loc: values.machine.flake, identifier: values.machine.flake,
}, },
}, },
mode: "format", mode: "format",

View File

@@ -20,7 +20,7 @@ export function CreateMachine() {
initialValues: { initialValues: {
opts: { opts: {
clan_dir: { clan_dir: {
loc: activeURI() || "", identifier: activeURI() || "",
}, },
machine: { machine: {
tags: ["all"], tags: ["all"],
@@ -49,7 +49,7 @@ export function CreateMachine() {
opts: { opts: {
...values.opts, ...values.opts,
clan_dir: { clan_dir: {
loc: active_dir, identifier: active_dir,
}, },
}, },
}); });

View File

@@ -140,7 +140,7 @@ const InstallMachine = (props: InstallMachineProps) => {
machine: { machine: {
name: props.name, name: props.name,
flake: { flake: {
loc: curr_uri, identifier: curr_uri,
}, },
}, },
target_host: target, target_host: target,

View File

@@ -83,7 +83,7 @@ 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: { loc: curr_uri }, flake: { identifier: curr_uri },
machine: props.machine_id, machine: props.machine_id,
target_host: target, target_host: target,
backend: "nixos-facter", backend: "nixos-facter",