Repository: grpc/grpc-go

Issue:
xds: change `ServerConfig` type to have a single method to return all configured dial options 
The `ServerConfig` type in internal `bootstrap` package currently stores the credentials configured for the server config as a dial option here: https://github.com/grpc/grpc-go/blob/master/internal/xds/bootstrap/bootstrap.go#L177-L181 and it has a method to return the configured credentials as dial option here: https://github.com/grpc/grpc-go/blob/8ea3460acace12cd1f2f0502784d42c21eeadf42/internal/xds/bootstrap/bootstrap.go#L222

As part of https://github.com/grpc/grpc-go/pull/7586, we are adding more dial options to the server config, and the PR currently adds a new method to return the new dial option. Once that PR is merged, we should instead have a single method in the `ServerConfig` type that returns a slice of dial options. 

These dial options are usually retrieved from the `ServerConfig` when a transport to the corresponding server is being created. This is currently where it is being used: https://github.com/grpc/grpc-go/blob/8ea3460acace12cd1f2f0502784d42c21eeadf42/xds/internal/xdsclient/transport/transport.go#L197 The caller here does not care what the dial options are, and instead of making multiple calls to retrieve all the configured dial options, it would be easier/nicer to make a single call on the `ServerConfig` that returns all configured dial options.

Discussion:

Solution patch:
diff --git a/internal/xds/bootstrap/bootstrap.go b/internal/xds/bootstrap/bootstrap.go
index c725bc1e..35aeea70 100644
--- a/internal/xds/bootstrap/bootstrap.go
+++ b/internal/xds/bootstrap/bootstrap.go
@@ -220,20 +220,14 @@ func (sc *ServerConfig) ServerFeaturesIgnoreResourceDeletion() bool {
 	return false
 }
 
-// CredsDialOption returns the first supported transport credentials from the
-// configuration, as a dial option.
-func (sc *ServerConfig) CredsDialOption() grpc.DialOption {
-	return sc.credsDialOption
-}
-
-// DialerOption returns the Dialer function that specifies how to dial the xDS
-// server determined by the first supported credentials from the configuration,
-// as a dial option.
-//
-// TODO(https://github.com/grpc/grpc-go/issues/7661): change ServerConfig type
-// to have a single method that returns all configured dial options.
-func (sc *ServerConfig) DialerOption() grpc.DialOption {
-	return sc.dialerOption
+// DialOptions returns a slice of all the configured dial options for this
+// server.
+func (sc *ServerConfig) DialOptions() []grpc.DialOption {
+	dopts := []grpc.DialOption{sc.credsDialOption}
+	if sc.dialerOption != nil {
+		dopts = append(dopts, sc.dialerOption)
+	}
+	return dopts
 }
 
 // Cleanups returns a collection of functions to be called when the xDS client
diff --git a/xds/internal/xdsclient/transport/transport.go b/xds/internal/xdsclient/transport/transport.go
index 134a9519..59b22172 100644
--- a/xds/internal/xdsclient/transport/transport.go
+++ b/xds/internal/xdsclient/transport/transport.go
@@ -192,19 +192,14 @@ func New(opts Options) (*Transport, error) {
 		return nil, errors.New("missing OnSend callback handler when creating a new transport")
 	}
 
-	// Dial the xDS management with the passed in credentials.
-	dopts := []grpc.DialOption{
-		opts.ServerCfg.CredsDialOption(),
-		grpc.WithKeepaliveParams(keepalive.ClientParameters{
-			// We decided to use these sane defaults in all languages, and
-			// kicked the can down the road as far making these configurable.
-			Time:    5 * time.Minute,
-			Timeout: 20 * time.Second,
-		}),
-	}
-	if dialerOpts := opts.ServerCfg.DialerOption(); dialerOpts != nil {
-		dopts = append(dopts, dialerOpts)
-	}
+	// Dial the xDS management server with dial options specified by the server
+	// configuration and a static keepalive configuration that is common across
+	// gRPC language implementations.
+	kpCfg := grpc.WithKeepaliveParams(keepalive.ClientParameters{
+		Time:    5 * time.Minute,
+		Timeout: 20 * time.Second,
+	})
+	dopts := append([]grpc.DialOption{kpCfg}, opts.ServerCfg.DialOptions()...)
 	grpcNewClient := transportinternal.GRPCNewClient.(func(string, ...grpc.DialOption) (*grpc.ClientConn, error))
 	cc, err := grpcNewClient(opts.ServerCfg.ServerURI(), dopts...)
 	if err != nil {



Test patch:
diff --git a/xds/internal/xdsclient/transport/transport_test.go b/xds/internal/xdsclient/transport/transport_test.go
index 7aac0ccd..b51f58b7 100644
--- a/xds/internal/xdsclient/transport/transport_test.go
+++ b/xds/internal/xdsclient/transport/transport_test.go
@@ -18,17 +18,11 @@
 package transport_test
 
 import (
-	"context"
-	"encoding/json"
-	"net"
 	"testing"
 
 	"google.golang.org/grpc"
-	"google.golang.org/grpc/credentials"
-	"google.golang.org/grpc/credentials/insecure"
 	"google.golang.org/grpc/internal/grpctest"
 	internalbootstrap "google.golang.org/grpc/internal/xds/bootstrap"
-	"google.golang.org/grpc/xds/bootstrap"
 	"google.golang.org/grpc/xds/internal/xdsclient/transport"
 	"google.golang.org/grpc/xds/internal/xdsclient/transport/internal"
 
@@ -102,80 +96,3 @@ func (s) TestNewWithGRPCDial(t *testing.T) {
 		t.Fatalf("transport.New(%+v) custom dialer called = true, want false", opts)
 	}
 }
-
-const testDialerCredsBuilderName = "test_dialer_creds"
-
-// testDialerCredsBuilder implements the `Credentials` interface defined in
-// package `xds/bootstrap` and encapsulates an insecure credential with a
-// custom Dialer that specifies how to dial the xDS server.
-type testDialerCredsBuilder struct{}
-
-func (t *testDialerCredsBuilder) Build(json.RawMessage) (credentials.Bundle, func(), error) {
-	return &testDialerCredsBundle{insecure.NewBundle()}, func() {}, nil
-}
-
-func (t *testDialerCredsBuilder) Name() string {
-	return testDialerCredsBuilderName
-}
-
-// testDialerCredsBundle implements the `Bundle` interface defined in package
-// `credentials` and encapsulates an insecure credential with a custom Dialer
-// that specifies how to dial the xDS server.
-type testDialerCredsBundle struct {
-	credentials.Bundle
-}
-
-func (t *testDialerCredsBundle) Dialer(context.Context, string) (net.Conn, error) {
-	return nil, nil
-}
-
-func (s) TestNewWithDialerFromCredentialsBundle(t *testing.T) {
-	// Override grpc.NewClient with a custom one.
-	doptsLen := 0
-	customGRPCNewClient := func(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
-		doptsLen = len(opts)
-		return grpc.NewClient(target, opts...)
-	}
-	oldGRPCNewClient := internal.GRPCNewClient
-	internal.GRPCNewClient = customGRPCNewClient
-	defer func() { internal.GRPCNewClient = oldGRPCNewClient }()
-
-	bootstrap.RegisterCredentials(&testDialerCredsBuilder{})
-	serverCfg, err := internalbootstrap.ServerConfigForTesting(internalbootstrap.ServerConfigTestingOptions{
-		URI:          "trafficdirector.googleapis.com:443",
-		ChannelCreds: []internalbootstrap.ChannelCreds{{Type: testDialerCredsBuilderName}},
-	})
-	if err != nil {
-		t.Fatalf("Failed to create server config for testing: %v", err)
-	}
-	if serverCfg.DialerOption() == nil {
-		t.Fatalf("Dialer for xDS transport in server config for testing is nil, want non-nil")
-	}
-	// Create a new transport.
-	opts := transport.Options{
-		ServerCfg: serverCfg,
-		NodeProto: &v3corepb.Node{},
-		OnRecvHandler: func(update transport.ResourceUpdate, onDone func()) error {
-			onDone()
-			return nil
-		},
-		OnErrorHandler: func(error) {},
-		OnSendHandler:  func(*transport.ResourceSendInfo) {},
-	}
-	c, err := transport.New(opts)
-	defer func() {
-		if c != nil {
-			c.Close()
-		}
-	}()
-	if err != nil {
-		t.Fatalf("transport.New(%v) failed: %v", opts, err)
-	}
-	// Verify there are three dial options passed to the custom grpc.NewClient.
-	// The first is opts.ServerCfg.CredsDialOption(), the second is
-	// grpc.WithKeepaliveParams(), and the third is opts.ServerCfg.DialerOption()
-	// from the credentials bundle.
-	if doptsLen != 3 {
-		t.Fatalf("transport.New(%v) custom grpc.NewClient called with %d dial options, want 3", opts, doptsLen)
-	}
-}