Merge pull request 'Clan_lib: add filtering by tag to list API' (#4197) from cli-fixup into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4197
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import TypedDict
|
||||
|
||||
from clan_lib.api import API
|
||||
from clan_lib.errors import ClanError
|
||||
@@ -10,15 +11,44 @@ from clan_lib.persist.inventory_store import InventoryStore
|
||||
from clan_lib.persist.util import set_value_by_path
|
||||
|
||||
|
||||
class MachineFilter(TypedDict):
|
||||
tags: list[str]
|
||||
|
||||
|
||||
class ListOptions(TypedDict):
|
||||
filter: MachineFilter
|
||||
|
||||
|
||||
@API.register
|
||||
def list_machines(flake: Flake) -> dict[str, InventoryMachine]:
|
||||
def list_machines(
|
||||
flake: Flake, opts: ListOptions | None = None
|
||||
) -> dict[str, InventoryMachine]:
|
||||
"""
|
||||
List machines in the inventory for the UI.
|
||||
List machines of a clan
|
||||
|
||||
Usage Example:
|
||||
|
||||
machines = list_machines(flake, {"filter": {"tags": ["foo" "bar"]}})
|
||||
|
||||
lists only machines that include both "foo" AND "bar"
|
||||
|
||||
"""
|
||||
inventory_store = InventoryStore(flake=flake)
|
||||
inventory = inventory_store.read()
|
||||
|
||||
machines = inventory.get("machines", {})
|
||||
|
||||
if opts and opts.get("filter"):
|
||||
filtered_machines = {}
|
||||
filter_tags = opts.get("filter", {}).get("tags", [])
|
||||
|
||||
for machine_name, machine in machines.items():
|
||||
machine_tags = machine.get("tags", [])
|
||||
if all(ft in machine_tags for ft in filter_tags):
|
||||
filtered_machines[machine_name] = machine
|
||||
|
||||
return filtered_machines
|
||||
|
||||
return machines
|
||||
|
||||
|
||||
|
||||
0
pkgs/clan-cli/clan_lib/machines/actions_test.py
Normal file
0
pkgs/clan-cli/clan_lib/machines/actions_test.py
Normal file
@@ -16,35 +16,39 @@ from clan_lib.nix_models.clan import InventoryMachine
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def convert_inventory_to_machines(
|
||||
flake: Flake, machines: dict[str, InventoryMachine]
|
||||
) -> dict[str, Machine]:
|
||||
return {
|
||||
name: Machine.from_inventory(name, flake, inventory_machine)
|
||||
for name, inventory_machine in machines.items()
|
||||
}
|
||||
|
||||
|
||||
def list_full_machines(flake: Flake) -> dict[str, Machine]:
|
||||
"""
|
||||
Like `list_machines`, but returns a full 'machine' instance for each machine.
|
||||
"""
|
||||
machines = list_machines(flake)
|
||||
|
||||
res: dict[str, Machine] = {}
|
||||
|
||||
for name in machines:
|
||||
machine = Machine(name=name, flake=flake)
|
||||
res[machine.name] = machine
|
||||
|
||||
return res
|
||||
return convert_inventory_to_machines(flake, machines)
|
||||
|
||||
|
||||
def query_machines_by_tags(flake: Flake, tags: list[str]) -> dict[str, Machine]:
|
||||
def query_machines_by_tags(
|
||||
flake: Flake, tags: list[str]
|
||||
) -> dict[str, InventoryMachine]:
|
||||
"""
|
||||
Query machines by their respective tags, if multiple tags are specified
|
||||
then only machines that have those respective tags specified will be listed.
|
||||
It is an intersection of the tags and machines.
|
||||
"""
|
||||
machines = list_full_machines(flake)
|
||||
machines = list_machines(flake)
|
||||
|
||||
filtered_machines = {}
|
||||
for machine in machines.values():
|
||||
inv_machine = get_machine(machine.flake, machine.name)
|
||||
machine_tags = inv_machine.get("tags", [])
|
||||
for machine_name, machine in machines.items():
|
||||
machine_tags = machine.get("tags", [])
|
||||
if all(tag in machine_tags for tag in tags):
|
||||
filtered_machines[machine.name] = machine
|
||||
filtered_machines[machine_name] = machine
|
||||
|
||||
return filtered_machines
|
||||
|
||||
|
||||
@@ -29,6 +29,15 @@ class Machine:
|
||||
name: str
|
||||
flake: Flake
|
||||
|
||||
@classmethod
|
||||
def from_inventory(
|
||||
cls,
|
||||
name: str,
|
||||
flake: Flake,
|
||||
_inventory_machine: InventoryMachine,
|
||||
) -> "Machine":
|
||||
return cls(name=name, flake=flake)
|
||||
|
||||
def get_inv_machine(self) -> "InventoryMachine":
|
||||
return get_machine(self.flake, self.name)
|
||||
|
||||
@@ -166,7 +175,7 @@ class Machine:
|
||||
@dataclass(frozen=True)
|
||||
class RemoteSource:
|
||||
data: Remote
|
||||
source: Literal["inventory", "nix_machine"]
|
||||
source: Literal["inventory", "machine"]
|
||||
|
||||
|
||||
@API.register
|
||||
@@ -179,15 +188,15 @@ def get_host(
|
||||
machine = Machine(name=name, flake=flake)
|
||||
inv_machine = machine.get_inv_machine()
|
||||
|
||||
source: Literal["inventory", "nix_machine"] = "inventory"
|
||||
source: Literal["inventory", "machine"] = "inventory"
|
||||
host_str = inv_machine.get("deploy", {}).get(field)
|
||||
|
||||
if host_str is None:
|
||||
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"
|
||||
machine.warn(
|
||||
f"'{field}' is not set in `inventory.machines.${name}.deploy.targetHost` - falling back to _slower_ nixos option: `clan.core.networking.targetHost`"
|
||||
)
|
||||
host_str = machine.select(f'config.clan.core.networking."{field}"')
|
||||
source = "nix_machine"
|
||||
source = "machine"
|
||||
|
||||
if not host_str:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user