diff --git a/pkgs/clan-cli/clan_lib/inventory/__init__.py b/pkgs/clan-cli/clan_lib/inventory/__init__.py deleted file mode 100644 index e2fd4eba4..000000000 --- a/pkgs/clan-cli/clan_lib/inventory/__init__.py +++ /dev/null @@ -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 -""" diff --git a/pkgs/clan-cli/clan_lib/inventory/actions.py b/pkgs/clan-cli/clan_lib/inventory/actions.py deleted file mode 100644 index 7977521e3..000000000 --- a/pkgs/clan-cli/clan_lib/inventory/actions.py +++ /dev/null @@ -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"]) diff --git a/pkgs/clan-cli/clan_lib/inventory/actions_test.py b/pkgs/clan-cli/clan_lib/inventory/actions_test.py deleted file mode 100644 index f55d43896..000000000 --- a/pkgs/clan-cli/clan_lib/inventory/actions_test.py +++ /dev/null @@ -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), - ] diff --git a/pkgs/clan-cli/clan_lib/persist/inventory_store.py b/pkgs/clan-cli/clan_lib/persist/inventory_store.py index 07dc85cb2..c85ed0c83 100644 --- a/pkgs/clan-cli/clan_lib/persist/inventory_store.py +++ b/pkgs/clan-cli/clan_lib/persist/inventory_store.py @@ -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: diff --git a/pkgs/clan-cli/clan_lib/tags/list.py b/pkgs/clan-cli/clan_lib/tags/list.py new file mode 100644 index 000000000..9fcb16cbb --- /dev/null +++ b/pkgs/clan-cli/clan_lib/tags/list.py @@ -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 diff --git a/pkgs/clan-cli/clan_lib/tags/list_test.py b/pkgs/clan-cli/clan_lib/tags/list_test.py new file mode 100644 index 000000000..41cfdda3e --- /dev/null +++ b/pkgs/clan-cli/clan_lib/tags/list_test.py @@ -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", + } + )