metropolis/cli/metroctl: implement install ssh

This implements another way of installing metropolis via ssh. It does
this by uploading the files to the target machine and then doing a kexec
into the install environment. If it fails at any point it will print the
error and reboot.

Change-Id: I1ac6538896709c386b053a84903fa04940c1f012
Reviewed-on: https://review.monogon.dev/c/monogon/+/2079
Tested-by: Jenkins CI
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/metropolis/cli/takeover/boot.go b/metropolis/cli/takeover/boot.go
new file mode 100644
index 0000000..eb71694
--- /dev/null
+++ b/metropolis/cli/takeover/boot.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+	"context"
+	"os"
+	"path/filepath"
+	"time"
+
+	"golang.org/x/sys/unix"
+
+	"source.monogon.dev/metropolis/node/core/devmgr"
+	"source.monogon.dev/osbase/supervisor"
+)
+
+// Main runnable for the installer.
+func takeoverRunnable(ctx context.Context) error {
+	l := supervisor.Logger(ctx)
+
+	devmgrSvc := devmgr.New()
+	supervisor.Run(ctx, "devmgr", devmgrSvc.Run)
+	supervisor.Signal(ctx, supervisor.SignalHealthy)
+
+	for {
+		devicePath := filepath.Join("/dev", os.Getenv(EnvInstallTarget))
+		l.Infof("Waiting for device: %s", devicePath)
+		_, err := os.Stat(devicePath)
+		if os.IsNotExist(err) {
+			time.Sleep(1 * time.Second)
+			continue
+		} else if err != nil {
+			return err
+		}
+		break
+	}
+
+	if err := installMetropolis(l); err != nil {
+		l.Errorf("Installation failed: %v", err)
+	} else {
+		l.Info("Installation succeeded")
+	}
+
+	time.Sleep(1 * time.Second)
+	unix.Sync()
+	unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART)
+
+	return nil
+}