refactor: replace eval_nix/build_nix with machine.select()
- Remove nix(), eval_nix(), and build_nix() methods from Machine class - Add select() method that handles machine-specific attribute prefixes - Update all usages to use machine.select() directly - Handle Path conversion and tmp_store logic at call sites - This simplifies the Machine API and prepares for deployment.json removal
This commit is contained in:
@@ -992,7 +992,7 @@ def test_dynamic_invalidation(
|
|||||||
# before generating, dependent generator validation should be empty; see bogus hardware-configuration.nix above
|
# before generating, dependent generator validation should be empty; see bogus hardware-configuration.nix above
|
||||||
# we have to avoid `*.files.value` in this initial select because the generators haven't been run yet
|
# we have to avoid `*.files.value` in this initial select because the generators haven't been run yet
|
||||||
# Generators 0: The initial generators before any 'vars generate'
|
# Generators 0: The initial generators before any 'vars generate'
|
||||||
generators_0 = machine.eval_nix(f"{gen_prefix}.*.{{validationHash}}")
|
generators_0 = machine.select(f"{gen_prefix}.*.{{validationHash}}")
|
||||||
assert generators_0["dependent_generator"]["validationHash"] is None
|
assert generators_0["dependent_generator"]["validationHash"] is None
|
||||||
|
|
||||||
# generate both my_generator and (the dependent) dependent_generator
|
# generate both my_generator and (the dependent) dependent_generator
|
||||||
@@ -1001,7 +1001,7 @@ def test_dynamic_invalidation(
|
|||||||
|
|
||||||
# after generating once, dependent generator validation should be set
|
# after generating once, dependent generator validation should be set
|
||||||
# Generators_1: The generators after the first 'vars generate'
|
# Generators_1: The generators after the first 'vars generate'
|
||||||
generators_1 = machine.eval_nix(gen_prefix)
|
generators_1 = machine.select(gen_prefix)
|
||||||
assert generators_1["dependent_generator"]["validationHash"] is not None
|
assert generators_1["dependent_generator"]["validationHash"] is not None
|
||||||
|
|
||||||
# @tangential: after generating once, neither generator should want to run again because `clan vars generate` should have re-evaluated the dependent generator's validationHash after executing the parent generator but before executing the dependent generator
|
# @tangential: after generating once, neither generator should want to run again because `clan vars generate` should have re-evaluated the dependent generator's validationHash after executing the parent generator but before executing the dependent generator
|
||||||
@@ -1014,7 +1014,7 @@ def test_dynamic_invalidation(
|
|||||||
cli.run(["vars", "generate", "--flake", str(flake.path), machine.name])
|
cli.run(["vars", "generate", "--flake", str(flake.path), machine.name])
|
||||||
clan_flake.invalidate_cache()
|
clan_flake.invalidate_cache()
|
||||||
# Generators_2: The generators after the second 'vars generate'
|
# Generators_2: The generators after the second 'vars generate'
|
||||||
generators_2 = machine.eval_nix(gen_prefix)
|
generators_2 = machine.select(gen_prefix)
|
||||||
assert (
|
assert (
|
||||||
generators_1["dependent_generator"]["validationHash"]
|
generators_1["dependent_generator"]["validationHash"]
|
||||||
== generators_2["dependent_generator"]["validationHash"]
|
== generators_2["dependent_generator"]["validationHash"]
|
||||||
|
|||||||
@@ -72,14 +72,20 @@ class Generator:
|
|||||||
|
|
||||||
def final_script(self) -> Path:
|
def final_script(self) -> Path:
|
||||||
assert self._machine is not None
|
assert self._machine is not None
|
||||||
final_script = self._machine.build_nix(
|
from clan_lib.nix import nix_test_store
|
||||||
f'config.clan.core.vars.generators."{self.name}".finalScript'
|
|
||||||
|
output = Path(
|
||||||
|
self._machine.select(
|
||||||
|
f'config.clan.core.vars.generators."{self.name}".finalScript'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return final_script
|
if tmp_store := nix_test_store():
|
||||||
|
output = tmp_store.joinpath(*output.parts[1:])
|
||||||
|
return output
|
||||||
|
|
||||||
def validation(self) -> str | None:
|
def validation(self) -> str | None:
|
||||||
assert self._machine is not None
|
assert self._machine is not None
|
||||||
return self._machine.eval_nix(
|
return self._machine.select(
|
||||||
f'config.clan.core.vars.generators."{self.name}".validationHash'
|
f'config.clan.core.vars.generators."{self.name}".validationHash'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class SecretStore(StoreBase):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def _store_backend(self) -> str:
|
def _store_backend(self) -> str:
|
||||||
backend = self.machine.eval_nix("config.clan.core.vars.settings.passBackend")
|
backend = self.machine.select("config.clan.core.vars.settings.passBackend")
|
||||||
return backend
|
return backend
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class VmConfig:
|
|||||||
|
|
||||||
|
|
||||||
def inspect_vm(machine: Machine) -> VmConfig:
|
def inspect_vm(machine: Machine) -> VmConfig:
|
||||||
data = machine.eval_nix("config.clan.core.vm.inspect")
|
data = machine.select("config.clan.core.vm.inspect")
|
||||||
# HACK!
|
# HACK!
|
||||||
data["flake_url"] = dataclasses.asdict(machine.flake)
|
data["flake_url"] = dataclasses.asdict(machine.flake)
|
||||||
return VmConfig.from_json(data)
|
return VmConfig.from_json(data)
|
||||||
|
|||||||
@@ -49,16 +49,24 @@ def facts_to_nixos_config(facts: dict[str, dict[str, bytes]]) -> dict:
|
|||||||
|
|
||||||
|
|
||||||
# TODO move this to the Machines class
|
# TODO move this to the Machines class
|
||||||
def build_vm(machine: Machine, tmpdir: Path) -> dict[str, str]:
|
def build_vm(
|
||||||
|
machine: Machine, tmpdir: Path, nix_options: list[str] | None = None
|
||||||
|
) -> dict[str, str]:
|
||||||
# TODO pass prompt here for the GTK gui
|
# TODO pass prompt here for the GTK gui
|
||||||
|
if nix_options is None:
|
||||||
|
nix_options = []
|
||||||
secrets_dir = get_secrets(machine, tmpdir)
|
secrets_dir = get_secrets(machine, tmpdir)
|
||||||
|
|
||||||
public_facts = machine.public_facts_store.get_all()
|
from clan_lib.nix import nix_test_store
|
||||||
|
|
||||||
nixos_config_file = machine.build_nix(
|
output = Path(
|
||||||
"config.system.clan.vm.create", extra_config=facts_to_nixos_config(public_facts)
|
machine.select(
|
||||||
|
"config.system.clan.vm.create",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
if tmp_store := nix_test_store():
|
||||||
|
output = tmp_store.joinpath(*output.parts[1:])
|
||||||
|
nixos_config_file = output
|
||||||
try:
|
try:
|
||||||
vm_data = json.loads(Path(nixos_config_file).read_text())
|
vm_data = json.loads(Path(nixos_config_file).read_text())
|
||||||
vm_data["secrets_dir"] = str(secrets_dir)
|
vm_data["secrets_dir"] = str(secrets_dir)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from clan_lib.machines.machines import Machine
|
|||||||
|
|
||||||
def create_backup(machine: Machine, provider: str | None = None) -> None:
|
def create_backup(machine: Machine, provider: str | None = None) -> None:
|
||||||
machine.info(f"creating backup for {machine.name}")
|
machine.info(f"creating backup for {machine.name}")
|
||||||
backup_scripts = machine.eval_nix("config.clan.core.backups")
|
backup_scripts = machine.select("config.clan.core.backups")
|
||||||
host = machine.target_host()
|
host = machine.target_host()
|
||||||
if provider is None:
|
if provider is None:
|
||||||
if not backup_scripts["providers"]:
|
if not backup_scripts["providers"]:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class Backup:
|
|||||||
|
|
||||||
def list_provider(machine: Machine, host: Remote, provider: str) -> list[Backup]:
|
def list_provider(machine: Machine, host: Remote, provider: str) -> list[Backup]:
|
||||||
results = []
|
results = []
|
||||||
backup_metadata = machine.eval_nix("config.clan.core.backups")
|
backup_metadata = machine.select("config.clan.core.backups")
|
||||||
list_command = backup_metadata["providers"][provider]["list"]
|
list_command = backup_metadata["providers"][provider]["list"]
|
||||||
proc = host.run(
|
proc = host.run(
|
||||||
[list_command],
|
[list_command],
|
||||||
@@ -41,7 +41,7 @@ def list_provider(machine: Machine, host: Remote, 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 = machine.eval_nix("config.clan.core.backups")
|
backup_metadata = machine.select("config.clan.core.backups")
|
||||||
results = []
|
results = []
|
||||||
with machine.target_host().ssh_control_master() as host:
|
with machine.target_host().ssh_control_master() as host:
|
||||||
if provider is None:
|
if provider is None:
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ from clan_lib.ssh.remote import Remote
|
|||||||
def restore_service(
|
def restore_service(
|
||||||
machine: Machine, host: Remote, name: str, provider: str, service: str
|
machine: Machine, host: Remote, name: str, provider: str, service: str
|
||||||
) -> None:
|
) -> None:
|
||||||
backup_metadata = machine.eval_nix("config.clan.core.backups")
|
backup_metadata = machine.select("config.clan.core.backups")
|
||||||
backup_folders = machine.eval_nix("config.clan.core.state")
|
backup_folders = machine.select("config.clan.core.state")
|
||||||
|
|
||||||
if service not in backup_folders:
|
if service not in backup_folders:
|
||||||
msg = f"Service {service} not found in configuration. Available services are: {', '.join(backup_folders.keys())}"
|
msg = f"Service {service} not found in configuration. Available services are: {', '.join(backup_folders.keys())}"
|
||||||
@@ -60,7 +60,7 @@ def restore_backup(
|
|||||||
errors = []
|
errors = []
|
||||||
with machine.target_host().ssh_control_master() as host:
|
with machine.target_host().ssh_control_master() as host:
|
||||||
if service is None:
|
if service is None:
|
||||||
backup_folders = machine.eval_nix("config.clan.core.state")
|
backup_folders = machine.select("config.clan.core.state")
|
||||||
for _service in backup_folders:
|
for _service in backup_folders:
|
||||||
try:
|
try:
|
||||||
restore_service(machine, host, name, provider, _service)
|
restore_service(machine, host, name, provider, _service)
|
||||||
|
|||||||
@@ -81,9 +81,10 @@ class Machine:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def deployment(self) -> dict:
|
def deployment(self) -> dict:
|
||||||
deployment = json.loads(
|
output = Path(self.select("config.system.clan.deployment.file"))
|
||||||
self.build_nix("config.system.clan.deployment.file").read_text()
|
if tmp_store := nix_test_store():
|
||||||
)
|
output = tmp_store.joinpath(*output.parts[1:])
|
||||||
|
deployment = json.loads(output.read_text())
|
||||||
return deployment
|
return deployment
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
@@ -159,13 +160,13 @@ class Machine:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def nix(
|
def select(
|
||||||
self,
|
self,
|
||||||
attr: str,
|
attr: str,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
Build the machine and return the path to the result
|
Select a nix attribute of the machine
|
||||||
accepts a secret store and a facts store # TODO
|
@attr: the attribute to get
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config = nix_config()
|
config = nix_config()
|
||||||
@@ -175,36 +176,6 @@ class Machine:
|
|||||||
f'clanInternals.machines."{system}"."{self.name}".{attr}'
|
f'clanInternals.machines."{system}"."{self.name}".{attr}'
|
||||||
)
|
)
|
||||||
|
|
||||||
def eval_nix(self, attr: str, extra_config: None | dict = None) -> Any:
|
|
||||||
"""
|
|
||||||
eval a nix attribute of the machine
|
|
||||||
@attr: the attribute to get
|
|
||||||
"""
|
|
||||||
|
|
||||||
if extra_config:
|
|
||||||
log.warning("extra_config in eval_nix is no longer supported")
|
|
||||||
|
|
||||||
return self.nix(attr)
|
|
||||||
|
|
||||||
def build_nix(self, attr: str, extra_config: None | dict = None) -> Path:
|
|
||||||
"""
|
|
||||||
build a nix attribute of the machine
|
|
||||||
@attr: the attribute to get
|
|
||||||
"""
|
|
||||||
|
|
||||||
if extra_config:
|
|
||||||
log.warning("extra_config in build_nix is no longer supported")
|
|
||||||
|
|
||||||
output = self.nix(attr)
|
|
||||||
output = Path(output)
|
|
||||||
if tmp_store := nix_test_store():
|
|
||||||
output = tmp_store.joinpath(*output.parts[1:])
|
|
||||||
assert output.exists(), f"The output {output} doesn't exist"
|
|
||||||
if isinstance(output, Path):
|
|
||||||
return output
|
|
||||||
msg = "build_nix returned not a Path"
|
|
||||||
raise ClanError(msg)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class RemoteSource:
|
class RemoteSource:
|
||||||
@@ -229,7 +200,7 @@ def get_host(
|
|||||||
machine.debug(
|
machine.debug(
|
||||||
f"'{field}' is not set in inventory, falling back to slower Nix config, set it either through the Nix or json interface to improve performance"
|
f"'{field}' is not set in inventory, falling back to slower Nix config, set it either through the Nix or json interface to improve performance"
|
||||||
)
|
)
|
||||||
host_str = machine.eval_nix(f'config.clan.core.networking."{field}"')
|
host_str = machine.select(f'config.clan.core.networking."{field}"')
|
||||||
source = "nix_machine"
|
source = "nix_machine"
|
||||||
|
|
||||||
if not host_str:
|
if not host_str:
|
||||||
|
|||||||
@@ -282,5 +282,5 @@ def test_clan_create_api(
|
|||||||
clan_dir_flake.invalidate_cache()
|
clan_dir_flake.invalidate_cache()
|
||||||
|
|
||||||
with pytest.raises(ClanError) as exc_info:
|
with pytest.raises(ClanError) as exc_info:
|
||||||
machine.build_nix("config.system.build.toplevel")
|
Path(machine.select("config.system.build.toplevel"))
|
||||||
assert "nixos-system-test-clan" in str(exc_info.value)
|
assert "nixos-system-test-clan" in str(exc_info.value)
|
||||||
|
|||||||
@@ -63,8 +63,7 @@ class TestMachine(Machine):
|
|||||||
def flake_dir(self) -> Path:
|
def flake_dir(self) -> Path:
|
||||||
return self.test_dir
|
return self.test_dir
|
||||||
|
|
||||||
@override
|
def select(self, attr: str) -> Any:
|
||||||
def nix(self, attr: str) -> Any:
|
|
||||||
"""
|
"""
|
||||||
Build the machine and return the path to the result
|
Build the machine and return the path to the result
|
||||||
accepts a secret store and a facts store # TODO
|
accepts a secret store and a facts store # TODO
|
||||||
|
|||||||
Reference in New Issue
Block a user