machines.Machine: refactor flake_dir -> flake; use Machine class in vm

This commit is contained in:
lassulus
2024-01-19 14:37:30 +01:00
parent b98e15d6d7
commit bdd1f9be6d
13 changed files with 144 additions and 137 deletions

View File

@@ -57,21 +57,15 @@ def create_parser(prog: str | None = None) -> argparse.ArgumentParser:
default=[], default=[],
) )
def flake_path(arg: str) -> Path: def flake_path(arg: str) -> str | Path:
flake_dir = Path(arg).resolve() flake_dir = Path(arg).resolve()
if not flake_dir.exists(): if flake_dir.exists() and flake_dir.is_dir():
raise argparse.ArgumentTypeError( return flake_dir
f"flake directory {flake_dir} does not exist" return arg
)
if not flake_dir.is_dir():
raise argparse.ArgumentTypeError(
f"flake directory {flake_dir} is not a directory"
)
return flake_dir
parser.add_argument( parser.add_argument(
"--flake", "--flake",
help="path to the flake where the clan resides in", help="path to the flake where the clan resides in, can be a remote flake or local",
default=get_clan_flake_toplevel(), default=get_clan_flake_toplevel(),
type=flake_path, type=flake_path,
) )

View File

@@ -31,7 +31,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:
machine = Machine(name=args.machine, flake_dir=args.flake) machine = Machine(name=args.machine, flake=args.flake)
create_backup(machine=machine, provider=args.provider) create_backup(machine=machine, provider=args.provider)

View File

@@ -45,9 +45,7 @@ def list_provider(machine: Machine, provider: str) -> list[Backup]:
def list_backups(machine: Machine, provider: str | None = None) -> list[Backup]: def list_backups(machine: Machine, provider: str | None = None) -> list[Backup]:
backup_metadata = json.loads( backup_metadata = json.loads(machine.eval_nix("config.clanCore.backups"))
machine.eval_nix(f"nixosConfigurations.{machine.name}.config.clanCore.backups")
)
results = [] results = []
if provider is None: if provider is None:
for _provider in backup_metadata["providers"]: for _provider in backup_metadata["providers"]:
@@ -60,7 +58,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:
machine = Machine(name=args.machine, flake_dir=args.flake) machine = Machine(name=args.machine, flake=args.flake)
backups = list_backups(machine=machine, provider=args.provider) backups = list_backups(machine=machine, provider=args.provider)
print(backups) print(backups)

View File

@@ -91,7 +91,7 @@ def restore_backup(
def restore_command(args: argparse.Namespace) -> None: def restore_command(args: argparse.Namespace) -> None:
machine = Machine(name=args.machine, flake_dir=args.flake) machine = Machine(name=args.machine, flake=args.flake)
backups = list_backups(machine=machine, provider=args.provider) backups = list_backups(machine=machine, provider=args.provider)
restore_backup( restore_backup(
machine=machine, machine=machine,

View File

@@ -47,7 +47,7 @@ def user_gcroot_dir() -> Path:
return p return p
def specific_groot_dir(*, clan_name: str, flake_url: str) -> Path: def machine_gcroot(*, clan_name: str, flake_url: str) -> Path:
# Always build icon so that we can symlink it to the gcroot # Always build icon so that we can symlink it to the gcroot
gcroot_dir = user_gcroot_dir() gcroot_dir = user_gcroot_dir()
clan_gcroot = gcroot_dir / clan_key_safe(clan_name, flake_url) clan_gcroot = gcroot_dir / clan_key_safe(clan_name, flake_url)

View File

@@ -3,9 +3,10 @@ from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from ..cmd import run from ..cmd import run
from ..dirs import specific_groot_dir from ..dirs import machine_gcroot
from ..errors import ClanError from ..errors import ClanError
from ..machines.list import list_machines from ..machines.list import list_machines
from ..machines.machines import Machine
from ..nix import nix_build, nix_config, nix_eval, nix_metadata from ..nix import nix_build, nix_config, nix_eval, nix_metadata
from ..vms.inspect import VmConfig, inspect_vm from ..vms.inspect import VmConfig, inspect_vm
@@ -29,23 +30,24 @@ def run_cmd(cmd: list[str]) -> str:
return proc.stdout.strip() return proc.stdout.strip()
def inspect_flake(flake_url: str | Path, flake_attr: str) -> FlakeConfig: def inspect_flake(flake_url: str | Path, machine_name: str) -> FlakeConfig:
config = nix_config() config = nix_config()
system = config["system"] system = config["system"]
# Check if the machine exists # Check if the machine exists
machines = list_machines(flake_url) machines = list_machines(flake_url)
if flake_attr not in machines: if machine_name not in machines:
raise ClanError( raise ClanError(
f"Machine {flake_attr} not found in {flake_url}. Available machines: {', '.join(machines)}" f"Machine {machine_name} not found in {flake_url}. Available machines: {', '.join(machines)}"
) )
vm = inspect_vm(flake_url, flake_attr) machine = Machine(machine_name, flake_url)
vm = inspect_vm(machine)
# Get the cLAN name # Get the cLAN name
cmd = nix_eval( cmd = nix_eval(
[ [
f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.clanCore.clanName' f'{flake_url}#clanInternals.machines."{system}"."{machine_name}".config.clanCore.clanName'
] ]
) )
res = run_cmd(cmd) res = run_cmd(cmd)
@@ -54,7 +56,7 @@ def inspect_flake(flake_url: str | Path, flake_attr: str) -> FlakeConfig:
# Get the clan icon path # Get the clan icon path
cmd = nix_eval( cmd = nix_eval(
[ [
f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.clanCore.clanIcon' f'{flake_url}#clanInternals.machines."{system}"."{machine_name}".config.clanCore.clanIcon'
] ]
) )
res = run_cmd(cmd) res = run_cmd(cmd)
@@ -67,10 +69,9 @@ def inspect_flake(flake_url: str | Path, flake_attr: str) -> FlakeConfig:
cmd = nix_build( cmd = nix_build(
[ [
f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.clanCore.clanIcon' f'{flake_url}#clanInternals.machines."{system}"."{machine_name}".config.clanCore.clanIcon'
], ],
specific_groot_dir(clan_name=clan_name, flake_url=str(flake_url)) machine_gcroot(clan_name=clan_name, flake_url=str(flake_url)) / "clanIcon",
/ "clanIcon",
) )
run_cmd(cmd) run_cmd(cmd)
@@ -81,7 +82,7 @@ def inspect_flake(flake_url: str | Path, flake_attr: str) -> FlakeConfig:
vm=vm, vm=vm,
flake_url=flake_url, flake_url=flake_url,
clan_name=clan_name, clan_name=clan_name,
flake_attr=flake_attr, flake_attr=machine_name,
nar_hash=meta["locked"]["narHash"], nar_hash=meta["locked"]["narHash"],
icon=icon_path, icon=icon_path,
description=meta.get("description"), description=meta.get("description"),
@@ -102,7 +103,7 @@ def inspect_command(args: argparse.Namespace) -> None:
flake=args.flake or Path.cwd(), flake=args.flake or Path.cwd(),
) )
res = inspect_flake( res = inspect_flake(
flake_url=inspect_options.flake, flake_attr=inspect_options.machine flake_url=inspect_options.flake, machine_name=inspect_options.machine
) )
print("cLAN name:", res.clan_name) print("cLAN name:", res.clan_name)
print("Icon:", res.icon) print("Icon:", res.icon)

View File

@@ -34,7 +34,7 @@ def install_nixos(machine: Machine, kexec: str | None = None) -> None:
cmd = [ cmd = [
"nixos-anywhere", "nixos-anywhere",
"-f", "-f",
f"{machine.flake_dir}#{flake_attr}", f"{machine.flake}#{flake_attr}",
"-t", "-t",
"--no-reboot", "--no-reboot",
"--extra-files", "--extra-files",
@@ -68,7 +68,7 @@ def install_command(args: argparse.Namespace) -> None:
target_host=args.target_host, target_host=args.target_host,
kexec=args.kexec, kexec=args.kexec,
) )
machine = Machine(opts.machine, flake_dir=opts.flake) machine = Machine(opts.machine, flake=opts.flake)
machine.deployment_address = opts.target_host machine.deployment_address = opts.target_host
install_nixos(machine, kexec=opts.kexec) install_nixos(machine, kexec=opts.kexec)

View File

@@ -1,33 +1,16 @@
import json import json
from pathlib import Path from pathlib import Path
from ..cmd import Log, run from ..cmd import run
from ..nix import nix_build, nix_config, nix_eval from ..nix import nix_build, nix_config, nix_eval
from ..ssh import Host, parse_deployment_address from ..ssh import Host, parse_deployment_address
def build_machine_data(machine_name: str, clan_dir: Path) -> dict:
config = nix_config()
system = config["system"]
proc = run(
nix_build(
[
f'{clan_dir}#clanInternals.machines."{system}"."{machine_name}".config.system.clan.deployment.file'
]
),
log=Log.BOTH,
error_msg="failed to build machine data",
)
return json.loads(Path(proc.stdout.strip()).read_text())
class Machine: class Machine:
def __init__( def __init__(
self, self,
name: str, name: str,
flake_dir: Path, flake: Path | str,
machine_data: dict | None = None, machine_data: dict | None = None,
) -> None: ) -> None:
""" """
@@ -36,22 +19,39 @@ class Machine:
@clan_dir: the directory of the clan, optional, if not set it will be determined from the current working directory @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 @machine_json: can be optionally used to skip evaluation of the machine, location of the json file with machine data
""" """
self.name = name self.name: str = name
self.flake_dir = flake_dir self.flake: str | Path = flake
self.eval_cache: dict[str, str] = {}
self.build_cache: dict[str, Path] = {}
# TODO do this lazily
if machine_data is None: if machine_data is None:
self.machine_data = build_machine_data(name, self.flake_dir) self.machine_data = json.loads(
self.build_nix("config.system.clan.deployment.file").read_text()
)
else: else:
self.machine_data = machine_data self.machine_data = machine_data
self.deployment_address = self.machine_data["deploymentAddress"] self.deployment_address = self.machine_data["deploymentAddress"]
self.secrets_module = self.machine_data["secretsModule"] self.secrets_module = self.machine_data.get("secretsModule", None)
self.secrets_data = json.loads( if "secretsData" in self.machine_data:
Path(self.machine_data["secretsData"]).read_text() self.secrets_data = json.loads(
) Path(self.machine_data["secretsData"]).read_text()
)
self.secrets_upload_directory = self.machine_data["secretsUploadDirectory"] self.secrets_upload_directory = self.machine_data["secretsUploadDirectory"]
self.eval_cache: dict[str, str] = {}
self.build_cache: dict[str, Path] = {} @property
def flake_dir(self) -> Path:
if isinstance(self.flake, Path):
return self.flake
if hasattr(self, "flake_path"):
return Path(self.flake_path)
print(nix_eval([f"{self.flake}"]))
self.flake_path = run(nix_eval([f"{self.flake}"])).stdout.strip()
return Path(self.flake_path)
@property @property
def host(self) -> Host: def host(self) -> Host:
@@ -67,9 +67,25 @@ class Machine:
if attr in self.eval_cache and not refresh: if attr in self.eval_cache and not refresh:
return self.eval_cache[attr] return self.eval_cache[attr]
output = run( config = nix_config()
nix_eval([f"path:{self.flake_dir}#{attr}"]), system = config["system"]
).stdout.strip()
if isinstance(self.flake, Path):
output = run(
nix_eval(
[
f'path:{self.flake}#clanInternals.machines."{system}"."{self.name}".{attr}'
]
),
).stdout.strip()
else:
output = run(
nix_eval(
[
f'{self.flake}#clanInternals.machines."{system}"."{self.name}".{attr}'
]
),
).stdout.strip()
self.eval_cache[attr] = output self.eval_cache[attr] = output
return output return output
@@ -80,8 +96,25 @@ class Machine:
""" """
if attr in self.build_cache and not refresh: if attr in self.build_cache and not refresh:
return self.build_cache[attr] return self.build_cache[attr]
outpath = run(
nix_build([f"path:{self.flake_dir}#{attr}"]), config = nix_config()
).stdout.strip() system = config["system"]
if isinstance(self.flake, Path):
outpath = run(
nix_build(
[
f'path:{self.flake}#clanInternals.machines."{system}"."{self.name}".{attr}'
]
),
).stdout.strip()
else:
outpath = run(
nix_build(
[
f'{self.flake}#clanInternals.machines."{system}"."{self.name}".{attr}'
]
),
).stdout.strip()
self.build_cache[attr] = Path(outpath) self.build_cache[attr] = Path(outpath)
return Path(outpath) return Path(outpath)

View File

@@ -93,9 +93,7 @@ def get_all_machines(clan_dir: Path) -> HostGroup:
name, name,
machine_data["deploymentAddress"], machine_data["deploymentAddress"],
meta={ meta={
"machine": Machine( "machine": Machine(name=name, flake=clan_dir, machine_data=machine_data)
name=name, flake_dir=clan_dir, machine_data=machine_data
)
}, },
) )
hosts.append(host) hosts.append(host)
@@ -105,7 +103,7 @@ def get_all_machines(clan_dir: Path) -> HostGroup:
def get_selected_machines(machine_names: list[str], flake_dir: Path) -> HostGroup: def get_selected_machines(machine_names: list[str], flake_dir: Path) -> HostGroup:
hosts = [] hosts = []
for name in machine_names: for name in machine_names:
machine = Machine(name=name, flake_dir=flake_dir) machine = Machine(name=name, flake=flake_dir)
hosts.append(machine.host) hosts.append(machine.host)
return HostGroup(hosts) return HostGroup(hosts)
@@ -115,7 +113,7 @@ def update(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")
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(name=args.machines[0], flake_dir=args.flake) machine = Machine(name=args.machines[0], flake=args.flake)
machine.deployment_address = args.target_host machine.deployment_address = args.target_host
host = parse_deployment_address( host = parse_deployment_address(
args.machines[0], args.machines[0],

View File

@@ -28,11 +28,11 @@ def generate_secrets(machine: Machine) -> None:
not secret_store.exists(service, secret) not secret_store.exists(service, secret)
for secret in machine.secrets_data[service]["secrets"] for secret in machine.secrets_data[service]["secrets"]
) or any( ) or any(
not (machine.flake_dir / fact).exists() not (machine.flake / fact).exists()
for fact in machine.secrets_data[service]["facts"].values() for fact in machine.secrets_data[service]["facts"].values()
) )
for fact in machine.secrets_data[service]["facts"].values(): for fact in machine.secrets_data[service]["facts"].values():
if not (machine.flake_dir / fact).exists(): if not (machine.flake / fact).exists():
print(f"fact {fact} is missing") print(f"fact {fact} is missing")
if needs_regeneration: if needs_regeneration:
env = os.environ.copy() env = os.environ.copy()
@@ -66,7 +66,7 @@ def generate_secrets(machine: Machine) -> None:
msg = f"did not generate a file for '{name}' when running the following command:\n" msg = f"did not generate a file for '{name}' when running the following command:\n"
msg += machine.secrets_data[service]["generator"] msg += machine.secrets_data[service]["generator"]
raise ClanError(msg) raise ClanError(msg)
fact_path = machine.flake_dir / fact_path fact_path = machine.flake / fact_path
fact_path.parent.mkdir(parents=True, exist_ok=True) fact_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(fact_file, fact_path) shutil.copyfile(fact_file, fact_path)
@@ -74,7 +74,7 @@ def generate_secrets(machine: Machine) -> None:
def generate_command(args: argparse.Namespace) -> None: def generate_command(args: argparse.Namespace) -> None:
machine = Machine(name=args.machine, flake_dir=args.flake) machine = Machine(name=args.machine, flake=args.flake)
generate_secrets(machine) generate_secrets(machine)

View File

@@ -43,7 +43,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_dir=args.flake) machine = Machine(name=args.machine, flake=args.flake)
upload_secrets(machine) upload_secrets(machine)

View File

@@ -3,15 +3,14 @@ import json
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from ..cmd import run from ..machines.machines import Machine
from ..nix import nix_config, nix_eval
@dataclass @dataclass
class VmConfig: class VmConfig:
clan_name: str machine_name: str
flake_url: str | Path flake_url: str | Path
flake_attr: str clan_name: str
cores: int cores: int
memory_size: int memory_size: int
@@ -19,21 +18,9 @@ class VmConfig:
wayland: bool = False wayland: bool = False
def inspect_vm(flake_url: str | Path, flake_attr: str) -> VmConfig: def inspect_vm(machine: Machine) -> VmConfig:
config = nix_config() data = json.loads(machine.eval_nix("config.clanCore.vm.inspect"))
system = config["system"] return VmConfig(machine_name=machine.name, flake_url=machine.flake, **data)
cmd = nix_eval(
[
f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.clanCore.vm.inspect',
"--refresh",
]
)
proc = run(cmd)
data = json.loads(proc.stdout)
return VmConfig(flake_url=flake_url, flake_attr=flake_attr, **data)
@dataclass @dataclass
@@ -47,9 +34,9 @@ def inspect_command(args: argparse.Namespace) -> None:
machine=args.machine, machine=args.machine,
flake=args.flake or Path.cwd(), flake=args.flake or Path.cwd(),
) )
res = inspect_vm(
flake_url=inspect_options.flake, flake_attr=inspect_options.machine machine = Machine(inspect_options.machine, inspect_options.flake)
) res = inspect_vm(machine)
print("Cores:", res.cores) print("Cores:", res.cores)
print("Memory size:", res.memory_size) print("Memory size:", res.memory_size)
print("Graphics:", res.graphics) print("Graphics:", res.graphics)

View File

@@ -1,20 +1,20 @@
import argparse import argparse
import importlib
import json import json
import logging import logging
import os import os
import sys
import tempfile import tempfile
import importlib
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import IO from typing import IO
from ..cmd import Log, run from ..cmd import Log, run
from ..dirs import module_root, specific_groot_dir, vm_state_dir from ..dirs import machine_gcroot, module_root, vm_state_dir
from ..errors import ClanError from ..errors import ClanError
from ..nix import nix_build, nix_config, nix_shell
from .inspect import VmConfig, inspect_vm
from ..machines.machines import Machine from ..machines.machines import Machine
from ..nix import nix_build, nix_config, nix_shell
from ..secrets.generate import generate_secrets
from .inspect import VmConfig, inspect_vm
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -75,7 +75,7 @@ def qemu_command(
# fmt: off # fmt: off
command = [ command = [
"qemu-kvm", "qemu-kvm",
"-name", vm.flake_attr, "-name", vm.machine_name,
"-m", f'{nixos_config["memorySize"]}M', "-m", f'{nixos_config["memorySize"]}M',
"-smp", str(nixos_config["cores"]), "-smp", str(nixos_config["cores"]),
"-cpu", "max", "-cpu", "max",
@@ -104,32 +104,34 @@ def qemu_command(
return command return command
def get_vm_create_info(vm: VmConfig, nix_options: list[str]) -> dict[str, str]: # TODO move this to the Machines class
def get_vm_create_info(
machine: Machine, vm: VmConfig, nix_options: list[str]
) -> dict[str, str]:
config = nix_config() config = nix_config()
system = config["system"] system = config["system"]
clan_dir = vm.flake_url clan_dir = machine.flake
machine = vm.flake_attr
cmd = nix_build( cmd = nix_build(
[ [
f'{clan_dir}#clanInternals.machines."{system}"."{machine}".config.system.clan.vm.create', f'{clan_dir}#clanInternals.machines."{system}"."{machine.name}".config.system.clan.vm.create',
*nix_options, *nix_options,
], ],
specific_groot_dir(clan_name=vm.clan_name, flake_url=str(vm.flake_url)) machine_gcroot(clan_name=vm.clan_name, flake_url=str(vm.flake_url))
/ f"vm-{machine}", / f"vm-{machine.name}",
)
proc = run(
cmd, log=Log.BOTH, error_msg=f"Could not build vm config for {machine.name}"
) )
proc = run(cmd, log=Log.BOTH, error_msg=f"Could not build vm config for {machine}")
try: try:
return json.loads(Path(proc.stdout.strip()).read_text()) return json.loads(Path(proc.stdout.strip()).read_text())
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
raise ClanError(f"Failed to parse vm config: {e}") raise ClanError(f"Failed to parse vm config: {e}")
def generate_secrets( def get_secrets(
vm: VmConfig, machine: Machine,
nixos_config: dict[str, str],
tmpdir: Path, tmpdir: Path,
log_fd: IO[str] | None,
) -> Path: ) -> Path:
secrets_dir = tmpdir / "secrets" secrets_dir = tmpdir / "secrets"
secrets_dir.mkdir(exist_ok=True) secrets_dir.mkdir(exist_ok=True)
@@ -138,19 +140,12 @@ def generate_secrets(
secret_store = secrets_module.SecretStore(machine=machine) secret_store = secrets_module.SecretStore(machine=machine)
# Only generate secrets for local clans # Only generate secrets for local clans
if isinstance(vm.flake_url, Path) and vm.flake_url.is_dir(): if isinstance(machine.flake, Path) and machine.flake.is_dir():
if Path(vm.flake_url).is_dir(): generate_secrets(machine)
run([nixos_config["generateSecrets"], vm.clan_name], env=env) else:
else: log.warning("won't generate secrets for non local clan")
log.warning("won't generate secrets for non local clan")
cmd = [nixos_config["uploadSecrets"]] secret_store.upload(secrets_dir)
run(
cmd,
env=env,
log=Log.BOTH,
error_msg=f"Could not upload secrets for {vm.flake_attr}",
)
return secrets_dir return secrets_dir
@@ -191,26 +186,28 @@ def prepare_disk(tmpdir: Path, log_fd: IO[str] | None) -> Path:
def run_vm( def run_vm(
vm: VmConfig, nix_options: list[str] = [], log_fd: IO[str] | None = None vm: VmConfig,
nix_options: list[str] = [],
log_fd: IO[str] | None = None,
) -> None: ) -> None:
""" """
log_fd can be used to stream the output of all commands to a UI log_fd can be used to stream the output of all commands to a UI
""" """
machine = vm.flake_attr machine = Machine(vm.machine_name, vm.flake_url)
log.debug(f"Creating VM for {machine}") log.debug(f"Creating VM for {machine}")
# TODO: We should get this from the vm argument # TODO: We should get this from the vm argument
nixos_config = get_vm_create_info(vm, nix_options) nixos_config = get_vm_create_info(machine, vm, nix_options)
with tempfile.TemporaryDirectory() as tmpdir_: with tempfile.TemporaryDirectory() as tmpdir_:
tmpdir = Path(tmpdir_) tmpdir = Path(tmpdir_)
xchg_dir = tmpdir / "xchg" xchg_dir = tmpdir / "xchg"
xchg_dir.mkdir(exist_ok=True) xchg_dir.mkdir(exist_ok=True)
secrets_dir = generate_secrets(vm, nixos_config, tmpdir, log_fd) secrets_dir = get_secrets(machine, tmpdir)
disk_img = prepare_disk(tmpdir, log_fd) disk_img = prepare_disk(tmpdir, log_fd)
state_dir = vm_state_dir(vm.clan_name, str(vm.flake_url), machine) state_dir = vm_state_dir(vm.clan_name, str(machine.flake), machine.name)
state_dir.mkdir(parents=True, exist_ok=True) state_dir.mkdir(parents=True, exist_ok=True)
qemu_cmd = qemu_command( qemu_cmd = qemu_command(
@@ -243,7 +240,6 @@ def run_vm(
@dataclass @dataclass
class RunOptions: class RunOptions:
machine: str machine: str
flake_url: str | None
flake: Path flake: Path
nix_options: list[str] = field(default_factory=list) nix_options: list[str] = field(default_factory=list)
wayland: bool = False wayland: bool = False
@@ -252,14 +248,14 @@ class RunOptions:
def run_command(args: argparse.Namespace) -> None: def run_command(args: argparse.Namespace) -> None:
run_options = RunOptions( run_options = RunOptions(
machine=args.machine, machine=args.machine,
flake_url=args.flake_url, flake=args.flake,
flake=args.flake or Path.cwd(),
nix_options=args.option, nix_options=args.option,
wayland=args.wayland, wayland=args.wayland,
) )
flake_url = run_options.flake_url or run_options.flake machine = Machine(run_options.machine, run_options.flake)
vm = inspect_vm(flake_url=flake_url, flake_attr=run_options.machine)
vm = inspect_vm(machine=machine)
# TODO: allow to set this in the config # TODO: allow to set this in the config
vm.wayland = run_options.wayland vm.wayland = run_options.wayland