gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/xds/internal/resolver/xds_resolver.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 18 // Package resolver implements the xds resolver, that does LDS and RDS to find 19 // the cluster to use. 20 package resolver 21 22 import ( 23 "errors" 24 "fmt" 25 "strings" 26 27 "gitee.com/ks-custle/core-gm/grpc/credentials" 28 "gitee.com/ks-custle/core-gm/grpc/internal/grpclog" 29 "gitee.com/ks-custle/core-gm/grpc/internal/grpcsync" 30 "gitee.com/ks-custle/core-gm/grpc/internal/pretty" 31 iresolver "gitee.com/ks-custle/core-gm/grpc/internal/resolver" 32 "gitee.com/ks-custle/core-gm/grpc/resolver" 33 "gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient" 34 "gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/bootstrap" 35 "gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource" 36 ) 37 38 const xdsScheme = "xds" 39 40 // NewBuilder creates a new xds resolver builder using a specific xds bootstrap 41 // config, so tests can use multiple xds clients in different ClientConns at 42 // the same time. 43 func NewBuilder(config []byte) (resolver.Builder, error) { 44 return &xdsResolverBuilder{ 45 newXDSClient: func() (xdsclient.XDSClient, error) { 46 return xdsclient.NewClientWithBootstrapContents(config) 47 }, 48 }, nil 49 } 50 51 // For overriding in unittests. 52 var newXDSClient = func() (xdsclient.XDSClient, error) { return xdsclient.New() } 53 54 func init() { 55 resolver.Register(&xdsResolverBuilder{}) 56 } 57 58 type xdsResolverBuilder struct { 59 newXDSClient func() (xdsclient.XDSClient, error) 60 } 61 62 // Build helps implement the resolver.Builder interface. 63 // 64 // The xds bootstrap process is performed (and a new xds client is built) every 65 // time an xds resolver is built. 66 func (b *xdsResolverBuilder) Build(t resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (_ resolver.Resolver, retErr error) { 67 r := &xdsResolver{ 68 target: t, 69 cc: cc, 70 closed: grpcsync.NewEvent(), 71 updateCh: make(chan suWithError, 1), 72 activeClusters: make(map[string]*clusterInfo), 73 } 74 defer func() { 75 if retErr != nil { 76 r.Close() 77 } 78 }() 79 r.logger = prefixLogger(r) 80 r.logger.Infof("Creating resolver for target: %+v", t) 81 82 newXDSClient := newXDSClient 83 if b.newXDSClient != nil { 84 newXDSClient = b.newXDSClient 85 } 86 87 client, err := newXDSClient() 88 if err != nil { 89 return nil, fmt.Errorf("xds: failed to create xds-client: %v", err) 90 } 91 r.client = client 92 bootstrapConfig := client.BootstrapConfig() 93 if bootstrapConfig == nil { 94 return nil, errors.New("bootstrap configuration is empty") 95 } 96 97 // If xds credentials were specified by the user, but bootstrap configs do 98 // not contain any certificate provider configuration, it is better to fail 99 // right now rather than failing when attempting to create certificate 100 // providers after receiving an CDS response with security configuration. 101 var creds credentials.TransportCredentials 102 switch { 103 case opts.DialCreds != nil: 104 creds = opts.DialCreds 105 case opts.CredsBundle != nil: 106 creds = opts.CredsBundle.TransportCredentials() 107 } 108 if xc, ok := creds.(interface{ UsesXDS() bool }); ok && xc.UsesXDS() { 109 if len(bootstrapConfig.CertProviderConfigs) == 0 { 110 return nil, errors.New("xds: xdsCreds specified but certificate_providers config missing in bootstrap file") 111 } 112 } 113 114 // Find the client listener template to use from the bootstrap config: 115 // - If authority is not set in the target, use the top level template 116 // - If authority is set, use the template from the authority map. 117 template := bootstrapConfig.ClientDefaultListenerResourceNameTemplate 118 if authority := r.target.URL.Host; authority != "" { 119 a := bootstrapConfig.Authorities[authority] 120 if a == nil { 121 return nil, fmt.Errorf("xds: authority %q is not found in the bootstrap file", authority) 122 } 123 if a.ClientListenerResourceNameTemplate != "" { 124 // This check will never be false, because 125 // ClientListenerResourceNameTemplate is required to start with 126 // xdstp://, and has a default value (not an empty string) if unset. 127 template = a.ClientListenerResourceNameTemplate 128 } 129 } 130 endpoint := r.target.URL.Path 131 if endpoint == "" { 132 endpoint = r.target.URL.Opaque 133 } 134 endpoint = strings.TrimPrefix(endpoint, "/") 135 resourceName := bootstrap.PopulateResourceTemplate(template, endpoint) 136 137 // Register a watch on the xdsClient for the user's dial target. 138 cancelWatch := watchService(r.client, resourceName, r.handleServiceUpdate, r.logger) 139 // Endpoint is deprecated, use GetEndpoint() instead. 140 //r.logger.Infof("Watch started on resource name %v with xds-client %p", r.target.Endpoint, r.client) 141 r.logger.Infof("Watch started on resource name %v with xds-client %p", r.target.GetEndpoint(), r.client) 142 r.cancelWatch = func() { 143 cancelWatch() 144 // Endpoint is deprecated, use GetEndpoint() instead. 145 //r.logger.Infof("Watch cancel on resource name %v with xds-client %p", r.target.Endpoint, r.client) 146 r.logger.Infof("Watch cancel on resource name %v with xds-client %p", r.target.GetEndpoint(), r.client) 147 } 148 149 go r.run() 150 return r, nil 151 } 152 153 // Name helps implement the resolver.Builder interface. 154 func (*xdsResolverBuilder) Scheme() string { 155 return xdsScheme 156 } 157 158 // suWithError wraps the ServiceUpdate and error received through a watch API 159 // callback, so that it can pushed onto the update channel as a single entity. 160 type suWithError struct { 161 su serviceUpdate 162 emptyUpdate bool 163 err error 164 } 165 166 // xdsResolver implements the resolver.Resolver interface. 167 // 168 // It registers a watcher for ServiceConfig updates with the xdsClient object 169 // (which performs LDS/RDS queries for the same), and passes the received 170 // updates to the ClientConn. 171 type xdsResolver struct { 172 target resolver.Target 173 cc resolver.ClientConn 174 closed *grpcsync.Event 175 176 logger *grpclog.PrefixLogger 177 178 // The underlying xdsClient which performs all xDS requests and responses. 179 client xdsclient.XDSClient 180 // A channel for the watch API callback to write service updates on to. The 181 // updates are read by the run goroutine and passed on to the ClientConn. 182 updateCh chan suWithError 183 // cancelWatch is the function to cancel the watcher. 184 cancelWatch func() 185 186 // activeClusters is a map from cluster name to a ref count. Only read or 187 // written during a service update (synchronous). 188 activeClusters map[string]*clusterInfo 189 190 curConfigSelector *configSelector 191 } 192 193 // sendNewServiceConfig prunes active clusters, generates a new service config 194 // based on the current set of active clusters, and sends an update to the 195 // channel with that service config and the provided config selector. Returns 196 // false if an error occurs while generating the service config and the update 197 // cannot be sent. 198 func (r *xdsResolver) sendNewServiceConfig(cs *configSelector) bool { 199 // Delete entries from r.activeClusters with zero references; 200 // otherwise serviceConfigJSON will generate a config including 201 // them. 202 r.pruneActiveClusters() 203 204 if cs == nil && len(r.activeClusters) == 0 { 205 // There are no clusters and we are sending a failing configSelector. 206 // Send an empty config, which picks pick-first, with no address, and 207 // puts the ClientConn into transient failure. 208 r.cc.UpdateState(resolver.State{ServiceConfig: r.cc.ParseServiceConfig("{}")}) 209 return true 210 } 211 212 sc, err := serviceConfigJSON(r.activeClusters) 213 if err != nil { 214 // JSON marshal error; should never happen. 215 r.logger.Errorf("%v", err) 216 r.cc.ReportError(err) 217 return false 218 } 219 // Endpoint is deprecated, use GetEndpoint() instead. 220 //r.logger.Infof("Received update on resource %v from xds-client %p, generated service config: %v", r.target.Endpoint, r.client, pretty.FormatJSON(sc)) 221 r.logger.Infof("Received update on resource %v from xds-client %p, generated service config: %v", r.target.GetEndpoint(), r.client, pretty.FormatJSON(sc)) 222 223 // Send the update to the ClientConn. 224 state := iresolver.SetConfigSelector(resolver.State{ 225 ServiceConfig: r.cc.ParseServiceConfig(string(sc)), 226 }, cs) 227 r.cc.UpdateState(xdsclient.SetClient(state, r.client)) 228 return true 229 } 230 231 // run is a long running goroutine which blocks on receiving service updates 232 // and passes it on the ClientConn. 233 func (r *xdsResolver) run() { 234 for { 235 select { 236 case <-r.closed.Done(): 237 return 238 case update := <-r.updateCh: 239 if update.err != nil { 240 // Endpoint is deprecated, use GetEndpoint() instead. 241 //r.logger.Warningf("Watch error on resource %v from xds-client %p, %v", r.target.Endpoint, r.client, update.err) 242 r.logger.Warningf("Watch error on resource %v from xds-client %p, %v", r.target.GetEndpoint(), r.client, update.err) 243 if xdsresource.ErrType(update.err) == xdsresource.ErrorTypeResourceNotFound { 244 // If error is resource-not-found, it means the LDS 245 // resource was removed. Ultimately send an empty service 246 // config, which picks pick-first, with no address, and 247 // puts the ClientConn into transient failure. Before we 248 // can do that, we may need to send a normal service config 249 // along with an erroring (nil) config selector. 250 r.sendNewServiceConfig(nil) 251 // Stop and dereference the active config selector, if one exists. 252 r.curConfigSelector.stop() 253 r.curConfigSelector = nil 254 continue 255 } 256 // Send error to ClientConn, and balancers, if error is not 257 // resource not found. No need to update resolver state if we 258 // can keep using the old config. 259 r.cc.ReportError(update.err) 260 continue 261 } 262 if update.emptyUpdate { 263 r.sendNewServiceConfig(r.curConfigSelector) 264 continue 265 } 266 267 // Create the config selector for this update. 268 cs, err := r.newConfigSelector(update.su) 269 if err != nil { 270 // Endpoint is deprecated, use GetEndpoint() instead. 271 //r.logger.Warningf("Error parsing update on resource %v from xds-client %p: %v", r.target.Endpoint, r.client, err) 272 r.logger.Warningf("Error parsing update on resource %v from xds-client %p: %v", r.target.GetEndpoint(), r.client, err) 273 r.cc.ReportError(err) 274 continue 275 } 276 277 if !r.sendNewServiceConfig(cs) { 278 // JSON error creating the service config (unexpected); erase 279 // this config selector and ignore this update, continuing with 280 // the previous config selector. 281 cs.stop() 282 continue 283 } 284 285 // Decrement references to the old config selector and assign the 286 // new one as the current one. 287 r.curConfigSelector.stop() 288 r.curConfigSelector = cs 289 } 290 } 291 } 292 293 // handleServiceUpdate is the callback which handles service updates. It writes 294 // the received update to the update channel, which is picked by the run 295 // goroutine. 296 func (r *xdsResolver) handleServiceUpdate(su serviceUpdate, err error) { 297 if r.closed.HasFired() { 298 // Do not pass updates to the ClientConn once the resolver is closed. 299 return 300 } 301 // Remove any existing entry in updateCh and replace with the new one. 302 select { 303 case <-r.updateCh: 304 default: 305 } 306 r.updateCh <- suWithError{su: su, err: err} 307 } 308 309 // ResolveNow is a no-op at this point. 310 func (*xdsResolver) ResolveNow(o resolver.ResolveNowOptions) {} 311 312 // Close closes the resolver, and also closes the underlying xdsClient. 313 func (r *xdsResolver) Close() { 314 // Note that Close needs to check for nils even if some of them are always 315 // set in the constructor. This is because the constructor defers Close() in 316 // error cases, and the fields might not be set when the error happens. 317 if r.cancelWatch != nil { 318 r.cancelWatch() 319 } 320 if r.client != nil { 321 r.client.Close() 322 } 323 r.closed.Fire() 324 r.logger.Infof("Shutdown") 325 }