All checks were successful
buildbot/nix-build .#checks.aarch64-darwin.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-iso-installer Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-flash-installer Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.x86_64-linux.check-for-breakpoints Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-age Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-webview-ui Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-bash Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-ts-api Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-cli-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-e2fsprogs Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-fakeroot Build done.
buildbot/nix-build .#checks.x86_64-linux.package-default Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-git Build done.
buildbot/nix-build .#checks.x86_64-linux.package-module-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-nix Build done.
buildbot/nix-build .#checks.x86_64-linux.renderClanOptions Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-openssh Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-test-backup Build done.
buildbot/nix-build .#checks.x86_64-linux."clan-dep-python3.11-qemu" Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-rsync Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-sops Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-sshpass Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-test-backup Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-test-backup Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-cli Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-flash-installer Build done.
buildbot/nix-build .#checks.x86_64-linux."clan-dep-python3.11-mypy" Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-vm-manager-no-breakpoints Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-clan-cli Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-default Build done.
buildbot/nix-build .#checks.x86_64-linux.borgbackup Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-vm-manager-pytest Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-archlinux Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-rpm Build done.
buildbot/nix-build .#checks.x86_64-linux.package-impure-checks Build done.
buildbot/nix-build .#checks.x86_64-linux.package-deploy-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-tor Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-zbar Build done.
buildbot/nix-build .#checks.x86_64-linux.deltachat Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-jsonschema-example-valid Build done.
buildbot/nix-build .#checks.x86_64-linux.container Build done.
buildbot/nix-build .#checks.x86_64-linux.matrix-synapse Build done.
buildbot/nix-build .#checks.x86_64-linux.package-editor Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-apk Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-deb Build done.
buildbot/nix-build .#checks.x86_64-linux.treefmt Build done.
buildbot/nix-build .#checks.x86_64-linux.module-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.package-merge-after-ci Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-jsonschema-nix-unit-tests Build done.
buildbot/nix-build .#checks.x86_64-linux.package-moonlight-sunshine-accept Build done.
buildbot/nix-build .#checks.x86_64-linux.package-tea-create-pr Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-pytest-with-core Build done.
buildbot/nix-build .#checks.x86_64-linux.secrets Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.zt-tcp-relay Build done.
buildbot/nix-build .#checks.x86_64-linux.package-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.syncthing Build done.
buildbot/nix-build .#checks.x86_64-linux.wayland-proxy-virtwl Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-clan-vm-manager Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-vm-manager Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-pytest-without-core Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zerotier-members Build done.
buildbot/nix-build .#checks.x86_64-linux.package-pending-reviews Build done.
buildbot/nix-build .#checks.x86_64-linux.package-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zt-tcp-relay Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zerotierone Build done.
buildbot/nix-build .#checks.x86_64-linux.package-function-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-flash-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.package-webview-ui Build done.
buildbot/nix-build .#checks.x86_64-linux.package-module-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.test-backups Build done.
checks / checks-impure (pull_request) Successful in 2m41s
buildbot/nix-build .#checks.x86_64-linux.flash Build done.
buildbot/nix-build .#checks.x86_64-linux.test-installation Build done.
buildbot/nix-eval Build done.
Add dynamic completion scaffolding to the clan `cli`. Also add a dynamic completion mechanism for machines for commands that have machines as their sole argument. More intricate dynamic completions will be implemented in follow up PR's.
344 lines
9.8 KiB
Python
344 lines
9.8 KiB
Python
import argparse
|
|
import logging
|
|
import sys
|
|
from collections.abc import Sequence
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
from typing import Any
|
|
|
|
from . import (
|
|
backups,
|
|
config,
|
|
facts,
|
|
flakes,
|
|
flash,
|
|
flatpak,
|
|
history,
|
|
machines,
|
|
secrets,
|
|
vms,
|
|
)
|
|
from .custom_logger import setup_logging
|
|
from .dirs import get_clan_flake_toplevel_or_env
|
|
from .errors import ClanCmdError, ClanError
|
|
from .profiler import profile
|
|
from .ssh import cli as ssh_cli
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
argcomplete: ModuleType | None = None
|
|
try:
|
|
import argcomplete # type: ignore[no-redef]
|
|
except ImportError:
|
|
pass
|
|
|
|
|
|
class AppendOptionAction(argparse.Action):
|
|
def __init__(self, option_strings: str, dest: str, **kwargs: Any) -> None:
|
|
super().__init__(option_strings, dest, **kwargs)
|
|
|
|
def __call__(
|
|
self,
|
|
parser: argparse.ArgumentParser,
|
|
namespace: argparse.Namespace,
|
|
values: str | Sequence[str] | None,
|
|
option_string: str | None = None,
|
|
) -> None:
|
|
lst = getattr(namespace, self.dest)
|
|
lst.append("--option")
|
|
assert isinstance(values, list), "values must be a list"
|
|
lst.append(values[0])
|
|
lst.append(values[1])
|
|
|
|
|
|
def flake_path(arg: str) -> str | Path:
|
|
flake_dir = Path(arg).resolve()
|
|
if flake_dir.exists() and flake_dir.is_dir():
|
|
return flake_dir
|
|
return arg
|
|
|
|
|
|
def add_common_flags(parser: argparse.ArgumentParser) -> None:
|
|
parser.add_argument(
|
|
"--debug",
|
|
help="Enable debug logging",
|
|
action="store_true",
|
|
default=False,
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--option",
|
|
help="Nix option to set",
|
|
nargs=2,
|
|
metavar=("name", "value"),
|
|
action=AppendOptionAction,
|
|
default=[],
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--flake",
|
|
help="path to the flake where the clan resides in, can be a remote flake or local, can be set through the [CLAN_DIR] environment variable",
|
|
default=get_clan_flake_toplevel_or_env(),
|
|
metavar="PATH",
|
|
type=flake_path,
|
|
)
|
|
|
|
|
|
def register_common_flags(parser: argparse.ArgumentParser) -> None:
|
|
has_subparsers = False
|
|
for action in parser._actions:
|
|
if isinstance(action, argparse._SubParsersAction):
|
|
for choice, child_parser in action.choices.items():
|
|
has_subparsers = True
|
|
register_common_flags(child_parser)
|
|
if not has_subparsers:
|
|
add_common_flags(parser)
|
|
|
|
|
|
def create_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
parser = argparse.ArgumentParser(
|
|
prog=prog,
|
|
description="The clan cli tool.",
|
|
epilog=(
|
|
"""
|
|
Online reference for the clan cli tool: https://docs.clan.lol/reference/cli/
|
|
For more detailed information, visit: https://docs.clan.lol
|
|
"""
|
|
),
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
)
|
|
subparsers = parser.add_subparsers()
|
|
|
|
parser_backups = subparsers.add_parser(
|
|
"backups",
|
|
help="manage backups of clan machines",
|
|
description="manage backups of clan machines",
|
|
epilog=(
|
|
"""
|
|
This subcommand provides an interface to backups that clan machines expose.
|
|
|
|
Examples:
|
|
|
|
$ clan backups list [MACHINE]
|
|
List backups for the machine [MACHINE]
|
|
|
|
$ clan backups create [MACHINE]
|
|
Create a backup for the machine [MACHINE].
|
|
|
|
$ clan backups restore [MACHINE] [PROVIDER] [NAME]
|
|
The backup to restore for the machine [MACHINE] with the configured [PROVIDER]
|
|
with the name [NAME].
|
|
|
|
For more detailed information, visit: https://docs.clan.lol/getting-started/backups/
|
|
"""
|
|
),
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
)
|
|
backups.register_parser(parser_backups)
|
|
|
|
parser_flake = subparsers.add_parser(
|
|
"flakes",
|
|
help="create a clan flake inside the current directory",
|
|
description="create a clan flake inside the current directory",
|
|
epilog=(
|
|
"""
|
|
Examples:
|
|
$ clan flakes create [DIR]
|
|
Will create a new clan flake in the specified directory and create it if it
|
|
doesn't exist yet. The flake will be created from a default template.
|
|
|
|
For more detailed information, visit: https://docs.clan.lol/getting-started
|
|
"""
|
|
),
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
)
|
|
|
|
flakes.register_parser(parser_flake)
|
|
|
|
parser_config = subparsers.add_parser(
|
|
"config",
|
|
help="read a nixos configuration option",
|
|
description="read a nixos configuration option",
|
|
epilog=(
|
|
"""
|
|
"""
|
|
),
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
)
|
|
config.register_parser(parser_config)
|
|
|
|
parser_ssh = subparsers.add_parser(
|
|
"ssh",
|
|
help="ssh to a remote machine",
|
|
description="ssh to a remote machine",
|
|
epilog=(
|
|
"""
|
|
This subcommand allows seamless ssh access to the nixos-image builders.
|
|
|
|
Examples:
|
|
|
|
$ clan ssh [ssh_args ...] --json [JSON]
|
|
Will ssh in to the machine based on the deployment information contained in
|
|
the json string. [JSON] can either be a json formatted string itself, or point
|
|
towards a file containing the deployment information
|
|
|
|
For more detailed information, visit: https://docs.clan.lol/getting-started/deploy
|
|
"""
|
|
),
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
)
|
|
ssh_cli.register_parser(parser_ssh)
|
|
|
|
parser_secrets = subparsers.add_parser(
|
|
"secrets",
|
|
help="manage secrets",
|
|
description="manage secrets",
|
|
epilog=(
|
|
"""
|
|
This subcommand provides an interface to secret facts.
|
|
|
|
Examples:
|
|
|
|
$ clan secrets list [regex]
|
|
Will list secrets for all managed machines.
|
|
It accepts an optional regex, allowing easy filtering of returned secrets.
|
|
|
|
$ clan secrets get [SECRET]
|
|
Will display the content of the specified secret.
|
|
|
|
For more detailed information, visit: https://docs.clan.lol/getting-started/secrets/
|
|
"""
|
|
),
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
)
|
|
secrets.register_parser(parser_secrets)
|
|
|
|
parser_facts = subparsers.add_parser(
|
|
"facts",
|
|
help="manage facts",
|
|
description="manage facts",
|
|
epilog=(
|
|
"""
|
|
|
|
This subcommand provides an interface to facts of clan machines.
|
|
Facts are artifacts that a service can generate.
|
|
There are public and secret facts.
|
|
Public facts can be referenced by other machines directly.
|
|
Public facts can include: ip addresses, public keys.
|
|
Secret facts can include: passwords, private keys.
|
|
|
|
A service is an included clan-module that implements facts generation functionality.
|
|
For example the zerotier module will generate private and public facts.
|
|
In this case the public fact will be the resulting zerotier-ip of the machine.
|
|
The secret fact will be the zerotier-identity-secret, which is used by zerotier
|
|
to prove the machine has control of the zerotier-ip.
|
|
|
|
Examples:
|
|
|
|
$ clan facts generate
|
|
Will generate facts for all machines.
|
|
|
|
$ clan facts generate --service [SERVICE] --regenerate
|
|
Will regenerate facts, if they are already generated for a specific service.
|
|
This is especially useful for resetting certain passwords while leaving the rest
|
|
of the facts for a machine in place.
|
|
|
|
For more detailed information, visit: https://docs.clan.lol/getting-started/secrets/
|
|
"""
|
|
),
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
)
|
|
facts.register_parser(parser_facts)
|
|
|
|
parser_machine = subparsers.add_parser(
|
|
"machines",
|
|
help="manage machines and their configuration",
|
|
description="manage machines and their configuration",
|
|
epilog=(
|
|
"""
|
|
This subcommand provides an interface to machines managed by clan.
|
|
|
|
Examples:
|
|
|
|
$ clan machines list
|
|
List all the machines managed by clan.
|
|
|
|
$ clan machines update [MACHINES]
|
|
Will update the specified machine [MACHINE], if [MACHINE] is omitted, the command
|
|
will attempt to update every configured machine.
|
|
|
|
$ clan machines install [MACHINES] [TARGET_HOST]
|
|
Will install the specified machine [MACHINE], to the specified [TARGET_HOST].
|
|
|
|
For more detailed information, visit: https://docs.clan.lol/getting-started/deploy
|
|
"""
|
|
),
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
)
|
|
machines.register_parser(parser_machine)
|
|
|
|
parser_vms = subparsers.add_parser(
|
|
"vms", help="manage virtual machines", description="manage virtual machines"
|
|
)
|
|
vms.register_parser(parser_vms)
|
|
|
|
parser_history = subparsers.add_parser(
|
|
"history",
|
|
help="manage history",
|
|
description="manage history",
|
|
)
|
|
history.register_parser(parser_history)
|
|
|
|
parser_flash = subparsers.add_parser(
|
|
"flash",
|
|
help="flash machines to usb sticks or into isos",
|
|
description="flash machines to usb sticks or into isos",
|
|
)
|
|
flash.register_parser(parser_flash)
|
|
|
|
if argcomplete:
|
|
argcomplete.autocomplete(parser)
|
|
|
|
register_common_flags(parser)
|
|
|
|
return parser
|
|
|
|
|
|
# this will be the entrypoint under /bin/clan (see pyproject.toml config)
|
|
@profile
|
|
def main() -> None:
|
|
parser = create_parser()
|
|
args = parser.parse_args()
|
|
|
|
if len(sys.argv) == 1:
|
|
parser.print_help()
|
|
|
|
if args.debug:
|
|
setup_logging(logging.DEBUG, root_log_name=__name__.split(".")[0])
|
|
log.debug("Debug log activated")
|
|
if flatpak.is_flatpak():
|
|
log.debug("Running inside a flatpak sandbox")
|
|
else:
|
|
setup_logging(logging.INFO, root_log_name=__name__.split(".")[0])
|
|
|
|
if not hasattr(args, "func"):
|
|
return
|
|
|
|
try:
|
|
args.func(args)
|
|
except ClanError as e:
|
|
if args.debug:
|
|
log.exception(e)
|
|
sys.exit(1)
|
|
if isinstance(e, ClanCmdError):
|
|
if e.cmd.msg:
|
|
log.error(e.cmd.msg)
|
|
sys.exit(1)
|
|
|
|
log.error(e)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|