xds: minor cleanup in xdsclient bootstrap code (#5195)

This commit is contained in:
Easwar Swaminathan 2022-02-15 15:41:49 -08:00 committed by GitHub
parent ebc30b8fc3
commit ec717cad73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 37 deletions

View File

@ -26,13 +26,13 @@ import (
const ( const (
// XDSBootstrapFileNameEnv is the env variable to set bootstrap file name. // XDSBootstrapFileNameEnv is the env variable to set bootstrap file name.
// Do not use this and read from env directly. Its value is read and kept in // Do not use this and read from env directly. Its value is read and kept in
// variable BootstrapFileName. // variable XDSBootstrapFileName.
// //
// When both bootstrap FileName and FileContent are set, FileName is used. // When both bootstrap FileName and FileContent are set, FileName is used.
XDSBootstrapFileNameEnv = "GRPC_XDS_BOOTSTRAP" XDSBootstrapFileNameEnv = "GRPC_XDS_BOOTSTRAP"
// XDSBootstrapFileContentEnv is the env variable to set bootstrapp file // XDSBootstrapFileContentEnv is the env variable to set bootstrap file
// content. Do not use this and read from env directly. Its value is read // content. Do not use this and read from env directly. Its value is read
// and kept in variable BootstrapFileName. // and kept in variable XDSBootstrapFileContent.
// //
// When both bootstrap FileName and FileContent are set, FileName is used. // When both bootstrap FileName and FileContent are set, FileName is used.
XDSBootstrapFileContentEnv = "GRPC_XDS_BOOTSTRAP_CONFIG" XDSBootstrapFileContentEnv = "GRPC_XDS_BOOTSTRAP_CONFIG"

View File

@ -37,13 +37,13 @@ import (
const xdsScheme = "xds" const xdsScheme = "xds"
// NewBuilder creates a new xds resolver builder using a specific xds bootstrap // NewBuilderForTesting creates a new xds resolver builder using a specific xds
// config, so tests can use multiple xds clients in different ClientConns at // bootstrap config, so tests can use multiple xds clients in different
// the same time. // ClientConns at the same time.
func NewBuilder(config []byte) (resolver.Builder, error) { func NewBuilderForTesting(config []byte) (resolver.Builder, error) {
return &xdsResolverBuilder{ return &xdsResolverBuilder{
newXDSClient: func() (xdsclient.XDSClient, error) { newXDSClient: func() (xdsclient.XDSClient, error) {
return xdsclient.NewClientWithBootstrapContents(config) return xdsclient.NewWithBootstrapContentsForTesting(config)
}, },
}, nil }, nil
} }

View File

@ -65,19 +65,20 @@ var gRPCVersion = fmt.Sprintf("%s %s", gRPCUserAgentName, grpc.Version)
// For overriding in unit tests. // For overriding in unit tests.
var bootstrapFileReadFunc = ioutil.ReadFile var bootstrapFileReadFunc = ioutil.ReadFile
// insecureCredsBuilder encapsulates a insecure credential that is built using a // insecureCredsBuilder implements the `Credentials` interface defined in
// JSON config. // package `xds/bootstrap` and encapsulates an insecure credential.
type insecureCredsBuilder struct{} type insecureCredsBuilder struct{}
func (i *insecureCredsBuilder) Build(json.RawMessage) (credentials.Bundle, error) { func (i *insecureCredsBuilder) Build(json.RawMessage) (credentials.Bundle, error) {
return insecure.NewBundle(), nil return insecure.NewBundle(), nil
} }
func (i *insecureCredsBuilder) Name() string { func (i *insecureCredsBuilder) Name() string {
return "insecure" return "insecure"
} }
// googleDefaultCredsBuilder encapsulates a Google Default credential that is built using a // googleDefaultCredsBuilder implements the `Credentials` interface defined in
// JSON config. // package `xds/boostrap` and encapsulates a Google Default credential.
type googleDefaultCredsBuilder struct{} type googleDefaultCredsBuilder struct{}
func (d *googleDefaultCredsBuilder) Build(json.RawMessage) (credentials.Bundle, error) { func (d *googleDefaultCredsBuilder) Build(json.RawMessage) (credentials.Bundle, error) {
@ -328,11 +329,13 @@ func bootstrapConfigFromEnvVariable() ([]byte, error) {
} }
// NewConfig returns a new instance of Config initialized by reading the // NewConfig returns a new instance of Config initialized by reading the
// bootstrap file found at ${GRPC_XDS_BOOTSTRAP}. // bootstrap file found at ${GRPC_XDS_BOOTSTRAP} or bootstrap contents specified
// at ${GRPC_XDS_BOOTSTRAP_CONFIG}. If both env vars are set, the former is
// preferred.
// //
// Currently, we support exactly one type of credential, which is // We support a credential registration mechanism and only credentials
// "google_default", where we use the host's default certs for transport // registered through that mechanism will be accepted here. See package
// credentials and a Google oauth token for call credentials. // `xds/bootstrap` for details.
// //
// This function tries to process as much of the bootstrap file as possible (in // This function tries to process as much of the bootstrap file as possible (in
// the presence of the errors) and may return a Config object with certain // the presence of the errors) and may return a Config object with certain
@ -346,13 +349,18 @@ func NewConfig() (*Config, error) {
return nil, fmt.Errorf("xds: Failed to read bootstrap config: %v", err) return nil, fmt.Errorf("xds: Failed to read bootstrap config: %v", err)
} }
logger.Debugf("Bootstrap content: %s", data) logger.Debugf("Bootstrap content: %s", data)
return NewConfigFromContents(data) return newConfigFromContents(data)
} }
// NewConfigFromContents returns a new Config using the specified bootstrap // NewConfigFromContentsForTesting returns a new Config using the specified
// file contents instead of reading the environment variable. This is only // bootstrap file contents instead of reading the environment variable.
// suitable for testing purposes. //
func NewConfigFromContents(data []byte) (*Config, error) { // This is only suitable for testing purposes.
func NewConfigFromContentsForTesting(data []byte) (*Config, error) {
return newConfigFromContents(data)
}
func newConfigFromContents(data []byte) (*Config, error) {
config := &Config{} config := &Config{}
var jsonData map[string]json.RawMessage var jsonData map[string]json.RawMessage
@ -483,7 +491,7 @@ func NewConfigFromContents(data []byte) (*Config, error) {
// file are populated here. // file are populated here.
// 3. For each server config (both top level and in each authority), we set its // 3. For each server config (both top level and in each authority), we set its
// node field to the v3.Node, or a v2.Node with the same content, depending on // node field to the v3.Node, or a v2.Node with the same content, depending on
// the server's transprot API version. // the server's transport API version.
func (c *Config) updateNodeProto(node *v3corepb.Node) error { func (c *Config) updateNodeProto(node *v3corepb.Node) error {
v3 := node v3 := node
if v3 == nil { if v3 == nil {
@ -493,11 +501,11 @@ func (c *Config) updateNodeProto(node *v3corepb.Node) error {
v3.UserAgentVersionType = &v3corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version} v3.UserAgentVersionType = &v3corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version}
v3.ClientFeatures = append(v3.ClientFeatures, clientFeatureNoOverprovisioning) v3.ClientFeatures = append(v3.ClientFeatures, clientFeatureNoOverprovisioning)
v2 := &v2corepb.Node{}
v3bytes, err := proto.Marshal(v3) v3bytes, err := proto.Marshal(v3)
if err != nil { if err != nil {
return fmt.Errorf("xds: proto.Marshal(%v): %v", v3, err) return fmt.Errorf("xds: proto.Marshal(%v): %v", v3, err)
} }
v2 := &v2corepb.Node{}
if err := proto.Unmarshal(v3bytes, v2); err != nil { if err := proto.Unmarshal(v3bytes, v2); err != nil {
return fmt.Errorf("xds: proto.Unmarshal(%v): %v", v3bytes, err) return fmt.Errorf("xds: proto.Unmarshal(%v): %v", v3bytes, err)
} }

View File

@ -160,10 +160,10 @@ func (c *clientRefCounted) Close() {
} }
} }
// NewWithConfigForTesting is exported for testing only. // NewWithConfigForTesting returns an xdsClient for the specified bootstrap
// config, separate from the global singleton.
// //
// Note that this function doesn't set the singleton, so that the testing states // This should be used for testing purposes only.
// don't leak.
func NewWithConfigForTesting(config *bootstrap.Config, watchExpiryTimeout time.Duration) (XDSClient, error) { func NewWithConfigForTesting(config *bootstrap.Config, watchExpiryTimeout time.Duration) (XDSClient, error) {
cl, err := newWithConfig(config, watchExpiryTimeout, defaultIdleAuthorityDeleteTimeout) cl, err := newWithConfig(config, watchExpiryTimeout, defaultIdleAuthorityDeleteTimeout)
if err != nil { if err != nil {
@ -172,10 +172,11 @@ func NewWithConfigForTesting(config *bootstrap.Config, watchExpiryTimeout time.D
return &clientRefCounted{clientImpl: cl, refCount: 1}, nil return &clientRefCounted{clientImpl: cl, refCount: 1}, nil
} }
// NewClientWithBootstrapContents returns an xds client for this config, // NewWithBootstrapContentsForTesting returns an xdsClient for this config,
// separate from the global singleton. This should be used for testing // separate from the global singleton.
// purposes only. //
func NewClientWithBootstrapContents(contents []byte) (XDSClient, error) { // This should be used for testing purposes only.
func NewWithBootstrapContentsForTesting(contents []byte) (XDSClient, error) {
// Normalize the contents // Normalize the contents
buf := bytes.Buffer{} buf := bytes.Buffer{}
err := json.Indent(&buf, contents, "", "") err := json.Indent(&buf, contents, "", "")
@ -198,7 +199,7 @@ func NewClientWithBootstrapContents(contents []byte) (XDSClient, error) {
c.mu.Unlock() c.mu.Unlock()
} }
bcfg, err := bootstrap.NewConfigFromContents(contents) bcfg, err := bootstrap.NewConfigFromContentsForTesting(contents)
if err != nil { if err != nil {
return nil, fmt.Errorf("xds: error with bootstrap config: %v", err) return nil, fmt.Errorf("xds: error with bootstrap config: %v", err)
} }

View File

@ -161,11 +161,13 @@ func (s *GRPCServer) initXDSClient() error {
} }
newXDSClient := newXDSClient newXDSClient := newXDSClient
if s.opts.bootstrapContents != nil { if s.opts.bootstrapContentsForTesting != nil {
// Bootstrap file contents may be specified as a server option for tests.
newXDSClient = func() (xdsclient.XDSClient, error) { newXDSClient = func() (xdsclient.XDSClient, error) {
return xdsclient.NewClientWithBootstrapContents(s.opts.bootstrapContents) return xdsclient.NewWithBootstrapContentsForTesting(s.opts.bootstrapContentsForTesting)
} }
} }
client, err := newXDSClient() client, err := newXDSClient()
if err != nil { if err != nil {
return fmt.Errorf("xds: failed to create xds-client: %v", err) return fmt.Errorf("xds: failed to create xds-client: %v", err)

View File

@ -26,8 +26,8 @@ import (
) )
type serverOptions struct { type serverOptions struct {
modeCallback ServingModeCallbackFunc modeCallback ServingModeCallbackFunc
bootstrapContents []byte bootstrapContentsForTesting []byte
} }
type serverOption struct { type serverOption struct {
@ -72,5 +72,5 @@ type ServingModeChangeArgs struct {
// Notice: This API is EXPERIMENTAL and may be changed or removed in a // Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release. // later release.
func BootstrapContentsForTesting(contents []byte) grpc.ServerOption { func BootstrapContentsForTesting(contents []byte) grpc.ServerOption {
return &serverOption{apply: func(o *serverOptions) { o.bootstrapContents = contents }} return &serverOption{apply: func(o *serverOptions) { o.bootstrapContentsForTesting = contents }}
} }

View File

@ -90,5 +90,5 @@ func init() {
// Notice: This API is EXPERIMENTAL and may be changed or removed in a // Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release. // later release.
func NewXDSResolverWithConfigForTesting(bootstrapConfig []byte) (resolver.Builder, error) { func NewXDSResolverWithConfigForTesting(bootstrapConfig []byte) (resolver.Builder, error) {
return xdsresolver.NewBuilder(bootstrapConfig) return xdsresolver.NewBuilderForTesting(bootstrapConfig)
} }