m/node: implement Port type for node ports

This allows us to use %v/%s to get a pretty port name where needed.

We also drive-by remove MasterServicePort which is a leftover from
a pre-curator cluster service implementation.

Change-Id: Id8feddf87269b13dd1dad2460a015c1a7ecbc6d7
Reviewed-on: https://review.monogon.dev/c/monogon/+/418
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/metropolis/test/launch/BUILD.bazel b/metropolis/test/launch/BUILD.bazel
index 22590fe..1cb6b24 100644
--- a/metropolis/test/launch/BUILD.bazel
+++ b/metropolis/test/launch/BUILD.bazel
@@ -6,6 +6,7 @@
     importpath = "source.monogon.dev/metropolis/test/launch",
     visibility = ["//metropolis:__subpackages__"],
     deps = [
+        "//metropolis/node:go_default_library",
         "//metropolis/pkg/freeport:go_default_library",
         "@org_golang_google_grpc//:go_default_library",
         "@org_golang_x_sys//unix:go_default_library",
diff --git a/metropolis/test/launch/cluster/cluster.go b/metropolis/test/launch/cluster/cluster.go
index 6be0efe..a4f11a2 100644
--- a/metropolis/test/launch/cluster/cluster.go
+++ b/metropolis/test/launch/cluster/cluster.go
@@ -62,14 +62,13 @@
 }
 
 // NodePorts is the list of ports a fully operational Metropolis node listens on
-var NodePorts = []uint16{
+var NodePorts = []node.Port{
 	node.ConsensusPort,
 
 	node.CuratorServicePort,
 	node.DebugServicePort,
 
 	node.KubernetesAPIPort,
-	node.MasterServicePort,
 	node.CuratorServicePort,
 	node.DebuggerPort,
 }
@@ -252,7 +251,7 @@
 
 // ClusterPorts contains all ports forwarded by Nanoswitch to the first VM in a
 // launched Metropolis cluster.
-var ClusterPorts = []uint16{
+var ClusterPorts = []node.Port{
 	node.CuratorServicePort,
 	node.DebugServicePort,
 
diff --git a/metropolis/test/launch/launch.go b/metropolis/test/launch/launch.go
index 0b15a58..4a4c803 100644
--- a/metropolis/test/launch/launch.go
+++ b/metropolis/test/launch/launch.go
@@ -32,6 +32,7 @@
 	"golang.org/x/sys/unix"
 	"google.golang.org/grpc"
 
+	"source.monogon.dev/metropolis/node"
 	"source.monogon.dev/metropolis/pkg/freeport"
 )
 
@@ -57,28 +58,28 @@
 
 // PortMap represents where VM ports are mapped to on the host. It maps from the VM
 // port number to the host port number.
-type PortMap map[uint16]uint16
+type PortMap map[node.Port]uint16
 
 // ToQemuForwards generates QEMU hostfwd values (https://qemu.weilnetz.de/doc/qemu-
 // doc.html#:~:text=hostfwd=) for all mapped ports.
 func (p PortMap) ToQemuForwards() []string {
 	var hostfwdOptions []string
 	for vmPort, hostPort := range p {
-		hostfwdOptions = append(hostfwdOptions, fmt.Sprintf("tcp::%v-:%v", hostPort, vmPort))
+		hostfwdOptions = append(hostfwdOptions, fmt.Sprintf("tcp::%d-:%d", hostPort, vmPort))
 	}
 	return hostfwdOptions
 }
 
 // DialGRPC creates a gRPC client for a VM port that's forwarded/mapped to the
 // host. The given port is automatically resolved to the host-mapped port.
-func (p PortMap) DialGRPC(port uint16, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
+func (p PortMap) DialGRPC(port node.Port, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
 	mappedPort, ok := p[port]
 	if !ok {
-		return nil, fmt.Errorf("cannot dial port: port %v is not mapped/forwarded", port)
+		return nil, fmt.Errorf("cannot dial port: port %d is not mapped/forwarded", port)
 	}
-	grpcClient, err := grpc.Dial(fmt.Sprintf("localhost:%v", mappedPort), opts...)
+	grpcClient, err := grpc.Dial(fmt.Sprintf("localhost:%d", mappedPort), opts...)
 	if err != nil {
-		return nil, fmt.Errorf("failed to dial port %v: %w", port, err)
+		return nil, fmt.Errorf("failed to dial port %d: %w", port, err)
 	}
 	return grpcClient, nil
 }
@@ -86,10 +87,10 @@
 // IdentityPortMap returns a port map where each given port is mapped onto itself
 // on the host. This is mainly useful for development against Metropolis. The dbg
 // command requires this mapping.
-func IdentityPortMap(ports []uint16) PortMap {
+func IdentityPortMap(ports []node.Port) PortMap {
 	portMap := make(PortMap)
 	for _, port := range ports {
-		portMap[port] = port
+		portMap[port] = uint16(port)
 	}
 	return portMap
 }
@@ -99,7 +100,7 @@
 // multiple instances of Metropolis nodes might be running. Please call this
 // function for each Launch command separately and as close to it as possible since
 // it cannot guarantee that the ports will remain free.
-func ConflictFreePortMap(ports []uint16) (PortMap, error) {
+func ConflictFreePortMap(ports []node.Port) (PortMap, error) {
 	portMap := make(PortMap)
 	for _, port := range ports {
 		mappedPort, listenCloser, err := freeport.AllocateTCPPort()
diff --git a/metropolis/test/nanoswitch/nanoswitch.go b/metropolis/test/nanoswitch/nanoswitch.go
index 5c674f5..212696d 100644
--- a/metropolis/test/nanoswitch/nanoswitch.go
+++ b/metropolis/test/nanoswitch/nanoswitch.go
@@ -122,7 +122,7 @@
 
 // userspaceProxy listens on port and proxies all TCP connections to the same
 // port on targetIP
-func userspaceProxy(targetIP net.IP, port uint16) supervisor.Runnable {
+func userspaceProxy(targetIP net.IP, port common.Port) supervisor.Runnable {
 	return func(ctx context.Context) error {
 		logger := supervisor.Logger(ctx)
 		tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: int(port)})