clan-cli: add update command

This commit is contained in:
Jörg Thalheim
2023-08-10 12:30:52 +02:00
parent c9b77e5927
commit a096d8ddcc
7 changed files with 199 additions and 25 deletions

View File

@@ -156,6 +156,7 @@ class Host:
host_key_check: HostKeyCheck = HostKeyCheck.STRICT,
meta: Dict[str, Any] = {},
verbose_ssh: bool = False,
ssh_options: dict[str, str] = {},
) -> None:
"""
Creates a Host
@@ -179,6 +180,7 @@ class Host:
self.host_key_check = host_key_check
self.meta = meta
self.verbose_ssh = verbose_ssh
self.ssh_options = ssh_options
def _prefix_output(
self,
@@ -451,6 +453,10 @@ class Host:
ssh_target = self.host
ssh_opts = ["-A"] if self.forward_agent else []
for k, v in self.ssh_options.items():
ssh_opts.extend(["-o", f"{k}={shlex.quote(v)}"])
if self.port:
ssh_opts.extend(["-p", str(self.port)])
if self.key:

View File

@@ -0,0 +1,105 @@
import argparse
import json
import subprocess
from .ssh import Host, HostGroup, HostKeyCheck
def deploy_nixos(hosts: HostGroup) -> None:
"""
Deploy to all hosts in parallel
"""
flake_store_paths = {}
for h in hosts.hosts:
flake_uri = str(h.meta.get("flake_uri", ".#"))
if flake_uri not in flake_store_paths:
res = subprocess.run(
[
"nix",
"--extra-experimental-features",
"nix-command flakes",
"flake",
"metadata",
"--json",
flake_uri,
],
check=True,
text=True,
stdout=subprocess.PIPE,
)
data = json.loads(res.stdout)
flake_store_paths[flake_uri] = data["path"]
def deploy(h: Host) -> None:
target = f"{h.user or 'root'}@{h.host}"
flake_store_path = flake_store_paths[str(h.meta.get("flake_uri", ".#"))]
flake_path = str(h.meta.get("flake_path", "/etc/nixos"))
ssh_arg = f"-p {h.port}" if h.port else ""
if h.host_key_check != HostKeyCheck.STRICT:
ssh_arg += " -o StrictHostKeyChecking=no"
if h.host_key_check == HostKeyCheck.NONE:
ssh_arg += " -o UserKnownHostsFile=/dev/null"
ssh_arg += " -i " + h.key if h.key else ""
h.run_local(
f"rsync --checksum -vaF --delete -e 'ssh {ssh_arg}' {flake_store_path}/ {target}:{flake_path}"
)
flake_attr = h.meta.get("flake_attr", "")
if flake_attr:
flake_attr = "#" + flake_attr
target_host = h.meta.get("target_host")
if target_host:
target_user = h.meta.get("target_user")
if target_user:
target_host = f"{target_user}@{target_host}"
extra_args = h.meta.get("extra_args", [])
cmd = (
["nixos-rebuild", "switch"]
+ extra_args
+ [
"--fast",
"--option",
"keep-going",
"true",
"--option",
"accept-flake-config",
"true",
"--build-host",
"",
"--flake",
f"{flake_path}{flake_attr}",
]
)
if target_host:
cmd.extend(["--target-host", target_host])
ret = h.run(cmd, check=False)
# re-retry switch if the first time fails
if ret.returncode != 0:
ret = h.run(cmd)
hosts.run_function(deploy)
# FIXME: we want some kind of inventory here.
def update(args: argparse.Namespace) -> None:
deploy_nixos(
HostGroup(
[Host(args.host, user=args.user, meta=dict(flake_attr=args.flake_attr))]
)
)
def register_parser(parser: argparse.ArgumentParser) -> None:
parser.add_mutually_exclusive_group(required=True)
# TODO pass all args we don't parse into ssh_args, currently it fails if arg starts with -
parser.add_argument("--flake-uri", type=str, default=".#", desc="nix flake uri")
parser.add_argument(
"--flake-attr", type=str, description="nixos configuration in the flake"
)
parser.add_argument("--user", type=str, default="root")
parser.add_argument("host", type=str)
parser.set_defaults(func=update)