Skip to content

Commit 0b57abb

Browse files
xds/googlec2p: support custom bootstrap config per channel. (#8648)
xds/googlec2p: Fix channel-specific xDS bootstrap configurations by allowing xdsclient creation with per-target config. Removes global fallback config usage, enabling multiple distinct xDS clients to coexist in the same process.
1 parent 58d4b2b commit 0b57abb

File tree

3 files changed

+195
-55
lines changed

3 files changed

+195
-55
lines changed

internal/xds/xdsclient/pool.go

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ type Pool struct {
4949
// config.
5050
mu sync.Mutex
5151
clients map[string]*clientImpl
52-
fallbackConfig *bootstrap.Config
52+
fallbackConfig *bootstrap.Config // TODO(i/8661): remove fallbackConfig.
5353
// getConfiguration is a sync.OnceValues that attempts to read the bootstrap
5454
// configuration from environment variables once.
5555
getConfiguration func() (*bootstrap.Config, error)
@@ -73,6 +73,11 @@ type OptionsForTesting struct {
7373
// MetricsRecorder is the metrics recorder the xDS Client will use. If
7474
// unspecified, uses a no-op MetricsRecorder.
7575
MetricsRecorder estats.MetricsRecorder
76+
77+
// Config is the xDS bootstrap configuration that will be used to initialize
78+
// the client. If unset, the client will use the config provided by env
79+
// variables.
80+
Config *bootstrap.Config
7681
}
7782

7883
// NewPool creates a new xDS client pool with the given bootstrap config.
@@ -91,6 +96,17 @@ func NewPool(config *bootstrap.Config) *Pool {
9196
}
9297
}
9398

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.
102+
//
103+
// The second return value represents a close function which the caller is
104+
// expected to invoke once they are done using the client. It is safe for the
105+
// caller to invoke this close function multiple times.
106+
func (p *Pool) NewClientWithConfig(name string, metricsRecorder estats.MetricsRecorder, config *bootstrap.Config) (XDSClient, func(), error) {
107+
return p.newRefCounted(name, metricsRecorder, defaultWatchExpiryTimeout, config)
108+
}
109+
94110
// NewClient returns an xDS client with the given name from the pool. If the
95111
// client doesn't already exist, it creates a new xDS client and adds it to the
96112
// pool.
@@ -99,7 +115,7 @@ func NewPool(config *bootstrap.Config) *Pool {
99115
// expected to invoke once they are done using the client. It is safe for the
100116
// caller to invoke this close function multiple times.
101117
func (p *Pool) NewClient(name string, metricsRecorder estats.MetricsRecorder) (XDSClient, func(), error) {
102-
return p.newRefCounted(name, metricsRecorder, defaultWatchExpiryTimeout)
118+
return p.newRefCounted(name, metricsRecorder, defaultWatchExpiryTimeout, nil)
103119
}
104120

105121
// NewClientForTesting returns an xDS client configured with the provided
@@ -126,7 +142,7 @@ func (p *Pool) NewClientForTesting(opts OptionsForTesting) (XDSClient, func(), e
126142
if opts.MetricsRecorder == nil {
127143
opts.MetricsRecorder = istats.NewMetricsRecorderList(nil)
128144
}
129-
c, cancel, err := p.newRefCounted(opts.Name, opts.MetricsRecorder, opts.WatchExpiryTimeout)
145+
c, cancel, err := p.newRefCounted(opts.Name, opts.MetricsRecorder, opts.WatchExpiryTimeout, opts.Config)
130146
if err != nil {
131147
return nil, nil, err
132148
}
@@ -159,6 +175,7 @@ func (p *Pool) GetClientForTesting(name string) (XDSClient, func(), error) {
159175
// SetFallbackBootstrapConfig is used to specify a bootstrap configuration
160176
// that will be used as a fallback when the bootstrap environment variables
161177
// are not defined.
178+
// TODO(i/8661): remove SetFallbackBootstrapConfig function.
162179
func (p *Pool) SetFallbackBootstrapConfig(config *bootstrap.Config) {
163180
p.mu.Lock()
164181
defer p.mu.Unlock()
@@ -251,30 +268,34 @@ func (p *Pool) clientRefCountedClose(name string) {
251268
// newRefCounted creates a new reference counted xDS client implementation for
252269
// name, if one does not exist already. If an xDS client for the given name
253270
// exists, it gets a reference to it and returns it.
254-
func (p *Pool) newRefCounted(name string, metricsRecorder estats.MetricsRecorder, watchExpiryTimeout time.Duration) (*clientImpl, func(), error) {
271+
func (p *Pool) newRefCounted(name string, metricsRecorder estats.MetricsRecorder, watchExpiryTimeout time.Duration, bConfig *bootstrap.Config) (*clientImpl, func(), error) {
255272
p.mu.Lock()
256273
defer p.mu.Unlock()
257274

258-
config, err := p.getConfiguration()
259-
if err != nil {
260-
return nil, nil, fmt.Errorf("xds: failed to read xDS bootstrap config from env vars: %v", err)
275+
if c := p.clients[name]; c != nil {
276+
c.incrRef()
277+
return c, sync.OnceFunc(func() { p.clientRefCountedClose(name) }), nil
261278
}
262279

280+
config := bConfig
263281
if config == nil {
264-
// If the environment variables are not set, then fallback bootstrap
265-
// configuration should be set before attempting to create an xDS client,
266-
// else xDS client creation will fail.
267-
config = p.fallbackConfig
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+
}
268293
}
294+
269295
if config == nil {
270296
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)
271297
}
272298

273-
if c := p.clients[name]; c != nil {
274-
c.incrRef()
275-
return c, sync.OnceFunc(func() { p.clientRefCountedClose(name) }), nil
276-
}
277-
278299
c, err := newClientImpl(config, metricsRecorder, name, watchExpiryTimeout)
279300
if err != nil {
280301
return nil, nil, err

xds/googledirectpath/googlec2p.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,16 @@ func getXdsServerURI() string {
119119
return fmt.Sprintf("dns:///directpath-pa.%s", universeDomain)
120120
}
121121

122+
type c2pResolverWrapper struct {
123+
resolver.Resolver
124+
cancel func() // Release the reference to the xDS client that was created in Build().
125+
}
126+
127+
func (r *c2pResolverWrapper) Close() {
128+
r.Resolver.Close()
129+
r.cancel()
130+
}
131+
122132
type c2pResolverBuilder struct{}
123133

124134
func (c2pResolverBuilder) Build(t resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
@@ -161,7 +171,6 @@ func (c2pResolverBuilder) Build(t resolver.Target, cc resolver.ClientConn, opts
161171
if err != nil {
162172
return nil, fmt.Errorf("failed to parse bootstrap contents: %s, %v", string(cfgJSON), err)
163173
}
164-
xdsClientPool.SetFallbackBootstrapConfig(config)
165174

166175
t = resolver.Target{
167176
URL: url.URL{
@@ -170,7 +179,24 @@ func (c2pResolverBuilder) Build(t resolver.Target, cc resolver.ClientConn, opts
170179
Path: t.URL.Path,
171180
},
172181
}
173-
return resolver.Get(xdsName).Build(t, cc, opts)
182+
183+
// Create a new xDS client for this target using the provided bootstrap
184+
// configuration. This client is stored in the xdsclient pool’s internal
185+
// cache, keeping it alive and associated with this resolver until Closed().
186+
// While the c2p resolver itself does not directly use the client, creating
187+
// it ensures that when the xDS resolver later requests a client for the
188+
// same target, the existing instance will be reused.
189+
_, cancel, err := xdsClientPool.NewClientWithConfig(t.String(), opts.MetricsRecorder, config)
190+
if err != nil {
191+
return nil, fmt.Errorf("failed to create xds client: %v", err)
192+
}
193+
194+
r, err := resolver.Get(xdsName).Build(t, cc, opts)
195+
if err != nil {
196+
cancel()
197+
return nil, err
198+
}
199+
return &c2pResolverWrapper{Resolver: r, cancel: cancel}, nil
174200
}
175201

176202
func (b c2pResolverBuilder) Scheme() string {

0 commit comments

Comments
 (0)