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:
@@ -10,6 +10,7 @@ from tempfile import TemporaryDirectory
|
||||
from clan_lib.cmd import RunOpts, run
|
||||
from clan_lib.errors import ClanError
|
||||
from clan_lib.git import commit_files
|
||||
from clan_lib.machines.list import list_full_machines
|
||||
from clan_lib.machines.machines import Machine
|
||||
from clan_lib.nix import nix_shell
|
||||
|
||||
@@ -18,7 +19,6 @@ from clan_cli.completions import (
|
||||
complete_machines,
|
||||
complete_services_for_machine,
|
||||
)
|
||||
from clan_cli.machines.list import list_full_machines
|
||||
|
||||
from .check import check_secrets
|
||||
from .public_modules import FactStoreBase
|
||||
|
||||
@@ -2,7 +2,7 @@ import argparse
|
||||
import logging
|
||||
|
||||
from clan_lib.flake import Flake
|
||||
from clan_lib.machines.list import list_full_machines, query_machines_by_tags
|
||||
from clan_lib.machines.actions import list_machines
|
||||
|
||||
from clan_cli.completions import add_dynamic_completer, complete_tags
|
||||
|
||||
@@ -12,11 +12,7 @@ log = logging.getLogger(__name__)
|
||||
def list_command(args: argparse.Namespace) -> None:
|
||||
flake: Flake = args.flake
|
||||
|
||||
if args.tags:
|
||||
for name in query_machines_by_tags(flake, args.tags):
|
||||
print(name)
|
||||
else:
|
||||
for name in list_full_machines(flake):
|
||||
for name in list_machines(flake, opts={"filter": {"tags": args.tags}}):
|
||||
print(name)
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import sys
|
||||
|
||||
from clan_lib.async_run import AsyncContext, AsyncOpts, AsyncRuntime
|
||||
from clan_lib.errors import ClanError
|
||||
from clan_lib.machines.list import list_full_machines, query_machines_by_tags
|
||||
from clan_lib.machines.machines import Machine
|
||||
from clan_lib.machines.suggestions import validate_machine_names
|
||||
from clan_lib.machines.update import deploy_machine
|
||||
@@ -15,7 +16,6 @@ from clan_cli.completions import (
|
||||
complete_machines,
|
||||
complete_tags,
|
||||
)
|
||||
from clan_cli.machines.list import list_full_machines, query_machines_by_tags
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ from clan_cli.completions import (
|
||||
complete_machines,
|
||||
complete_services_for_machine,
|
||||
)
|
||||
from clan_cli.machines.list import list_full_machines
|
||||
from clan_cli.vars._types import StoreBase
|
||||
from clan_cli.vars.migration import check_can_migrate, migrate_files
|
||||
from clan_lib.api import API
|
||||
@@ -22,6 +21,7 @@ from clan_lib.cmd import RunOpts, run
|
||||
from clan_lib.errors import ClanError
|
||||
from clan_lib.flake import Flake
|
||||
from clan_lib.git import commit_files
|
||||
from clan_lib.machines.list import list_full_machines
|
||||
from clan_lib.nix import nix_config, nix_shell, nix_test_store
|
||||
|
||||
from .check import check_vars
|
||||
|
||||
@@ -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