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.
218 lines
5.8 KiB
Python
218 lines
5.8 KiB
Python
import argparse
|
|
import importlib
|
|
import json
|
|
import logging
|
|
import os
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from tempfile import TemporaryDirectory
|
|
|
|
from ..cmd import Log, run
|
|
from ..completions import add_dynamic_completer, complete_machines
|
|
from ..facts.generate import generate_facts
|
|
from ..machines.machines import Machine
|
|
from ..nix import nix_shell
|
|
from ..ssh.cli import is_ipv6, is_reachable, qrcode_scan
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class ClanError(Exception):
|
|
pass
|
|
|
|
|
|
def install_nixos(
|
|
machine: Machine,
|
|
kexec: str | None = None,
|
|
debug: bool = False,
|
|
password: str | None = None,
|
|
no_reboot: bool = False,
|
|
extra_args: list[str] = [],
|
|
) -> None:
|
|
secret_facts_module = importlib.import_module(machine.secret_facts_module)
|
|
log.info(f"installing {machine.name}")
|
|
secret_facts_store = secret_facts_module.SecretStore(machine=machine)
|
|
|
|
h = machine.target_host
|
|
target_host = f"{h.user or 'root'}@{h.host}"
|
|
log.info(f"target host: {target_host}")
|
|
|
|
generate_facts([machine], None, False)
|
|
|
|
with TemporaryDirectory() as tmpdir_:
|
|
tmpdir = Path(tmpdir_)
|
|
upload_dir_ = machine.secrets_upload_directory
|
|
|
|
if upload_dir_.startswith("/"):
|
|
upload_dir_ = upload_dir_[1:]
|
|
upload_dir = tmpdir / upload_dir_
|
|
upload_dir.mkdir(parents=True)
|
|
secret_facts_store.upload(upload_dir)
|
|
|
|
if password:
|
|
os.environ["SSHPASS"] = password
|
|
|
|
cmd = [
|
|
"nixos-anywhere",
|
|
"--flake",
|
|
f"{machine.flake}#{machine.name}",
|
|
"--extra-files",
|
|
str(tmpdir),
|
|
*extra_args,
|
|
]
|
|
|
|
if no_reboot:
|
|
cmd.append("--no-reboot")
|
|
|
|
if password:
|
|
cmd += [
|
|
"--env-password",
|
|
"--ssh-option",
|
|
"IdentitiesOnly=yes",
|
|
]
|
|
|
|
if machine.target_host.port:
|
|
cmd += ["--ssh-port", str(machine.target_host.port)]
|
|
if kexec:
|
|
cmd += ["--kexec", kexec]
|
|
if debug:
|
|
cmd.append("--debug")
|
|
cmd.append(target_host)
|
|
|
|
run(
|
|
nix_shell(
|
|
["nixpkgs#nixos-anywhere"],
|
|
cmd,
|
|
),
|
|
log=Log.BOTH,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class InstallOptions:
|
|
flake: Path
|
|
machine: str
|
|
target_host: str
|
|
kexec: str | None
|
|
confirm: bool
|
|
debug: bool
|
|
no_reboot: bool
|
|
json_ssh_deploy: dict[str, str] | None
|
|
nix_options: list[str]
|
|
|
|
|
|
def install_command(args: argparse.Namespace) -> None:
|
|
json_ssh_deploy = None
|
|
if args.json:
|
|
json_file = Path(args.json)
|
|
if json_file.is_file():
|
|
json_ssh_deploy = json.loads(json_file.read_text())
|
|
else:
|
|
json_ssh_deploy = json.loads(args.json)
|
|
elif args.png:
|
|
json_ssh_deploy = json.loads(qrcode_scan(args.png))
|
|
|
|
if not json_ssh_deploy and not args.target_host:
|
|
raise ClanError("No target host provided, please provide a target host.")
|
|
|
|
if json_ssh_deploy:
|
|
target_host = f"root@{find_reachable_host_from_deploy_json(json_ssh_deploy)}"
|
|
password = json_ssh_deploy["pass"]
|
|
else:
|
|
target_host = args.target_host
|
|
password = None
|
|
|
|
opts = InstallOptions(
|
|
flake=args.flake,
|
|
machine=args.machine,
|
|
target_host=target_host,
|
|
kexec=args.kexec,
|
|
confirm=not args.yes,
|
|
debug=args.debug,
|
|
no_reboot=args.no_reboot,
|
|
json_ssh_deploy=json_ssh_deploy,
|
|
nix_options=args.option,
|
|
)
|
|
machine = Machine(opts.machine, flake=opts.flake)
|
|
machine.target_host_address = opts.target_host
|
|
|
|
if opts.confirm:
|
|
ask = input(f"Install {machine.name} to {opts.target_host}? [y/N] ")
|
|
if ask != "y":
|
|
return
|
|
|
|
install_nixos(
|
|
machine,
|
|
kexec=opts.kexec,
|
|
debug=opts.debug,
|
|
password=password,
|
|
no_reboot=opts.no_reboot,
|
|
extra_args=opts.nix_options,
|
|
)
|
|
|
|
|
|
def find_reachable_host_from_deploy_json(deploy_json: dict[str, str]) -> str:
|
|
host = None
|
|
for addr in deploy_json["addrs"]:
|
|
if is_reachable(addr):
|
|
if is_ipv6(addr):
|
|
host = f"[{addr}]"
|
|
else:
|
|
host = addr
|
|
break
|
|
if not host:
|
|
raise ClanError(
|
|
f"""
|
|
Could not reach any of the host addresses provided in the json string.
|
|
Please doublecheck if they are reachable from your machine.
|
|
Try `ping [ADDR]` with one of the addresses: {deploy_json['addrs']}
|
|
"""
|
|
)
|
|
return host
|
|
|
|
|
|
def register_install_parser(parser: argparse.ArgumentParser) -> None:
|
|
parser.add_argument(
|
|
"--kexec",
|
|
type=str,
|
|
help="use another kexec tarball to bootstrap NixOS",
|
|
)
|
|
parser.add_argument(
|
|
"--no-reboot",
|
|
action="store_true",
|
|
help="do not reboot after installation",
|
|
default=False,
|
|
)
|
|
parser.add_argument(
|
|
"--yes",
|
|
action="store_true",
|
|
help="do not ask for confirmation",
|
|
default=False,
|
|
)
|
|
|
|
machines_parser = parser.add_argument(
|
|
"machine",
|
|
type=str,
|
|
help="machine to install",
|
|
)
|
|
add_dynamic_completer(machines_parser, complete_machines)
|
|
|
|
parser.add_argument(
|
|
"target_host",
|
|
type=str,
|
|
nargs="?",
|
|
help="ssh address to install to in the form of user@host:2222",
|
|
)
|
|
group = parser.add_mutually_exclusive_group(required=False)
|
|
group.add_argument(
|
|
"-j",
|
|
"--json",
|
|
help="specify the json file for ssh data (generated by starting the clan installer)",
|
|
)
|
|
group.add_argument(
|
|
"-P",
|
|
"--png",
|
|
help="specify the json file for ssh data as the qrcode image (generated by starting the clan installer)",
|
|
)
|
|
parser.set_defaults(func=install_command)
|