diff options
Diffstat (limited to 'contractor')
-rwxr-xr-x | contractor | 146 |
1 files changed, 119 insertions, 27 deletions
@@ -10,7 +10,6 @@ import sys import time import subprocess from subprocess import PIPE, STDOUT - import yaml @@ -18,8 +17,14 @@ import yaml DEFAULT_CONFIGS = {os.path.expanduser("~/.config/contractor/config.yaml")} -# The device in the manager VM for the workspace disk. -WS_DEV = "/dev/vdb" +# The disk image file on the manager VM for the workspace disk. +WS_IMG = "/home/manager/workspace.img" +WS_SIZE = "20G" +WS_MNT = "/mnt" + + +# The device on the worker for the workspace disk. +WORKER_WS_DEV = "vdb" # The worker VM image file on manager VM. @@ -30,14 +35,6 @@ WORKER_IMG = "worker.img" TEMP_IMG = "temp.img" -# The UID of the worker account, on the worker VM. -WORKER_UID = 1000 - - -# The GID of the worker account, on the worker VM. -WORKER_GID = 1000 - - class ExecResult: def __init__(self, stdout, stderr, exit_code): self.stdout = stdout @@ -248,7 +245,7 @@ class CopyWorkerImage(RemoteExecution): def argv(self): return ["sh", "-c", "rm -f temp.img; cp worker.img temp.img"] self.ssh(["rm", "-f", TEMP_IMG]) - ret = self.ssh(["cp", WORKER_IMG, TEMP_IMG]) + self.ssh(["cp", WORKER_IMG, TEMP_IMG]) class StartGuestNetworking(MayFail): @@ -259,12 +256,52 @@ class StartGuestNetworking(MayFail): return virsh("net-start", "default") +class GetUID(RemoteExecution): + def msg(self): + return "get UID on manager" + + def argv(self): + return ["id", "-u"] + + +class GetGID(RemoteExecution): + def msg(self): + return "get GID of on manager" + + def argv(self): + return ["id", "-g"] + + +class RemoveWS(RemoteExecution): + def msg(self): + return "remove workspace image on manager" + + def argv(self): + return ["rm", "-f", WS_IMG] + + +class CreateWS(RemoteExecution): + def msg(self): + return "creating workspace on manager" + + def argv(self): + return ["qemu-img", "create", "-q", "-f", "raw", WS_IMG, WS_SIZE] + + +class MkfsWS(RemoteExecution): + def msg(self): + return "mkfs workspace on manager" + + def argv(self): + return ["sudo", "mkfs", "-t", "ext4", "-q", WS_IMG] + + class MountWS(RemoteExecution): def msg(self): return "mounting workspace on manager" def argv(self): - return ["sudo", "mount", WS_DEV, "/mnt"] + return ["sudo", "mount", "-oloop", WS_IMG, WS_MNT] class MountWSonWorker(RemoteExecution): @@ -272,7 +309,7 @@ class MountWSonWorker(RemoteExecution): return "mounting workspace on worker" def argv(self): - return ["sudo", "mount", "/dev/vdb", "/workspace"] + return ["sudo", "mount", "/dev/{}".format(WORKER_WS_DEV), "/workspace"] class TryUnmountWS(MayFail): @@ -280,7 +317,7 @@ class TryUnmountWS(MayFail): return "trying to unmount workspace on manager" def argv(self): - return ["sudo", "umount", "--quiet", WS_DEV] + return ["sudo", "umount", "--quiet", WS_IMG] class UnmountWS(RemoteExecution): @@ -288,15 +325,19 @@ class UnmountWS(RemoteExecution): return "unmounting workspace on manager" def argv(self): - return ["sudo", "umount", "--quiet", WS_DEV] + return ["sudo", "umount", "--quiet", WS_IMG] class ChownWS(RemoteExecution): + def __init__(self, uid, gid): + self.uid = uid + self.gid = gid + def msg(self): return "set ownerships on workspace" def argv(self): - return ["sudo", "chown", "-R", "{}:{}".format(WORKER_UID, WORKER_GID), "/mnt"] + return ["sudo", "chown", "-R", "{}:{}".format(self.uid, self.gid), WS_MNT] class Mkdir(RemoteExecution): @@ -368,8 +409,8 @@ class AttachWS(RemoteExecution): "--quiet", "attach-disk", "worker", - WS_DEV, - "vdb", + WS_IMG, + WORKER_WS_DEV, "--targetbus", "virtio", "--live", @@ -510,19 +551,19 @@ def upload_worker_image(vrb, filename, dest, port): def sync_to_workspace(vrb, frm, dest, port, subdir): - destdir = "/mnt/{}".format(subdir) + destdir = "{}/{}".format(WS_MNT, subdir) vrb("syncing local {} to manager {}".format(frm, destdir)) - er = rsync("{}/.".format(frm), "{}:{}/.".format(dest, destdir), port) + er = rsync(f"{frm}/.", f"{dest}:{destdir}/.", port) if er.failed(): error("Failed to rsync saved workspace to worker") sys.exit(1) def sync_from_workspace(vrb, dest, port, ws): - vrb("syncing manager /mnt to local {}".format(ws)) + vrb("syncing manager {!r} to local {!r} (port {!r})".format(WS_MNT, ws, port)) if not os.path.exists(ws): os.makedirs(ws) - er = rsync("{}:/mnt/.".format(dest), "{}/.".format(ws), port) + er = rsync(f"{dest}:{WS_MNT}/.", f"{ws}/.", port) if er.failed(): error("Failed to rsync workspace from worker") sys.exit(1) @@ -549,6 +590,28 @@ def cmd_dump(args): sys.stdout.write("{}\n".format(json.dumps(bs.as_dict(), indent=4))) +def cmd_provision(args): + ssh_opts = [ + "ControlMaster=auto", + "ControlPersist=60s", + "StrictHostKeyChecking=accept-new", + "UserKnownHostsFile=/dev/null", + ] + + env = dict(os.environ) + env["ANSIBLE_SSH_ARGS"] = " ".join(f"-o{opt}" for opt in ssh_opts) + + argv = [ + "ansible-playbook", + "-i", + "hosts", + "manager.yml", + f"-eansible_ssh_host={args.manager_address}", + f"-eansible_ssh_port={args.manager_port}", + ] + subprocess.check_call(argv, env=env) + + def cmd_status(args): dest, port = manager_destination(args) verbose(args, "manager VM is {}:{}".format(dest, port)) @@ -560,7 +623,9 @@ def cmd_status(args): def cmd_build(args): - vrb = lambda msg: verbose(args, msg) + def vrb(msg): + verbose(args, msg) + vrb("building according to {}".format(args.spec)) bs = load_build_spec(args.spec) dest, port = manager_destination(args) @@ -582,9 +647,27 @@ def cmd_build(args): CopyWorkerImage(), StartGuestNetworking(), CreateWorkerVM(), + ] + exec_quietly(manager, *execs) + + with Timer(vrb, "start-worker"): + execs = [GetUID()] + er = exec_quietly(manager, *execs) + manager_uid = int(er.stdout) + + with Timer(vrb, "start-worker"): + execs = [GetGID()] + er = exec_quietly(manager, *execs) + manager_gid = int(er.stdout) + + with Timer(vrb, "start-worker"): + execs = [ TryUnmountWS(), + RemoveWS(), + CreateWS(), + MkfsWS(), MountWS(), - ChownWS(), + ChownWS(manager_uid, manager_gid), ] exec_quietly(manager, *execs) @@ -596,7 +679,10 @@ def cmd_build(args): sync_to_workspace(vrb, ws, dest, port, ".") with Timer(vrb, "upload-source"): - exec_quietly(manager, Mkdir("/mnt/src", owner=WORKER_UID, group=WORKER_GID)) + exec_quietly( + manager, + Mkdir("{}/src".format(WS_MNT), owner=manager_uid, group=manager_gid), + ) src = bs.source() sync_to_workspace(vrb, src, dest, port, "src") @@ -659,6 +745,9 @@ def load_default_config(args): def load_config(filename, args): + def identity(x): + return x + with open(filename) as f: config = yaml.safe_load(f) @@ -673,7 +762,7 @@ def load_config(filename, args): if key in config: func = keys[key] if func is None: - func = lambda x: x + func = identity setattr(args, key, func(config[key])) @@ -696,6 +785,9 @@ def main(): dump.add_argument("spec") dump.set_defaults(func=cmd_dump) + provision = sub.add_parser("provision", help="provision manager VM") + provision.set_defaults(func=cmd_provision, **manager_defaults) + status = sub.add_parser("status", help="check status of manager VM") status.add_argument("-m", "--manager-address", help="address of manager VM") status.add_argument("-p", "--manager-port", help="SSH port of manager VM") |