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;` To exclude machines being updated `clan.deployment.requireExplicitUpdate = true;`
can be set in the machine config. 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 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 ( from clan_cli.completions import (
add_dynamic_completer, add_dynamic_completer,
complete_machines, complete_machines,
complete_tags,
) )
from clan_cli.facts.generate import generate_facts from clan_cli.facts.generate import generate_facts
from clan_cli.facts.upload import upload_secrets 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.generate import generate_vars
from clan_cli.vars.upload import upload_secret_vars from clan_cli.vars.upload import upload_secret_vars
@@ -213,10 +214,24 @@ def update_command(args: argparse.Namespace) -> None:
raise ClanError(msg) raise ClanError(msg)
machines: list[Machine] = [] machines: list[Machine] = []
# if no machines are passed, we will update all machines if args.tags:
selected_machines = ( tag_filtered_machines = query_machines_by_tags(args.flake, args.tags)
args.machines if args.machines else list_full_machines(args.flake).keys() 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(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: for machine_name in selected_machines:
machine = Machine( machine = Machine(
@@ -244,7 +259,7 @@ def update_command(args: argparse.Namespace) -> None:
return True return True
machines_to_update = machines 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: if implicit_all:
machines_to_update = list(filter(filter_machine, machines)) 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) 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( parser.add_argument(
"--host-key-check", "--host-key-check",
choices=["strict", "ask", "tofu", "none"], choices=["strict", "ask", "tofu", "none"],

View File

@@ -65,6 +65,31 @@ def test_machine_subcommands(
assert "vm2" in output.out 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 @pytest.mark.with_core
def test_machine_delete( def test_machine_delete(
monkeypatch: pytest.MonkeyPatch, monkeypatch: pytest.MonkeyPatch,