diff options
author | Lars Wirzenius <liw@liw.fi> | 2023-05-27 07:32:26 +0000 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2023-05-27 07:32:26 +0000 |
commit | 016060db2b1968d25a23d54e42b5566d14d3e16d (patch) | |
tree | 6f319b361050dce3221159961cccf186a70dc7e7 | |
parent | ad845a7d26bd43f2b0bff9cb9113c486fd4a19c7 (diff) | |
parent | 7230abdc1ece4b08b46da0483a5d75d6f5c5d5de (diff) | |
download | v-i-016060db2b1968d25a23d54e42b5566d14d3e16d.tar.gz |
Merge branch 'liw/tweaks' into 'main'
small improvements to make for a nicer experience
See merge request larswirzenius/v-i!47
-rw-r--r-- | config.yaml | 4 | ||||
-rwxr-xr-x | configure-installer | 112 | ||||
-rw-r--r-- | installer.vmdb | 1 | ||||
-rwxr-xr-x | v-i | 41 |
4 files changed, 141 insertions, 17 deletions
diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..e6dd414 --- /dev/null +++ b/config.yaml @@ -0,0 +1,4 @@ +user_ca_pub_cmd: sshca ca public-key liw.fi/ca/user/v5 +host_key_cmd: sshca host private-key $HOST +host_cert_cmd: sshca host certify liw.fi/ca/host/v5 $HOST +cmd_as_user: liw diff --git a/configure-installer b/configure-installer index 7851acd..fce3695 100755 --- a/configure-installer +++ b/configure-installer @@ -4,13 +4,19 @@ import argparse import glob import os import subprocess +import sys import tempfile import yaml +verbose = False + + def log(msg): - if True: - print(msg) + if verbose: + sys.stderr.write(msg) + sys.stderr.write("\n") + sys.stderr.flush() class Config: @@ -18,7 +24,11 @@ class Config: "authorized_keys_file": None, "host_cert_file": None, "host_key_file": None, + "host_cert_cmd": None, + "host_key_cmd": None, "user_ca_pub_file": None, + "user_ca_pub_cmd": None, + "cmd_as_user": None, } exandable = [ @@ -42,7 +52,55 @@ class Config: for key in self.exandable: if self.config[key] is not None: self.config[key] = os.path.expanduser(self.config[key]) - log(f"config: {self.config}") + log(f"config:") + for key in self.config: + log(f" {key}={self.config[key]!r}") + + def user_ca_pub(self): + return self._get_from_file_or_cmd("user_ca_pub", "user CA public key", None) + + def host_key(self, hostname): + return self._get_from_file_or_cmd("host_key", "host private key", hostname) + + def host_cert(self, hostname): + return self._get_from_file_or_cmd("host_cert", "host certificate", hostname) + + def _get_from_file_or_cmd(self, prefix, msg, hostname): + log("_get: A") + filename = self.config.get(f"{prefix}_file") + log("_get: B") + if filename is not None: + log("_get: C") + log(f"reading {msg} from {filename}") + log("_get: D") + return cat(filename) + + log("_get: E") + cmd = self.config.get(f"{prefix}_cmd") + log("_get: F") + if hostname is not None: + log("_get: G") + cmd = hostname.join(cmd.split("$HOST")) + log("_get: H") + if cmd is not None: + log("_get: I") + user = self.config.get("cmd_as_user") + log("_get: J") + if user is not None: + log("_get: K") + log(f"reading {msg} from command (as {user}): {cmd}") + log("_get: L") + return run(cmd, user=user) + else: + log("_get: M") + log(f"reading {msg} from command: {cmd}") + log("_get: N") + return run(cmd) + + log("_get: O") + log(f"can't read {msg}") + log("_get: Z") + return None def mount(drive, mp): @@ -55,6 +113,30 @@ def unmount(path): subprocess.run(["umount", path]) +def run(cmd, user=None): + log(f"run: A - user={user!r}") + if user is not None: + log("run: B") + argv = ["sudo", "-u", user, "--", "/bin/bash", "-c", cmd] + log("run: C") + log(f"argv: {argv}") + log("run: D") + p = subprocess.run(argv, capture_output=True) + log("run: E") + else: + log("run: F") + log(f"cmd={cmd!r}") + p = subprocess.run(cmd, shell=True, capture_output=True) + log("run: G") + if p.returncode != 0: + log("run: H") + sys.stderr.write(p.stderr.decode()) + log("run: J") + sys.exit(1) + log("run: Z") + return p.stdout.decode() + + def cat(path): log(f"reading {path}") with open(path) as f: @@ -75,13 +157,11 @@ def dir_exists(mp, path): raise Exception(f"{full} does not exist") -def host_id(config, mp): - key_path = config["host_key_file"] - cert_path = config["host_cert_file"] - if key_path is None or cert_path is None: - return - key = cat(key_path) - cert = cat(cert_path) +def host_id(config, mp, installer_hostname): + key = config.host_key(installer_hostname) + cert = config.host_cert(installer_hostname) + if key is None: + sys.exit("could not find host key for installer") config_d = "/etc/ssh/sshd_config.d" host_key = "/etc/ssh/ssh_host_key" @@ -117,10 +197,7 @@ def authorized_keys(config, mp): def user_ca(config, mp): - ca_path = config["user_ca_pub_file"] - if ca_path is None: - return - ca_key = cat(ca_path) + ca_key = config.user_ca_pub() include = f"{mp}/etc/ssh/sshd_config.d/user_ca.conf" write(include, "TrustedUserCAKeys /etc/ssh/user_ca_pubs\n", 0, 0, 0o644) @@ -133,6 +210,8 @@ def main(): log("configure-image starting") p = argparse.ArgumentParser() + p.add_argument("--verbose", action="store_true", help="be verbose") + p.add_argument("--hostname", default="v-i", help="host name of installer image") p.add_argument("config", metavar="FILE", help="configuration file") p.add_argument( "drive", @@ -141,6 +220,9 @@ def main(): ) args = p.parse_args() + global verbose + verbose = args.verbose + config = Config() config.read(args.config) @@ -151,7 +233,7 @@ def main(): mount(drive2, mp) try: dir_exists(mp, "etc/ssh") - host_id(config, mp) + host_id(config, mp, args.hostname) authorized_keys(config, mp) user_ca(config, mp) finally: diff --git a/installer.vmdb b/installer.vmdb index deb3151..eb88a9f 100644 --- a/installer.vmdb +++ b/installer.vmdb @@ -88,3 +88,4 @@ steps: - grub: uefi tag: root efi: efi + quiet: true @@ -8,6 +8,7 @@ import shutil import subprocess import sys import tempfile +import time import yaml @@ -379,6 +380,31 @@ def vmdb_spec(system, ansible_vars): return {"steps": steps} +class Timings: + def __init__(self): + self.started = time.time() + self.times = {} + self.whats = [] + + def reached(self, what): + assert what not in self.whats + assert what not in self.times + self.times[what] = time.time() + self.whats.append(what) + + def report(self): + prev = self.started + for what in self.whats: + secs = "%.1f" % (self.times[what] - prev) + prev = self.times[what] + log(f"Duration: {what}: {secs} s") + print(f"Duration: {what}: {secs} s") + + +def cache_name(spec): + return f"{spec.debian_release}.cache.tar.gz" + + class SystemSpec: def __init__(self, filename): REQUIRED = "required" @@ -441,11 +467,13 @@ class SystemSpec: def main(): + timings = Timings() + p = argparse.ArgumentParser() p.add_argument("--verbose", action="store_true") p.add_argument("--very-verbose", action="store_true") p.add_argument("--log", default="install.log") - p.add_argument("--cache", default="cache.tar.gz") + p.add_argument("--cache", default=None) p.add_argument("spec") args = p.parse_args() @@ -463,6 +491,9 @@ def main(): system = SystemSpec(args.spec) log(f"spec: {system!r}") + cache = args.cache or cache_name(system) + log(f"cache: {cache}") + ansible_vars = dict(system.ansible_vars) ansible_vars["hostname"] = system.hostname for filename in system.ansible_vars_files: @@ -471,8 +502,10 @@ def main(): vars_dict = yaml.safe_load(f) ansible_vars.update(vars_dict) log(f"ansible_vars: {ansible_vars!r}") + timings.reached("read configuration") clean_up_disks([system.drive] + system.extra_drives) + timings.reached("clean up storage") vmdb = vmdb_spec(system, ansible_vars) tmp = tempfile.mkdtemp() @@ -490,7 +523,7 @@ def main(): argv = [ "vmdb2", - f"--rootfs-tarball={args.cache}", + f"--rootfs-tarball={cache}", f"--log={args.log}", f"--image={system.drive}", specfile, @@ -498,12 +531,16 @@ def main(): if verbose: argv.append("--verbose") run(argv, check=True, capture_output=True) + timings.reached("vmdb2") log("cleanup") shutil.rmtree(tmp) + timings.reached("clean up") log("OK, done") print("OK, done") + timings.report() + main() |