summaryrefslogtreecommitdiff
path: root/contractor
diff options
context:
space:
mode:
Diffstat (limited to 'contractor')
-rwxr-xr-xcontractor146
1 files changed, 119 insertions, 27 deletions
diff --git a/contractor b/contractor
index 66693ed..75d809f 100755
--- a/contractor
+++ b/contractor
@@ -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")