pkgs/clan: Add --tags support to clan machines update

This commit is contained in:
a-kenji
2025-06-18 13:41:04 +02:00
parent 30ffef515e
commit fc0e8fa1bd
3 changed files with 64 additions and 6 deletions

View File

@@ -37,6 +37,16 @@ Examples:
To exclude machines being updated `clan.deployment.requireExplicitUpdate = true;`
can be set in the machine config.
$ clan machines update --tags [TAGS..]
Will update all machines that have the specified tags associated through the inventory.
If multiple tags are specified machines are matched against both tags.
$ clan machines update --tags vm
Will update all machines that are associated with the "vm" tag through the inventory.
$ clan machines update machine1 machine2 --tags production
Will update only machine1 and machine2 if they both have the "production" tag.
For more detailed information, visit: https://docs.clan.lol/guides/getting-started/deploy
"""
),

View File

@@ -19,10 +19,11 @@ from clan_lib.ssh.remote import HostKeyCheck, Remote
from clan_cli.completions import (
add_dynamic_completer,
complete_machines,
complete_tags,
)
from clan_cli.facts.generate import generate_facts
from clan_cli.facts.upload import upload_secrets
from clan_cli.machines.list import list_full_machines
from clan_cli.machines.list import list_full_machines, query_machines_by_tags
from clan_cli.vars.generate import generate_vars
from clan_cli.vars.upload import upload_secret_vars
@@ -213,11 +214,25 @@ def update_command(args: argparse.Namespace) -> None:
raise ClanError(msg)
machines: list[Machine] = []
# if no machines are passed, we will update all machines
if args.tags:
tag_filtered_machines = query_machines_by_tags(args.flake, args.tags)
if args.machines:
selected_machines = [
name for name in args.machines if name in tag_filtered_machines
]
else:
selected_machines = list(tag_filtered_machines.keys())
else:
selected_machines = (
args.machines if args.machines else list_full_machines(args.flake).keys()
args.machines
if args.machines
else list(list_full_machines(args.flake).keys())
)
if args.tags and not selected_machines:
msg = f"No machines found with tags: {', '.join(args.tags)}"
raise ClanError(msg)
for machine_name in selected_machines:
machine = Machine(
name=machine_name,
@@ -244,7 +259,7 @@ def update_command(args: argparse.Namespace) -> None:
return True
machines_to_update = machines
implicit_all: bool = len(args.machines) == 0
implicit_all: bool = len(args.machines) == 0 and not args.tags
if implicit_all:
machines_to_update = list(filter(filter_machine, machines))
@@ -314,6 +329,14 @@ def register_update_parser(parser: argparse.ArgumentParser) -> None:
)
add_dynamic_completer(machines_parser, complete_machines)
tag_parser = parser.add_argument(
"--tags",
nargs="+",
default=[],
help="Tags that machines should be queried for. Multiple tags will intersect.",
)
add_dynamic_completer(tag_parser, complete_tags)
parser.add_argument(
"--host-key-check",
choices=["strict", "ask", "tofu", "none"],

View File

@@ -65,6 +65,31 @@ def test_machine_subcommands(
assert "vm2" in output.out
@pytest.mark.impure
def test_machines_update_with_tags(
test_flake_with_core: fixtures_flakes.FlakeForTest,
capture_output: CaptureOutput,
) -> None:
import argparse
from clan_cli.machines.update import register_update_parser
parser = argparse.ArgumentParser()
register_update_parser(parser)
args = parser.parse_args(["--tags", "vm", "production"])
assert hasattr(args, "tags")
assert args.tags == ["vm", "production"]
args = parser.parse_args(["machine1", "machine2"])
assert hasattr(args, "tags")
assert args.tags == []
args = parser.parse_args(["machine1", "--tags", "vm"])
assert args.machines == ["machine1"]
assert args.tags == ["vm"]
@pytest.mark.with_core
def test_machine_delete(
monkeypatch: pytest.MonkeyPatch,