api/machines: populate instance_refs

This commit is contained in:
Johannes Kirschbauer
2025-09-01 16:47:47 +02:00
parent 727d4e70ae
commit c2a3f5e498
2 changed files with 67 additions and 5 deletions

View File

@@ -7,6 +7,7 @@ from clan_lib.errors import ClanError
from clan_lib.flake.flake import Flake
from clan_lib.machines.machines import Machine
from clan_lib.nix_models.clan import (
InventoryInstance,
InventoryMachine,
)
from clan_lib.persist.inventory_store import InventoryStore
@@ -45,7 +46,25 @@ class MachineState(TypedDict):
class MachineResponse:
data: InventoryMachine
# Reference the installed service instances
instance_refs: list[str] = field(default_factory=list)
instance_refs: set[str] = field(default_factory=set)
def machine_instances(
machine_name: str,
instances: dict[str, InventoryInstance],
tag_map: dict[str, set[str]],
) -> set[str]:
res: set[str] = set()
for instance_name, instance in instances.items():
for role in instance.get("roles", {}).values():
if machine_name in role.get("machines", {}):
res.add(instance_name)
for tag in role.get("tags", {}):
if tag in tag_map and machine_name in tag_map[tag]:
res.add(instance_name)
return res
@API.register
@@ -59,14 +78,30 @@ def list_machines(
raw_machines = inventory.get("machines", {})
tag_map: dict[str, set[str]] = {}
for machine_name, machine in raw_machines.items():
for tag in machine.get("tags", []):
if tag not in tag_map:
tag_map[tag] = set()
tag_map[tag].add(machine_name)
instances = inventory.get("instances", {})
res: dict[str, MachineResponse] = {}
for machine_name, machine in raw_machines.items():
m = MachineResponse(
data=InventoryMachine(**machine),
instance_refs=machine_instances(machine_name, instances, tag_map),
)
# Check filters
if opts and opts.filter.tags is not None:
machine_tags = machine.get("tags", [])
if all(ft in machine_tags for ft in opts.filter.tags):
res[machine_name] = MachineResponse(data=InventoryMachine(**machine))
else:
res[machine_name] = MachineResponse(data=InventoryMachine(**machine))
if not all(ft in machine_tags for ft in opts.filter.tags):
continue
res[machine_name] = m
return res

View File

@@ -67,6 +67,33 @@ def test_list_inventory_machines(clan_flake: Callable[..., Flake]) -> None:
assert list(machines.keys()) == ["jon", "sara", "vanessa"]
@pytest.mark.with_core
def test_list_machines_instance_refs(clan_flake: Callable[..., Flake]) -> None:
flake = clan_flake(
{
"inventory": {
"machines": {
"jon": {},
"sara": {},
},
"instances": {
"admin": {
"roles": {"default": {"machines": {"jon": {}}}},
},
"borgbackup": {
"roles": {"default": {"tags": {"all": {}}}},
},
},
},
},
)
machines = list_machines(flake)
assert machines["sara"].instance_refs == set({"borgbackup"})
assert machines["jon"].instance_refs == set({"admin", "borgbackup"})
@pytest.mark.with_core
def test_set_machine_no_op(clan_flake: Callable[..., Flake]) -> None:
flake = clan_flake(