github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/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 "encoding/json" 22 "errors" 23 "fmt" 24 25 "github.com/hxx258456/ccgo/grpc/balancer" 26 "github.com/hxx258456/ccgo/grpc/balancer/base" 27 "github.com/hxx258456/ccgo/grpc/connectivity" 28 "github.com/hxx258456/ccgo/grpc/credentials" 29 "github.com/hxx258456/ccgo/grpc/credentials/tls/certprovider" 30 "github.com/hxx258456/ccgo/grpc/internal/buffer" 31 xdsinternal "github.com/hxx258456/ccgo/grpc/internal/credentials/xds" 32 "github.com/hxx258456/ccgo/grpc/internal/grpclog" 33 "github.com/hxx258456/ccgo/grpc/internal/grpcsync" 34 "github.com/hxx258456/ccgo/grpc/internal/pretty" 35 internalserviceconfig "github.com/hxx258456/ccgo/grpc/internal/serviceconfig" 36 "github.com/hxx258456/ccgo/grpc/resolver" 37 "github.com/hxx258456/ccgo/grpc/serviceconfig" 38 "github.com/hxx258456/ccgo/grpc/xds/internal/balancer/clusterresolver" 39 "github.com/hxx258456/ccgo/grpc/xds/internal/balancer/ringhash" 40 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient" 41 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource" 42 ) 43 44 const ( 45 cdsName = "cds_experimental" 46 ) 47 48 var ( 49 errBalancerClosed = errors.New("cdsBalancer is closed") 50 51 // newChildBalancer is a helper function to build a new cluster_resolver 52 // balancer and will be overridden in unittests. 53 newChildBalancer = func(cc balancer.ClientConn, opts balancer.BuildOptions) (balancer.Balancer, error) { 54 builder := balancer.Get(clusterresolver.Name) 55 if builder == nil { 56 return nil, fmt.Errorf("xds: no balancer builder with name %v", clusterresolver.Name) 57 } 58 // We directly pass the parent clientConn to the underlying 59 // cluster_resolver balancer because the cdsBalancer does not deal with 60 // subConns. 61 return builder.Build(cc, opts), nil 62 } 63 buildProvider = buildProviderFunc 64 ) 65 66 func init() { 67 balancer.Register(bb{}) 68 } 69 70 // bb implements the balancer.Builder interface to help build a cdsBalancer. 71 // It also implements the balancer.ConfigParser interface to help parse the 72 // JSON service config, to be passed to the cdsBalancer. 73 type bb struct{} 74 75 // Build creates a new CDS balancer with the ClientConn. 76 func (bb) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer { 77 b := &cdsBalancer{ 78 bOpts: opts, 79 updateCh: buffer.NewUnbounded(), 80 closed: grpcsync.NewEvent(), 81 done: grpcsync.NewEvent(), 82 xdsHI: xdsinternal.NewHandshakeInfo(nil, nil), 83 } 84 b.logger = prefixLogger((b)) 85 b.logger.Infof("Created") 86 var creds credentials.TransportCredentials 87 switch { 88 case opts.DialCreds != nil: 89 creds = opts.DialCreds 90 case opts.CredsBundle != nil: 91 creds = opts.CredsBundle.TransportCredentials() 92 } 93 if xc, ok := creds.(interface{ UsesXDS() bool }); ok && xc.UsesXDS() { 94 b.xdsCredsInUse = true 95 } 96 b.logger.Infof("xDS credentials in use: %v", b.xdsCredsInUse) 97 b.clusterHandler = newClusterHandler(b) 98 b.ccw = &ccWrapper{ 99 ClientConn: cc, 100 xdsHI: b.xdsHI, 101 } 102 go b.run() 103 return b 104 } 105 106 // Name returns the name of balancers built by this builder. 107 func (bb) Name() string { 108 return cdsName 109 } 110 111 // lbConfig represents the loadBalancingConfig section of the service config 112 // for the cdsBalancer. 113 type lbConfig struct { 114 serviceconfig.LoadBalancingConfig 115 ClusterName string `json:"Cluster"` 116 } 117 118 // ParseConfig parses the JSON load balancer config provided into an 119 // internal form or returns an error if the config is invalid. 120 func (bb) ParseConfig(c json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { 121 var cfg lbConfig 122 if err := json.Unmarshal(c, &cfg); err != nil { 123 return nil, fmt.Errorf("xds: unable to unmarshal lbconfig: %s, error: %v", string(c), err) 124 } 125 return &cfg, nil 126 } 127 128 // ccUpdate wraps a clientConn update received from gRPC (pushed from the 129 // xdsResolver). A valid clusterName causes the cdsBalancer to register a CDS 130 // watcher with the xdsClient, while a non-nil error causes it to cancel the 131 // existing watch and propagate the error to the underlying cluster_resolver 132 // balancer. 133 type ccUpdate struct { 134 clusterName string 135 err error 136 } 137 138 // scUpdate wraps a subConn update received from gRPC. This is directly passed 139 // on to the cluster_resolver balancer. 140 type scUpdate struct { 141 subConn balancer.SubConn 142 state balancer.SubConnState 143 } 144 145 type exitIdle struct{} 146 147 // cdsBalancer implements a CDS based LB policy. It instantiates a 148 // cluster_resolver balancer to further resolve the serviceName received from 149 // CDS, into localities and endpoints. Implements the balancer.Balancer 150 // interface which is exposed to gRPC and implements the balancer.ClientConn 151 // interface which is exposed to the cluster_resolver balancer. 152 type cdsBalancer struct { 153 ccw *ccWrapper // ClientConn interface passed to child LB. 154 bOpts balancer.BuildOptions // BuildOptions passed to child LB. 155 updateCh *buffer.Unbounded // Channel for gRPC and xdsClient updates. 156 xdsClient xdsclient.XDSClient // xDS client to watch Cluster resource. 157 clusterHandler *clusterHandler // To watch the clusters. 158 childLB balancer.Balancer 159 logger *grpclog.PrefixLogger 160 closed *grpcsync.Event 161 done *grpcsync.Event 162 163 // The certificate providers are cached here to that they can be closed when 164 // a new provider is to be created. 165 cachedRoot certprovider.Provider 166 cachedIdentity certprovider.Provider 167 xdsHI *xdsinternal.HandshakeInfo 168 xdsCredsInUse bool 169 } 170 171 // handleClientConnUpdate handles a ClientConnUpdate received from gRPC. Good 172 // updates lead to registration of a CDS watch. Updates with error lead to 173 // cancellation of existing watch and propagation of the same error to the 174 // cluster_resolver balancer. 175 func (b *cdsBalancer) handleClientConnUpdate(update *ccUpdate) { 176 // We first handle errors, if any, and then proceed with handling the 177 // update, only if the status quo has changed. 178 if err := update.err; err != nil { 179 b.handleErrorFromUpdate(err, true) 180 return 181 } 182 b.clusterHandler.updateRootCluster(update.clusterName) 183 } 184 185 // handleSecurityConfig processes the security configuration received from the 186 // management server, creates appropriate certificate provider plugins, and 187 // updates the HandhakeInfo which is added as an address attribute in 188 // NewSubConn() calls. 189 func (b *cdsBalancer) handleSecurityConfig(config *xdsresource.SecurityConfig) error { 190 // If xdsCredentials are not in use, i.e, the user did not want to get 191 // security configuration from an xDS server, we should not be acting on the 192 // received security config here. Doing so poses a security threat. 193 if !b.xdsCredsInUse { 194 return nil 195 } 196 197 // Security config being nil is a valid case where the management server has 198 // not sent any security configuration. The xdsCredentials implementation 199 // handles this by delegating to its fallback credentials. 200 if config == nil { 201 // We need to explicitly set the fields to nil here since this might be 202 // a case of switching from a good security configuration to an empty 203 // one where fallback credentials are to be used. 204 b.xdsHI.SetRootCertProvider(nil) 205 b.xdsHI.SetIdentityCertProvider(nil) 206 b.xdsHI.SetSANMatchers(nil) 207 return nil 208 } 209 210 bc := b.xdsClient.BootstrapConfig() 211 if bc == nil || bc.CertProviderConfigs == nil { 212 // Bootstrap did not find any certificate provider configs, but the user 213 // has specified xdsCredentials and the management server has sent down 214 // security configuration. 215 return errors.New("xds: certificate_providers config missing in bootstrap file") 216 } 217 cpc := bc.CertProviderConfigs 218 219 // A root provider is required whether we are using TLS or mTLS. 220 rootProvider, err := buildProvider(cpc, config.RootInstanceName, config.RootCertName, false, true) 221 if err != nil { 222 return err 223 } 224 225 // The identity provider is only present when using mTLS. 226 var identityProvider certprovider.Provider 227 if name, cert := config.IdentityInstanceName, config.IdentityCertName; name != "" { 228 var err error 229 identityProvider, err = buildProvider(cpc, name, cert, true, false) 230 if err != nil { 231 return err 232 } 233 } 234 235 // Close the old providers and cache the new ones. 236 if b.cachedRoot != nil { 237 b.cachedRoot.Close() 238 } 239 if b.cachedIdentity != nil { 240 b.cachedIdentity.Close() 241 } 242 b.cachedRoot = rootProvider 243 b.cachedIdentity = identityProvider 244 245 // We set all fields here, even if some of them are nil, since they 246 // could have been non-nil earlier. 247 b.xdsHI.SetRootCertProvider(rootProvider) 248 b.xdsHI.SetIdentityCertProvider(identityProvider) 249 b.xdsHI.SetSANMatchers(config.SubjectAltNameMatchers) 250 return nil 251 } 252 253 func buildProviderFunc(configs map[string]*certprovider.BuildableConfig, instanceName, certName string, wantIdentity, wantRoot bool) (certprovider.Provider, error) { 254 cfg, ok := configs[instanceName] 255 if !ok { 256 return nil, fmt.Errorf("certificate provider instance %q not found in bootstrap file", instanceName) 257 } 258 provider, err := cfg.Build(certprovider.BuildOptions{ 259 CertName: certName, 260 WantIdentity: wantIdentity, 261 WantRoot: wantRoot, 262 }) 263 if err != nil { 264 // This error is not expected since the bootstrap process parses the 265 // config and makes sure that it is acceptable to the plugin. Still, it 266 // is possible that the plugin parses the config successfully, but its 267 // Build() method errors out. 268 return nil, fmt.Errorf("xds: failed to get security plugin instance (%+v): %v", cfg, err) 269 } 270 return provider, nil 271 } 272 273 // handleWatchUpdate handles a watch update from the xDS Client. Good updates 274 // lead to clientConn updates being invoked on the underlying cluster_resolver balancer. 275 func (b *cdsBalancer) handleWatchUpdate(update clusterHandlerUpdate) { 276 if err := update.err; err != nil { 277 b.logger.Warningf("Watch error from xds-client %p: %v", b.xdsClient, err) 278 b.handleErrorFromUpdate(err, false) 279 return 280 } 281 282 b.logger.Infof("Watch update from xds-client %p, content: %+v, security config: %v", b.xdsClient, pretty.ToJSON(update.updates), pretty.ToJSON(update.securityCfg)) 283 284 // Process the security config from the received update before building the 285 // child policy or forwarding the update to it. We do this because the child 286 // policy may try to create a new subConn inline. Processing the security 287 // configuration here and setting up the handshakeInfo will make sure that 288 // such attempts are handled properly. 289 if err := b.handleSecurityConfig(update.securityCfg); err != nil { 290 // If the security config is invalid, for example, if the provider 291 // instance is not found in the bootstrap config, we need to put the 292 // channel in transient failure. 293 b.logger.Warningf("Invalid security config update from xds-client %p: %v", b.xdsClient, err) 294 b.handleErrorFromUpdate(err, false) 295 return 296 } 297 298 // The first good update from the watch API leads to the instantiation of an 299 // cluster_resolver balancer. Further updates/errors are propagated to the existing 300 // cluster_resolver balancer. 301 if b.childLB == nil { 302 childLB, err := newChildBalancer(b.ccw, b.bOpts) 303 if err != nil { 304 b.logger.Errorf("Failed to create child policy of type %s, %v", clusterresolver.Name, err) 305 return 306 } 307 b.childLB = childLB 308 b.logger.Infof("Created child policy %p of type %s", b.childLB, clusterresolver.Name) 309 } 310 311 dms := make([]clusterresolver.DiscoveryMechanism, len(update.updates)) 312 for i, cu := range update.updates { 313 switch cu.ClusterType { 314 case xdsresource.ClusterTypeEDS: 315 dms[i] = clusterresolver.DiscoveryMechanism{ 316 Type: clusterresolver.DiscoveryMechanismTypeEDS, 317 Cluster: cu.ClusterName, 318 EDSServiceName: cu.EDSServiceName, 319 MaxConcurrentRequests: cu.MaxRequests, 320 } 321 if cu.EnableLRS { 322 // An empty string here indicates that the cluster_resolver balancer should use the 323 // same xDS server for load reporting as it does for EDS 324 // requests/responses. 325 dms[i].LoadReportingServerName = new(string) 326 327 } 328 case xdsresource.ClusterTypeLogicalDNS: 329 dms[i] = clusterresolver.DiscoveryMechanism{ 330 Type: clusterresolver.DiscoveryMechanismTypeLogicalDNS, 331 DNSHostname: cu.DNSHostName, 332 } 333 default: 334 b.logger.Infof("unexpected cluster type %v when handling update from cluster handler", cu.ClusterType) 335 } 336 } 337 lbCfg := &clusterresolver.LBConfig{ 338 DiscoveryMechanisms: dms, 339 } 340 341 // lbPolicy is set only when the policy is ringhash. The default (when it's 342 // not set) is roundrobin. And similarly, we only need to set XDSLBPolicy 343 // for ringhash (it also defaults to roundrobin). 344 if lbp := update.lbPolicy; lbp != nil { 345 lbCfg.XDSLBPolicy = &internalserviceconfig.BalancerConfig{ 346 Name: ringhash.Name, 347 Config: &ringhash.LBConfig{ 348 MinRingSize: lbp.MinimumRingSize, 349 MaxRingSize: lbp.MaximumRingSize, 350 }, 351 } 352 } 353 354 ccState := balancer.ClientConnState{ 355 ResolverState: xdsclient.SetClient(resolver.State{}, b.xdsClient), 356 BalancerConfig: lbCfg, 357 } 358 if err := b.childLB.UpdateClientConnState(ccState); err != nil { 359 b.logger.Errorf("xds: cluster_resolver balancer.UpdateClientConnState(%+v) returned error: %v", ccState, err) 360 } 361 } 362 363 // run is a long-running goroutine which handles all updates from gRPC. All 364 // methods which are invoked directly by gRPC or xdsClient simply push an 365 // update onto a channel which is read and acted upon right here. 366 func (b *cdsBalancer) run() { 367 for { 368 select { 369 case u := <-b.updateCh.Get(): 370 b.updateCh.Load() 371 switch update := u.(type) { 372 case *ccUpdate: 373 b.handleClientConnUpdate(update) 374 case *scUpdate: 375 // SubConn updates are passthrough and are simply handed over to 376 // the underlying cluster_resolver balancer. 377 if b.childLB == nil { 378 b.logger.Errorf("xds: received scUpdate {%+v} with no cluster_resolver balancer", update) 379 break 380 } 381 b.childLB.UpdateSubConnState(update.subConn, update.state) 382 case exitIdle: 383 if b.childLB == nil { 384 b.logger.Errorf("xds: received ExitIdle with no child balancer") 385 break 386 } 387 // This implementation assumes the child balancer supports 388 // ExitIdle (but still checks for the interface's existence to 389 // avoid a panic if not). If the child does not, no subconns 390 // will be connected. 391 if ei, ok := b.childLB.(balancer.ExitIdler); ok { 392 ei.ExitIdle() 393 } 394 } 395 case u := <-b.clusterHandler.updateChannel: 396 b.handleWatchUpdate(u) 397 case <-b.closed.Done(): 398 b.clusterHandler.close() 399 if b.childLB != nil { 400 b.childLB.Close() 401 b.childLB = nil 402 } 403 if b.cachedRoot != nil { 404 b.cachedRoot.Close() 405 } 406 if b.cachedIdentity != nil { 407 b.cachedIdentity.Close() 408 } 409 b.logger.Infof("Shutdown") 410 b.done.Fire() 411 return 412 } 413 } 414 } 415 416 // handleErrorFromUpdate handles both the error from parent ClientConn (from 417 // resolver) and the error from xds client (from the watcher). fromParent is 418 // true if error is from parent ClientConn. 419 // 420 // If the error is connection error, it's passed down to the child policy. 421 // Nothing needs to be done in CDS (e.g. it doesn't go into fallback). 422 // 423 // If the error is resource-not-found: 424 // - If it's from resolver, it means LDS resources were removed. The CDS watch 425 // should be canceled. 426 // - If it's from xds client, it means CDS resource were removed. The CDS 427 // watcher should keep watching. 428 // 429 // In both cases, the error will be forwarded to the child balancer. And if 430 // error is resource-not-found, the child balancer will stop watching EDS. 431 func (b *cdsBalancer) handleErrorFromUpdate(err error, fromParent bool) { 432 // This is not necessary today, because xds client never sends connection 433 // errors. 434 if fromParent && xdsresource.ErrType(err) == xdsresource.ErrorTypeResourceNotFound { 435 b.clusterHandler.close() 436 } 437 if b.childLB != nil { 438 if xdsresource.ErrType(err) != xdsresource.ErrorTypeConnection { 439 // Connection errors will be sent to the child balancers directly. 440 // There's no need to forward them. 441 b.childLB.ResolverError(err) 442 } 443 } else { 444 // If child balancer was never created, fail the RPCs with 445 // errors. 446 b.ccw.UpdateState(balancer.State{ 447 ConnectivityState: connectivity.TransientFailure, 448 Picker: base.NewErrPicker(err), 449 }) 450 } 451 } 452 453 // UpdateClientConnState receives the serviceConfig (which contains the 454 // clusterName to watch for in CDS) and the xdsClient object from the 455 // xdsResolver. 456 func (b *cdsBalancer) UpdateClientConnState(state balancer.ClientConnState) error { 457 if b.closed.HasFired() { 458 b.logger.Warningf("xds: received ClientConnState {%+v} after cdsBalancer was closed", state) 459 return errBalancerClosed 460 } 461 462 if b.xdsClient == nil { 463 c := xdsclient.FromResolverState(state.ResolverState) 464 if c == nil { 465 return balancer.ErrBadResolverState 466 } 467 b.xdsClient = c 468 } 469 470 b.logger.Infof("Received update from resolver, balancer config: %+v", pretty.ToJSON(state.BalancerConfig)) 471 // The errors checked here should ideally never happen because the 472 // ServiceConfig in this case is prepared by the xdsResolver and is not 473 // something that is received on the wire. 474 lbCfg, ok := state.BalancerConfig.(*lbConfig) 475 if !ok { 476 b.logger.Warningf("xds: unexpected LoadBalancingConfig type: %T", state.BalancerConfig) 477 return balancer.ErrBadResolverState 478 } 479 if lbCfg.ClusterName == "" { 480 b.logger.Warningf("xds: no clusterName found in LoadBalancingConfig: %+v", lbCfg) 481 return balancer.ErrBadResolverState 482 } 483 b.updateCh.Put(&ccUpdate{clusterName: lbCfg.ClusterName}) 484 return nil 485 } 486 487 // ResolverError handles errors reported by the xdsResolver. 488 func (b *cdsBalancer) ResolverError(err error) { 489 if b.closed.HasFired() { 490 b.logger.Warningf("xds: received resolver error {%v} after cdsBalancer was closed", err) 491 return 492 } 493 b.updateCh.Put(&ccUpdate{err: err}) 494 } 495 496 // UpdateSubConnState handles subConn updates from gRPC. 497 func (b *cdsBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { 498 if b.closed.HasFired() { 499 b.logger.Warningf("xds: received subConn update {%v, %v} after cdsBalancer was closed", sc, state) 500 return 501 } 502 b.updateCh.Put(&scUpdate{subConn: sc, state: state}) 503 } 504 505 // Close cancels the CDS watch, closes the child policy and closes the 506 // cdsBalancer. 507 func (b *cdsBalancer) Close() { 508 b.closed.Fire() 509 <-b.done.Done() 510 } 511 512 func (b *cdsBalancer) ExitIdle() { 513 b.updateCh.Put(exitIdle{}) 514 } 515 516 // ccWrapper wraps the balancer.ClientConn passed to the CDS balancer at 517 // creation and intercepts the NewSubConn() and UpdateAddresses() call from the 518 // child policy to add security configuration required by xDS credentials. 519 // 520 // Other methods of the balancer.ClientConn interface are not overridden and 521 // hence get the original implementation. 522 type ccWrapper struct { 523 balancer.ClientConn 524 525 // The certificate providers in this HandshakeInfo are updated based on the 526 // received security configuration in the Cluster resource. 527 xdsHI *xdsinternal.HandshakeInfo 528 } 529 530 // NewSubConn intercepts NewSubConn() calls from the child policy and adds an 531 // address attribute which provides all information required by the xdsCreds 532 // handshaker to perform the TLS handshake. 533 func (ccw *ccWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { 534 newAddrs := make([]resolver.Address, len(addrs)) 535 for i, addr := range addrs { 536 newAddrs[i] = xdsinternal.SetHandshakeInfo(addr, ccw.xdsHI) 537 } 538 return ccw.ClientConn.NewSubConn(newAddrs, opts) 539 } 540 541 func (ccw *ccWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) { 542 newAddrs := make([]resolver.Address, len(addrs)) 543 for i, addr := range addrs { 544 newAddrs[i] = xdsinternal.SetHandshakeInfo(addr, ccw.xdsHI) 545 } 546 ccw.ClientConn.UpdateAddresses(sc, newAddrs) 547 }