google.golang.org/grpc@v1.74.2/xds/internal/balancer/cdsbalancer/cdsbalancer.go (about) 1 /* 2 * Copyright 2019 gRPC authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Package cdsbalancer implements a balancer to handle CDS responses. 18 package cdsbalancer 19 20 import ( 21 "context" 22 "crypto/x509" 23 "encoding/json" 24 "fmt" 25 "sync/atomic" 26 "unsafe" 27 28 "google.golang.org/grpc/balancer" 29 "google.golang.org/grpc/balancer/base" 30 "google.golang.org/grpc/connectivity" 31 "google.golang.org/grpc/credentials" 32 "google.golang.org/grpc/credentials/tls/certprovider" 33 "google.golang.org/grpc/internal/balancer/nop" 34 xdsinternal "google.golang.org/grpc/internal/credentials/xds" 35 "google.golang.org/grpc/internal/grpclog" 36 "google.golang.org/grpc/internal/grpcsync" 37 "google.golang.org/grpc/internal/pretty" 38 "google.golang.org/grpc/resolver" 39 "google.golang.org/grpc/serviceconfig" 40 "google.golang.org/grpc/xds/internal/balancer/clusterresolver" 41 "google.golang.org/grpc/xds/internal/xdsclient" 42 "google.golang.org/grpc/xds/internal/xdsclient/xdsresource" 43 ) 44 45 const ( 46 cdsName = "cds_experimental" 47 aggregateClusterMaxDepth = 16 48 ) 49 50 var ( 51 errBalancerClosed = fmt.Errorf("cds_experimental LB policy is closed") 52 errExceedsMaxDepth = fmt.Errorf("aggregate cluster graph exceeds max depth (%d)", aggregateClusterMaxDepth) 53 54 // newChildBalancer is a helper function to build a new cluster_resolver 55 // balancer and will be overridden in unittests. 56 newChildBalancer = func(cc balancer.ClientConn, opts balancer.BuildOptions) (balancer.Balancer, error) { 57 builder := balancer.Get(clusterresolver.Name) 58 if builder == nil { 59 return nil, fmt.Errorf("xds: no balancer builder with name %v", clusterresolver.Name) 60 } 61 // We directly pass the parent clientConn to the underlying 62 // cluster_resolver balancer because the cdsBalancer does not deal with 63 // subConns. 64 return builder.Build(cc, opts), nil 65 } 66 buildProvider = buildProviderFunc 67 68 // x509SystemCertPoolFunc is used for mocking the system cert pool for 69 // tests. 70 x509SystemCertPoolFunc = x509.SystemCertPool 71 ) 72 73 func init() { 74 balancer.Register(bb{}) 75 } 76 77 // bb implements the balancer.Builder interface to help build a cdsBalancer. 78 // It also implements the balancer.ConfigParser interface to help parse the 79 // JSON service config, to be passed to the cdsBalancer. 80 type bb struct{} 81 82 // Build creates a new CDS balancer with the ClientConn. 83 func (bb) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer { 84 builder := balancer.Get(clusterresolver.Name) 85 if builder == nil { 86 // Shouldn't happen, registered through imported Cluster Resolver, 87 // defensive programming. 88 logger.Errorf("%q LB policy is needed but not registered", clusterresolver.Name) 89 return nop.NewBalancer(cc, fmt.Errorf("%q LB policy is needed but not registered", clusterresolver.Name)) 90 } 91 parser, ok := builder.(balancer.ConfigParser) 92 if !ok { 93 // Shouldn't happen, imported Cluster Resolver builder has this method. 94 logger.Errorf("%q LB policy does not implement a config parser", clusterresolver.Name) 95 return nop.NewBalancer(cc, fmt.Errorf("%q LB policy does not implement a config parser", clusterresolver.Name)) 96 } 97 98 ctx, cancel := context.WithCancel(context.Background()) 99 hi := xdsinternal.NewHandshakeInfo(nil, nil, nil, false) 100 xdsHIPtr := unsafe.Pointer(hi) 101 b := &cdsBalancer{ 102 bOpts: opts, 103 childConfigParser: parser, 104 serializer: grpcsync.NewCallbackSerializer(ctx), 105 serializerCancel: cancel, 106 xdsHIPtr: &xdsHIPtr, 107 watchers: make(map[string]*watcherState), 108 } 109 b.ccw = &ccWrapper{ 110 ClientConn: cc, 111 xdsHIPtr: b.xdsHIPtr, 112 } 113 b.logger = prefixLogger(b) 114 b.logger.Infof("Created") 115 116 var creds credentials.TransportCredentials 117 switch { 118 case opts.DialCreds != nil: 119 creds = opts.DialCreds 120 case opts.CredsBundle != nil: 121 creds = opts.CredsBundle.TransportCredentials() 122 } 123 if xc, ok := creds.(interface{ UsesXDS() bool }); ok && xc.UsesXDS() { 124 b.xdsCredsInUse = true 125 } 126 b.logger.Infof("xDS credentials in use: %v", b.xdsCredsInUse) 127 return b 128 } 129 130 // Name returns the name of balancers built by this builder. 131 func (bb) Name() string { 132 return cdsName 133 } 134 135 // lbConfig represents the loadBalancingConfig section of the service config 136 // for the cdsBalancer. 137 type lbConfig struct { 138 serviceconfig.LoadBalancingConfig 139 ClusterName string `json:"Cluster"` 140 } 141 142 // ParseConfig parses the JSON load balancer config provided into an 143 // internal form or returns an error if the config is invalid. 144 func (bb) ParseConfig(c json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { 145 var cfg lbConfig 146 if err := json.Unmarshal(c, &cfg); err != nil { 147 return nil, fmt.Errorf("xds: unable to unmarshal lbconfig: %s, error: %v", string(c), err) 148 } 149 return &cfg, nil 150 } 151 152 // cdsBalancer implements a CDS based LB policy. It instantiates a 153 // cluster_resolver balancer to further resolve the serviceName received from 154 // CDS, into localities and endpoints. Implements the balancer.Balancer 155 // interface which is exposed to gRPC and implements the balancer.ClientConn 156 // interface which is exposed to the cluster_resolver balancer. 157 type cdsBalancer struct { 158 // The following fields are initialized at build time and are either 159 // read-only after that or provide their own synchronization, and therefore 160 // do not need to be guarded by a mutex. 161 ccw *ccWrapper // ClientConn interface passed to child LB. 162 bOpts balancer.BuildOptions // BuildOptions passed to child LB. 163 childConfigParser balancer.ConfigParser // Config parser for cluster_resolver LB policy. 164 logger *grpclog.PrefixLogger // Prefix logger for all logging. 165 xdsCredsInUse bool 166 167 xdsHIPtr *unsafe.Pointer // Accessed atomically. 168 169 // The serializer and its cancel func are initialized at build time, and the 170 // rest of the fields here are only accessed from serializer callbacks (or 171 // from balancer.Balancer methods, which themselves are guaranteed to be 172 // mutually exclusive) and hence do not need to be guarded by a mutex. 173 serializer *grpcsync.CallbackSerializer // Serializes updates from gRPC and xDS client. 174 serializerCancel context.CancelFunc // Stops the above serializer. 175 childLB balancer.Balancer // Child policy, built upon resolution of the cluster graph. 176 xdsClient xdsclient.XDSClient // xDS client to watch Cluster resources. 177 watchers map[string]*watcherState // Set of watchers and associated state, keyed by cluster name. 178 lbCfg *lbConfig // Current load balancing configuration. 179 180 // The certificate providers are cached here to that they can be closed when 181 // a new provider is to be created. 182 cachedRoot certprovider.Provider 183 cachedIdentity certprovider.Provider 184 } 185 186 // handleSecurityConfig processes the security configuration received from the 187 // management server, creates appropriate certificate provider plugins, and 188 // updates the HandshakeInfo which is added as an address attribute in 189 // NewSubConn() calls. 190 // 191 // Only executed in the context of a serializer callback. 192 func (b *cdsBalancer) handleSecurityConfig(config *xdsresource.SecurityConfig) error { 193 // If xdsCredentials are not in use, i.e, the user did not want to get 194 // security configuration from an xDS server, we should not be acting on the 195 // received security config here. Doing so poses a security threat. 196 if !b.xdsCredsInUse { 197 return nil 198 } 199 var xdsHI *xdsinternal.HandshakeInfo 200 201 // Security config being nil is a valid case where the management server has 202 // not sent any security configuration. The xdsCredentials implementation 203 // handles this by delegating to its fallback credentials. 204 if config == nil { 205 // We need to explicitly set the fields to nil here since this might be 206 // a case of switching from a good security configuration to an empty 207 // one where fallback credentials are to be used. 208 xdsHI = xdsinternal.NewHandshakeInfo(nil, nil, nil, false) 209 atomic.StorePointer(b.xdsHIPtr, unsafe.Pointer(xdsHI)) 210 return nil 211 212 } 213 214 // A root provider is required whether we are using TLS or mTLS. 215 cpc := b.xdsClient.BootstrapConfig().CertProviderConfigs() 216 var rootProvider certprovider.Provider 217 if config.UseSystemRootCerts { 218 rootProvider = systemRootCertsProvider{} 219 } else { 220 rp, err := buildProvider(cpc, config.RootInstanceName, config.RootCertName, false, true) 221 if err != nil { 222 return err 223 } 224 rootProvider = rp 225 } 226 227 // The identity provider is only present when using mTLS. 228 var identityProvider certprovider.Provider 229 if name, cert := config.IdentityInstanceName, config.IdentityCertName; name != "" { 230 var err error 231 identityProvider, err = buildProvider(cpc, name, cert, true, false) 232 if err != nil { 233 return err 234 } 235 } 236 237 // Close the old providers and cache the new ones. 238 if b.cachedRoot != nil { 239 b.cachedRoot.Close() 240 } 241 if b.cachedIdentity != nil { 242 b.cachedIdentity.Close() 243 } 244 b.cachedRoot = rootProvider 245 b.cachedIdentity = identityProvider 246 xdsHI = xdsinternal.NewHandshakeInfo(rootProvider, identityProvider, config.SubjectAltNameMatchers, false) 247 atomic.StorePointer(b.xdsHIPtr, unsafe.Pointer(xdsHI)) 248 return nil 249 } 250 251 func buildProviderFunc(configs map[string]*certprovider.BuildableConfig, instanceName, certName string, wantIdentity, wantRoot bool) (certprovider.Provider, error) { 252 cfg, ok := configs[instanceName] 253 if !ok { 254 // Defensive programming. If a resource received from the management 255 // server contains a certificate provider instance name that is not 256 // found in the bootstrap, the resource is NACKed by the xDS client. 257 return nil, fmt.Errorf("certificate provider instance %q not found in bootstrap file", instanceName) 258 } 259 provider, err := cfg.Build(certprovider.BuildOptions{ 260 CertName: certName, 261 WantIdentity: wantIdentity, 262 WantRoot: wantRoot, 263 }) 264 if err != nil { 265 // This error is not expected since the bootstrap process parses the 266 // config and makes sure that it is acceptable to the plugin. Still, it 267 // is possible that the plugin parses the config successfully, but its 268 // Build() method errors out. 269 return nil, fmt.Errorf("xds: failed to get security plugin instance (%+v): %v", cfg, err) 270 } 271 return provider, nil 272 } 273 274 // A convenience method to create a watcher for cluster `name`. It also 275 // registers the watch with the xDS client, and adds the newly created watcher 276 // to the list of watchers maintained by the LB policy. 277 func (b *cdsBalancer) createAndAddWatcherForCluster(name string) { 278 w := &clusterWatcher{ 279 name: name, 280 parent: b, 281 } 282 ws := &watcherState{ 283 watcher: w, 284 cancelWatch: xdsresource.WatchCluster(b.xdsClient, name, w), 285 } 286 b.watchers[name] = ws 287 } 288 289 // UpdateClientConnState receives the serviceConfig (which contains the 290 // clusterName to watch for in CDS) and the xdsClient object from the 291 // xdsResolver. 292 func (b *cdsBalancer) UpdateClientConnState(state balancer.ClientConnState) error { 293 if b.xdsClient == nil { 294 c := xdsclient.FromResolverState(state.ResolverState) 295 if c == nil { 296 b.logger.Warningf("Received balancer config with no xDS client") 297 return balancer.ErrBadResolverState 298 } 299 b.xdsClient = c 300 } 301 b.logger.Infof("Received balancer config update: %s", pretty.ToJSON(state.BalancerConfig)) 302 303 // The errors checked here should ideally never happen because the 304 // ServiceConfig in this case is prepared by the xdsResolver and is not 305 // something that is received on the wire. 306 lbCfg, ok := state.BalancerConfig.(*lbConfig) 307 if !ok { 308 b.logger.Warningf("Received unexpected balancer config type: %T", state.BalancerConfig) 309 return balancer.ErrBadResolverState 310 } 311 if lbCfg.ClusterName == "" { 312 b.logger.Warningf("Received balancer config with no cluster name") 313 return balancer.ErrBadResolverState 314 } 315 316 // Do nothing and return early if configuration has not changed. 317 if b.lbCfg != nil && b.lbCfg.ClusterName == lbCfg.ClusterName { 318 return nil 319 } 320 b.lbCfg = lbCfg 321 322 // Handle the update in a blocking fashion. 323 errCh := make(chan error, 1) 324 callback := func(context.Context) { 325 // A config update with a changed top-level cluster name means that none 326 // of our old watchers make any sense any more. 327 b.closeAllWatchers() 328 329 // Create a new watcher for the top-level cluster. Upon resolution, it 330 // could end up creating more watchers if turns out to be an aggregate 331 // cluster. 332 b.createAndAddWatcherForCluster(lbCfg.ClusterName) 333 errCh <- nil 334 } 335 onFailure := func() { 336 // The call to Schedule returns false *only* if the serializer has been 337 // closed, which happens only when we receive an update after close. 338 errCh <- errBalancerClosed 339 } 340 b.serializer.ScheduleOr(callback, onFailure) 341 return <-errCh 342 } 343 344 // ResolverError handles errors reported by the xdsResolver. 345 func (b *cdsBalancer) ResolverError(err error) { 346 b.serializer.TrySchedule(func(context.Context) { 347 // Missing Listener or RouteConfiguration on the management server 348 // results in a 'resource not found' error from the xDS resolver. In 349 // these cases, we should stap watching all of the current clusters 350 // being watched. 351 if xdsresource.ErrType(err) == xdsresource.ErrorTypeResourceNotFound { 352 b.closeAllWatchers() 353 b.closeChildPolicyAndReportTF(err) 354 return 355 } 356 var root string 357 if b.lbCfg != nil { 358 root = b.lbCfg.ClusterName 359 } 360 b.onClusterError(root, err) 361 }) 362 } 363 364 // UpdateSubConnState handles subConn updates from gRPC. 365 func (b *cdsBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { 366 b.logger.Errorf("UpdateSubConnState(%v, %+v) called unexpectedly", sc, state) 367 } 368 369 // Closes all registered cluster watchers and removes them from the internal map. 370 // 371 // Only executed in the context of a serializer callback. 372 func (b *cdsBalancer) closeAllWatchers() { 373 for name, state := range b.watchers { 374 state.cancelWatch() 375 delete(b.watchers, name) 376 } 377 } 378 379 // closeChildPolicyAndReportTF closes the child policy, if it exists, and 380 // updates the connectivity state of the channel to TransientFailure with an 381 // error picker. 382 // 383 // Only executed in the context of a serializer callback. 384 func (b *cdsBalancer) closeChildPolicyAndReportTF(err error) { 385 if b.childLB != nil { 386 b.childLB.Close() 387 b.childLB = nil 388 } 389 b.ccw.UpdateState(balancer.State{ 390 ConnectivityState: connectivity.TransientFailure, 391 Picker: base.NewErrPicker(err), 392 }) 393 } 394 395 // Close cancels the CDS watch, closes the child policy and closes the 396 // cdsBalancer. 397 func (b *cdsBalancer) Close() { 398 b.serializer.TrySchedule(func(context.Context) { 399 b.closeAllWatchers() 400 401 if b.childLB != nil { 402 b.childLB.Close() 403 b.childLB = nil 404 } 405 if b.cachedRoot != nil { 406 b.cachedRoot.Close() 407 } 408 if b.cachedIdentity != nil { 409 b.cachedIdentity.Close() 410 } 411 b.logger.Infof("Shutdown") 412 }) 413 b.serializerCancel() 414 <-b.serializer.Done() 415 } 416 417 func (b *cdsBalancer) ExitIdle() { 418 b.serializer.TrySchedule(func(context.Context) { 419 if b.childLB == nil { 420 b.logger.Warningf("Received ExitIdle with no child policy") 421 return 422 } 423 // This implementation assumes the child balancer supports 424 // ExitIdle (but still checks for the interface's existence to 425 // avoid a panic if not). If the child does not, no subconns 426 // will be connected. 427 b.childLB.ExitIdle() 428 }) 429 } 430 431 // Node ID needs to be manually added to errors generated in the following 432 // scenarios: 433 // - resource-does-not-exist: since the xDS watch API uses a separate callback 434 // instead of returning an error value. TODO(gRFC A88): Once A88 is 435 // implemented, the xDS client will be able to add the node ID to 436 // resource-does-not-exist errors as well, and we can get rid of this 437 // special handling. 438 // - received a good update from the xDS client, but the update either contains 439 // an invalid security configuration or contains invalid aggragate cluster 440 // config. 441 func (b *cdsBalancer) annotateErrorWithNodeID(err error) error { 442 nodeID := b.xdsClient.BootstrapConfig().Node().GetId() 443 return fmt.Errorf("[xDS node id: %v]: %w", nodeID, err) 444 } 445 446 // Handles a good Cluster update from the xDS client. Kicks off the discovery 447 // mechanism generation process from the top-level cluster and if the cluster 448 // graph is resolved, generates child policy config and pushes it down. 449 // 450 // Only executed in the context of a serializer callback. 451 func (b *cdsBalancer) onClusterUpdate(name string, update xdsresource.ClusterUpdate) { 452 state := b.watchers[name] 453 if state == nil { 454 // We are currently not watching this cluster anymore. Return early. 455 return 456 } 457 458 b.logger.Infof("Received Cluster resource: %s", pretty.ToJSON(update)) 459 460 // Update the watchers map with the update for the cluster. 461 state.lastUpdate = &update 462 463 // For an aggregate cluster, always use the security configuration on the 464 // root cluster. 465 if name == b.lbCfg.ClusterName { 466 // Process the security config from the received update before building the 467 // child policy or forwarding the update to it. We do this because the child 468 // policy may try to create a new subConn inline. Processing the security 469 // configuration here and setting up the handshakeInfo will make sure that 470 // such attempts are handled properly. 471 if err := b.handleSecurityConfig(update.SecurityCfg); err != nil { 472 // If the security config is invalid, for example, if the provider 473 // instance is not found in the bootstrap config, we need to put the 474 // channel in transient failure. 475 b.onClusterError(name, b.annotateErrorWithNodeID(fmt.Errorf("received Cluster resource contains invalid security config: %v", err))) 476 return 477 } 478 } 479 480 clustersSeen := make(map[string]bool) 481 dms, ok, err := b.generateDMsForCluster(b.lbCfg.ClusterName, 0, nil, clustersSeen) 482 if err != nil { 483 b.onClusterError(b.lbCfg.ClusterName, b.annotateErrorWithNodeID(fmt.Errorf("failed to generate discovery mechanisms: %v", err))) 484 return 485 } 486 if ok { 487 if len(dms) == 0 { 488 b.onClusterError(b.lbCfg.ClusterName, b.annotateErrorWithNodeID(fmt.Errorf("aggregate cluster graph has no leaf clusters"))) 489 return 490 } 491 // Child policy is built the first time we resolve the cluster graph. 492 if b.childLB == nil { 493 childLB, err := newChildBalancer(b.ccw, b.bOpts) 494 if err != nil { 495 b.logger.Errorf("Failed to create child policy of type %s: %v", clusterresolver.Name, err) 496 return 497 } 498 b.childLB = childLB 499 b.logger.Infof("Created child policy %p of type %s", b.childLB, clusterresolver.Name) 500 } 501 502 // Prepare the child policy configuration, convert it to JSON, have it 503 // parsed by the child policy to convert it into service config and push 504 // an update to it. 505 childCfg := &clusterresolver.LBConfig{ 506 DiscoveryMechanisms: dms, 507 // The LB policy is configured by the root cluster. 508 XDSLBPolicy: b.watchers[b.lbCfg.ClusterName].lastUpdate.LBPolicy, 509 } 510 cfgJSON, err := json.Marshal(childCfg) 511 if err != nil { 512 // Shouldn't happen, since we just prepared struct. 513 b.logger.Errorf("cds_balancer: error marshalling prepared config: %v", childCfg) 514 return 515 } 516 517 var sc serviceconfig.LoadBalancingConfig 518 if sc, err = b.childConfigParser.ParseConfig(cfgJSON); err != nil { 519 b.logger.Errorf("cds_balancer: cluster_resolver config generated %v is invalid: %v", string(cfgJSON), err) 520 return 521 } 522 523 ccState := balancer.ClientConnState{ 524 ResolverState: xdsclient.SetClient(resolver.State{}, b.xdsClient), 525 BalancerConfig: sc, 526 } 527 if err := b.childLB.UpdateClientConnState(ccState); err != nil { 528 b.logger.Errorf("Encountered error when sending config {%+v} to child policy: %v", ccState, err) 529 } 530 } 531 // We no longer need the clusters that we did not see in this iteration of 532 // generateDMsForCluster(). 533 for cluster := range clustersSeen { 534 state, ok := b.watchers[cluster] 535 if ok { 536 continue 537 } 538 state.cancelWatch() 539 delete(b.watchers, cluster) 540 } 541 } 542 543 // Handles an ambient error Cluster update from the xDS client to not stop 544 // using the previously seen resource. 545 // 546 // Only executed in the context of a serializer callback. 547 func (b *cdsBalancer) onClusterAmbientError(name string, err error) { 548 b.logger.Warningf("Cluster resource %q received ambient error update: %v", name, err) 549 550 if xdsresource.ErrType(err) != xdsresource.ErrorTypeConnection && b.childLB != nil { 551 // Connection errors will be sent to the child balancers directly. 552 // There's no need to forward them. 553 b.childLB.ResolverError(err) 554 } 555 } 556 557 // Handles an error Cluster update from the xDS client to stop using the 558 // previously seen resource. Propagates the error down to the child policy 559 // if one exists, and puts the channel in TRANSIENT_FAILURE. 560 // 561 // Only executed in the context of a serializer callback. 562 func (b *cdsBalancer) onClusterResourceError(name string, err error) { 563 b.logger.Warningf("CDS watch for resource %q reported resource error", name) 564 b.closeChildPolicyAndReportTF(err) 565 } 566 567 // Generates discovery mechanisms for the cluster graph rooted at `name`. This 568 // method is called recursively if `name` corresponds to an aggregate cluster, 569 // with the base case for recursion being a leaf cluster. If a new cluster is 570 // encountered when traversing the graph, a watcher is created for it. 571 // 572 // Inputs: 573 // - name: name of the cluster to start from 574 // - depth: recursion depth of the current cluster, starting from root 575 // - dms: prioritized list of current discovery mechanisms 576 // - clustersSeen: cluster names seen so far in the graph traversal 577 // 578 // Outputs: 579 // - new prioritized list of discovery mechanisms 580 // - boolean indicating if traversal of the aggregate cluster graph is 581 // complete. If false, the above list of discovery mechanisms is ignored. 582 // - error indicating if any error was encountered as part of the graph 583 // traversal. If error is non-nil, the other return values are ignored. 584 // 585 // Only executed in the context of a serializer callback. 586 func (b *cdsBalancer) generateDMsForCluster(name string, depth int, dms []clusterresolver.DiscoveryMechanism, clustersSeen map[string]bool) ([]clusterresolver.DiscoveryMechanism, bool, error) { 587 if depth >= aggregateClusterMaxDepth { 588 return dms, false, errExceedsMaxDepth 589 } 590 591 if clustersSeen[name] { 592 // Discovery mechanism already seen through a different branch. 593 return dms, true, nil 594 } 595 clustersSeen[name] = true 596 597 state, ok := b.watchers[name] 598 if !ok { 599 // If we have not seen this cluster so far, create a watcher for it, add 600 // it to the map, start the watch and return. 601 b.createAndAddWatcherForCluster(name) 602 603 // And since we just created the watcher, we know that we haven't 604 // resolved the cluster graph yet. 605 return dms, false, nil 606 } 607 608 // A watcher exists, but no update has been received yet. 609 if state.lastUpdate == nil { 610 return dms, false, nil 611 } 612 613 var dm clusterresolver.DiscoveryMechanism 614 cluster := state.lastUpdate 615 switch cluster.ClusterType { 616 case xdsresource.ClusterTypeAggregate: 617 // This boolean is used to track if any of the clusters in the graph is 618 // not yet completely resolved or returns errors, thereby allowing us to 619 // traverse as much of the graph as possible (and start the associated 620 // watches where required) to ensure that clustersSeen contains all 621 // clusters in the graph that we can traverse to. 622 missingCluster := false 623 var err error 624 for _, child := range cluster.PrioritizedClusterNames { 625 var ok bool 626 dms, ok, err = b.generateDMsForCluster(child, depth+1, dms, clustersSeen) 627 if err != nil || !ok { 628 missingCluster = true 629 } 630 } 631 return dms, !missingCluster, err 632 case xdsresource.ClusterTypeEDS: 633 dm = clusterresolver.DiscoveryMechanism{ 634 Type: clusterresolver.DiscoveryMechanismTypeEDS, 635 Cluster: cluster.ClusterName, 636 EDSServiceName: cluster.EDSServiceName, 637 MaxConcurrentRequests: cluster.MaxRequests, 638 LoadReportingServer: cluster.LRSServerConfig, 639 } 640 case xdsresource.ClusterTypeLogicalDNS: 641 dm = clusterresolver.DiscoveryMechanism{ 642 Type: clusterresolver.DiscoveryMechanismTypeLogicalDNS, 643 Cluster: cluster.ClusterName, 644 DNSHostname: cluster.DNSHostName, 645 MaxConcurrentRequests: cluster.MaxRequests, 646 LoadReportingServer: cluster.LRSServerConfig, 647 } 648 } 649 odJSON := cluster.OutlierDetection 650 // "In the cds LB policy, if the outlier_detection field is not set in 651 // the Cluster resource, a "no-op" outlier_detection config will be 652 // generated in the corresponding DiscoveryMechanism config, with all 653 // fields unset." - A50 654 if odJSON == nil { 655 // This will pick up top level defaults in Cluster Resolver 656 // ParseConfig, but sre and fpe will be nil still so still a 657 // "no-op" config. 658 odJSON = json.RawMessage(`{}`) 659 } 660 dm.OutlierDetection = odJSON 661 662 dm.TelemetryLabels = cluster.TelemetryLabels 663 664 return append(dms, dm), true, nil 665 } 666 667 func (b *cdsBalancer) onClusterError(name string, err error) { 668 if b.childLB != nil { 669 b.onClusterAmbientError(name, err) 670 } else { 671 b.onClusterResourceError(name, err) 672 } 673 } 674 675 // ccWrapper wraps the balancer.ClientConn passed to the CDS balancer at 676 // creation and intercepts the NewSubConn() and UpdateAddresses() call from the 677 // child policy to add security configuration required by xDS credentials. 678 // 679 // Other methods of the balancer.ClientConn interface are not overridden and 680 // hence get the original implementation. 681 type ccWrapper struct { 682 balancer.ClientConn 683 684 xdsHIPtr *unsafe.Pointer 685 } 686 687 // NewSubConn intercepts NewSubConn() calls from the child policy and adds an 688 // address attribute which provides all information required by the xdsCreds 689 // handshaker to perform the TLS handshake. 690 func (ccw *ccWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { 691 newAddrs := make([]resolver.Address, len(addrs)) 692 for i, addr := range addrs { 693 newAddrs[i] = xdsinternal.SetHandshakeInfo(addr, ccw.xdsHIPtr) 694 } 695 696 // No need to override opts.StateListener; just forward all calls to the 697 // child that created the SubConn. 698 return ccw.ClientConn.NewSubConn(newAddrs, opts) 699 } 700 701 func (ccw *ccWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) { 702 newAddrs := make([]resolver.Address, len(addrs)) 703 for i, addr := range addrs { 704 newAddrs[i] = xdsinternal.SetHandshakeInfo(addr, ccw.xdsHIPtr) 705 } 706 ccw.ClientConn.UpdateAddresses(sc, newAddrs) 707 } 708 709 // systemRootCertsProvider implements a certprovider.Provider that returns the 710 // system default root certificates for validation. 711 type systemRootCertsProvider struct{} 712 713 func (systemRootCertsProvider) Close() {} 714 715 func (systemRootCertsProvider) KeyMaterial(context.Context) (*certprovider.KeyMaterial, error) { 716 rootCAs, err := x509SystemCertPoolFunc() 717 if err != nil { 718 return nil, err 719 } 720 return &certprovider.KeyMaterial{ 721 Roots: rootCAs, 722 }, nil 723 }