m/c/metroctl: implement the "describe" command
This implements metroctl's "describe".
cmd.TerminateIfFound was adjusted to meet new test needs.
Change-Id: If86f35bc648d99396e7d5be48ab459d6b13334ce
Reviewed-on: https://review.monogon.dev/c/monogon/+/850
Reviewed-by: Sergiusz Bazanski <serge@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/cli/metroctl/test/test.go b/metropolis/cli/metroctl/test/test.go
index 2863021..0d9b2e1 100644
--- a/metropolis/cli/metroctl/test/test.go
+++ b/metropolis/cli/metroctl/test/test.go
@@ -16,19 +16,42 @@
"source.monogon.dev/metropolis/test/util"
)
+// resolveMetroctl resolves metroctl filesystem path. It will return a correct
+// path, or terminate test execution.
+func resolveMetroctl() string {
+ path, err := datafile.ResolveRunfile("metropolis/cli/metroctl/metroctl_/metroctl")
+ if err != nil {
+ log.Fatalf("Couldn't resolve metroctl binary: %v", err)
+ }
+ return path
+}
+
+// mctlRun starts metroctl, and waits till it exits. It returns nil, if the run
+// was successful.
+func mctlRun(t *testing.T, ctx context.Context, args []string) error {
+ t.Helper()
+
+ path := resolveMetroctl()
+ log.Printf("$ metroctl %s", strings.Join(args, " "))
+ logf := func(line string) {
+ log.Printf("metroctl: %s", line)
+ }
+ _, err := cmd.RunCommand(ctx, path, args, cmd.WaitUntilCompletion(logf))
+ return err
+}
+
// mctlExpectOutput returns true in the event the expected string is found in
// metroctl output, and false otherwise.
func mctlExpectOutput(t *testing.T, ctx context.Context, args []string, expect string) (bool, error) {
t.Helper()
- path, err := datafile.ResolveRunfile("metropolis/cli/metroctl/metroctl_/metroctl")
- if err != nil {
- return false, fmt.Errorf("couldn't resolve metroctl binary: %v", err)
- }
-
+ path := resolveMetroctl()
log.Printf("$ metroctl %s", strings.Join(args, " "))
// Terminate metroctl as soon as the expected output is found.
- found, err := cmd.RunCommand(ctx, path, args, cmd.TerminateIfFound(expect))
+ logf := func(line string) {
+ log.Printf("metroctl: %s", line)
+ }
+ found, err := cmd.RunCommand(ctx, path, args, cmd.TerminateIfFound(expect, logf))
if err != nil {
return false, fmt.Errorf("while running metroctl: %v", err)
}
@@ -183,4 +206,51 @@
return mctlFailIfFound(t, ctx, args, cl.NodeIDs[0])
})
})
+ t.Run("describe --filter", func(t *testing.T) {
+ util.TestEventual(t, "metroctl list --filter", ctx, 10*time.Second, func(ctx context.Context) error {
+ nid := cl.NodeIDs[0]
+ naddr := cl.Nodes[nid].ManagementAddress
+
+ var args []string
+ args = append(args, commonOpts...)
+ args = append(args, endpointOpts...)
+
+ // Filter out the first node. Afterwards, only one node should be left.
+ args = append(args, "node", "describe", "--output", "describe.txt", "--filter", fmt.Sprintf("node.status.external_address==\"%s\"", naddr))
+ if err := mctlRun(t, ctx, args); err != nil {
+ return err
+ }
+
+ // Try matching metroctl output against the advertised format.
+ ob, err := os.ReadFile("describe.txt")
+ if err != nil {
+ return fmt.Errorf("while reading metroctl output: %v", err)
+ }
+ var onid, ostate, onaddr, onstatus, onroles string
+ var ontimeout int
+ _, err = fmt.Sscanf(string(ob[:]), "%s\t%s\t%s\t%s\t%s\t%ds\n", &onid, &ostate, &onaddr, &onstatus, &onroles, &ontimeout)
+ if err != nil {
+ return fmt.Errorf("while parsing metroctl output: %v", err)
+ }
+ if onid != nid {
+ return fmt.Errorf("node id mismatch")
+ }
+ if ostate != "NODE_STATE_UP" {
+ return fmt.Errorf("node state mismatch")
+ }
+ if onaddr != naddr {
+ return fmt.Errorf("node address mismatch")
+ }
+ if onstatus != "HEALTHY" {
+ return fmt.Errorf("node status mismatch")
+ }
+ if onroles != "KubernetesWorker,ConsensusMember" {
+ return fmt.Errorf("node role mismatch")
+ }
+ if ontimeout < 0 || ontimeout > 30 {
+ return fmt.Errorf("node timeout mismatch")
+ }
+ return nil
+ })
+ })
}