vars: set vars via cli; improve getting vars via cli;
This commit is contained in:
@@ -7,6 +7,7 @@ from .check import register_check_parser
|
||||
from .generate import register_generate_parser
|
||||
from .get import register_get_parser
|
||||
from .list import register_list_parser
|
||||
from .set import register_set_parser
|
||||
from .upload import register_upload_parser
|
||||
|
||||
|
||||
@@ -85,6 +86,25 @@ For more detailed information, visit: {help_hyperlink("secrets", "https://docs.c
|
||||
)
|
||||
register_get_parser(get_parser)
|
||||
|
||||
set_parser = subparser.add_parser(
|
||||
"set",
|
||||
help="set a specific var",
|
||||
epilog=(
|
||||
f"""
|
||||
This subcommand allows setting a specific var for a specific machine.
|
||||
|
||||
Examples:
|
||||
|
||||
$ clan vars set my-server zerotier/vpn-ip
|
||||
Will set the var for the specified machine.
|
||||
|
||||
For more detailed information, visit: {help_hyperlink("secrets", "https://docs.clan.lol/getting-started/secrets")}
|
||||
"""
|
||||
),
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
)
|
||||
register_set_parser(set_parser)
|
||||
|
||||
parser_generate = subparser.add_parser(
|
||||
"generate",
|
||||
help="(re-)generate vars for specific or all machines",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# !/usr/bin/env python3
|
||||
import json
|
||||
import shutil
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
@@ -32,6 +33,9 @@ class Var:
|
||||
except UnicodeDecodeError:
|
||||
return "<binary blob>"
|
||||
|
||||
def set(self, value: bytes) -> None:
|
||||
self.store.set(self.generator, self.name, value, self.shared, self.deployed)
|
||||
|
||||
@property
|
||||
def exists(self) -> bool:
|
||||
return self.store.exists(self.generator, self.name, self.shared)
|
||||
@@ -109,8 +113,7 @@ class StoreBase(ABC):
|
||||
directory = self.directory(generator_name, var_name, shared)
|
||||
# delete directory
|
||||
if directory.exists():
|
||||
for f in directory.glob("*"):
|
||||
f.unlink()
|
||||
shutil.rmtree(directory)
|
||||
# re-create directory
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
new_file = self._set(generator_name, var_name, value, shared, deployed)
|
||||
|
||||
@@ -3,7 +3,6 @@ import importlib
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from getpass import getpass
|
||||
from graphlib import TopologicalSorter
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
@@ -22,6 +21,7 @@ from clan_cli.machines.machines import Machine
|
||||
from clan_cli.nix import nix_shell
|
||||
|
||||
from .check import check_vars
|
||||
from .prompt import prompt
|
||||
from .public_modules import FactStoreBase
|
||||
from .secret_modules import SecretStoreBase
|
||||
|
||||
@@ -133,11 +133,11 @@ def execute_generator(
|
||||
if machine.vars_generators[generator_name]["prompts"]:
|
||||
tmpdir_prompts.mkdir()
|
||||
env["prompts"] = str(tmpdir_prompts)
|
||||
for prompt_name, prompt in machine.vars_generators[generator_name][
|
||||
for prompt_name, prompt_ in machine.vars_generators[generator_name][
|
||||
"prompts"
|
||||
].items():
|
||||
prompt_file = tmpdir_prompts / prompt_name
|
||||
value = prompt_func(prompt["description"], prompt["type"])
|
||||
value = prompt(prompt_["description"], prompt_["type"])
|
||||
prompt_file.write_text(value)
|
||||
|
||||
if sys.platform == "linux":
|
||||
@@ -184,21 +184,6 @@ def execute_generator(
|
||||
return True
|
||||
|
||||
|
||||
def prompt_func(description: str, input_type: str) -> str:
|
||||
if input_type == "line":
|
||||
result = input(f"Enter the value for {description}: ")
|
||||
elif input_type == "multiline":
|
||||
print(f"Enter the value for {description} (Finish with Ctrl-D): ")
|
||||
result = sys.stdin.read()
|
||||
elif input_type == "hidden":
|
||||
result = getpass(f"Enter the value for {description} (hidden): ")
|
||||
else:
|
||||
msg = f"Unknown input type: {input_type} for prompt {description}"
|
||||
raise ClanError(msg)
|
||||
log.info("Input received. Processing...")
|
||||
return result
|
||||
|
||||
|
||||
def _get_subgraph(graph: dict[str, set], vertex: str) -> dict[str, set]:
|
||||
visited = set()
|
||||
queue = [vertex]
|
||||
|
||||
@@ -13,14 +13,15 @@ from .list import all_vars
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_var(machine: Machine, var_id: str) -> Var | None:
|
||||
def get_var(machine: Machine, var_id: str) -> Var:
|
||||
vars_ = all_vars(machine)
|
||||
results = []
|
||||
for var in vars_:
|
||||
if var_id in var.id:
|
||||
results.append(var)
|
||||
if len(results) == 0:
|
||||
return None
|
||||
msg = f"No var found for search string: {var_id}"
|
||||
raise ClanError(msg)
|
||||
if len(results) > 1:
|
||||
error = (
|
||||
f"Found multiple vars for {var_id}:\n - "
|
||||
@@ -29,28 +30,33 @@ def get_var(machine: Machine, var_id: str) -> Var | None:
|
||||
)
|
||||
raise ClanError(error)
|
||||
# we have exactly one result at this point
|
||||
result = results[0]
|
||||
if var_id == result.id:
|
||||
return result
|
||||
msg = f"Did you mean: {result.id}"
|
||||
var = results[0]
|
||||
if var_id == var.id:
|
||||
return var
|
||||
msg = f"Did you mean: {var.id}"
|
||||
raise ClanError(msg)
|
||||
|
||||
|
||||
def get_command(
|
||||
machine: str, var_id: str, flake: FlakeId, quiet: bool, **kwargs: dict
|
||||
) -> None:
|
||||
def get_command(machine: str, var_id: str, flake: FlakeId) -> None:
|
||||
_machine = Machine(name=machine, flake=flake)
|
||||
var = get_var(_machine, var_id)
|
||||
if var is None:
|
||||
msg = f"No var found for search string: {var_id}"
|
||||
raise ClanError(msg)
|
||||
if not var.exists:
|
||||
msg = f"Var {var.id} has not been generated yet"
|
||||
raise ClanError(msg)
|
||||
if quiet:
|
||||
if sys.stdout.isatty():
|
||||
sys.stdout.buffer.write(var.value)
|
||||
else:
|
||||
print(f"{var.id}: {var.printable_value}")
|
||||
print(var.printable_value)
|
||||
|
||||
|
||||
def _get_command(
|
||||
args: argparse.Namespace,
|
||||
) -> None:
|
||||
get_command(
|
||||
machine=args.machine,
|
||||
var_id=args.var_id,
|
||||
flake=args.flake,
|
||||
)
|
||||
|
||||
|
||||
def register_get_parser(parser: argparse.ArgumentParser) -> None:
|
||||
@@ -65,10 +71,4 @@ def register_get_parser(parser: argparse.ArgumentParser) -> None:
|
||||
help="The var id to get the value for. Example: ssh-keys/pubkey",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--quiet",
|
||||
"-q",
|
||||
help="Only print the value of the var",
|
||||
action="store_true",
|
||||
)
|
||||
parser.set_defaults(func=lambda args: get_command(**vars(args)))
|
||||
parser.set_defaults(func=_get_command)
|
||||
|
||||
22
pkgs/clan-cli/clan_cli/vars/prompt.py
Normal file
22
pkgs/clan-cli/clan_cli/vars/prompt.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import logging
|
||||
import sys
|
||||
from getpass import getpass
|
||||
|
||||
from clan_cli.errors import ClanError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def prompt(description: str, input_type: str) -> str:
|
||||
if input_type == "line":
|
||||
result = input(f"Enter the value for {description}: ")
|
||||
elif input_type == "multiline":
|
||||
print(f"Enter the value for {description} (Finish with Ctrl-D): ")
|
||||
result = sys.stdin.read()
|
||||
elif input_type == "hidden":
|
||||
result = getpass(f"Enter the value for {description} (hidden): ")
|
||||
else:
|
||||
msg = f"Unknown input type: {input_type} for prompt {description}"
|
||||
raise ClanError(msg)
|
||||
log.info("Input received. Processing...")
|
||||
return result
|
||||
41
pkgs/clan-cli/clan_cli/vars/set.py
Normal file
41
pkgs/clan-cli/clan_cli/vars/set.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from clan_cli.clan_uri import FlakeId
|
||||
from clan_cli.completions import add_dynamic_completer, complete_machines
|
||||
from clan_cli.machines.machines import Machine
|
||||
from clan_cli.vars.get import get_var
|
||||
|
||||
from .prompt import prompt
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_command(machine: str, var_id: str, flake: FlakeId) -> None:
|
||||
_machine = Machine(name=machine, flake=flake)
|
||||
var = get_var(_machine, var_id)
|
||||
if sys.stdin.isatty():
|
||||
new_value = prompt(var.id, "hidden").encode("utf-8")
|
||||
else:
|
||||
new_value = sys.stdin.buffer.read()
|
||||
var.set(new_value)
|
||||
|
||||
|
||||
def _get_command(args: argparse.Namespace) -> None:
|
||||
get_command(args.machine, args.var_id, args.flake)
|
||||
|
||||
|
||||
def register_set_parser(parser: argparse.ArgumentParser) -> None:
|
||||
machines_arg = parser.add_argument(
|
||||
"machine",
|
||||
help="The machine to set a var for",
|
||||
)
|
||||
add_dynamic_completer(machines_arg, complete_machines)
|
||||
|
||||
parser.add_argument(
|
||||
"var_id",
|
||||
help="The var id for which to set the value. Example: ssh-keys/pubkey",
|
||||
)
|
||||
|
||||
parser.set_defaults(func=_get_command)
|
||||
Reference in New Issue
Block a user