treewide: replace deprecated grpc.Dial with grpc.NewClient

Change-Id: I925912ca1ee01d547fd9c1813eb083a2cd9a590a
Reviewed-on: https://review.monogon.dev/c/monogon/+/3858
Reviewed-by: Jan Schär <jan@monogon.tech>
Tested-by: Jenkins CI
diff --git a/cloud/agent/agent.go b/cloud/agent/agent.go
index 028a7d6..c185b83 100644
--- a/cloud/agent/agent.go
+++ b/cloud/agent/agent.go
@@ -96,12 +96,12 @@
 		rootCAs.AddCert(caCert)
 	}
 
-	conn, err := grpc.Dial(agentInit.TakeoverInit.BmaasEndpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
+	conn, err := grpc.NewClient(agentInit.TakeoverInit.BmaasEndpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
 		Certificates: []tls.Certificate{*ephemeralCert},
 		RootCAs:      rootCAs,
 	})))
 	if err != nil {
-		return fmt.Errorf("error dialing BMaaS gRPC endpoint: %w", err)
+		return fmt.Errorf("error creating BMaaS gRPC client: %w", err)
 	}
 	c := bpb.NewAgentCallbackClient(conn)
 
diff --git a/cloud/bmaas/server/agent_callback_service_test.go b/cloud/bmaas/server/agent_callback_service_test.go
index e0ec9d0..a055d1d 100644
--- a/cloud/bmaas/server/agent_callback_service_test.go
+++ b/cloud/bmaas/server/agent_callback_service_test.go
@@ -65,9 +65,9 @@
 		if err != nil {
 			t.Fatalf("could not generate ephemeral credentials: %v", err)
 		}
-		conn, err := grpc.Dial(s.ListenPublic, grpc.WithTransportCredentials(creds))
+		conn, err := grpc.NewClient(s.ListenPublic, grpc.WithTransportCredentials(creds))
 		if err != nil {
-			t.Fatalf("Dial failed: %v", err)
+			t.Fatalf("NewClient failed: %v", err)
 		}
 		defer conn.Close()
 
@@ -143,9 +143,9 @@
 		if err != nil {
 			t.Fatalf("could not generate ephemeral credentials: %v", err)
 		}
-		conn, err := grpc.Dial(s.ListenPublic, grpc.WithTransportCredentials(creds))
+		conn, err := grpc.NewClient(s.ListenPublic, grpc.WithTransportCredentials(creds))
 		if err != nil {
-			t.Fatalf("Dial failed: %v", err)
+			t.Fatalf("NewClient failed: %v", err)
 		}
 		defer conn.Close()
 
diff --git a/metropolis/cli/dbg/main.go b/metropolis/cli/dbg/main.go
index 4a3d32e..0ee1c6e 100644
--- a/metropolis/cli/dbg/main.go
+++ b/metropolis/cli/dbg/main.go
@@ -29,9 +29,9 @@
 	ctx := context.Background()
 	// Hardcode localhost since this should never be used to interface with a
 	// production node because of missing encryption & authentication
-	grpcClient, err := grpc.Dial("localhost:7837", grpc.WithTransportCredentials(insecure.NewCredentials()))
+	grpcClient, err := grpc.NewClient("localhost:7837", grpc.WithTransportCredentials(insecure.NewCredentials()))
 	if err != nil {
-		fmt.Printf("Failed to dial debug service (is it running): %v\n", err)
+		fmt.Printf("Failed create debug service: %v\n", err)
 	}
 	debugClient := apb.NewNodeDebugServiceClient(grpcClient)
 	if len(os.Args) < 2 {
diff --git a/metropolis/cli/metroctl/cmd_cluster_configure.go b/metropolis/cli/metroctl/cmd_cluster_configure.go
index 73483c1..2439c95 100644
--- a/metropolis/cli/metroctl/cmd_cluster_configure.go
+++ b/metropolis/cli/metroctl/cmd_cluster_configure.go
@@ -103,9 +103,9 @@
 		}
 
 		ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
-		cc, err := dialAuthenticated(ctx)
+		cc, err := newAuthenticatedClient(ctx)
 		if err != nil {
-			return fmt.Errorf("while dialing node: %w", err)
+			return fmt.Errorf("while creating client: %w", err)
 		}
 		mgmt := apb.NewManagementClient(cc)
 
diff --git a/metropolis/cli/metroctl/cmd_cluster_takeownership.go b/metropolis/cli/metroctl/cmd_cluster_takeownership.go
index 3566ea0..d917bc3 100644
--- a/metropolis/cli/metroctl/cmd_cluster_takeownership.go
+++ b/metropolis/cli/metroctl/cmd_cluster_takeownership.go
@@ -64,9 +64,9 @@
 		}
 		opts = append(opts, grpc.WithTransportCredentials(creds))
 
-		cc, err := grpc.Dial(resolver.MetropolisControlAddress, opts...)
+		cc, err := grpc.NewClient(resolver.MetropolisControlAddress, opts...)
 		if err != nil {
-			return fmt.Errorf("while dialing the cluster: %w", err)
+			return fmt.Errorf("while creating client: %w", err)
 		}
 		aaa := apb.NewAAAClient(cc)
 
diff --git a/metropolis/cli/metroctl/cmd_install.go b/metropolis/cli/metroctl/cmd_install.go
index 1f73d76..a34e470 100644
--- a/metropolis/cli/metroctl/cmd_install.go
+++ b/metropolis/cli/metroctl/cmd_install.go
@@ -86,9 +86,9 @@
 			},
 		}
 	} else {
-		cc, err := dialAuthenticated(ctx)
+		cc, err := newAuthenticatedClient(ctx)
 		if err != nil {
-			return nil, fmt.Errorf("while dialing node: %w", err)
+			return nil, fmt.Errorf("while creating client: %w", err)
 		}
 		mgmt := api.NewManagementClient(cc)
 		resT, err := mgmt.GetRegisterTicket(ctx, &api.GetRegisterTicketRequest{})
diff --git a/metropolis/cli/metroctl/cmd_node.go b/metropolis/cli/metroctl/cmd_node.go
index 2e0b537..620a7f5 100644
--- a/metropolis/cli/metroctl/cmd_node.go
+++ b/metropolis/cli/metroctl/cmd_node.go
@@ -35,9 +35,9 @@
 	Example: "metroctl node describe metropolis-c556e31c3fa2bf0a36e9ccb9fd5d6056",
 	RunE: func(cmd *cobra.Command, args []string) error {
 		ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
-		cc, err := dialAuthenticated(ctx)
+		cc, err := newAuthenticatedClient(ctx)
 		if err != nil {
-			return fmt.Errorf("while dialing node: %w", err)
+			return fmt.Errorf("while creating client: %w", err)
 		}
 		mgmt := apb.NewManagementClient(cc)
 
@@ -67,9 +67,9 @@
 	Example: "metroctl node list --filter node.status.external_address==\"10.8.0.2\"",
 	RunE: func(cmd *cobra.Command, args []string) error {
 		ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
-		cc, err := dialAuthenticated(ctx)
+		cc, err := newAuthenticatedClient(ctx)
 		if err != nil {
-			return fmt.Errorf("while dialing node: %w", err)
+			return fmt.Errorf("while creating client: %w", err)
 		}
 		mgmt := apb.NewManagementClient(cc)
 
@@ -130,7 +130,7 @@
 			return fmt.Errorf("could not get CA certificate: %w", err)
 		}
 
-		conn, err := dialAuthenticated(ctx)
+		conn, err := newAuthenticatedClient(ctx)
 		if err != nil {
 			return err
 		}
@@ -182,9 +182,9 @@
 
 			go func(n *apb.Node) {
 				defer wg.Done()
-				cc, err := dialAuthenticatedNode(ctx, n.Id, n.Status.ExternalAddress, cacert)
+				cc, err := newAuthenticatedNodeClient(ctx, n.Id, n.Status.ExternalAddress, cacert)
 				if err != nil {
-					log.Fatalf("failed to dial node: %v", err)
+					log.Fatalf("failed to create node client: %v", err)
 				}
 				nodeMgmt := apb.NewNodeManagementClient(cc)
 				log.Printf("sending update request to: %s (%s)", n.Id, n.Status.ExternalAddress)
@@ -258,7 +258,7 @@
 		}
 
 		ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
-		conn, err := dialAuthenticated(ctx)
+		conn, err := newAuthenticatedClient(ctx)
 		if err != nil {
 			return err
 		}
@@ -304,12 +304,12 @@
 	Args: PrintUsageOnWrongArgs(cobra.ExactArgs(1)),
 }
 
-func dialNode(ctx context.Context, node string) (apb.NodeManagementClient, error) {
+func newNodeClient(ctx context.Context, node string) (apb.NodeManagementClient, error) {
 	// First connect to the main management service and figure out the node's IP
 	// address.
-	cc, err := dialAuthenticated(ctx)
+	cc, err := newAuthenticatedClient(ctx)
 	if err != nil {
-		return nil, fmt.Errorf("while dialing node: %w", err)
+		return nil, fmt.Errorf("while creating client: %w", err)
 	}
 	mgmt := apb.NewManagementClient(cc)
 	nodes, err := core.GetNodes(ctx, mgmt, fmt.Sprintf("node.id == %q", node))
@@ -333,10 +333,10 @@
 		return nil, fmt.Errorf("could not get CA certificate: %w", err)
 	}
 
-	// Dial the actual node at its management port.
-	cl, err := dialAuthenticatedNode(ctx, n.Id, n.Status.ExternalAddress, cacert)
+	// Create a gprc client with the actual node and its management port.
+	cl, err := newAuthenticatedNodeClient(ctx, n.Id, n.Status.ExternalAddress, cacert)
 	if err != nil {
-		return nil, fmt.Errorf("while dialing node: %w", err)
+		return nil, fmt.Errorf("while creating client: %w", err)
 	}
 	nmgmt := apb.NewNodeManagementClient(cl)
 	return nmgmt, nil
@@ -392,9 +392,9 @@
 			req.NextBoot = apb.RebootRequest_NEXT_BOOT_START_ROLLBACK
 		}
 
-		nmgmt, err := dialNode(ctx, args[0])
+		nmgmt, err := newNodeClient(ctx, args[0])
 		if err != nil {
-			return fmt.Errorf("failed to dial node: %w", err)
+			return fmt.Errorf("failed to create node client: %w", err)
 		}
 
 		if _, err := nmgmt.Reboot(ctx, &req); err != nil {
@@ -414,7 +414,7 @@
 	RunE: func(cmd *cobra.Command, args []string) error {
 		ctx := cmd.Context()
 
-		nmgmt, err := dialNode(ctx, args[0])
+		nmgmt, err := newNodeClient(ctx, args[0])
 		if err != nil {
 			return err
 		}
diff --git a/metropolis/cli/metroctl/cmd_node_approve.go b/metropolis/cli/metroctl/cmd_node_approve.go
index 65d2811..c966d6e 100644
--- a/metropolis/cli/metroctl/cmd_node_approve.go
+++ b/metropolis/cli/metroctl/cmd_node_approve.go
@@ -22,9 +22,9 @@
 	Args:  PrintUsageOnWrongArgs(cobra.MaximumNArgs(1)), // One positional argument: node ID
 	RunE: func(cmd *cobra.Command, args []string) error {
 		ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
-		cc, err := dialAuthenticated(ctx)
+		cc, err := newAuthenticatedClient(ctx)
 		if err != nil {
-			return fmt.Errorf("while dialing node: %w", err)
+			return fmt.Errorf("while creating client: %w", err)
 		}
 		mgmt := api.NewManagementClient(cc)
 
diff --git a/metropolis/cli/metroctl/cmd_node_logs.go b/metropolis/cli/metroctl/cmd_node_logs.go
index 5baf2f6..4f5c5e4 100644
--- a/metropolis/cli/metroctl/cmd_node_logs.go
+++ b/metropolis/cli/metroctl/cmd_node_logs.go
@@ -63,9 +63,9 @@
 
 		// First connect to the main management service and figure out the node's IP
 		// address.
-		cc, err := dialAuthenticated(ctx)
+		cc, err := newAuthenticatedClient(ctx)
 		if err != nil {
-			return fmt.Errorf("while dialing node: %w", err)
+			return fmt.Errorf("while creating client: %w", err)
 		}
 		mgmt := api.NewManagementClient(cc)
 		nodes, err := core.GetNodes(ctx, mgmt, fmt.Sprintf("node.id == %q", args[0]))
@@ -90,10 +90,10 @@
 		}
 
 		fmt.Printf("=== Logs from %s (%s):\n", n.Id, n.Status.ExternalAddress)
-		// Dial the actual node at its management port.
-		cl, err := dialAuthenticatedNode(ctx, n.Id, n.Status.ExternalAddress, cacert)
+		// Create a gprc client with the actual node and its management port.
+		cl, err := newAuthenticatedNodeClient(ctx, n.Id, n.Status.ExternalAddress, cacert)
 		if err != nil {
-			return fmt.Errorf("while dialing node: %w", err)
+			return fmt.Errorf("while creating client: %w", err)
 		}
 		nmgmt := api.NewNodeManagementClient(cl)
 
diff --git a/metropolis/cli/metroctl/cmd_node_metrics.go b/metropolis/cli/metroctl/cmd_node_metrics.go
index 6b108ae..648fe60 100644
--- a/metropolis/cli/metroctl/cmd_node_metrics.go
+++ b/metropolis/cli/metroctl/cmd_node_metrics.go
@@ -48,9 +48,9 @@
 
 		// First connect to the main management service and figure out the node's IP
 		// address.
-		cc, err := dialAuthenticated(ctx)
+		cc, err := newAuthenticatedClient(ctx)
 		if err != nil {
-			return fmt.Errorf("while dialing node: %w", err)
+			return fmt.Errorf("while creating client: %w", err)
 		}
 		mgmt := api.NewManagementClient(cc)
 		nodes, err := core.GetNodes(ctx, mgmt, fmt.Sprintf("node.id == %q", args[0]))
diff --git a/metropolis/cli/metroctl/cmd_node_set.go b/metropolis/cli/metroctl/cmd_node_set.go
index 677a425..41e2d08 100644
--- a/metropolis/cli/metroctl/cmd_node_set.go
+++ b/metropolis/cli/metroctl/cmd_node_set.go
@@ -34,9 +34,9 @@
 	Args:    PrintUsageOnWrongArgs(cobra.MinimumNArgs(2)),
 	RunE: func(cmd *cobra.Command, args []string) error {
 		ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
-		cc, err := dialAuthenticated(ctx)
+		cc, err := newAuthenticatedClient(ctx)
 		if err != nil {
-			return fmt.Errorf("while dialing node: %w", err)
+			return fmt.Errorf("while creating client: %w", err)
 		}
 		mgmt := api.NewManagementClient(cc)
 
@@ -78,9 +78,9 @@
 	Args:    PrintUsageOnWrongArgs(cobra.ArbitraryArgs),
 	RunE: func(cmd *cobra.Command, args []string) error {
 		ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
-		cc, err := dialAuthenticated(ctx)
+		cc, err := newAuthenticatedClient(ctx)
 		if err != nil {
-			return fmt.Errorf("while dialing node: %w", err)
+			return fmt.Errorf("while creating client: %w", err)
 		}
 		mgmt := api.NewManagementClient(cc)
 
diff --git a/metropolis/cli/metroctl/core/ca_tofu.go b/metropolis/cli/metroctl/core/ca_tofu.go
index 3f3c135..c19c303 100644
--- a/metropolis/cli/metroctl/core/ca_tofu.go
+++ b/metropolis/cli/metroctl/core/ca_tofu.go
@@ -156,9 +156,9 @@
 		return nil, err
 	}
 	opts = append(opts, grpc.WithTransportCredentials(creds))
-	cc, err := grpc.Dial(resolver.MetropolisControlAddress, opts...)
+	cc, err := grpc.NewClient(resolver.MetropolisControlAddress, opts...)
 	if err != nil {
-		return nil, fmt.Errorf("while dialing cluster to retrieve CA: %w", err)
+		return nil, fmt.Errorf("while creating grpc client: %w", err)
 	}
 	cur := ipb.NewCuratorLocalClient(cc)
 	res, err := cur.GetCACertificate(ctx, &ipb.GetCACertificateRequest{})
diff --git a/metropolis/cli/metroctl/core/rpc.go b/metropolis/cli/metroctl/core/rpc.go
index 8b377f5..a8f933c 100644
--- a/metropolis/cli/metroctl/core/rpc.go
+++ b/metropolis/cli/metroctl/core/rpc.go
@@ -53,7 +53,7 @@
 	return opts, nil
 }
 
-func DialNode(ctx context.Context, opkey ed25519.PrivateKey, ocert, ca *x509.Certificate, proxyAddr, nodeId, nodeAddr string) (*grpc.ClientConn, error) {
+func NewNodeClient(ctx context.Context, opkey ed25519.PrivateKey, ocert, ca *x509.Certificate, proxyAddr, nodeId, nodeAddr string) (*grpc.ClientConn, error) {
 	var dialOpts []grpc.DialOption
 
 	if opkey == nil {
@@ -77,7 +77,7 @@
 	dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds))
 
 	endpoint := net.JoinHostPort(nodeAddr, node.NodeManagementPort.PortString())
-	return grpc.Dial(endpoint, dialOpts...)
+	return grpc.NewClient(endpoint, dialOpts...)
 }
 
 // GetNodes retrieves node records, filtered by the supplied node filter
diff --git a/metropolis/cli/metroctl/rpc.go b/metropolis/cli/metroctl/rpc.go
index ec3a4e4..b3a9819 100644
--- a/metropolis/cli/metroctl/rpc.go
+++ b/metropolis/cli/metroctl/rpc.go
@@ -20,9 +20,9 @@
 	"source.monogon.dev/metropolis/node/core/rpc/resolver"
 )
 
-func dialAuthenticated(ctx context.Context) (*grpc.ClientConn, error) {
-	// Collect credentials, validate command parameters, and try dialing the
-	// cluster.
+func newAuthenticatedClient(ctx context.Context) (*grpc.ClientConn, error) {
+	// Collect credentials, validate command parameters, and create the grpc
+	// client.
 	ocert, opkey, err := core.GetOwnerCredentials(flags.configPath)
 	if errors.Is(err, core.ErrNoCredentials) {
 		return nil, fmt.Errorf("you have to take ownership of the cluster first: %w", err)
@@ -50,23 +50,23 @@
 	}
 	opts = append(opts, grpc.WithTransportCredentials(creds))
 
-	cc, err := grpc.Dial(resolver.MetropolisControlAddress, opts...)
+	cc, err := grpc.NewClient(resolver.MetropolisControlAddress, opts...)
 	if err != nil {
-		return nil, fmt.Errorf("while dialing cluster: %w", err)
+		return nil, fmt.Errorf("while creating client: %w", err)
 	}
 	return cc, nil
 }
 
-func dialAuthenticatedNode(ctx context.Context, id, address string, cacert *x509.Certificate) (*grpc.ClientConn, error) {
-	// Collect credentials, validate command parameters, and try dialing the
-	// cluster.
+func newAuthenticatedNodeClient(ctx context.Context, id, address string, cacert *x509.Certificate) (*grpc.ClientConn, error) {
+	// Collect credentials, validate command parameters, and create the grpc
+	// client.
 	ocert, opkey, err := core.GetOwnerCredentials(flags.configPath)
 	if errors.Is(err, core.ErrNoCredentials) {
 		return nil, fmt.Errorf("you have to take ownership of the cluster first: %w", err)
 	}
-	cc, err := core.DialNode(ctx, opkey, ocert, cacert, flags.proxyAddr, id, address)
+	cc, err := core.NewNodeClient(ctx, opkey, ocert, cacert, flags.proxyAddr, id, address)
 	if err != nil {
-		return nil, fmt.Errorf("while dialing node: %w", err)
+		return nil, fmt.Errorf("while creating client: %w", err)
 	}
 	return cc, nil
 }
diff --git a/metropolis/node/core/cluster/cluster_join.go b/metropolis/node/core/cluster/cluster_join.go
index eb0ff73..1a0a442 100644
--- a/metropolis/node/core/cluster/cluster_join.go
+++ b/metropolis/node/core/cluster/cluster_join.go
@@ -63,9 +63,9 @@
 	if err != nil {
 		return fmt.Errorf("could not create ephemeral credentials: %w", err)
 	}
-	eph, err := grpc.Dial(resolver.MetropolisControlAddress, grpc.WithTransportCredentials(ephCreds), grpc.WithResolvers(r))
+	eph, err := grpc.NewClient(resolver.MetropolisControlAddress, grpc.WithTransportCredentials(ephCreds), grpc.WithResolvers(r))
 	if err != nil {
-		return fmt.Errorf("could not dial cluster with join credentials: %w", err)
+		return fmt.Errorf("could not create client with join credentials: %w", err)
 	}
 	cur := ipb.NewCuratorClient(eph)
 
diff --git a/metropolis/node/core/cluster/cluster_register.go b/metropolis/node/core/cluster/cluster_register.go
index a6089cb..61cd22a 100644
--- a/metropolis/node/core/cluster/cluster_register.go
+++ b/metropolis/node/core/cluster/cluster_register.go
@@ -35,7 +35,7 @@
 // test multiple nodes in a cluster. It does notably not run either
 // etcd/consensus or the curator, which also prevents Kubernetes from running.
 func (m *Manager) register(ctx context.Context, register *apb.NodeParameters_ClusterRegister) error {
-	//// Do a validation pass on the provided NodeParameters.Register data, fail early
+	// Do a validation pass on the provided NodeParameters.Register data, fail early
 	// if it looks invalid.
 	ca, err := x509.ParseCertificate(register.CaCertificate)
 	if err != nil {
@@ -90,9 +90,9 @@
 	if err != nil {
 		return fmt.Errorf("could not create ephemeral credentials: %w", err)
 	}
-	eph, err := grpc.Dial(resolver.MetropolisControlAddress, grpc.WithTransportCredentials(ephCreds), grpc.WithResolvers(r))
+	eph, err := grpc.NewClient(resolver.MetropolisControlAddress, grpc.WithTransportCredentials(ephCreds), grpc.WithResolvers(r))
 	if err != nil {
-		return fmt.Errorf("could not dial cluster with ephemeral credentials: %w", err)
+		return fmt.Errorf("could not create client with ephemeral credentials: %w", err)
 	}
 	cur := ipb.NewCuratorClient(eph)
 
diff --git a/metropolis/node/core/curator/impl_leader_test.go b/metropolis/node/core/curator/impl_leader_test.go
index 72c8658..e593bbd 100644
--- a/metropolis/node/core/curator/impl_leader_test.go
+++ b/metropolis/node/core/curator/impl_leader_test.go
@@ -243,17 +243,17 @@
 	// Create an authenticated manager gRPC client.
 	creds := rpc.NewAuthenticatedCredentials(ownerCreds, rpc.WantRemoteCluster(ca))
 	gcreds := grpc.WithTransportCredentials(creds)
-	mcl, err := grpc.Dial("local", withLocalDialer, gcreds)
+	mcl, err := grpc.NewClient("passthrough:///local", withLocalDialer, gcreds)
 	if err != nil {
-		t.Fatalf("Dialing external GRPC failed: %v", err)
+		t.Fatalf("Creating external GRPC client failed: %v", err)
 	}
 
 	// Create a node gRPC client for the local node.
 	creds = rpc.NewAuthenticatedCredentials(nodeCredentials.TLSCredentials(), rpc.WantRemoteCluster(ca))
 	gcreds = grpc.WithTransportCredentials(creds)
-	lcl, err := grpc.Dial("local", withLocalDialer, gcreds)
+	lcl, err := grpc.NewClient("passthrough:///local", withLocalDialer, gcreds)
 	if err != nil {
-		t.Fatalf("Dialing external GRPC failed: %v", err)
+		t.Fatalf("Creating external GRPC client failed: %v", err)
 	}
 
 	// Create an ephemeral node gRPC client for the 'other node'.
@@ -261,9 +261,9 @@
 	if err != nil {
 		t.Fatalf("NewEphemeralCredentials: %v", err)
 	}
-	ocl, err := grpc.Dial("local", withLocalDialer, grpc.WithTransportCredentials(otherEphCreds))
+	ocl, err := grpc.NewClient("passthrough:///local", withLocalDialer, grpc.WithTransportCredentials(otherEphCreds))
 	if err != nil {
-		t.Fatalf("Dialing external GRPC failed: %v", err)
+		t.Fatalf("Creating external GRPC client failed: %v", err)
 	}
 
 	// Close the clients on context cancel.
@@ -858,9 +858,9 @@
 	if err != nil {
 		t.Fatalf("NewEphemeralCredentials: %v", err)
 	}
-	eph, err := grpc.Dial("local", withLocalDialer, grpc.WithTransportCredentials(ephCreds))
+	eph, err := grpc.NewClient("passthrough:///local", withLocalDialer, grpc.WithTransportCredentials(ephCreds))
 	if err != nil {
-		t.Fatalf("Dialing external GRPC failed: %v", err)
+		t.Fatalf("Creating external GRPC client failed: %v", err)
 	}
 	cur := ipb.NewCuratorClient(eph)
 	jr, err := cur.JoinNode(ctx, &ipb.JoinNodeRequest{
@@ -1794,9 +1794,9 @@
 			withLocalDialer := grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) {
 				return cl.curatorLis.Dial()
 			})
-			eph, err := grpc.Dial("local", withLocalDialer, grpc.WithTransportCredentials(ephCreds))
+			eph, err := grpc.NewClient("passthrough:///local", withLocalDialer, grpc.WithTransportCredentials(ephCreds))
 			if err != nil {
-				t.Fatalf("Dialing external GRPC failed: %v", err)
+				t.Fatalf("Creating external GRPC client failed: %v", err)
 			}
 			t.Cleanup(func() {
 				eph.Close()
diff --git a/metropolis/node/core/mgmt/svc_logs_test.go b/metropolis/node/core/mgmt/svc_logs_test.go
index f650b4e..ee2e03d 100644
--- a/metropolis/node/core/mgmt/svc_logs_test.go
+++ b/metropolis/node/core/mgmt/svc_logs_test.go
@@ -46,9 +46,9 @@
 	withLocalDialer := grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) {
 		return externalLis.Dial()
 	})
-	cl, err := grpc.Dial("local", withLocalDialer, grpc.WithTransportCredentials(insecure.NewCredentials()))
+	cl, err := grpc.NewClient("passthrough:///local", withLocalDialer, grpc.WithTransportCredentials(insecure.NewCredentials()))
 	if err != nil {
-		t.Fatalf("Dialing GRPC failed: %v", err)
+		t.Fatalf("Creating GRPC client failed: %v", err)
 	}
 
 	return s, cl
diff --git a/metropolis/node/core/roleserve/values.go b/metropolis/node/core/roleserve/values.go
index fda776c..3708e45 100644
--- a/metropolis/node/core/roleserve/values.go
+++ b/metropolis/node/core/roleserve/values.go
@@ -53,9 +53,9 @@
 
 func newCuratorConnection(creds *identity.NodeCredentials, res *resolver.Resolver) *CuratorConnection {
 	c := rpc.NewAuthenticatedCredentials(creds.TLSCredentials(), rpc.WantRemoteCluster(creds.ClusterCA()))
-	conn, err := grpc.Dial(resolver.MetropolisControlAddress, grpc.WithTransportCredentials(c), grpc.WithResolvers(res))
+	conn, err := grpc.NewClient(resolver.MetropolisControlAddress, grpc.WithTransportCredentials(c), grpc.WithResolvers(res))
 	if err != nil {
-		// TOOD(q3k): triple check that Dial will not fail
+		// TODO: triple check that NewClient will not fail
 		panic(err)
 	}
 	return &CuratorConnection{
diff --git a/metropolis/node/core/roleserve/worker_statuspush_test.go b/metropolis/node/core/roleserve/worker_statuspush_test.go
index 54683e7..78c5f9c 100644
--- a/metropolis/node/core/roleserve/worker_statuspush_test.go
+++ b/metropolis/node/core/roleserve/worker_statuspush_test.go
@@ -114,9 +114,9 @@
 	withLocalDialer := grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) {
 		return lis.Dial()
 	})
-	cl, err := grpc.Dial("local", withLocalDialer, grpc.WithTransportCredentials(insecure.NewCredentials()))
+	cl, err := grpc.NewClient("passthrough:///local", withLocalDialer, grpc.WithTransportCredentials(insecure.NewCredentials()))
 	if err != nil {
-		t.Fatalf("Dial failed: %v", err)
+		t.Fatalf("NewClient failed: %v", err)
 	}
 	defer cl.Close()
 
diff --git a/metropolis/node/core/rpc/resolver/resolver.go b/metropolis/node/core/rpc/resolver/resolver.go
index 51b9cdf..c865130 100644
--- a/metropolis/node/core/rpc/resolver/resolver.go
+++ b/metropolis/node/core/rpc/resolver/resolver.go
@@ -222,10 +222,10 @@
 		Time:    10 * time.Second,
 		Timeout: 5 * time.Second,
 	}))
-	cl, err := grpc.Dial(MetropolisControlAddress, opts...)
+	cl, err := grpc.NewClient(MetropolisControlAddress, opts...)
 	if err != nil {
 		// This generally shouldn't happen.
-		return fmt.Errorf("could not dial gRPC: %w", err)
+		return fmt.Errorf("could not create gRPC client: %w", err)
 	}
 	defer cl.Close()
 
@@ -342,9 +342,9 @@
 		Timeout:             5 * time.Second,
 		PermitWithoutStream: true,
 	}))
-	cl, err := grpc.Dial(via, opts...)
+	cl, err := grpc.NewClient(via, opts...)
 	if err != nil {
-		r.logger.Infof("WATCHLEADER: dialing %s failed: %v", via, err)
+		r.logger.Infof("WATCHLEADER: creating client %s failed: %v", via, err)
 		return false
 	}
 	defer cl.Close()
diff --git a/metropolis/node/core/rpc/resolver/resolver_test.go b/metropolis/node/core/rpc/resolver/resolver_test.go
index 72da280..f9125c9 100644
--- a/metropolis/node/core/rpc/resolver/resolver_test.go
+++ b/metropolis/node/core/rpc/resolver/resolver_test.go
@@ -176,9 +176,10 @@
 		Certificates:       []tls.Certificate{eph.Manager},
 		InsecureSkipVerify: true,
 	})
-	cl, err := grpc.Dial("metropolis:///control", grpc.WithTransportCredentials(creds), grpc.WithResolvers(r))
+
+	cl, err := grpc.NewClient(MetropolisControlAddress, grpc.WithTransportCredentials(creds), grpc.WithResolvers(r))
 	if err != nil {
-		t.Fatalf("Could not dial: %v", err)
+		t.Fatalf("Could not create client: %v", err)
 	}
 
 	// Test logic follows.
diff --git a/metropolis/node/core/rpc/server_authentication_test.go b/metropolis/node/core/rpc/server_authentication_test.go
index a30ee86..90f638a 100644
--- a/metropolis/node/core/rpc/server_authentication_test.go
+++ b/metropolis/node/core/rpc/server_authentication_test.go
@@ -66,11 +66,11 @@
 	})
 
 	// Authenticate as manager externally, ensure that GetRegisterTicket runs.
-	cl, err := grpc.Dial("local",
+	cl, err := grpc.NewClient("passthrough:///local",
 		grpc.WithTransportCredentials(NewAuthenticatedCredentials(eph.Manager, WantRemoteCluster(eph.CA))),
 		withLocalDialer)
 	if err != nil {
-		t.Fatalf("Dial: %v", err)
+		t.Fatalf("NewClient: %v", err)
 	}
 	defer cl.Close()
 	mgmt := apb.NewManagementClient(cl)
@@ -81,11 +81,11 @@
 
 	// Authenticate as node externally, ensure that GetRegisterTicket is refused
 	// (this is because nodes miss the GET_REGISTER_TICKET permissions).
-	cl, err = grpc.Dial("local",
+	cl, err = grpc.NewClient("passthrough:///local",
 		grpc.WithTransportCredentials(NewAuthenticatedCredentials(eph.Nodes[0].TLSCredentials(), WantRemoteCluster(eph.CA))),
 		withLocalDialer)
 	if err != nil {
-		t.Fatalf("Dial: %v", err)
+		t.Fatalf("NewClient: %v", err)
 	}
 	defer cl.Close()
 	mgmt = apb.NewManagementClient(cl)
@@ -114,9 +114,9 @@
 	if err != nil {
 		t.Fatalf("NewEphemeralCredentials: %v", err)
 	}
-	cl, err = grpc.Dial("local", grpc.WithTransportCredentials(ephCreds), withLocalDialer)
+	cl, err = grpc.NewClient("passthrough:///local", grpc.WithTransportCredentials(ephCreds), withLocalDialer)
 	if err != nil {
-		t.Fatalf("Dial: %v", err)
+		t.Fatalf("NewClient: %v", err)
 	}
 	defer cl.Close()
 	mgmt = apb.NewManagementClient(cl)
diff --git a/metropolis/test/e2e/suites/core/run_test.go b/metropolis/test/e2e/suites/core/run_test.go
index 692969d..dca9b7d 100644
--- a/metropolis/test/e2e/suites/core/run_test.go
+++ b/metropolis/test/e2e/suites/core/run_test.go
@@ -104,10 +104,10 @@
 
 	// Dial first node's curator.
 	creds := rpc.NewAuthenticatedCredentials(cluster.Owner, rpc.WantInsecure())
-	remote := net.JoinHostPort(cluster.NodeIDs[0], common.CuratorServicePort.PortString())
-	cl, err := grpc.Dial(remote, grpc.WithContextDialer(cluster.DialNode), grpc.WithTransportCredentials(creds))
+	remote := net.JoinHostPort(cluster.Nodes[cluster.NodeIDs[0]].ManagementAddress, common.CuratorServicePort.PortString())
+	cl, err := grpc.NewClient(remote, grpc.WithContextDialer(cluster.DialNode), grpc.WithTransportCredentials(creds))
 	if err != nil {
-		t.Fatalf("failed to dial first node's curator: %v", err)
+		t.Fatalf("failed to create first node's curator client: %v", err)
 	}
 	defer cl.Close()
 	mgmt := apb.NewManagementClient(cl)
diff --git a/metropolis/test/launch/cluster.go b/metropolis/test/launch/cluster.go
index dab453b..a0075ed 100644
--- a/metropolis/test/launch/cluster.go
+++ b/metropolis/test/launch/cluster.go
@@ -206,20 +206,16 @@
 		r := resolver.New(c.ctxT, resolver.WithLogger(logging.NewFunctionBackend(func(severity logging.Severity, msg string) {
 			launch.Log("Cluster: client resolver: %s: %s", severity, msg)
 		})))
-		for _, n := range c.NodeIDs {
-			ep, err := resolver.NodeWithDefaultPort(n)
-			if err != nil {
-				return nil, fmt.Errorf("could not add node %q by DNS: %w", n, err)
-			}
-			r.AddEndpoint(ep)
+		for _, n := range c.Nodes {
+			r.AddEndpoint(resolver.NodeAtAddressWithDefaultPort(n.ManagementAddress))
 		}
-		authClient, err := grpc.Dial(resolver.MetropolisControlAddress,
+		authClient, err := grpc.NewClient(resolver.MetropolisControlAddress,
 			grpc.WithTransportCredentials(authCreds),
 			grpc.WithResolvers(r),
 			grpc.WithContextDialer(c.DialNode),
 		)
 		if err != nil {
-			return nil, fmt.Errorf("dialing with owner credentials failed: %w", err)
+			return nil, fmt.Errorf("creating client with owner credentials failed: %w", err)
 		}
 		c.authClient = authClient
 	}
@@ -651,12 +647,13 @@
 
 // NodeInCluster represents information about a node that's part of a Cluster.
 type NodeInCluster struct {
-	// ID of the node, which can be used to dial this node's services via DialNode.
+	// ID of the node, which can be used to dial this node's services via
+	// NewNodeClient.
 	ID     string
 	Pubkey []byte
-	// Address of the node on the network ran by nanoswitch. Not reachable from the
-	// host unless dialed via DialNode or via the nanoswitch SOCKS proxy (reachable
-	// on Cluster.Ports[SOCKSPort]).
+	// Address of the node on the network ran by nanoswitch. Not reachable from
+	// the host unless dialed via NewNodeClient or via the nanoswitch SOCKS
+	// proxy (reachable on Cluster.Ports[SOCKSPort]).
 	ManagementAddress string
 }
 
@@ -677,9 +674,9 @@
 	initDialer := func(_ context.Context, addr string) (net.Conn, error) {
 		return socksDialer.Dial("tcp", addr)
 	}
-	initClient, err := grpc.Dial(remote, grpc.WithContextDialer(initDialer), grpc.WithTransportCredentials(initCreds))
+	initClient, err := grpc.NewClient(remote, grpc.WithContextDialer(initDialer), grpc.WithTransportCredentials(initCreds))
 	if err != nil {
-		return nil, nil, fmt.Errorf("dialing with ephemeral credentials failed: %w", err)
+		return nil, nil, fmt.Errorf("creating client with ephemeral credentials failed: %w", err)
 	}
 	defer initClient.Close()
 
@@ -705,9 +702,9 @@
 
 	// Now connect authenticated and get the node ID.
 	creds := rpc.NewAuthenticatedCredentials(*cert, rpc.WantInsecure())
-	authClient, err := grpc.Dial(remote, grpc.WithContextDialer(initDialer), grpc.WithTransportCredentials(creds))
+	authClient, err := grpc.NewClient(remote, grpc.WithContextDialer(initDialer), grpc.WithTransportCredentials(creds))
 	if err != nil {
-		return nil, nil, fmt.Errorf("dialing with owner credentials failed: %w", err)
+		return nil, nil, fmt.Errorf("creating client with owner credentials failed: %w", err)
 	}
 	defer authClient.Close()
 	mgmt := apb.NewManagementClient(authClient)
@@ -1258,7 +1255,7 @@
 //
 // For example:
 //
-//	grpc.Dial("metropolis-deadbeef:1234", grpc.WithContextDialer(c.DialNode))
+//	grpc.NewClient("passthrough:///metropolis-deadbeef:1234", grpc.WithContextDialer(c.DialNode))
 func (c *Cluster) DialNode(_ context.Context, addr string) (net.Conn, error) {
 	host, port, err := net.SplitHostPort(addr)
 	if err != nil {
diff --git a/metropolis/test/util/curator.go b/metropolis/test/util/curator.go
index 35c0b97..a4b76ff 100644
--- a/metropolis/test/util/curator.go
+++ b/metropolis/test/util/curator.go
@@ -105,9 +105,9 @@
 	withLocalDialer := grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) {
 		return externalLis.Dial()
 	})
-	cl, err := grpc.Dial("local", withLocalDialer, grpc.WithTransportCredentials(insecure.NewCredentials()))
+	cl, err := grpc.NewClient("passthrough:///local", withLocalDialer, grpc.WithTransportCredentials(insecure.NewCredentials()))
 	if err != nil {
-		t.Fatalf("Dialing GRPC failed: %v", err)
+		t.Fatalf("Creating GRPC Client failed: %v", err)
 	}
 
 	return cur, cl