api/tags: add from all possible sources

This commit is contained in:
Johannes Kirschbauer
2025-08-11 23:23:01 +02:00
committed by Brian McGee
parent 2608bee30a
commit cbd3b08296
6 changed files with 114 additions and 113 deletions

View File

@@ -1,12 +0,0 @@
"""
DEPRECATED:
Don't use this module anymore
Instead use:
'clan_lib.persist.inventoryStore'
Which is an abstraction over the inventory
Interacting with 'clan_lib.inventory' is NOT recommended and will be removed
"""

View File

@@ -1,29 +0,0 @@
from typing import TypedDict
from clan_lib.api import API
from clan_lib.flake import Flake
from clan_lib.persist.inventory_store import InventoryStore
readonly_tags = {"all", "nixos", "darwin"}
class MachineTag(TypedDict):
name: str
readonly: bool
@API.register
def list_inventory_tags(flake: Flake) -> list[MachineTag]:
inventory_store = InventoryStore(flake=flake)
inventory = inventory_store.read()
machines = inventory.get("machines", {})
tags: dict[str, MachineTag] = {}
for _, machine in machines.items():
machine_tags = machine.get("tags", [])
for tag in machine_tags:
tags[tag] = MachineTag(name=tag, readonly=tag in readonly_tags)
return sorted(tags.values(), key=lambda x: x["name"])

View File

@@ -1,72 +0,0 @@
from collections.abc import Callable
import pytest
from clan_lib.flake import Flake
from .actions import MachineTag, list_inventory_tags
@pytest.mark.with_core
def test_list_inventory_tags(clan_flake: Callable[..., Flake]) -> None:
flake = clan_flake(
{
"inventory": {
"machines": {
"jon": {
# machineClass defaults to nixos
"tags": ["foo", "bar"],
},
"sara": {
"machineClass": "darwin",
"tags": ["foo", "baz", "fizz"],
},
"bob": {
"machineClass": "nixos",
"tags": ["foo", "bar"],
},
},
}
},
)
tags = list_inventory_tags(flake)
assert tags == [
MachineTag(name="all", readonly=True),
MachineTag(name="bar", readonly=False),
MachineTag(name="baz", readonly=False),
MachineTag(name="darwin", readonly=True),
MachineTag(name="fizz", readonly=False),
MachineTag(name="foo", readonly=False),
MachineTag(name="nixos", readonly=True),
]
@pytest.mark.with_core
def test_list_inventory_tags_defaults(clan_flake: Callable[..., Flake]) -> None:
flake = clan_flake(
{
"inventory": {
"machines": {
"jon": {
# machineClass defaults to nixos
},
"sara": {
"machineClass": "darwin",
},
"bob": {
"machineClass": "nixos",
},
},
}
},
)
tags = list_inventory_tags(flake)
assert tags == [
MachineTag(name="all", readonly=True),
MachineTag(name="darwin", readonly=True),
MachineTag(name="nixos", readonly=True),
]

View File

@@ -12,6 +12,7 @@ from clan_lib.nix_models.clan import (
InventoryMachinesType,
InventoryMetaType,
InventoryServicesType,
InventoryTagsType,
)
from .util import (
@@ -106,6 +107,7 @@ class InventorySnapshot(TypedDict):
instances: NotRequired[InventoryInstancesType]
meta: NotRequired[InventoryMetaType]
services: NotRequired[InventoryServicesType]
tags: NotRequired[InventoryTagsType]
class InventoryStore:

View File

@@ -0,0 +1,34 @@
from typing import Any
from clan_lib.api import API
from clan_lib.flake import Flake
from clan_lib.persist.inventory_store import InventoryStore
@API.register
def list_tags(flake: Flake) -> set[str]:
inventory_store = InventoryStore(flake=flake)
inventory = inventory_store.read()
machines = inventory.get("machines", {})
tags: set[str] = set()
for machine in machines.values():
machine_tags = machine.get("tags", [])
for tag in machine_tags:
tags.add(tag)
instances = inventory.get("instances", {})
for instance in instances.values():
roles: dict[str, Any] = instance.get("roles", {})
for role in roles.values():
role_tags = role.get("tags", {})
for tag in role_tags:
tags.add(tag)
global_tags = inventory.get("tags", {})
for tag in global_tags:
tags.add(tag)
return tags

View File

@@ -0,0 +1,78 @@
from collections.abc import Callable
import pytest
from clan_lib.flake import Flake
from clan_lib.persist.inventory_store import InventoryStore
from clan_lib.persist.util import get_value_by_path, set_value_by_path
from clan_lib.tags.list import list_tags
@pytest.mark.with_core
def test_list_inventory_tags(clan_flake: Callable[..., Flake]) -> None:
flake = clan_flake(
{
"inventory": {
"machines": {
"jon": {
"tags": ["foo", "bar"],
},
"sara": {
"tags": ["foo", "baz", "fizz"],
},
"bob": {
"tags": ["foo", "bar"],
},
},
"instances": {
"instance1": {
"roles": {
"role1": {"tags": {"predefined": {}, "maybe": {}}},
"role2": {"tags": {"predefined2": {}, "maybe2": {}}},
}
},
},
}
},
raw=r"""
{
inventory.tags = {
"global" = [ "future_machine" ];
};
}
""",
)
inventory_store = InventoryStore(flake=flake)
inventory = inventory_store.read()
curr_tags = get_value_by_path(inventory, "machines.jon.tags", [])
new_tags = ["managed1", "managed2"]
set_value_by_path(inventory, "machines.jon.tags", [*curr_tags, *new_tags])
inventory_store.write(inventory, message="Test add tags via API")
# Check that the tags were updated
persisted = inventory_store._get_persisted() # noqa: SLF001
assert get_value_by_path(persisted, "machines.jon.tags", []) == new_tags
tags = list_tags(flake)
assert tags == set(
{
# Predefined tags
"all",
"global",
"darwin",
"nixos",
# Tags defined in nix
"bar",
"baz",
"fizz",
"foo",
"predefined",
"predefined2",
"maybe",
"maybe2",
# Tags managed by the UI
"managed1",
"managed2",
}
)