metroctl: add metrics command

This is a little helper command to access Node metrics easily for people
(eg. developers!) who don't have a metrics collection infrastructure set
up.

Change-Id: Ibe3b4356db88e31c3156289ab8d8ca2985266b4b
Reviewed-on: https://review.monogon.dev/c/monogon/+/3288
Tested-by: Jenkins CI
Reviewed-by: Tim Windelschmidt <tim@monogon.tech>
diff --git a/metropolis/cli/metroctl/rpc.go b/metropolis/cli/metroctl/rpc.go
index cab6d4f..512cb98 100644
--- a/metropolis/cli/metroctl/rpc.go
+++ b/metropolis/cli/metroctl/rpc.go
@@ -6,7 +6,10 @@
 	"crypto/x509"
 	"errors"
 	"log"
+	"net"
+	"net/http"
 
+	"golang.org/x/net/proxy"
 	"google.golang.org/grpc"
 
 	"source.monogon.dev/metropolis/cli/metroctl/core"
@@ -21,6 +24,9 @@
 	if errors.Is(err, core.ErrNoCredentials) {
 		log.Fatalf("You have to take ownership of the cluster first: %v", err)
 	}
+	if err != nil {
+		log.Fatalf("Failed to get owner credentials: %v", err)
+	}
 	if len(flags.clusterEndpoints) == 0 {
 		log.Fatal("Please provide at least one cluster endpoint using the --endpoint parameter.")
 	}
@@ -61,3 +67,33 @@
 	}
 	return cc
 }
+
+func newAuthenticatedNodeHTTPTransport(ctx context.Context, id string) *http.Transport {
+	cacert, err := core.GetClusterCAWithTOFU(ctx, connectOptions())
+	if err != nil {
+		log.Fatalf("Could not get CA certificate: %v", err)
+	}
+	ocert, opkey, err := core.GetOwnerCredentials(flags.configPath)
+	if errors.Is(err, core.ErrNoCredentials) {
+		log.Fatalf("You have to take ownership of the cluster first: %v", err)
+	}
+	tlsc := tls.Certificate{
+		Certificate: [][]byte{ocert.Raw},
+		PrivateKey:  opkey,
+	}
+	tlsconf := rpc.NewAuthenticatedTLSConfig(tlsc, rpc.WantRemoteCluster(cacert), rpc.WantRemoteNode(id))
+	transport := &http.Transport{
+		TLSClientConfig: tlsconf,
+	}
+	if flags.proxyAddr != "" {
+		dialer, err := proxy.SOCKS5("tcp", flags.proxyAddr, nil, proxy.Direct)
+		if err != nil {
+			log.Fatalf("Failed to create proxy dialer: %v", err)
+		}
+		transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
+			// TODO(q3k): handle context
+			return dialer.Dial(network, addr)
+		}
+	}
+	return transport
+}