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/list.go b/metropolis/cli/metroctl/list.go
index afdf6d9..13e44cb 100644
--- a/metropolis/cli/metroctl/list.go
+++ b/metropolis/cli/metroctl/list.go
@@ -8,8 +8,7 @@
 
 	"source.monogon.dev/metropolis/cli/metroctl/core"
 	clicontext "source.monogon.dev/metropolis/cli/pkg/context"
-	"source.monogon.dev/metropolis/node/core/identity"
-	"source.monogon.dev/metropolis/proto/api"
+	apb "source.monogon.dev/metropolis/proto/api"
 )
 
 var listCmd = &cobra.Command{
@@ -27,7 +26,7 @@
 func doList(cmd *cobra.Command, args []string) {
 	ctx := clicontext.WithInterrupt(context.Background())
 	cc := dialAuthenticated(ctx)
-	mgmt := api.NewManagementClient(cc)
+	mgmt := apb.NewManagementClient(cc)
 
 	// Narrow down the output set to supplied node IDs, if any.
 	qids := make(map[string]bool)
@@ -42,20 +41,8 @@
 		log.Fatalf("While calling Management.GetNodes: %v", err)
 	}
 
-	enc := newOutputEncoder()
-	defer enc.close()
-
-	for _, n := range nodes {
-		// Filter the information we want client-side.
-		if len(qids) != 0 {
-			nid := identity.NodeID(n.Pubkey)
-			if _, e := qids[nid]; !e {
-				continue
-			}
-		}
-
-		if err := enc.writeNodeID(n); err != nil {
-			log.Fatalf("While listing nodes: %v", err)
-		}
+	of := func(enc *encoder, n *apb.Node) error {
+		return enc.writeNodeID(n)
 	}
+	printNodes(of, nodes, args)
 }