github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/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  	"github.com/hxx258456/ccgo/grpc/credentials"
    28  	"github.com/hxx258456/ccgo/grpc/internal/grpclog"
    29  	"github.com/hxx258456/ccgo/grpc/internal/grpcsync"
    30  	"github.com/hxx258456/ccgo/grpc/internal/pretty"
    31  	iresolver "github.com/hxx258456/ccgo/grpc/internal/resolver"
    32  	"github.com/hxx258456/ccgo/grpc/resolver"
    33  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient"
    34  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/bootstrap"
    35  	"github.com/hxx258456/ccgo/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  	r.logger.Infof("Watch started on resource name %v with xds-client %p", r.target.Endpoint, r.client)
   140  	r.cancelWatch = func() {
   141  		cancelWatch()
   142  		r.logger.Infof("Watch cancel on resource name %v with xds-client %p", r.target.Endpoint, r.client)
   143  	}
   144  
   145  	go r.run()
   146  	return r, nil
   147  }
   148  
   149  // Name helps implement the resolver.Builder interface.
   150  func (*xdsResolverBuilder) Scheme() string {
   151  	return xdsScheme
   152  }
   153  
   154  // suWithError wraps the ServiceUpdate and error received through a watch API
   155  // callback, so that it can pushed onto the update channel as a single entity.
   156  type suWithError struct {
   157  	su          serviceUpdate
   158  	emptyUpdate bool
   159  	err         error
   160  }
   161  
   162  // xdsResolver implements the resolver.Resolver interface.
   163  //
   164  // It registers a watcher for ServiceConfig updates with the xdsClient object
   165  // (which performs LDS/RDS queries for the same), and passes the received
   166  // updates to the ClientConn.
   167  type xdsResolver struct {
   168  	target resolver.Target
   169  	cc     resolver.ClientConn
   170  	closed *grpcsync.Event
   171  
   172  	logger *grpclog.PrefixLogger
   173  
   174  	// The underlying xdsClient which performs all xDS requests and responses.
   175  	client xdsclient.XDSClient
   176  	// A channel for the watch API callback to write service updates on to. The
   177  	// updates are read by the run goroutine and passed on to the ClientConn.
   178  	updateCh chan suWithError
   179  	// cancelWatch is the function to cancel the watcher.
   180  	cancelWatch func()
   181  
   182  	// activeClusters is a map from cluster name to a ref count.  Only read or
   183  	// written during a service update (synchronous).
   184  	activeClusters map[string]*clusterInfo
   185  
   186  	curConfigSelector *configSelector
   187  }
   188  
   189  // sendNewServiceConfig prunes active clusters, generates a new service config
   190  // based on the current set of active clusters, and sends an update to the
   191  // channel with that service config and the provided config selector.  Returns
   192  // false if an error occurs while generating the service config and the update
   193  // cannot be sent.
   194  func (r *xdsResolver) sendNewServiceConfig(cs *configSelector) bool {
   195  	// Delete entries from r.activeClusters with zero references;
   196  	// otherwise serviceConfigJSON will generate a config including
   197  	// them.
   198  	r.pruneActiveClusters()
   199  
   200  	if cs == nil && len(r.activeClusters) == 0 {
   201  		// There are no clusters and we are sending a failing configSelector.
   202  		// Send an empty config, which picks pick-first, with no address, and
   203  		// puts the ClientConn into transient failure.
   204  		r.cc.UpdateState(resolver.State{ServiceConfig: r.cc.ParseServiceConfig("{}")})
   205  		return true
   206  	}
   207  
   208  	sc, err := serviceConfigJSON(r.activeClusters)
   209  	if err != nil {
   210  		// JSON marshal error; should never happen.
   211  		r.logger.Errorf("%v", err)
   212  		r.cc.ReportError(err)
   213  		return false
   214  	}
   215  	r.logger.Infof("Received update on resource %v from xds-client %p, generated service config: %v", r.target.Endpoint, r.client, pretty.FormatJSON(sc))
   216  
   217  	// Send the update to the ClientConn.
   218  	state := iresolver.SetConfigSelector(resolver.State{
   219  		ServiceConfig: r.cc.ParseServiceConfig(string(sc)),
   220  	}, cs)
   221  	r.cc.UpdateState(xdsclient.SetClient(state, r.client))
   222  	return true
   223  }
   224  
   225  // run is a long running goroutine which blocks on receiving service updates
   226  // and passes it on the ClientConn.
   227  func (r *xdsResolver) run() {
   228  	for {
   229  		select {
   230  		case <-r.closed.Done():
   231  			return
   232  		case update := <-r.updateCh:
   233  			if update.err != nil {
   234  				r.logger.Warningf("Watch error on resource %v from xds-client %p, %v", r.target.Endpoint, r.client, update.err)
   235  				if xdsresource.ErrType(update.err) == xdsresource.ErrorTypeResourceNotFound {
   236  					// If error is resource-not-found, it means the LDS
   237  					// resource was removed. Ultimately send an empty service
   238  					// config, which picks pick-first, with no address, and
   239  					// puts the ClientConn into transient failure.  Before we
   240  					// can do that, we may need to send a normal service config
   241  					// along with an erroring (nil) config selector.
   242  					r.sendNewServiceConfig(nil)
   243  					// Stop and dereference the active config selector, if one exists.
   244  					r.curConfigSelector.stop()
   245  					r.curConfigSelector = nil
   246  					continue
   247  				}
   248  				// Send error to ClientConn, and balancers, if error is not
   249  				// resource not found.  No need to update resolver state if we
   250  				// can keep using the old config.
   251  				r.cc.ReportError(update.err)
   252  				continue
   253  			}
   254  			if update.emptyUpdate {
   255  				r.sendNewServiceConfig(r.curConfigSelector)
   256  				continue
   257  			}
   258  
   259  			// Create the config selector for this update.
   260  			cs, err := r.newConfigSelector(update.su)
   261  			if err != nil {
   262  				r.logger.Warningf("Error parsing update on resource %v from xds-client %p: %v", r.target.Endpoint, r.client, err)
   263  				r.cc.ReportError(err)
   264  				continue
   265  			}
   266  
   267  			if !r.sendNewServiceConfig(cs) {
   268  				// JSON error creating the service config (unexpected); erase
   269  				// this config selector and ignore this update, continuing with
   270  				// the previous config selector.
   271  				cs.stop()
   272  				continue
   273  			}
   274  
   275  			// Decrement references to the old config selector and assign the
   276  			// new one as the current one.
   277  			r.curConfigSelector.stop()
   278  			r.curConfigSelector = cs
   279  		}
   280  	}
   281  }
   282  
   283  // handleServiceUpdate is the callback which handles service updates. It writes
   284  // the received update to the update channel, which is picked by the run
   285  // goroutine.
   286  func (r *xdsResolver) handleServiceUpdate(su serviceUpdate, err error) {
   287  	if r.closed.HasFired() {
   288  		// Do not pass updates to the ClientConn once the resolver is closed.
   289  		return
   290  	}
   291  	// Remove any existing entry in updateCh and replace with the new one.
   292  	select {
   293  	case <-r.updateCh:
   294  	default:
   295  	}
   296  	r.updateCh <- suWithError{su: su, err: err}
   297  }
   298  
   299  // ResolveNow is a no-op at this point.
   300  func (*xdsResolver) ResolveNow(o resolver.ResolveNowOptions) {}
   301  
   302  // Close closes the resolver, and also closes the underlying xdsClient.
   303  func (r *xdsResolver) Close() {
   304  	// Note that Close needs to check for nils even if some of them are always
   305  	// set in the constructor. This is because the constructor defers Close() in
   306  	// error cases, and the fields might not be set when the error happens.
   307  	if r.cancelWatch != nil {
   308  		r.cancelWatch()
   309  	}
   310  	if r.client != nil {
   311  		r.client.Close()
   312  	}
   313  	r.closed.Fire()
   314  	r.logger.Infof("Shutdown")
   315  }