mirror of https://github.com/grpc/grpc-go.git
				
				
				
			xds: Add v3 support for client bootstrap. (#3723)
This commit is contained in:
		
							parent
							
								
									abfbf74f21
								
							
						
					
					
						commit
						d8193ee9cc
					
				|  | @ -25,7 +25,6 @@ import ( | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" |  | ||||||
| 	"github.com/golang/protobuf/jsonpb" | 	"github.com/golang/protobuf/jsonpb" | ||||||
| 	wrapperspb "github.com/golang/protobuf/ptypes/wrappers" | 	wrapperspb "github.com/golang/protobuf/ptypes/wrappers" | ||||||
| 	"github.com/google/go-cmp/cmp" | 	"github.com/google/go-cmp/cmp" | ||||||
|  | @ -51,7 +50,7 @@ func init() { | ||||||
| 		return &bootstrap.Config{ | 		return &bootstrap.Config{ | ||||||
| 			BalancerName: testBalancerNameFooBar, | 			BalancerName: testBalancerNameFooBar, | ||||||
| 			Creds:        grpc.WithInsecure(), | 			Creds:        grpc.WithInsecure(), | ||||||
| 			NodeProto:    &corepb.Node{}, | 			NodeProto:    testutils.EmptyNodeProtoV2, | ||||||
| 		}, nil | 		}, nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,7 +24,6 @@ import ( | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2" | 	xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2" | ||||||
| 	corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" |  | ||||||
| 	"github.com/golang/protobuf/proto" | 	"github.com/golang/protobuf/proto" | ||||||
| 	"github.com/google/go-cmp/cmp" | 	"github.com/google/go-cmp/cmp" | ||||||
| 	"google.golang.org/grpc" | 	"google.golang.org/grpc" | ||||||
|  | @ -101,7 +100,7 @@ func (s) TestClientWrapperWatchEDS(t *testing.T) { | ||||||
| 				return &bootstrap.Config{ | 				return &bootstrap.Config{ | ||||||
| 					BalancerName: fakeServer.Address, | 					BalancerName: fakeServer.Address, | ||||||
| 					Creds:        grpc.WithInsecure(), | 					Creds:        grpc.WithInsecure(), | ||||||
| 					NodeProto:    &corepb.Node{}, | 					NodeProto:    testutils.EmptyNodeProtoV2, | ||||||
| 				}, nil | 				}, nil | ||||||
| 			} | 			} | ||||||
| 			defer func() { bootstrapConfigNew = oldBootstrapConfigNew }() | 			defer func() { bootstrapConfigNew = oldBootstrapConfigNew }() | ||||||
|  | @ -138,7 +137,7 @@ func (s) TestClientWrapperWatchEDS(t *testing.T) { | ||||||
| 			wantReq := &xdspb.DiscoveryRequest{ | 			wantReq := &xdspb.DiscoveryRequest{ | ||||||
| 				TypeUrl:       edsType, | 				TypeUrl:       edsType, | ||||||
| 				ResourceNames: []string{test.wantResourceName}, | 				ResourceNames: []string{test.wantResourceName}, | ||||||
| 				Node:          &corepb.Node{}, | 				Node:          testutils.EmptyNodeProtoV2, | ||||||
| 			} | 			} | ||||||
| 			if !proto.Equal(edsReq.Req, wantReq) { | 			if !proto.Equal(edsReq.Req, wantReq) { | ||||||
| 				t.Fatalf("got EDS request %v, expected: %v, diff: %s", edsReq.Req, wantReq, cmp.Diff(edsReq.Req, wantReq, cmp.Comparer(proto.Equal))) | 				t.Fatalf("got EDS request %v, expected: %v, diff: %s", edsReq.Req, wantReq, cmp.Diff(edsReq.Req, wantReq, cmp.Comparer(proto.Equal))) | ||||||
|  |  | ||||||
|  | @ -27,15 +27,25 @@ import ( | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"os" | 	"os" | ||||||
| 
 | 
 | ||||||
| 	corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" | 	v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" | ||||||
|  | 	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" | ||||||
| 	"github.com/golang/protobuf/jsonpb" | 	"github.com/golang/protobuf/jsonpb" | ||||||
|  | 	"github.com/golang/protobuf/proto" | ||||||
| 	"google.golang.org/grpc" | 	"google.golang.org/grpc" | ||||||
| 	"google.golang.org/grpc/credentials/google" | 	"google.golang.org/grpc/credentials/google" | ||||||
|  | 	"google.golang.org/grpc/xds/internal/version" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	// Environment variable which holds the name of the xDS bootstrap file.
 | 	// Environment variable which holds the name of the xDS bootstrap file.
 | ||||||
| 	fileEnv = "GRPC_XDS_BOOTSTRAP" | 	bootstrapFileEnv = "GRPC_XDS_BOOTSTRAP" | ||||||
|  | 	// Environment variable which controls the use of xDS v3 API.
 | ||||||
|  | 	v3SupportEnv = "GRPC_XDS_EXPERIMENTAL_V3_SUPPORT" | ||||||
|  | 	// The "server_features" field in the bootstrap file contains a list of
 | ||||||
|  | 	// features supported by the server. A value of "xds_v3" indicates that the
 | ||||||
|  | 	// server supports the v3 version of the xDS transport protocol.
 | ||||||
|  | 	serverFeaturesV3 = "xds_v3" | ||||||
|  | 
 | ||||||
| 	// Type name for Google default credentials.
 | 	// Type name for Google default credentials.
 | ||||||
| 	googleDefaultCreds              = "google_default" | 	googleDefaultCreds              = "google_default" | ||||||
| 	gRPCUserAgentName               = "gRPC Go" | 	gRPCUserAgentName               = "gRPC Go" | ||||||
|  | @ -45,7 +55,7 @@ const ( | ||||||
| var gRPCVersion = fmt.Sprintf("%s %s", gRPCUserAgentName, grpc.Version) | var gRPCVersion = fmt.Sprintf("%s %s", gRPCUserAgentName, grpc.Version) | ||||||
| 
 | 
 | ||||||
| // For overriding in unit tests.
 | // For overriding in unit tests.
 | ||||||
| var fileReadFunc = ioutil.ReadFile | var bootstrapFileReadFunc = ioutil.ReadFile | ||||||
| 
 | 
 | ||||||
| // Config provides the xDS client with several key bits of information that it
 | // Config provides the xDS client with several key bits of information that it
 | ||||||
| // requires in its interaction with an xDS server. The Config is initialized
 | // requires in its interaction with an xDS server. The Config is initialized
 | ||||||
|  | @ -59,8 +69,13 @@ type Config struct { | ||||||
| 	// Creds contains the credentials to be used while talking to the xDS
 | 	// Creds contains the credentials to be used while talking to the xDS
 | ||||||
| 	// server, as a grpc.DialOption.
 | 	// server, as a grpc.DialOption.
 | ||||||
| 	Creds grpc.DialOption | 	Creds grpc.DialOption | ||||||
| 	// NodeProto contains the node proto to be used in xDS requests.
 | 	// TransportAPI indicates the API version of xDS transport protocol to use.
 | ||||||
| 	NodeProto *corepb.Node | 	// This describes the xDS gRPC endpoint and version of
 | ||||||
|  | 	// DiscoveryRequest/Response used on the wire.
 | ||||||
|  | 	TransportAPI version.TransportAPI | ||||||
|  | 	// NodeProto contains the Node proto to be used in xDS requests. The actual
 | ||||||
|  | 	// type depends on the transport protocol version used.
 | ||||||
|  | 	NodeProto proto.Message | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type channelCreds struct { | type channelCreds struct { | ||||||
|  | @ -85,9 +100,10 @@ type xdsServer struct { | ||||||
| //          "type": <string containing channel cred type>,
 | //          "type": <string containing channel cred type>,
 | ||||||
| //          "config": <JSON object containing config for the type>
 | //          "config": <JSON object containing config for the type>
 | ||||||
| //        }
 | //        }
 | ||||||
| //      ]
 | //      ],
 | ||||||
|  | //      "server_features": [ ... ]
 | ||||||
| //    },
 | //    },
 | ||||||
| //    "node": <JSON form of corepb.Node proto>
 | //    "node": <JSON form of Node proto>
 | ||||||
| // }
 | // }
 | ||||||
| //
 | //
 | ||||||
| // Currently, we support exactly one type of credential, which is
 | // Currently, we support exactly one type of credential, which is
 | ||||||
|  | @ -101,13 +117,13 @@ type xdsServer struct { | ||||||
| func NewConfig() (*Config, error) { | func NewConfig() (*Config, error) { | ||||||
| 	config := &Config{} | 	config := &Config{} | ||||||
| 
 | 
 | ||||||
| 	fName, ok := os.LookupEnv(fileEnv) | 	fName, ok := os.LookupEnv(bootstrapFileEnv) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return nil, fmt.Errorf("xds: Environment variable %v not defined", fileEnv) | 		return nil, fmt.Errorf("xds: Environment variable %v not defined", bootstrapFileEnv) | ||||||
| 	} | 	} | ||||||
| 	logger.Infof("Got bootstrap file location from %v environment variable: %v", fileEnv, fName) | 	logger.Infof("Got bootstrap file location from %v environment variable: %v", bootstrapFileEnv, fName) | ||||||
| 
 | 
 | ||||||
| 	data, err := fileReadFunc(fName) | 	data, err := bootstrapFileReadFunc(fName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("xds: Failed to read bootstrap file %s with error %v", fName, err) | 		return nil, fmt.Errorf("xds: Failed to read bootstrap file %s with error %v", fName, err) | ||||||
| 	} | 	} | ||||||
|  | @ -118,11 +134,18 @@ func NewConfig() (*Config, error) { | ||||||
| 		return nil, fmt.Errorf("xds: Failed to parse file %s (content %v) with error: %v", fName, string(data), err) | 		return nil, fmt.Errorf("xds: Failed to parse file %s (content %v) with error: %v", fName, string(data), err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	serverSupportsV3 := false | ||||||
| 	m := jsonpb.Unmarshaler{AllowUnknownFields: true} | 	m := jsonpb.Unmarshaler{AllowUnknownFields: true} | ||||||
| 	for k, v := range jsonData { | 	for k, v := range jsonData { | ||||||
| 		switch k { | 		switch k { | ||||||
| 		case "node": | 		case "node": | ||||||
| 			n := &corepb.Node{} | 			// We unconditionally convert the JSON into a v3.Node proto. The v3
 | ||||||
|  | 			// proto does not contain the deprecated field "build_version" from
 | ||||||
|  | 			// the v2 proto. We do not expect the bootstrap file to contain the
 | ||||||
|  | 			// "build_version" field. In any case, the unmarshal will succeed
 | ||||||
|  | 			// because we have set the `AllowUnknownFields` option on the
 | ||||||
|  | 			// unmarshaler.
 | ||||||
|  | 			n := &v3corepb.Node{} | ||||||
| 			if err := m.Unmarshal(bytes.NewReader(v), n); err != nil { | 			if err := m.Unmarshal(bytes.NewReader(v), n); err != nil { | ||||||
| 				return nil, fmt.Errorf("xds: jsonpb.Unmarshal(%v) for field %q failed during bootstrap: %v", string(v), k, err) | 				return nil, fmt.Errorf("xds: jsonpb.Unmarshal(%v) for field %q failed during bootstrap: %v", string(v), k, err) | ||||||
| 			} | 			} | ||||||
|  | @ -144,6 +167,17 @@ func NewConfig() (*Config, error) { | ||||||
| 					break | 					break | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 		case "server_features": | ||||||
|  | 			var features []string | ||||||
|  | 			if err := json.Unmarshal(v, &features); err != nil { | ||||||
|  | 				return nil, fmt.Errorf("xds: json.Unmarshal(%v) for field %q failed during bootstrap: %v", string(v), k, err) | ||||||
|  | 			} | ||||||
|  | 			for _, f := range features { | ||||||
|  | 				switch f { | ||||||
|  | 				case serverFeaturesV3: | ||||||
|  | 					serverSupportsV3 = true | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		// Do not fail the xDS bootstrap when an unknown field is seen. This can
 | 		// Do not fail the xDS bootstrap when an unknown field is seen. This can
 | ||||||
| 		// happen when an older version client reads a newer version bootstrap
 | 		// happen when an older version client reads a newer version bootstrap
 | ||||||
|  | @ -154,20 +188,69 @@ func NewConfig() (*Config, error) { | ||||||
| 		return nil, fmt.Errorf("xds: Required field %q not found in bootstrap", "xds_servers.server_uri") | 		return nil, fmt.Errorf("xds: Required field %q not found in bootstrap", "xds_servers.server_uri") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// If we don't find a nodeProto in the bootstrap file, we just create an
 | 	// We end up using v3 transport protocol version only if the following
 | ||||||
| 	// empty one here. That way, callers of this function can always expect
 | 	// conditions are met:
 | ||||||
| 	// that the NodeProto field is non-nil.
 | 	// 1. Server supports v3, indicated by the presence of "xds_v3" in
 | ||||||
| 	if config.NodeProto == nil { | 	//    server_features.
 | ||||||
| 		config.NodeProto = &corepb.Node{} | 	// 2. Environment variable "GRPC_XDS_EXPERIMENTAL_V3_SUPPORT" is set to
 | ||||||
|  | 	//    true.
 | ||||||
|  | 	// The default value of the enum type "version.TransportAPI" is v2.
 | ||||||
|  | 	if v3Env := os.Getenv(v3SupportEnv); v3Env == "true" { | ||||||
|  | 		if serverSupportsV3 { | ||||||
|  | 			config.TransportAPI = version.TransportV3 | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	// BuildVersion is deprecated, and is replaced by user_agent_name and
 |  | ||||||
| 	// user_agent_version. But the management servers are still using the old
 |  | ||||||
| 	// field, so we will keep both set.
 |  | ||||||
| 	config.NodeProto.BuildVersion = gRPCVersion |  | ||||||
| 	config.NodeProto.UserAgentName = gRPCUserAgentName |  | ||||||
| 	config.NodeProto.UserAgentVersionType = &corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version} |  | ||||||
| 	config.NodeProto.ClientFeatures = append(config.NodeProto.ClientFeatures, clientFeatureNoOverprovisioning) |  | ||||||
| 
 | 
 | ||||||
|  | 	if err := config.updateNodeProto(); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
| 	logger.Infof("Bootstrap config for creating xds-client: %+v", config) | 	logger.Infof("Bootstrap config for creating xds-client: %+v", config) | ||||||
| 	return config, nil | 	return config, nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // updateNodeProto updates the node proto read from the bootstrap file.
 | ||||||
|  | //
 | ||||||
|  | // Node proto in Config contains a v3.Node protobuf message corresponding to the
 | ||||||
|  | // JSON contents found in the bootstrap file. This method performs some post
 | ||||||
|  | // processing on it:
 | ||||||
|  | // 1. If we don't find a nodeProto in the bootstrap file, we create an empty one
 | ||||||
|  | // here. That way, callers of this function can always expect that the NodeProto
 | ||||||
|  | // field is non-nil.
 | ||||||
|  | // 2. If the transport protocol version to be used is not v3, we convert the
 | ||||||
|  | // current v3.Node proto in a v2.Node proto.
 | ||||||
|  | // 3. Some additional fields which are not expected to be set in the bootstrap
 | ||||||
|  | // file are populated here.
 | ||||||
|  | func (c *Config) updateNodeProto() error { | ||||||
|  | 	if c.TransportAPI == version.TransportV3 { | ||||||
|  | 		v3, _ := c.NodeProto.(*v3corepb.Node) | ||||||
|  | 		if v3 == nil { | ||||||
|  | 			v3 = &v3corepb.Node{} | ||||||
|  | 		} | ||||||
|  | 		v3.UserAgentName = gRPCUserAgentName | ||||||
|  | 		v3.UserAgentVersionType = &v3corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version} | ||||||
|  | 		v3.ClientFeatures = append(v3.ClientFeatures, clientFeatureNoOverprovisioning) | ||||||
|  | 		c.NodeProto = v3 | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	v2 := &v2corepb.Node{} | ||||||
|  | 	if c.NodeProto != nil { | ||||||
|  | 		v3, err := proto.Marshal(c.NodeProto) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return fmt.Errorf("xds: proto.Marshal(%v): %v", c.NodeProto, err) | ||||||
|  | 		} | ||||||
|  | 		if err := proto.Unmarshal(v3, v2); err != nil { | ||||||
|  | 			return fmt.Errorf("xds: proto.Unmarshal(%v): %v", v3, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	c.NodeProto = v2 | ||||||
|  | 
 | ||||||
|  | 	// BuildVersion is deprecated, and is replaced by user_agent_name and
 | ||||||
|  | 	// user_agent_version. But the management servers are still using the old
 | ||||||
|  | 	// field, so we will keep both set.
 | ||||||
|  | 	v2.BuildVersion = gRPCVersion | ||||||
|  | 	v2.UserAgentName = gRPCUserAgentName | ||||||
|  | 	v2.UserAgentVersionType = &v2corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version} | ||||||
|  | 	v2.ClientFeatures = append(v2.ClientFeatures, clientFeatureNoOverprovisioning) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -21,70 +21,28 @@ | ||||||
| package bootstrap | package bootstrap | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" | 	v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" | ||||||
|  | 	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" | ||||||
| 	"github.com/golang/protobuf/proto" | 	"github.com/golang/protobuf/proto" | ||||||
| 	structpb "github.com/golang/protobuf/ptypes/struct" | 	structpb "github.com/golang/protobuf/ptypes/struct" | ||||||
|  | 	"github.com/google/go-cmp/cmp" | ||||||
| 	"google.golang.org/grpc" | 	"google.golang.org/grpc" | ||||||
| 	"google.golang.org/grpc/credentials/google" | 	"google.golang.org/grpc/credentials/google" | ||||||
|  | 	"google.golang.org/grpc/xds/internal/version" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	nodeProto = &corepb.Node{ | 	v2BootstrapFileMap = map[string]string{ | ||||||
| 		Id: "ENVOY_NODE_ID", |  | ||||||
| 		Metadata: &structpb.Struct{ |  | ||||||
| 			Fields: map[string]*structpb.Value{ |  | ||||||
| 				"TRAFFICDIRECTOR_GRPC_HOSTNAME": { |  | ||||||
| 					Kind: &structpb.Value_StringValue{StringValue: "trafficdirector"}, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 		BuildVersion:         gRPCVersion, |  | ||||||
| 		UserAgentName:        gRPCUserAgentName, |  | ||||||
| 		UserAgentVersionType: &corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version}, |  | ||||||
| 		ClientFeatures:       []string{clientFeatureNoOverprovisioning}, |  | ||||||
| 	} |  | ||||||
| 	nilCredsConfig = &Config{ |  | ||||||
| 		BalancerName: "trafficdirector.googleapis.com:443", |  | ||||||
| 		Creds:        nil, |  | ||||||
| 		NodeProto:    nodeProto, |  | ||||||
| 	} |  | ||||||
| 	nonNilCredsConfig = &Config{ |  | ||||||
| 		BalancerName: "trafficdirector.googleapis.com:443", |  | ||||||
| 		Creds:        grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), |  | ||||||
| 		NodeProto:    nodeProto, |  | ||||||
| 	} |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // TODO: enable leak check for this package when
 |  | ||||||
| // https://github.com/googleapis/google-cloud-go/issues/2417 is fixed.
 |  | ||||||
| 
 |  | ||||||
| // TestNewConfig exercises the functionality in NewConfig with different
 |  | ||||||
| // bootstrap file contents. It overrides the fileReadFunc by returning
 |  | ||||||
| // bootstrap file contents defined in this test, instead of reading from a
 |  | ||||||
| // file.
 |  | ||||||
| func TestNewConfig(t *testing.T) { |  | ||||||
| 	bootstrapFileMap := map[string]string{ |  | ||||||
| 		"empty":          "", |  | ||||||
| 		"badJSON":        `["test": 123]`, |  | ||||||
| 		"noBalancerName": `{"node": {"id": "ENVOY_NODE_ID"}}`, |  | ||||||
| 		"emptyNodeProto": ` | 		"emptyNodeProto": ` | ||||||
| 		{ | 		{ | ||||||
| 			"xds_servers" : [{ | 			"xds_servers" : [{ | ||||||
| 				"server_uri": "trafficdirector.googleapis.com:443" | 				"server_uri": "trafficdirector.googleapis.com:443" | ||||||
| 			}] | 			}] | ||||||
| 		}`, | 		}`, | ||||||
| 		"emptyXdsServer": ` |  | ||||||
| 		{ |  | ||||||
| 			"node": { |  | ||||||
| 				"id": "ENVOY_NODE_ID", |  | ||||||
| 				"metadata": { |  | ||||||
| 				    "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" |  | ||||||
| 			    } |  | ||||||
| 			} |  | ||||||
| 		}`, |  | ||||||
| 		"unknownTopLevelFieldInFile": ` | 		"unknownTopLevelFieldInFile": ` | ||||||
| 		{ | 		{ | ||||||
| 			"node": { | 			"node": { | ||||||
|  | @ -208,80 +166,286 @@ func TestNewConfig(t *testing.T) { | ||||||
| 			] | 			] | ||||||
| 		}`, | 		}`, | ||||||
| 	} | 	} | ||||||
|  | 	v3BootstrapFileMap = map[string]string{ | ||||||
|  | 		"serverDoesNotSupportsV3": ` | ||||||
|  | 		{ | ||||||
|  | 			"node": { | ||||||
|  | 				"id": "ENVOY_NODE_ID", | ||||||
|  | 				"metadata": { | ||||||
|  | 				    "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" | ||||||
|  | 			    } | ||||||
|  | 			}, | ||||||
|  | 			"xds_servers" : [{ | ||||||
|  | 				"server_uri": "trafficdirector.googleapis.com:443", | ||||||
|  | 				"channel_creds": [ | ||||||
|  | 					{ "type": "google_default" } | ||||||
|  | 				] | ||||||
|  | 			}], | ||||||
|  | 			"server_features" : ["foo", "bar"] | ||||||
|  | 		}`, | ||||||
|  | 		"serverSupportsV3": ` | ||||||
|  | 		{ | ||||||
|  | 			"node": { | ||||||
|  | 				"id": "ENVOY_NODE_ID", | ||||||
|  | 				"metadata": { | ||||||
|  | 				    "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" | ||||||
|  | 			    } | ||||||
|  | 			}, | ||||||
|  | 			"xds_servers" : [{ | ||||||
|  | 				"server_uri": "trafficdirector.googleapis.com:443", | ||||||
|  | 				"channel_creds": [ | ||||||
|  | 					{ "type": "google_default" } | ||||||
|  | 				] | ||||||
|  | 			}], | ||||||
|  | 			"server_features" : ["foo", "bar", "xds_v3"] | ||||||
|  | 		}`, | ||||||
|  | 	} | ||||||
|  | 	metadata = &structpb.Struct{ | ||||||
|  | 		Fields: map[string]*structpb.Value{ | ||||||
|  | 			"TRAFFICDIRECTOR_GRPC_HOSTNAME": { | ||||||
|  | 				Kind: &structpb.Value_StringValue{StringValue: "trafficdirector"}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	v2NodeProto = &v2corepb.Node{ | ||||||
|  | 		Id:                   "ENVOY_NODE_ID", | ||||||
|  | 		Metadata:             metadata, | ||||||
|  | 		BuildVersion:         gRPCVersion, | ||||||
|  | 		UserAgentName:        gRPCUserAgentName, | ||||||
|  | 		UserAgentVersionType: &v2corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version}, | ||||||
|  | 		ClientFeatures:       []string{clientFeatureNoOverprovisioning}, | ||||||
|  | 	} | ||||||
|  | 	v3NodeProto = &v3corepb.Node{ | ||||||
|  | 		Id:                   "ENVOY_NODE_ID", | ||||||
|  | 		Metadata:             metadata, | ||||||
|  | 		UserAgentName:        gRPCUserAgentName, | ||||||
|  | 		UserAgentVersionType: &v3corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version}, | ||||||
|  | 		ClientFeatures:       []string{clientFeatureNoOverprovisioning}, | ||||||
|  | 	} | ||||||
|  | 	nilCredsConfigV2 = &Config{ | ||||||
|  | 		BalancerName: "trafficdirector.googleapis.com:443", | ||||||
|  | 		Creds:        nil, | ||||||
|  | 		NodeProto:    v2NodeProto, | ||||||
|  | 	} | ||||||
|  | 	nonNilCredsConfigV2 = &Config{ | ||||||
|  | 		BalancerName: "trafficdirector.googleapis.com:443", | ||||||
|  | 		Creds:        grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), | ||||||
|  | 		NodeProto:    v2NodeProto, | ||||||
|  | 	} | ||||||
|  | 	nonNilCredsConfigV3 = &Config{ | ||||||
|  | 		BalancerName: "trafficdirector.googleapis.com:443", | ||||||
|  | 		Creds:        grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), | ||||||
|  | 		TransportAPI: version.TransportV3, | ||||||
|  | 		NodeProto:    v3NodeProto, | ||||||
|  | 	} | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| 	oldFileReadFunc := fileReadFunc | func (c *Config) compare(want *Config) error { | ||||||
| 	fileReadFunc = func(name string) ([]byte, error) { | 	if c.BalancerName != want.BalancerName { | ||||||
|  | 		return fmt.Errorf("config.BalancerName is %s, want %s", c.BalancerName, want.BalancerName) | ||||||
|  | 	} | ||||||
|  | 	// Since Creds is of type grpc.DialOption interface, where the
 | ||||||
|  | 	// implementation is provided by a function, it is not possible to compare.
 | ||||||
|  | 	if (c.Creds != nil) != (want.Creds != nil) { | ||||||
|  | 		return fmt.Errorf("config.Creds is %#v, want %#v", c.Creds, want.Creds) | ||||||
|  | 	} | ||||||
|  | 	if c.TransportAPI != want.TransportAPI { | ||||||
|  | 		return fmt.Errorf("config.TransportAPI is %v, want %v", c.TransportAPI, want.TransportAPI) | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 	if diff := cmp.Diff(want.NodeProto, c.NodeProto, cmp.Comparer(proto.Equal)); diff != "" { | ||||||
|  | 		return fmt.Errorf("config.NodeProto diff (-want, +got):\n%s", diff) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func setupBootstrapOverride(bootstrapFileMap map[string]string) func() { | ||||||
|  | 	oldFileReadFunc := bootstrapFileReadFunc | ||||||
|  | 	bootstrapFileReadFunc = func(name string) ([]byte, error) { | ||||||
| 		if b, ok := bootstrapFileMap[name]; ok { | 		if b, ok := bootstrapFileMap[name]; ok { | ||||||
| 			return []byte(b), nil | 			return []byte(b), nil | ||||||
| 		} | 		} | ||||||
| 		return nil, os.ErrNotExist | 		return nil, os.ErrNotExist | ||||||
| 	} | 	} | ||||||
| 	defer func() { | 	return func() { | ||||||
| 		fileReadFunc = oldFileReadFunc | 		bootstrapFileReadFunc = oldFileReadFunc | ||||||
| 		os.Unsetenv(fileEnv) | 		os.Unsetenv(bootstrapFileEnv) | ||||||
| 	}() | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TODO: enable leak check for this package when
 | ||||||
|  | // https://github.com/googleapis/google-cloud-go/issues/2417 is fixed.
 | ||||||
|  | 
 | ||||||
|  | // TestNewConfigV2ProtoFailure exercises the functionality in NewConfig with
 | ||||||
|  | // different bootstrap file contents which are expected to fail.
 | ||||||
|  | func TestNewConfigV2ProtoFailure(t *testing.T) { | ||||||
|  | 	bootstrapFileMap := map[string]string{ | ||||||
|  | 		"empty":          "", | ||||||
|  | 		"badJSON":        `["test": 123]`, | ||||||
|  | 		"noBalancerName": `{"node": {"id": "ENVOY_NODE_ID"}}`, | ||||||
|  | 		"emptyXdsServer": ` | ||||||
|  | 		{ | ||||||
|  | 			"node": { | ||||||
|  | 				"id": "ENVOY_NODE_ID", | ||||||
|  | 				"metadata": { | ||||||
|  | 				    "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" | ||||||
|  | 			    } | ||||||
|  | 			} | ||||||
|  | 		}`, | ||||||
|  | 	} | ||||||
|  | 	cancel := setupBootstrapOverride(bootstrapFileMap) | ||||||
|  | 	defer cancel() | ||||||
| 
 | 
 | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
| 		name       string | 		name      string | ||||||
| 		wantConfig *Config | 		wantError bool | ||||||
| 		wantError  bool |  | ||||||
| 	}{ | 	}{ | ||||||
| 		{"nonExistentBootstrapFile", nil, true}, | 		{"nonExistentBootstrapFile", true}, | ||||||
| 		{"empty", nil, true}, | 		{"empty", true}, | ||||||
| 		{"badJSON", nil, true}, | 		{"badJSON", true}, | ||||||
| 		{"emptyNodeProto", &Config{ | 		{"noBalancerName", true}, | ||||||
| 			BalancerName: "trafficdirector.googleapis.com:443", | 		{"emptyXdsServer", true}, | ||||||
| 			NodeProto: &corepb.Node{ |  | ||||||
| 				BuildVersion:         gRPCVersion, |  | ||||||
| 				UserAgentName:        gRPCUserAgentName, |  | ||||||
| 				UserAgentVersionType: &corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version}, |  | ||||||
| 				ClientFeatures:       []string{clientFeatureNoOverprovisioning}, |  | ||||||
| 			}, |  | ||||||
| 		}, false}, |  | ||||||
| 		{"noBalancerName", nil, true}, |  | ||||||
| 		{"emptyXdsServer", nil, true}, |  | ||||||
| 		{"unknownTopLevelFieldInFile", nilCredsConfig, false}, |  | ||||||
| 		{"unknownFieldInNodeProto", nilCredsConfig, false}, |  | ||||||
| 		{"unknownFieldInXdsServer", nilCredsConfig, false}, |  | ||||||
| 		{"emptyChannelCreds", nilCredsConfig, false}, |  | ||||||
| 		{"nonGoogleDefaultCreds", nilCredsConfig, false}, |  | ||||||
| 		{"multipleChannelCreds", nonNilCredsConfig, false}, |  | ||||||
| 		{"goodBootstrap", nonNilCredsConfig, false}, |  | ||||||
| 		{"multipleXDSServers", nonNilCredsConfig, false}, |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, test := range tests { | 	for _, test := range tests { | ||||||
| 		t.Run(test.name, func(t *testing.T) { | 		t.Run(test.name, func(t *testing.T) { | ||||||
| 			if err := os.Setenv(fileEnv, test.name); err != nil { | 			if err := os.Setenv(bootstrapFileEnv, test.name); err != nil { | ||||||
| 				t.Fatalf("os.Setenv(%s, %s) failed with error: %v", fileEnv, test.name, err) | 				t.Fatalf("os.Setenv(%s, %s) failed with error: %v", bootstrapFileEnv, test.name, err) | ||||||
| 			} | 			} | ||||||
| 			config, err := NewConfig() | 			if _, err := NewConfig(); err == nil { | ||||||
| 			if err != nil { | 				t.Fatalf("NewConfig() returned nil error, expected to fail") | ||||||
| 				if !test.wantError { |  | ||||||
| 					t.Fatalf("unexpected error %v", err) |  | ||||||
| 				} |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			if test.wantError { |  | ||||||
| 				t.Fatalf("wantError: %v, got error %v", test.wantError, err) |  | ||||||
| 			} |  | ||||||
| 			if config.BalancerName != test.wantConfig.BalancerName { |  | ||||||
| 				t.Errorf("config.BalancerName is %s, want %s", config.BalancerName, test.wantConfig.BalancerName) |  | ||||||
| 			} |  | ||||||
| 			if !proto.Equal(config.NodeProto, test.wantConfig.NodeProto) { |  | ||||||
| 				t.Errorf("config.NodeProto is %#v, want %#v", config.NodeProto, test.wantConfig.NodeProto) |  | ||||||
| 			} |  | ||||||
| 			if (config.Creds != nil) != (test.wantConfig.Creds != nil) { |  | ||||||
| 				t.Errorf("config.Creds is %#v, want %#v", config.Creds, test.wantConfig.Creds) |  | ||||||
| 			} | 			} | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestNewConfigEnvNotSet(t *testing.T) { | // TestNewConfigV2ProtoSuccess exercises the functionality in NewConfig with
 | ||||||
| 	os.Unsetenv(fileEnv) | // different bootstrap file contents. It overrides the fileReadFunc by returning
 | ||||||
| 	config, err := NewConfig() | // bootstrap file contents defined in this test, instead of reading from a file.
 | ||||||
| 	if err == nil { | func TestNewConfigV2ProtoSuccess(t *testing.T) { | ||||||
| 		t.Errorf("NewConfig() returned: %#v, <nil>, wanted non-nil error", config) | 	cancel := setupBootstrapOverride(v2BootstrapFileMap) | ||||||
|  | 	defer cancel() | ||||||
|  | 
 | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name       string | ||||||
|  | 		wantConfig *Config | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			"emptyNodeProto", &Config{ | ||||||
|  | 				BalancerName: "trafficdirector.googleapis.com:443", | ||||||
|  | 				NodeProto: &v2corepb.Node{ | ||||||
|  | 					BuildVersion:         gRPCVersion, | ||||||
|  | 					UserAgentName:        gRPCUserAgentName, | ||||||
|  | 					UserAgentVersionType: &v2corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version}, | ||||||
|  | 					ClientFeatures:       []string{clientFeatureNoOverprovisioning}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{"unknownTopLevelFieldInFile", nilCredsConfigV2}, | ||||||
|  | 		{"unknownFieldInNodeProto", nilCredsConfigV2}, | ||||||
|  | 		{"unknownFieldInXdsServer", nilCredsConfigV2}, | ||||||
|  | 		{"emptyChannelCreds", nilCredsConfigV2}, | ||||||
|  | 		{"nonGoogleDefaultCreds", nilCredsConfigV2}, | ||||||
|  | 		{"multipleChannelCreds", nonNilCredsConfigV2}, | ||||||
|  | 		{"goodBootstrap", nonNilCredsConfigV2}, | ||||||
|  | 		{"multipleXDSServers", nonNilCredsConfigV2}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, test := range tests { | ||||||
|  | 		t.Run(test.name, func(t *testing.T) { | ||||||
|  | 			if err := os.Setenv(bootstrapFileEnv, test.name); err != nil { | ||||||
|  | 				t.Fatalf("os.Setenv(%s, %s) failed with error: %v", bootstrapFileEnv, test.name, err) | ||||||
|  | 			} | ||||||
|  | 			c, err := NewConfig() | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("NewConfig() failed: %v", err) | ||||||
|  | 			} | ||||||
|  | 			if err := c.compare(test.wantConfig); err != nil { | ||||||
|  | 				t.Fatal(err) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestNewConfigV3SupportNotEnabledOnClient verifies bootstrap functionality
 | ||||||
|  | // when the GRPC_XDS_EXPERIMENTAL_V3_SUPPORT environment variable is not enabled
 | ||||||
|  | // on the client. In this case, whether the server supports v3 or not, the
 | ||||||
|  | // client will end up using v2.
 | ||||||
|  | func TestNewConfigV3SupportNotEnabledOnClient(t *testing.T) { | ||||||
|  | 	if err := os.Setenv(v3SupportEnv, "false"); err != nil { | ||||||
|  | 		t.Fatalf("os.Setenv(%s, %s) failed with error: %v", v3SupportEnv, "true", err) | ||||||
|  | 	} | ||||||
|  | 	defer os.Unsetenv(v3SupportEnv) | ||||||
|  | 
 | ||||||
|  | 	cancel := setupBootstrapOverride(v3BootstrapFileMap) | ||||||
|  | 	defer cancel() | ||||||
|  | 
 | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name       string | ||||||
|  | 		wantConfig *Config | ||||||
|  | 	}{ | ||||||
|  | 		{"serverDoesNotSupportsV3", nonNilCredsConfigV2}, | ||||||
|  | 		{"serverSupportsV3", nonNilCredsConfigV2}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, test := range tests { | ||||||
|  | 		t.Run(test.name, func(t *testing.T) { | ||||||
|  | 			if err := os.Setenv(bootstrapFileEnv, test.name); err != nil { | ||||||
|  | 				t.Fatalf("os.Setenv(%s, %s) failed with error: %v", bootstrapFileEnv, test.name, err) | ||||||
|  | 			} | ||||||
|  | 			c, err := NewConfig() | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("NewConfig() failed: %v", err) | ||||||
|  | 			} | ||||||
|  | 			if err := c.compare(test.wantConfig); err != nil { | ||||||
|  | 				t.Fatal(err) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestNewConfigV3SupportEnabledOnClient verifies bootstrap functionality when
 | ||||||
|  | // the GRPC_XDS_EXPERIMENTAL_V3_SUPPORT environment variable is enabled on the
 | ||||||
|  | // client. Here the client ends up using v2 or v3 based on what the server
 | ||||||
|  | // supports.
 | ||||||
|  | func TestNewConfigV3SupportEnabledOnClient(t *testing.T) { | ||||||
|  | 	if err := os.Setenv(v3SupportEnv, "true"); err != nil { | ||||||
|  | 		t.Fatalf("os.Setenv(%s, %s) failed with error: %v", v3SupportEnv, "true", err) | ||||||
|  | 	} | ||||||
|  | 	defer os.Unsetenv(v3SupportEnv) | ||||||
|  | 
 | ||||||
|  | 	cancel := setupBootstrapOverride(v3BootstrapFileMap) | ||||||
|  | 	defer cancel() | ||||||
|  | 
 | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name       string | ||||||
|  | 		wantConfig *Config | ||||||
|  | 	}{ | ||||||
|  | 		{"serverDoesNotSupportsV3", nonNilCredsConfigV2}, | ||||||
|  | 		{"serverSupportsV3", nonNilCredsConfigV3}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, test := range tests { | ||||||
|  | 		t.Run(test.name, func(t *testing.T) { | ||||||
|  | 			if err := os.Setenv(bootstrapFileEnv, test.name); err != nil { | ||||||
|  | 				t.Fatalf("os.Setenv(%s, %s) failed with error: %v", bootstrapFileEnv, test.name, err) | ||||||
|  | 			} | ||||||
|  | 			c, err := NewConfig() | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("NewConfig() failed: %v", err) | ||||||
|  | 			} | ||||||
|  | 			if err := c.compare(test.wantConfig); err != nil { | ||||||
|  | 				t.Fatal(err) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestNewConfigBootstrapFileEnvNotSet tests the case where the bootstrap file
 | ||||||
|  | // environment variable is not set.
 | ||||||
|  | func TestNewConfigBootstrapFileEnvNotSet(t *testing.T) { | ||||||
|  | 	os.Unsetenv(bootstrapFileEnv) | ||||||
|  | 	if _, err := NewConfig(); err == nil { | ||||||
|  | 		t.Errorf("NewConfig() returned nil error, expected to fail") | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -34,6 +34,7 @@ import ( | ||||||
| 	"google.golang.org/grpc/internal/grpcsync" | 	"google.golang.org/grpc/internal/grpcsync" | ||||||
| 	"google.golang.org/grpc/keepalive" | 	"google.golang.org/grpc/keepalive" | ||||||
| 	"google.golang.org/grpc/xds/internal/client/bootstrap" | 	"google.golang.org/grpc/xds/internal/client/bootstrap" | ||||||
|  | 	"google.golang.org/grpc/xds/internal/version" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Options provides all parameters required for the creation of an xDS client.
 | // Options provides all parameters required for the creation of an xDS client.
 | ||||||
|  | @ -131,7 +132,13 @@ func New(opts Options) (*Client, error) { | ||||||
| 	c.logger = prefixLogger((c)) | 	c.logger = prefixLogger((c)) | ||||||
| 	c.logger.Infof("Created ClientConn to xDS server: %s", opts.Config.BalancerName) | 	c.logger.Infof("Created ClientConn to xDS server: %s", opts.Config.BalancerName) | ||||||
| 
 | 
 | ||||||
| 	c.v2c = newXDSV2Client(c, cc, opts.Config.NodeProto, backoff.DefaultExponential.Backoff, c.logger) | 	if opts.Config.TransportAPI == version.TransportV2 { | ||||||
|  | 		c.v2c = newXDSV2Client(c, cc, opts.Config.NodeProto.(*corepb.Node), backoff.DefaultExponential.Backoff, c.logger) | ||||||
|  | 	} else { | ||||||
|  | 		// TODO(easwars): Remove this once v3Client is ready.
 | ||||||
|  | 		return nil, errors.New("xds v3 client is not yet supported") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	c.logger.Infof("Created") | 	c.logger.Infof("Created") | ||||||
| 	go c.run() | 	go c.run() | ||||||
| 	return c, nil | 	return c, nil | ||||||
|  |  | ||||||
|  | @ -55,7 +55,7 @@ func clientOpts(balancerName string) Options { | ||||||
| 		Config: bootstrap.Config{ | 		Config: bootstrap.Config{ | ||||||
| 			BalancerName: balancerName, | 			BalancerName: balancerName, | ||||||
| 			Creds:        grpc.WithInsecure(), | 			Creds:        grpc.WithInsecure(), | ||||||
| 			NodeProto:    &corepb.Node{}, | 			NodeProto:    testutils.EmptyNodeProtoV2, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -78,7 +78,7 @@ func (s) TestNew(t *testing.T) { | ||||||
| 			opts: Options{ | 			opts: Options{ | ||||||
| 				Config: bootstrap.Config{ | 				Config: bootstrap.Config{ | ||||||
| 					Creds:     grpc.WithInsecure(), | 					Creds:     grpc.WithInsecure(), | ||||||
| 					NodeProto: &corepb.Node{}, | 					NodeProto: testutils.EmptyNodeProtoV2, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			wantErr: true, | 			wantErr: true, | ||||||
|  | @ -88,7 +88,7 @@ func (s) TestNew(t *testing.T) { | ||||||
| 			opts: Options{ | 			opts: Options{ | ||||||
| 				Config: bootstrap.Config{ | 				Config: bootstrap.Config{ | ||||||
| 					BalancerName: "dummy", | 					BalancerName: "dummy", | ||||||
| 					NodeProto:    &corepb.Node{}, | 					NodeProto:    testutils.EmptyNodeProtoV2, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			wantErr: true, | 			wantErr: true, | ||||||
|  |  | ||||||
|  | @ -37,8 +37,6 @@ import ( | ||||||
| 	"google.golang.org/grpc/xds/internal/client/bootstrap" | 	"google.golang.org/grpc/xds/internal/client/bootstrap" | ||||||
| 	"google.golang.org/grpc/xds/internal/testutils" | 	"google.golang.org/grpc/xds/internal/testutils" | ||||||
| 	"google.golang.org/grpc/xds/internal/testutils/fakeclient" | 	"google.golang.org/grpc/xds/internal/testutils/fakeclient" | ||||||
| 
 |  | ||||||
| 	corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
|  | @ -51,7 +49,7 @@ var ( | ||||||
| 	validConfig = bootstrap.Config{ | 	validConfig = bootstrap.Config{ | ||||||
| 		BalancerName: balancerName, | 		BalancerName: balancerName, | ||||||
| 		Creds:        grpc.WithInsecure(), | 		Creds:        grpc.WithInsecure(), | ||||||
| 		NodeProto:    &corepb.Node{}, | 		NodeProto:    testutils.EmptyNodeProtoV2, | ||||||
| 	} | 	} | ||||||
| 	target = resolver.Target{Endpoint: targetStr} | 	target = resolver.Target{Endpoint: targetStr} | ||||||
| ) | ) | ||||||
|  | @ -138,7 +136,7 @@ func TestResolverBuilder(t *testing.T) { | ||||||
| 			rbo:  resolver.BuildOptions{}, | 			rbo:  resolver.BuildOptions{}, | ||||||
| 			config: bootstrap.Config{ | 			config: bootstrap.Config{ | ||||||
| 				Creds:     grpc.WithInsecure(), | 				Creds:     grpc.WithInsecure(), | ||||||
| 				NodeProto: &corepb.Node{}, | 				NodeProto: testutils.EmptyNodeProtoV2, | ||||||
| 			}, | 			}, | ||||||
| 			wantErr: true, | 			wantErr: true, | ||||||
| 		}, | 		}, | ||||||
|  | @ -147,7 +145,7 @@ func TestResolverBuilder(t *testing.T) { | ||||||
| 			rbo:  resolver.BuildOptions{}, | 			rbo:  resolver.BuildOptions{}, | ||||||
| 			config: bootstrap.Config{ | 			config: bootstrap.Config{ | ||||||
| 				BalancerName: balancerName, | 				BalancerName: balancerName, | ||||||
| 				NodeProto:    &corepb.Node{}, | 				NodeProto:    testutils.EmptyNodeProtoV2, | ||||||
| 			}, | 			}, | ||||||
| 			xdsClientFunc: getXDSClientMakerFunc(xdsclient.Options{Config: validConfig}), | 			xdsClientFunc: getXDSClientMakerFunc(xdsclient.Options{Config: validConfig}), | ||||||
| 			wantErr:       false, | 			wantErr:       false, | ||||||
|  |  | ||||||
|  | @ -18,13 +18,16 @@ | ||||||
| package testutils | package testutils | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" | 	v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" | ||||||
| 	"google.golang.org/grpc/xds/internal" | 	"google.golang.org/grpc/xds/internal" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // EmptyNodeProtoV2 is a node proto with no fields set.
 | ||||||
|  | var EmptyNodeProtoV2 = &v2corepb.Node{} | ||||||
|  | 
 | ||||||
| // LocalityIDToProto converts a LocalityID to its proto representation.
 | // LocalityIDToProto converts a LocalityID to its proto representation.
 | ||||||
| func LocalityIDToProto(l internal.LocalityID) *corepb.Locality { | func LocalityIDToProto(l internal.LocalityID) *v2corepb.Locality { | ||||||
| 	return &corepb.Locality{ | 	return &v2corepb.Locality{ | ||||||
| 		Region:  l.Region, | 		Region:  l.Region, | ||||||
| 		Zone:    l.Zone, | 		Zone:    l.Zone, | ||||||
| 		SubZone: l.SubZone, | 		SubZone: l.SubZone, | ||||||
|  | @ -0,0 +1,32 @@ | ||||||
|  | /* | ||||||
|  |  * | ||||||
|  |  * Copyright 2020 gRPC authors. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | // Package version defines supported xDS API versions.
 | ||||||
|  | package version | ||||||
|  | 
 | ||||||
|  | // TransportAPI refers to the API version for xDS transport protocol. This
 | ||||||
|  | // describes the xDS gRPC endpoint and version of DiscoveryRequest/Response used
 | ||||||
|  | // on the wire.
 | ||||||
|  | type TransportAPI int | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	// TransportV2 refers to the v2 xDS transport protocol.
 | ||||||
|  | 	TransportV2 TransportAPI = iota | ||||||
|  | 	// TransportV3 refers to the v3 xDS transport protocol.
 | ||||||
|  | 	TransportV3 | ||||||
|  | ) | ||||||
		Loading…
	
		Reference in New Issue