m/c/metroctl: exercise the takeownership command
This introduces a new test harness together with a test exercising
takeownership.
Change-Id: I998506093c25e489d8cb2810e90b4c94530bf4a6
Reviewed-on: https://review.monogon.dev/c/monogon/+/839
Tested-by: Jenkins CI
Reviewed-by: Sergiusz Bazanski <serge@monogon.tech>
diff --git a/metropolis/cli/metroctl/test/BUILD.bazel b/metropolis/cli/metroctl/test/BUILD.bazel
new file mode 100644
index 0000000..382c73f
--- /dev/null
+++ b/metropolis/cli/metroctl/test/BUILD.bazel
@@ -0,0 +1,33 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+
+go_test(
+ name = "metroctl_test",
+ srcs = ["test.go"],
+ data = [
+ "//metropolis/cli/metroctl",
+ "//metropolis/node:image",
+ "//metropolis/node:swtpm_data",
+ "//third_party/edk2:firmware",
+ ],
+ rundir = ".",
+ deps = [
+ "//metropolis/cli/pkg/datafile",
+ "//metropolis/node",
+ "//metropolis/pkg/cmd",
+ "//metropolis/test/launch/cluster",
+ "//metropolis/test/util",
+ ],
+)
+
+go_library(
+ name = "test_lib",
+ srcs = ["test.go"],
+ importpath = "source.monogon.dev/metropolis/cli/metroctl/test",
+ visibility = ["//visibility:private"],
+ deps = [
+ "//metropolis/cli/pkg/datafile",
+ "//metropolis/pkg/cmd",
+ "//metropolis/test/launch/cluster",
+ "//metropolis/test/util",
+ ],
+)
diff --git a/metropolis/cli/metroctl/test/test.go b/metropolis/cli/metroctl/test/test.go
new file mode 100644
index 0000000..41cd2b5
--- /dev/null
+++ b/metropolis/cli/metroctl/test/test.go
@@ -0,0 +1,94 @@
+package test
+
+import (
+ "context"
+ "encoding/pem"
+ "fmt"
+ "log"
+ "os"
+ "strings"
+ "testing"
+ "time"
+
+ "source.monogon.dev/metropolis/cli/pkg/datafile"
+ "source.monogon.dev/metropolis/pkg/cmd"
+ "source.monogon.dev/metropolis/test/launch/cluster"
+ "source.monogon.dev/metropolis/test/util"
+)
+
+func expectMetroctl(t *testing.T, ctx context.Context, args []string, expect string) error {
+ t.Helper()
+
+ path, err := datafile.ResolveRunfile("metropolis/cli/metroctl/metroctl_/metroctl")
+ if err != nil {
+ return fmt.Errorf("couldn't resolve metroctl binary: %v", err)
+ }
+
+ log.Printf("$ metroctl %s", strings.Join(args, " "))
+ found, err := cmd.RunCommand(ctx, path, args, expect)
+ if err != nil {
+ return fmt.Errorf("while running metroctl: %v", err)
+ }
+ if !found {
+ return fmt.Errorf("expected string wasn't found while running metroctl.")
+ }
+ return nil
+}
+
+func TestMetroctl(t *testing.T) {
+ ctx, ctxC := context.WithCancel(context.Background())
+ defer ctxC()
+
+ co := cluster.ClusterOptions{
+ NumNodes: 2,
+ }
+ cl, err := cluster.LaunchCluster(context.Background(), co)
+ if err != nil {
+ t.Fatalf("LaunchCluster failed: %v", err)
+ }
+ defer func() {
+ err := cl.Close()
+ if err != nil {
+ t.Fatalf("cluster Close failed: %v", err)
+ }
+ }()
+
+ socksRemote := fmt.Sprintf("localhost:%d", cl.Ports[cluster.SOCKSPort])
+ var clusterEndpoints []string
+ for _, ep := range cl.Nodes {
+ clusterEndpoints = append(clusterEndpoints, ep.ManagementAddress)
+ }
+
+ ownerPem := pem.EncodeToMemory(&pem.Block{
+ Type: "METROPOLIS INITIAL OWNER PRIVATE KEY",
+ Bytes: cluster.InsecurePrivateKey,
+ })
+ if err := os.WriteFile("owner-key.pem", ownerPem, 0644); err != nil {
+ log.Fatal("Couldn't write owner-key.pem")
+ }
+
+ commonOpts := []string{
+ "--proxy=" + socksRemote,
+ "--config=.",
+ }
+
+ var endpointOpts []string
+ for _, ep := range clusterEndpoints {
+ endpointOpts = append(endpointOpts, "--endpoints="+ep)
+ }
+
+ log.Printf("metroctl: Cluster's running, starting tests...")
+ st := t.Run("Init", func(t *testing.T) {
+ util.TestEventual(t, "metroctl takeownership", ctx, 30*time.Second, func(ctx context.Context) error {
+ // takeownership needs just a single endpoint pointing at the initial node.
+ var args []string
+ args = append(args, commonOpts...)
+ args = append(args, endpointOpts[0])
+ args = append(args, "takeownership")
+ return expectMetroctl(t, ctx, args, "Successfully retrieved owner credentials")
+ })
+ })
+ if !st {
+ t.Fatalf("metroctl: Couldn't get cluster ownership.")
+ }
+}