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  }