From 1e3e7b6d37623271830566f00b2a3662946fa38d Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Wed, 11 Jun 2025 15:57:46 +0200 Subject: [PATCH] Chore(machine/templates): simplify template args for machines command --- pkgs/clan-cli/clan_cli/machines/create.py | 150 +++++--------------- pkgs/clan-cli/clan_cli/machines/morph.py | 8 +- pkgs/clan-cli/clan_lib/templates/handler.py | 13 +- 3 files changed, 51 insertions(+), 120 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/machines/create.py b/pkgs/clan-cli/clan_cli/machines/create.py index 3ee7a5233..b2f523034 100644 --- a/pkgs/clan-cli/clan_cli/machines/create.py +++ b/pkgs/clan-cli/clan_cli/machines/create.py @@ -2,7 +2,6 @@ import argparse import logging import re from dataclasses import dataclass -from pathlib import Path from clan_lib.api import API from clan_lib.dirs import get_clan_flake_toplevel_or_env @@ -13,15 +12,9 @@ from clan_lib.nix_models.clan import InventoryMachine from clan_lib.nix_models.clan import InventoryMachineDeploy as MachineDeploy from clan_lib.persist.inventory_store import InventoryStore from clan_lib.persist.util import set_value_by_path -from clan_lib.templates import ( - InputPrio, - TemplateName, - get_template, -) -from clan_lib.templates.filesystem import copy_from_nixstore +from clan_lib.templates.handler import machine_template from clan_cli.completions import add_dynamic_completer, complete_tags -from clan_cli.machines.list import list_full_machines log = logging.getLogger(__name__) @@ -30,9 +23,8 @@ log = logging.getLogger(__name__) class CreateOptions: clan_dir: Flake machine: InventoryMachine + template: str = "new-machine" target_host: str | None = None - input_prio: InputPrio | None = None - template_name: str | None = None @API.register @@ -54,42 +46,12 @@ def create_machine( description = "Import machine only works on local clans" raise ClanError(msg, description=description) - if not opts.template_name: - opts.template_name = "new-machine" - clan_dir = opts.clan_dir.path - # TODO(@Qubasa): make this a proper template handler - # i.e. with_template (use context manager) - # And move the checks and template handling into the template handler - template = get_template( - TemplateName(opts.template_name), - "machine", - input_prio=opts.input_prio, - clan_dir=opts.clan_dir, - ) - log.info(f"Found template '{template.name}' in '{template.input_variant}'") - machine_name = opts.machine.get("name") - if opts.template_name in list_full_machines( - Flake(str(clan_dir)) - ) and not opts.machine.get("name"): - msg = f"{opts.template_name} is already defined in {clan_dir}" - raise ClanError(msg) - - machine_name = machine_name if machine_name else opts.template_name - src = Path(template.src["path"]) - - if not src.exists(): - msg = f"Template {template} does not exist" - raise ClanError(msg) - if not src.is_dir(): - msg = f"Template {template} is not a directory" - raise ClanError(msg) - - dst = clan_dir / "machines" - dst.mkdir(exist_ok=True) - dst /= machine_name + if not machine_name: + msg = "Machine name is required" + raise ClanError(msg, location="Create Machine") # TODO: Move this into nix code hostname_regex = r"^(?!-)[A-Za-z0-9-]{1,63}(? None: @@ -159,25 +109,15 @@ def create_command(args: argparse.Namespace) -> None: ) raise ClanError(msg, description=description) - if len(args.input) == 0: - args.input = ["clan", "clan-core"] - - if args.no_self: - input_prio = InputPrio.try_inputs(tuple(args.input)) - else: - input_prio = InputPrio.try_self_then_inputs(tuple(args.input)) - machine = InventoryMachine( name=args.machine_name, tags=args.tags, deploy=MachineDeploy(targetHost=args.target_host), ) opts = CreateOptions( - input_prio=input_prio, clan_dir=clan_dir, machine=machine, - template_name=args.template_name, - target_host=args.target_host, + template=args.template, ) create_machine(opts) @@ -196,29 +136,15 @@ def register_create_parser(parser: argparse.ArgumentParser) -> None: help="Tags to associate with the machine. Can be used to assign multiple machines to services.", ) add_dynamic_completer(tag_parser, complete_tags) - parser.add_argument( - "--template-name", - type=str, - help="The name of the template machine to import", - ) parser.add_argument( "--target-host", type=str, help="Address of the machine to install and update, in the format of user@host:1234", ) parser.add_argument( - "--input", + "-t", + "--template", type=str, - help="""Flake input name to use as template source - can be specified multiple times, inputs are tried in order of definition - Example: --input clan --input clan-core - """, - action="append", - default=[], - ) - parser.add_argument( - "--no-self", - help="Do not look into own flake for templates", - action="store_true", - default=False, + help="Reference to the template to use for the machine. In the format '#template_name'", + default="new-machine", ) diff --git a/pkgs/clan-cli/clan_cli/machines/morph.py b/pkgs/clan-cli/clan_cli/machines/morph.py index 6dbf7f669..2357b57d2 100644 --- a/pkgs/clan-cli/clan_cli/machines/morph.py +++ b/pkgs/clan-cli/clan_cli/machines/morph.py @@ -41,7 +41,7 @@ def random_hostname() -> str: def morph_machine( - flake: Flake, template_name: str, ask_confirmation: bool, name: str | None = None + flake: Flake, template: str, ask_confirmation: bool, name: str | None = None ) -> None: cmd = nix_command( [ @@ -70,7 +70,7 @@ def morph_machine( name = random_hostname() create_opts = CreateOptions( - template_name=template_name, + template=template, machine=InventoryMachine(name=name), clan_dir=Flake(str(flakedir)), ) @@ -149,7 +149,7 @@ def morph_command(args: argparse.Namespace) -> None: morph_machine( flake=Flake(str(args.flake)), - template_name=args.template_name, + template=args.template, ask_confirmation=args.confirm_firing, name=args.name, ) @@ -159,7 +159,7 @@ def register_morph_parser(parser: argparse.ArgumentParser) -> None: parser.set_defaults(func=morph_command) parser.add_argument( - "template_name", + "template", default="new-machine", type=str, help="The name of the template to use", diff --git a/pkgs/clan-cli/clan_lib/templates/handler.py b/pkgs/clan-cli/clan_lib/templates/handler.py index cecc887c3..c8189e2ef 100644 --- a/pkgs/clan-cli/clan_lib/templates/handler.py +++ b/pkgs/clan-cli/clan_lib/templates/handler.py @@ -15,7 +15,7 @@ log = logging.getLogger(__name__) @contextmanager -def with_machine_template( +def machine_template( flake: Flake, template_ident: str, dst_machine_name: str ) -> Iterator[Path]: """ @@ -28,7 +28,7 @@ def with_machine_template( Example usage: - >>> with with_machine_template( + >>> with machine_template( ... Flake("/home/johannes/git/clan-core"), ".#new-machine", "my-machine" ... ) as machine_path: ... # Use `machine_path` here if you want to access the created machine directory @@ -46,7 +46,10 @@ def with_machine_template( ) # Get the clan template from the specifier - [flake_ref, template_selector] = transform_url("machine", template_ident, local_path=flake) + [flake_ref, template_selector] = transform_url( + "machine", template_ident, local_path=flake + ) + template_flake = Flake(flake_ref) template = template_flake.select(template_selector) @@ -82,12 +85,14 @@ def with_machine_template( dst_machine_dir = specific_machine_dir(tmp_machine) + dst_machine_dir.mkdir(exist_ok=True, parents=True) + copy_from_nixstore(src_path, dst_machine_dir) try: yield dst_machine_dir except Exception as e: - log.error(f"An error occurred inside the 'with_machine_template' context: {e}") + log.error(f"An error occurred inside the 'machine_template' context: {e}") # Ensure that the directory is removed to avoid half-created machines # Everything in the with block is considered part of the context