@@ -25,6 +25,7 @@ import (
2525
2626 v3statuspb "github.com/envoyproxy/go-control-plane/envoy/service/status/v3"
2727 estats "google.golang.org/grpc/experimental/stats"
28+ "google.golang.org/grpc/internal/envconfig"
2829 istats "google.golang.org/grpc/internal/stats"
2930 "google.golang.org/grpc/internal/xds/bootstrap"
3031 "google.golang.org/protobuf/proto"
@@ -43,12 +44,14 @@ var (
4344// Pool represents a pool of xDS clients that share the same bootstrap
4445// configuration.
4546type Pool struct {
46- mu sync.Mutex
47- clients map [string ]* clientImpl
47+ // Note that mu should ideally only have to guard clients. But here, we need
48+ // it to guard config as well since SetFallbackBootstrapConfig writes to
49+ // config.
50+ mu sync.Mutex
51+ clients map [string ]* clientImpl
52+ fallbackConfig * bootstrap.Config // TODO(i/8661): remove fallbackConfig.
4853 // getConfiguration is a sync.OnceValues that attempts to read the bootstrap
49- // configuration from environment variables once if bootstrap.GetConfiguration
50- // is set in the case of DefaultPool. When the Pool is created with a
51- // specific configuration, it returns that configuration directly.
54+ // configuration from environment variables once.
5255 getConfiguration func () (* bootstrap.Config , error )
5356}
5457
@@ -79,82 +82,46 @@ type OptionsForTesting struct {
7982
8083// NewPool creates a new xDS client pool with the given bootstrap config.
8184//
82- // If a nil bootstrap config is passed, the caller is expected to pass the
83- // config when creating the xDS client by using NewClientWithConfig instead of
84- // NewClient. If they specify a nil config and use NewClient, client creation
85- // will fail.
85+ // If a nil bootstrap config is passed and SetFallbackBootstrapConfig is not
86+ // called before a call to NewClient, the latter will fail. i.e. if there is an
87+ // attempt to create an xDS client from the pool without specifying bootstrap
88+ // configuration (either at pool creation time or by setting the fallback
89+ // bootstrap configuration), xDS client creation will fail.
8690func NewPool (config * bootstrap.Config ) * Pool {
8791 return & Pool {
8892 clients : make (map [string ]* clientImpl ),
8993 getConfiguration : func () (* bootstrap.Config , error ) {
90- if config == nil {
91- return nil , fmt .Errorf ("xds: bootstrap config cannot be nil" )
92- }
9394 return config , nil
9495 },
9596 }
9697}
9798
98- // getBootstrapConfiguration returns the config specified at pool creation time.
99- func (p * Pool ) getBootstrapConfiguration () (* bootstrap.Config , error ) {
100- p .mu .Lock ()
101- defer p .mu .Unlock ()
102- config , err := p .getConfiguration ()
103- if err != nil {
104- return nil , fmt .Errorf ("xds: failed to read xDS bootstrap config: %v" , err )
105- }
106- return config , nil
107- }
108-
109- // getConfig returns the provided config if it is non-nil. Otherwise, it
110- // delegates to getBootstrapConfiguration to retrieve the configuration.
111- func (p * Pool ) getConfig (config * bootstrap.Config ) (* bootstrap.Config , error ) {
112- if config != nil {
113- return config , nil
114- }
115- return p .getBootstrapConfiguration ()
116- }
117-
118- // NewClientWithConfig returns an xDS client with the given name from the pool.
119- // If the client doesn't already exist, it creates a new xDS client using the
120- // provided config and adds it to the pool. The provided config takes precedence
121- // over any config passed during pool creation. This should be used when a
122- // non-default config is required. If the provided config is nil, it attempts
123- // to load the configuration provided at pool creation time.
99+ // NewClientWithConfig returns an xDS client with the given name from the pool. If the
100+ // client doesn't already exist, it creates a new xDS client and adds it to the
101+ // pool.
124102//
125103// The second return value represents a close function which the caller is
126104// expected to invoke once they are done using the client. It is safe for the
127105// caller to invoke this close function multiple times.
128106func (p * Pool ) NewClientWithConfig (name string , metricsRecorder estats.MetricsRecorder , config * bootstrap.Config ) (XDSClient , func (), error ) {
129- config , err := p .getConfig (config )
130- if err != nil {
131- return nil , nil , err
132- }
133107 return p .newRefCounted (name , metricsRecorder , defaultWatchExpiryTimeout , config )
134108}
135109
136110// NewClient returns an xDS client with the given name from the pool. If the
137- // client doesn't already exist, it loads the configuration and creates a new
138- // xDS client and adds it to the pool.
111+ // client doesn't already exist, it creates a new xDS client and adds it to the
112+ // pool.
139113//
140114// The second return value represents a close function which the caller is
141115// expected to invoke once they are done using the client. It is safe for the
142116// caller to invoke this close function multiple times.
143117func (p * Pool ) NewClient (name string , metricsRecorder estats.MetricsRecorder ) (XDSClient , func (), error ) {
144- config , err := p .getConfig (nil )
145- if err != nil {
146- return nil , nil , err
147- }
148- return p .newRefCounted (name , metricsRecorder , defaultWatchExpiryTimeout , config )
118+ return p .newRefCounted (name , metricsRecorder , defaultWatchExpiryTimeout , nil )
149119}
150120
151121// NewClientForTesting returns an xDS client configured with the provided
152122// options from the pool. If the client doesn't already exist, it creates a new
153123// xDS client and adds it to the pool.
154124//
155- // If the config in options is nil, it attempts to load the configuration
156- // provided at the time of pool creation.
157- //
158125// The second return value represents a close function which the caller is
159126// expected to invoke once they are done using the client. It is safe for the
160127// caller to invoke this close function multiple times.
@@ -175,13 +142,7 @@ func (p *Pool) NewClientForTesting(opts OptionsForTesting) (XDSClient, func(), e
175142 if opts .MetricsRecorder == nil {
176143 opts .MetricsRecorder = istats .NewMetricsRecorderList (nil )
177144 }
178-
179- config , err := p .getConfig (opts .Config )
180- if err != nil {
181- return nil , nil , err
182- }
183-
184- c , cancel , err := p .newRefCounted (opts .Name , opts .MetricsRecorder , opts .WatchExpiryTimeout , config )
145+ c , cancel , err := p .newRefCounted (opts .Name , opts .MetricsRecorder , opts .WatchExpiryTimeout , opts .Config )
185146 if err != nil {
186147 return nil , nil , err
187148 }
@@ -211,6 +172,16 @@ func (p *Pool) GetClientForTesting(name string) (XDSClient, func(), error) {
211172 return c , sync .OnceFunc (func () { p .clientRefCountedClose (name ) }), nil
212173}
213174
175+ // SetFallbackBootstrapConfig is used to specify a bootstrap configuration
176+ // that will be used as a fallback when the bootstrap environment variables
177+ // are not defined.
178+ // TODO(i/8661): remove SetFallbackBootstrapConfig function.
179+ func (p * Pool ) SetFallbackBootstrapConfig (config * bootstrap.Config ) {
180+ p .mu .Lock ()
181+ defer p .mu .Unlock ()
182+ p .fallbackConfig = config
183+ }
184+
214185// DumpResources returns the status and contents of all xDS resources.
215186func (p * Pool ) DumpResources () * v3statuspb.ClientStatusResponse {
216187 p .mu .Lock ()
@@ -241,7 +212,10 @@ func (p *Pool) BootstrapConfigForTesting() *bootstrap.Config {
241212 p .mu .Lock ()
242213 defer p .mu .Unlock ()
243214 cfg , _ := p .getConfiguration ()
244- return cfg
215+ if cfg != nil {
216+ return cfg
217+ }
218+ return p .fallbackConfig
245219}
246220
247221// UnsetBootstrapConfigForTesting unsets the bootstrap configuration used by
@@ -251,6 +225,7 @@ func (p *Pool) BootstrapConfigForTesting() *bootstrap.Config {
251225func (p * Pool ) UnsetBootstrapConfigForTesting () {
252226 p .mu .Lock ()
253227 defer p .mu .Unlock ()
228+ p .fallbackConfig = nil
254229 p .getConfiguration = sync .OnceValues (bootstrap .GetConfiguration )
255230}
256231
@@ -293,9 +268,7 @@ func (p *Pool) clientRefCountedClose(name string) {
293268// newRefCounted creates a new reference counted xDS client implementation for
294269// name, if one does not exist already. If an xDS client for the given name
295270// exists, it gets a reference to it and returns it.
296- //
297- // The config should not be nil.
298- func (p * Pool ) newRefCounted (name string , metricsRecorder estats.MetricsRecorder , watchExpiryTimeout time.Duration , config * bootstrap.Config ) (* clientImpl , func (), error ) {
271+ func (p * Pool ) newRefCounted (name string , metricsRecorder estats.MetricsRecorder , watchExpiryTimeout time.Duration , bConfig * bootstrap.Config ) (* clientImpl , func (), error ) {
299272 p .mu .Lock ()
300273 defer p .mu .Unlock ()
301274
@@ -304,6 +277,25 @@ func (p *Pool) newRefCounted(name string, metricsRecorder estats.MetricsRecorder
304277 return c , sync .OnceFunc (func () { p .clientRefCountedClose (name ) }), nil
305278 }
306279
280+ config := bConfig
281+ if config == nil {
282+ var err error
283+ config , err = p .getConfiguration ()
284+ if err != nil {
285+ return nil , nil , fmt .Errorf ("xds: failed to read xDS bootstrap config from env vars: %v" , err )
286+ }
287+ if config == nil {
288+ // If the environment variables are not set, then fallback bootstrap
289+ // configuration should be set before attempting to create an xDS client,
290+ // else xDS client creation will fail.
291+ config = p .fallbackConfig
292+ }
293+ }
294+
295+ if config == nil {
296+ return nil , nil , fmt .Errorf ("failed to read xDS bootstrap config from env vars: bootstrap environment variables (%q or %q) not defined and fallback config not set" , envconfig .XDSBootstrapFileNameEnv , envconfig .XDSBootstrapFileContentEnv )
297+ }
298+
307299 c , err := newClientImpl (config , metricsRecorder , name , watchExpiryTimeout )
308300 if err != nil {
309301 return nil , nil , err
0 commit comments