google.golang.org/grpc@v1.72.2/xds/internal/xdsclient/authority.go (about)

     1  /*
     2   *
     3   * Copyright 2021 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package xdsclient
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"sync"
    24  	"sync/atomic"
    25  
    26  	"google.golang.org/grpc/experimental/stats"
    27  	"google.golang.org/grpc/grpclog"
    28  	igrpclog "google.golang.org/grpc/internal/grpclog"
    29  	"google.golang.org/grpc/internal/grpcsync"
    30  	"google.golang.org/grpc/internal/xds/bootstrap"
    31  	"google.golang.org/grpc/xds/internal/xdsclient/transport/ads"
    32  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource"
    33  	"google.golang.org/protobuf/types/known/anypb"
    34  	"google.golang.org/protobuf/types/known/timestamppb"
    35  
    36  	v3adminpb "github.com/envoyproxy/go-control-plane/envoy/admin/v3"
    37  	v3statuspb "github.com/envoyproxy/go-control-plane/envoy/service/status/v3"
    38  )
    39  
    40  type resourceState struct {
    41  	watchers          map[xdsresource.ResourceWatcher]bool // Set of watchers for this resource.
    42  	cache             xdsresource.ResourceData             // Most recent ACKed update for this resource.
    43  	md                xdsresource.UpdateMetadata           // Metadata for the most recent update.
    44  	deletionIgnored   bool                                 // True, if resource deletion was ignored for a prior update.
    45  	xdsChannelConfigs map[*xdsChannelWithConfig]bool       // Set of xdsChannels where this resource is subscribed.
    46  }
    47  
    48  // xdsChannelForADS is used to acquire a reference to an xdsChannel. This
    49  // functionality is provided by the xdsClient.
    50  //
    51  // The arguments to the function are as follows:
    52  //   - the server config for the xdsChannel
    53  //   - the calling authority on which a set of callbacks are invoked by the
    54  //     xdsChannel on ADS stream events
    55  //
    56  // Returns a reference to the xdsChannel and a function to release the same. A
    57  // non-nil error is returned if the channel creation fails and the first two
    58  // return values are meaningless in this case.
    59  type xdsChannelForADS func(*bootstrap.ServerConfig, *authority) (*xdsChannel, func(), error)
    60  
    61  // xdsChannelWithConfig is a struct that holds an xdsChannel and its associated
    62  // ServerConfig, along with a cleanup function to release the xdsChannel.
    63  type xdsChannelWithConfig struct {
    64  	channel      *xdsChannel
    65  	serverConfig *bootstrap.ServerConfig
    66  	cleanup      func()
    67  }
    68  
    69  // authority provides the functionality required to communicate with a
    70  // management server corresponding to an authority name specified in the
    71  // bootstrap configuration.
    72  //
    73  // It holds references to one or more xdsChannels, one for each server
    74  // configuration in the bootstrap, to allow fallback from a primary management
    75  // server to a secondary management server. Authorities that contain similar
    76  // server configuration entries will end up sharing the xdsChannel for that
    77  // server configuration. The xdsChannels are owned and managed by the xdsClient.
    78  //
    79  // It also contains a cache of resource state for resources requested from
    80  // management server(s). This cache contains the list of registered watchers and
    81  // the most recent resource configuration received from the management server.
    82  type authority struct {
    83  	// The following fields are initialized at creation time and are read-only
    84  	// afterwards, and therefore don't need to be protected with a mutex.
    85  	name                      string                       // Name of the authority from bootstrap configuration.
    86  	watcherCallbackSerializer *grpcsync.CallbackSerializer // Serializer to run watcher callbacks, owned by the xDS client implementation.
    87  	getChannelForADS          xdsChannelForADS             // Function to get an xdsChannel for ADS, provided by the xDS client implementation.
    88  	xdsClientSerializer       *grpcsync.CallbackSerializer // Serializer to run call ins from the xDS client, owned by this authority.
    89  	xdsClientSerializerClose  func()                       // Function to close the above serializer.
    90  	logger                    *igrpclog.PrefixLogger       // Logger for this authority.
    91  	target                    string                       // The gRPC Channel target.
    92  	metricsRecorder           stats.MetricsRecorder        // The metrics recorder used for emitting metrics.
    93  
    94  	// The below defined fields must only be accessed in the context of the
    95  	// serializer callback, owned by this authority.
    96  
    97  	// A two level map containing the state of all the resources being watched.
    98  	//
    99  	// The first level map key is the ResourceType (Listener, Route etc). This
   100  	// allows us to have a single map for all resources instead of having per
   101  	// resource-type maps.
   102  	//
   103  	// The second level map key is the resource name, with the value being the
   104  	// actual state of the resource.
   105  	resources map[xdsresource.Type]map[string]*resourceState
   106  
   107  	// An ordered list of xdsChannels corresponding to the list of server
   108  	// configurations specified for this authority in the bootstrap. The
   109  	// ordering specifies the order in which these channels are preferred for
   110  	// fallback.
   111  	xdsChannelConfigs []*xdsChannelWithConfig
   112  
   113  	// The current active xdsChannel. Here, active does not mean that the
   114  	// channel has a working connection to the server. It simply points to the
   115  	// channel that we are trying to work with, based on fallback logic.
   116  	activeXDSChannel *xdsChannelWithConfig
   117  }
   118  
   119  // authorityBuildOptions wraps arguments required to create a new authority.
   120  type authorityBuildOptions struct {
   121  	serverConfigs    bootstrap.ServerConfigs      // Server configs for the authority
   122  	name             string                       // Name of the authority
   123  	serializer       *grpcsync.CallbackSerializer // Callback serializer for invoking watch callbacks
   124  	getChannelForADS xdsChannelForADS             // Function to acquire a reference to an xdsChannel
   125  	logPrefix        string                       // Prefix for logging
   126  	target           string                       // Target for the gRPC Channel that owns xDS Client/Authority
   127  	metricsRecorder  stats.MetricsRecorder        // metricsRecorder to emit metrics
   128  }
   129  
   130  // newAuthority creates a new authority instance with the provided
   131  // configuration. The authority is responsible for managing the state of
   132  // resources requested from the management server, as well as acquiring and
   133  // releasing references to channels used to communicate with the management
   134  // server.
   135  //
   136  // Note that no channels to management servers are created at this time. Instead
   137  // a channel to the first server configuration is created when the first watch
   138  // is registered, and more channels are created as needed by the fallback logic.
   139  func newAuthority(args authorityBuildOptions) *authority {
   140  	ctx, cancel := context.WithCancel(context.Background())
   141  	l := grpclog.Component("xds")
   142  	logPrefix := args.logPrefix + fmt.Sprintf("[authority %q] ", args.name)
   143  	ret := &authority{
   144  		name:                      args.name,
   145  		watcherCallbackSerializer: args.serializer,
   146  		getChannelForADS:          args.getChannelForADS,
   147  		xdsClientSerializer:       grpcsync.NewCallbackSerializer(ctx),
   148  		xdsClientSerializerClose:  cancel,
   149  		logger:                    igrpclog.NewPrefixLogger(l, logPrefix),
   150  		resources:                 make(map[xdsresource.Type]map[string]*resourceState),
   151  		target:                    args.target,
   152  		metricsRecorder:           args.metricsRecorder,
   153  	}
   154  
   155  	// Create an ordered list of xdsChannels with their server configs. The
   156  	// actual channel to the first server configuration is created when the
   157  	// first watch is registered, and channels to other server configurations
   158  	// are created as needed to support fallback.
   159  	for _, sc := range args.serverConfigs {
   160  		ret.xdsChannelConfigs = append(ret.xdsChannelConfigs, &xdsChannelWithConfig{serverConfig: sc})
   161  	}
   162  	return ret
   163  }
   164  
   165  // adsStreamFailure is called to notify the authority about an ADS stream
   166  // failure on an xdsChannel to the management server identified by the provided
   167  // server config. The error is forwarded to all the resource watchers.
   168  //
   169  // This method is called by the xDS client implementation (on all interested
   170  // authorities) when a stream error is reported by an xdsChannel.
   171  //
   172  // Errors of type xdsresource.ErrTypeStreamFailedAfterRecv are ignored.
   173  func (a *authority) adsStreamFailure(serverConfig *bootstrap.ServerConfig, err error) {
   174  	a.xdsClientSerializer.TrySchedule(func(context.Context) {
   175  		a.handleADSStreamFailure(serverConfig, err)
   176  	})
   177  }
   178  
   179  // Handles ADS stream failure by invoking watch callbacks and triggering
   180  // fallback if the associated conditions are met.
   181  //
   182  // Only executed in the context of a serializer callback.
   183  func (a *authority) handleADSStreamFailure(serverConfig *bootstrap.ServerConfig, err error) {
   184  	if a.logger.V(2) {
   185  		a.logger.Infof("Connection to server %s failed with error: %v", serverConfig, err)
   186  	}
   187  
   188  	// We do not consider it an error if the ADS stream was closed after having
   189  	// received a response on the stream. This is because there are legitimate
   190  	// reasons why the server may need to close the stream during normal
   191  	// operations, such as needing to rebalance load or the underlying
   192  	// connection hitting its max connection age limit. See gRFC A57 for more
   193  	// details.
   194  	if xdsresource.ErrType(err) == xdsresource.ErrTypeStreamFailedAfterRecv {
   195  		a.logger.Warningf("Watchers not notified since ADS stream failed after having received at least one response: %v", err)
   196  		return
   197  	}
   198  
   199  	// Two conditions need to be met for fallback to be triggered:
   200  	// 1. There is a connectivity failure on the ADS stream, as described in
   201  	//    gRFC A57. For us, this means that the ADS stream was closed before the
   202  	//    first server response was received. We already checked that condition
   203  	//    earlier in this method.
   204  	// 2. There is at least one watcher for a resource that is not cached.
   205  	//    Cached resources include ones that
   206  	//    - have been successfully received and can be used.
   207  	//    - are considered non-existent according to xDS Protocol Specification.
   208  	if !a.watcherExistsForUncachedResource() {
   209  		if a.logger.V(2) {
   210  			a.logger.Infof("No watchers for uncached resources. Not triggering fallback")
   211  		}
   212  		// Since we are not triggering fallback, propagate the connectivity
   213  		// error to all watchers and return early.
   214  		a.propagateConnectivityErrorToAllWatchers(err)
   215  		return
   216  	}
   217  
   218  	// Attempt to fallback to servers with lower priority than the failing one.
   219  	currentServerIdx := a.serverIndexForConfig(serverConfig)
   220  	for i := currentServerIdx + 1; i < len(a.xdsChannelConfigs); i++ {
   221  		if a.fallbackToServer(a.xdsChannelConfigs[i]) {
   222  			// Since we have successfully triggered fallback, we don't have to
   223  			// notify watchers about the connectivity error.
   224  			return
   225  		}
   226  	}
   227  
   228  	// Having exhausted all available servers, we must notify watchers of the
   229  	// connectivity error - A71.
   230  	a.propagateConnectivityErrorToAllWatchers(err)
   231  }
   232  
   233  // propagateConnectivityErrorToAllWatchers propagates the given connection error
   234  // to all watchers of all resources.
   235  //
   236  // Only executed in the context of a serializer callback.
   237  func (a *authority) propagateConnectivityErrorToAllWatchers(err error) {
   238  	for _, rType := range a.resources {
   239  		for _, state := range rType {
   240  			for watcher := range state.watchers {
   241  				a.watcherCallbackSerializer.TrySchedule(func(context.Context) {
   242  					watcher.OnError(xdsresource.NewErrorf(xdsresource.ErrorTypeConnection, "xds: error received from xDS stream: %v", err), func() {})
   243  				})
   244  			}
   245  		}
   246  	}
   247  }
   248  
   249  // serverIndexForConfig returns the index of the xdsChannelConfig matching the
   250  // provided server config, panicking if no match is found (which indicates a
   251  // programming error).
   252  func (a *authority) serverIndexForConfig(sc *bootstrap.ServerConfig) int {
   253  	for i, cfg := range a.xdsChannelConfigs {
   254  		if cfg.serverConfig.Equal(sc) {
   255  			return i
   256  		}
   257  	}
   258  	panic(fmt.Sprintf("no server config matching %v found", sc))
   259  }
   260  
   261  // Determines the server to fallback to and triggers fallback to the same. If
   262  // required, creates an xdsChannel to that server, and re-subscribes to all
   263  // existing resources.
   264  //
   265  // Only executed in the context of a serializer callback.
   266  func (a *authority) fallbackToServer(xc *xdsChannelWithConfig) bool {
   267  	if a.logger.V(2) {
   268  		a.logger.Infof("Attempting to initiate fallback to server %q", xc.serverConfig)
   269  	}
   270  
   271  	if xc.channel != nil {
   272  		if a.logger.V(2) {
   273  			a.logger.Infof("Channel to the next server in the list %q already exists", xc.serverConfig)
   274  		}
   275  		return false
   276  	}
   277  
   278  	channel, cleanup, err := a.getChannelForADS(xc.serverConfig, a)
   279  	if err != nil {
   280  		a.logger.Errorf("Failed to create xDS channel: %v", err)
   281  		return false
   282  	}
   283  	xc.channel = channel
   284  	xc.cleanup = cleanup
   285  	a.activeXDSChannel = xc
   286  
   287  	// Subscribe to all existing resources from the new management server.
   288  	for typ, resources := range a.resources {
   289  		for name, state := range resources {
   290  			if a.logger.V(2) {
   291  				a.logger.Infof("Resubscribing to resource of type %q and name %q", typ.TypeName(), name)
   292  			}
   293  			xc.channel.subscribe(typ, name)
   294  
   295  			// Add the new channel to the list of xdsChannels from which this
   296  			// resource has been requested from. Retain the cached resource and
   297  			// the set of existing watchers (and other metadata fields) in the
   298  			// resource state.
   299  			state.xdsChannelConfigs[xc] = true
   300  		}
   301  	}
   302  	return true
   303  }
   304  
   305  // adsResourceUpdate is called to notify the authority about a resource update
   306  // received on the ADS stream.
   307  //
   308  // This method is called by the xDS client implementation (on all interested
   309  // authorities) when a stream error is reported by an xdsChannel.
   310  func (a *authority) adsResourceUpdate(serverConfig *bootstrap.ServerConfig, rType xdsresource.Type, updates map[string]ads.DataAndErrTuple, md xdsresource.UpdateMetadata, onDone func()) {
   311  	a.xdsClientSerializer.TrySchedule(func(context.Context) {
   312  		a.handleADSResourceUpdate(serverConfig, rType, updates, md, onDone)
   313  	})
   314  }
   315  
   316  // handleADSResourceUpdate processes an update from the xDS client, updating the
   317  // resource cache and notifying any registered watchers of the update.
   318  //
   319  // If the update is received from a higher priority xdsChannel that was
   320  // previously down, we revert to it and close all lower priority xdsChannels.
   321  //
   322  // Once the update has been processed by all watchers, the authority is expected
   323  // to invoke the onDone callback.
   324  //
   325  // Only executed in the context of a serializer callback.
   326  func (a *authority) handleADSResourceUpdate(serverConfig *bootstrap.ServerConfig, rType xdsresource.Type, updates map[string]ads.DataAndErrTuple, md xdsresource.UpdateMetadata, onDone func()) {
   327  	a.handleRevertingToPrimaryOnUpdate(serverConfig)
   328  
   329  	// We build a list of callback funcs to invoke, and invoke them at the end
   330  	// of this method instead of inline (when handling the update for a
   331  	// particular resource), because we want to make sure that all calls to
   332  	// increment watcherCnt happen before any callbacks are invoked. This will
   333  	// ensure that the onDone callback is never invoked before all watcher
   334  	// callbacks are invoked, and the watchers have processed the update.
   335  	watcherCnt := new(atomic.Int64)
   336  	done := func() {
   337  		if watcherCnt.Add(-1) == 0 {
   338  			onDone()
   339  		}
   340  	}
   341  	funcsToSchedule := []func(context.Context){}
   342  	defer func() {
   343  		if len(funcsToSchedule) == 0 {
   344  			// When there are no watchers for the resources received as part of
   345  			// this update, invoke onDone explicitly to unblock the next read on
   346  			// the ADS stream.
   347  			onDone()
   348  			return
   349  		}
   350  		for _, f := range funcsToSchedule {
   351  			a.watcherCallbackSerializer.ScheduleOr(f, onDone)
   352  		}
   353  	}()
   354  
   355  	resourceStates := a.resources[rType]
   356  	for name, uErr := range updates {
   357  		state, ok := resourceStates[name]
   358  		if !ok {
   359  			continue
   360  		}
   361  
   362  		// On error, keep previous version of the resource. But update status
   363  		// and error.
   364  		if uErr.Err != nil {
   365  			xdsClientResourceUpdatesInvalidMetric.Record(a.metricsRecorder, 1, a.target, serverConfig.ServerURI(), rType.TypeName())
   366  			state.md.ErrState = md.ErrState
   367  			state.md.Status = md.Status
   368  			for watcher := range state.watchers {
   369  				watcher := watcher
   370  				err := uErr.Err
   371  				watcherCnt.Add(1)
   372  				funcsToSchedule = append(funcsToSchedule, func(context.Context) { watcher.OnError(err, done) })
   373  			}
   374  			continue
   375  		}
   376  
   377  		xdsClientResourceUpdatesValidMetric.Record(a.metricsRecorder, 1, a.target, serverConfig.ServerURI(), rType.TypeName())
   378  
   379  		if state.deletionIgnored {
   380  			state.deletionIgnored = false
   381  			a.logger.Infof("A valid update was received for resource %q of type %q after previously ignoring a deletion", name, rType.TypeName())
   382  		}
   383  		// Notify watchers if any of these conditions are met:
   384  		//   - this is the first update for this resource
   385  		//   - this update is different from the one currently cached
   386  		//   - the previous update for this resource was NACKed, but the update
   387  		//     before that was the same as this update.
   388  		if state.cache == nil || !state.cache.RawEqual(uErr.Resource) || state.md.ErrState != nil {
   389  			// Update the resource cache.
   390  			if a.logger.V(2) {
   391  				a.logger.Infof("Resource type %q with name %q added to cache", rType.TypeName(), name)
   392  			}
   393  			state.cache = uErr.Resource
   394  
   395  			for watcher := range state.watchers {
   396  				watcher := watcher
   397  				resource := uErr.Resource
   398  				watcherCnt.Add(1)
   399  				funcsToSchedule = append(funcsToSchedule, func(context.Context) { watcher.OnUpdate(resource, done) })
   400  			}
   401  		}
   402  
   403  		// Set status to ACK, and clear error state. The metadata might be a
   404  		// NACK metadata because some other resources in the same response
   405  		// are invalid.
   406  		state.md = md
   407  		state.md.ErrState = nil
   408  		state.md.Status = xdsresource.ServiceStatusACKed
   409  		if md.ErrState != nil {
   410  			state.md.Version = md.ErrState.Version
   411  		}
   412  	}
   413  
   414  	// If this resource type requires that all resources be present in every
   415  	// SotW response from the server, a response that does not include a
   416  	// previously seen resource will be interpreted as a deletion of that
   417  	// resource unless ignore_resource_deletion option was set in the server
   418  	// config.
   419  	if !rType.AllResourcesRequiredInSotW() {
   420  		return
   421  	}
   422  	for name, state := range resourceStates {
   423  		if state.cache == nil {
   424  			// If the resource state does not contain a cached update, which can
   425  			// happen when:
   426  			// - resource was newly requested but has not yet been received, or,
   427  			// - resource was removed as part of a previous update,
   428  			// we don't want to generate an error for the watchers.
   429  			//
   430  			// For the first of the above two conditions, this ADS response may
   431  			// be in reaction to an earlier request that did not yet request the
   432  			// new resource, so its absence from the response does not
   433  			// necessarily indicate that the resource does not exist. For that
   434  			// case, we rely on the request timeout instead.
   435  			//
   436  			// For the second of the above two conditions, we already generated
   437  			// an error when we received the first response which removed this
   438  			// resource. So, there is no need to generate another one.
   439  			continue
   440  		}
   441  		if _, ok := updates[name]; ok {
   442  			// If the resource was present in the response, move on.
   443  			continue
   444  		}
   445  		if state.md.Status == xdsresource.ServiceStatusNotExist {
   446  			// The metadata status is set to "ServiceStatusNotExist" if a
   447  			// previous update deleted this resource, in which case we do not
   448  			// want to repeatedly call the watch callbacks with a
   449  			// "resource-not-found" error.
   450  			continue
   451  		}
   452  		if serverConfig.ServerFeaturesIgnoreResourceDeletion() {
   453  			// Per A53, resource deletions are ignored if the
   454  			// `ignore_resource_deletion` server feature is enabled through the
   455  			// bootstrap configuration. If the resource deletion is to be
   456  			// ignored, the resource is not removed from the cache and the
   457  			// corresponding OnResourceDoesNotExist() callback is not invoked on
   458  			// the watchers.
   459  			if !state.deletionIgnored {
   460  				state.deletionIgnored = true
   461  				a.logger.Warningf("Ignoring resource deletion for resource %q of type %q", name, rType.TypeName())
   462  			}
   463  			continue
   464  		}
   465  
   466  		// If we get here, it means that the resource exists in cache, but not
   467  		// in the new update. Delete the resource from cache, and send a
   468  		// resource not found error to indicate that the resource has been
   469  		// removed. Metadata for the resource is still maintained, as this is
   470  		// required by CSDS.
   471  		state.cache = nil
   472  		state.md = xdsresource.UpdateMetadata{Status: xdsresource.ServiceStatusNotExist}
   473  		for watcher := range state.watchers {
   474  			watcher := watcher
   475  			watcherCnt.Add(1)
   476  			funcsToSchedule = append(funcsToSchedule, func(context.Context) { watcher.OnResourceDoesNotExist(done) })
   477  		}
   478  	}
   479  }
   480  
   481  // adsResourceDoesNotExist is called by the xDS client implementation (on all
   482  // interested authorities) to notify the authority that a subscribed resource
   483  // does not exist.
   484  func (a *authority) adsResourceDoesNotExist(rType xdsresource.Type, resourceName string) {
   485  	a.xdsClientSerializer.TrySchedule(func(context.Context) {
   486  		a.handleADSResourceDoesNotExist(rType, resourceName)
   487  	})
   488  }
   489  
   490  // handleADSResourceDoesNotExist is called when a subscribed resource does not
   491  // exist. It removes the resource from the cache, updates the metadata status
   492  // to ServiceStatusNotExist, and notifies all watchers that the resource does
   493  // not exist.
   494  func (a *authority) handleADSResourceDoesNotExist(rType xdsresource.Type, resourceName string) {
   495  	if a.logger.V(2) {
   496  		a.logger.Infof("Watch for resource %q of type %s timed out", resourceName, rType.TypeName())
   497  	}
   498  
   499  	resourceStates := a.resources[rType]
   500  	if resourceStates == nil {
   501  		if a.logger.V(2) {
   502  			a.logger.Infof("Resource %q of type %s currently not being watched", resourceName, rType.TypeName())
   503  		}
   504  		return
   505  	}
   506  	state, ok := resourceStates[resourceName]
   507  	if !ok {
   508  		if a.logger.V(2) {
   509  			a.logger.Infof("Resource %q of type %s currently not being watched", resourceName, rType.TypeName())
   510  		}
   511  		return
   512  	}
   513  
   514  	state.cache = nil
   515  	state.md = xdsresource.UpdateMetadata{Status: xdsresource.ServiceStatusNotExist}
   516  	for watcher := range state.watchers {
   517  		watcher := watcher
   518  		a.watcherCallbackSerializer.TrySchedule(func(context.Context) { watcher.OnResourceDoesNotExist(func() {}) })
   519  	}
   520  }
   521  
   522  // handleRevertingToPrimaryOnUpdate is called when a resource update is received
   523  // from the xDS client.
   524  //
   525  // If the update is from the currently active server, nothing is done. Else, all
   526  // lower priority servers are closed and the active server is reverted to the
   527  // highest priority server that sent the update.
   528  //
   529  // This method is only executed in the context of a serializer callback.
   530  func (a *authority) handleRevertingToPrimaryOnUpdate(serverConfig *bootstrap.ServerConfig) {
   531  	if a.activeXDSChannel != nil && a.activeXDSChannel.serverConfig.Equal(serverConfig) {
   532  		// If the resource update is from the current active server, nothing
   533  		// needs to be done from fallback point of view.
   534  		return
   535  	}
   536  
   537  	if a.logger.V(2) {
   538  		a.logger.Infof("Received update from non-active server %q", serverConfig)
   539  	}
   540  
   541  	// If the resource update is not from the current active server, it means
   542  	// that we have received an update from a higher priority server and we need
   543  	// to revert back to it. This method guarantees that when an update is
   544  	// received from a server, all lower priority servers are closed.
   545  	serverIdx := a.serverIndexForConfig(serverConfig)
   546  	a.activeXDSChannel = a.xdsChannelConfigs[serverIdx]
   547  
   548  	// Close all lower priority channels.
   549  	//
   550  	// But before closing any channel, we need to unsubscribe from any resources
   551  	// that were subscribed to on this channel. Resources could be subscribed to
   552  	// from multiple channels as we fallback to lower priority servers. But when
   553  	// a higher priority one comes back up, we need to unsubscribe from all
   554  	// lower priority ones before releasing the reference to them.
   555  	for i := serverIdx + 1; i < len(a.xdsChannelConfigs); i++ {
   556  		cfg := a.xdsChannelConfigs[i]
   557  
   558  		for rType, rState := range a.resources {
   559  			for resourceName, state := range rState {
   560  				for xcc := range state.xdsChannelConfigs {
   561  					if xcc != cfg {
   562  						continue
   563  					}
   564  					// If the current resource is subscribed to on this channel,
   565  					// unsubscribe, and remove the channel from the list of
   566  					// channels that this resource is subscribed to.
   567  					xcc.channel.unsubscribe(rType, resourceName)
   568  					delete(state.xdsChannelConfigs, xcc)
   569  				}
   570  			}
   571  		}
   572  
   573  		// Release the reference to the channel.
   574  		if cfg.cleanup != nil {
   575  			if a.logger.V(2) {
   576  				a.logger.Infof("Closing lower priority server %q", cfg.serverConfig)
   577  			}
   578  			cfg.cleanup()
   579  			cfg.cleanup = nil
   580  		}
   581  		cfg.channel = nil
   582  	}
   583  }
   584  
   585  // watchResource registers a new watcher for the specified resource type and
   586  // name. It returns a function that can be called to cancel the watch.
   587  //
   588  // If this is the first watch for any resource on this authority, an xdsChannel
   589  // to the first management server (from the list of server configurations) will
   590  // be created.
   591  //
   592  // If this is the first watch for the given resource name, it will subscribe to
   593  // the resource with the xdsChannel. If a cached copy of the resource exists, it
   594  // will immediately notify the new watcher. When the last watcher for a resource
   595  // is removed, it will unsubscribe the resource from the xdsChannel.
   596  func (a *authority) watchResource(rType xdsresource.Type, resourceName string, watcher xdsresource.ResourceWatcher) func() {
   597  	cleanup := func() {}
   598  	done := make(chan struct{})
   599  
   600  	a.xdsClientSerializer.ScheduleOr(func(context.Context) {
   601  		defer close(done)
   602  
   603  		if a.logger.V(2) {
   604  			a.logger.Infof("New watch for type %q, resource name %q", rType.TypeName(), resourceName)
   605  		}
   606  
   607  		xdsChannel, err := a.xdsChannelToUse()
   608  		if err != nil {
   609  			a.watcherCallbackSerializer.TrySchedule(func(context.Context) { watcher.OnError(err, func() {}) })
   610  			return
   611  		}
   612  
   613  		// Lookup the entry for the resource type in the top-level map. If there is
   614  		// no entry for this resource type, create one.
   615  		resources := a.resources[rType]
   616  		if resources == nil {
   617  			resources = make(map[string]*resourceState)
   618  			a.resources[rType] = resources
   619  		}
   620  
   621  		// Lookup the resource state for the particular resource name that the watch
   622  		// is being registered for. If this is the first watch for this resource
   623  		// name, request it from the management server.
   624  		state := resources[resourceName]
   625  		if state == nil {
   626  			if a.logger.V(2) {
   627  				a.logger.Infof("First watch for type %q, resource name %q", rType.TypeName(), resourceName)
   628  			}
   629  			state = &resourceState{
   630  				watchers:          make(map[xdsresource.ResourceWatcher]bool),
   631  				md:                xdsresource.UpdateMetadata{Status: xdsresource.ServiceStatusRequested},
   632  				xdsChannelConfigs: map[*xdsChannelWithConfig]bool{xdsChannel: true},
   633  			}
   634  			resources[resourceName] = state
   635  			xdsChannel.channel.subscribe(rType, resourceName)
   636  		}
   637  		// Always add the new watcher to the set of watchers.
   638  		state.watchers[watcher] = true
   639  
   640  		// If we have a cached copy of the resource, notify the new watcher
   641  		// immediately.
   642  		if state.cache != nil {
   643  			if a.logger.V(2) {
   644  				a.logger.Infof("Resource type %q with resource name %q found in cache: %s", rType.TypeName(), resourceName, state.cache.ToJSON())
   645  			}
   646  			// state can only be accessed in the context of an
   647  			// xdsClientSerializer callback. Hence making a copy of the cached
   648  			// resource here for watchCallbackSerializer.
   649  			resource := state.cache
   650  			a.watcherCallbackSerializer.TrySchedule(func(context.Context) { watcher.OnUpdate(resource, func() {}) })
   651  		}
   652  		// If last update was NACK'd, notify the new watcher of error
   653  		// immediately as well.
   654  		if state.md.Status == xdsresource.ServiceStatusNACKed {
   655  			if a.logger.V(2) {
   656  				a.logger.Infof("Resource type %q with resource name %q was NACKed", rType.TypeName(), resourceName)
   657  			}
   658  			// state can only be accessed in the context of an
   659  			// xdsClientSerializer callback. Hence making a copy of the error
   660  			// here for watchCallbackSerializer.
   661  			err := state.md.ErrState.Err
   662  			a.watcherCallbackSerializer.TrySchedule(func(context.Context) { watcher.OnError(err, func() {}) })
   663  		}
   664  		// If the metadata field is updated to indicate that the management
   665  		// server does not have this resource, notify the new watcher.
   666  		if state.md.Status == xdsresource.ServiceStatusNotExist {
   667  			a.watcherCallbackSerializer.TrySchedule(func(context.Context) { watcher.OnResourceDoesNotExist(func() {}) })
   668  		}
   669  		cleanup = a.unwatchResource(rType, resourceName, watcher)
   670  	}, func() {
   671  		if a.logger.V(2) {
   672  			a.logger.Infof("Failed to schedule a watch for type %q, resource name %q, because the xDS client is closed", rType.TypeName(), resourceName)
   673  		}
   674  		close(done)
   675  	})
   676  	<-done
   677  	return cleanup
   678  }
   679  
   680  func (a *authority) unwatchResource(rType xdsresource.Type, resourceName string, watcher xdsresource.ResourceWatcher) func() {
   681  	return sync.OnceFunc(func() {
   682  		done := make(chan struct{})
   683  		a.xdsClientSerializer.ScheduleOr(func(context.Context) {
   684  			defer close(done)
   685  
   686  			if a.logger.V(2) {
   687  				a.logger.Infof("Canceling a watch for type %q, resource name %q", rType.TypeName(), resourceName)
   688  			}
   689  
   690  			// Lookup the resource type from the resource cache. The entry is
   691  			// guaranteed to be present, since *we* were the ones who added it in
   692  			// there when the watch was registered.
   693  			resources := a.resources[rType]
   694  			state := resources[resourceName]
   695  
   696  			// Delete this particular watcher from the list of watchers, so that its
   697  			// callback will not be invoked in the future.
   698  			delete(state.watchers, watcher)
   699  			if len(state.watchers) > 0 {
   700  				if a.logger.V(2) {
   701  					a.logger.Infof("Other watchers exist for type %q, resource name %q", rType.TypeName(), resourceName)
   702  				}
   703  				return
   704  			}
   705  
   706  			// There are no more watchers for this resource. Unsubscribe this
   707  			// resource from all channels where it was subscribed to and delete
   708  			// the state associated with it.
   709  			if a.logger.V(2) {
   710  				a.logger.Infof("Removing last watch for resource name %q", resourceName)
   711  			}
   712  			for xcc := range state.xdsChannelConfigs {
   713  				xcc.channel.unsubscribe(rType, resourceName)
   714  			}
   715  			delete(resources, resourceName)
   716  
   717  			// If there are no more watchers for this resource type, delete the
   718  			// resource type from the top-level map.
   719  			if len(resources) == 0 {
   720  				if a.logger.V(2) {
   721  					a.logger.Infof("Removing last watch for resource type %q", rType.TypeName())
   722  				}
   723  				delete(a.resources, rType)
   724  			}
   725  			// If there are no more watchers for any resource type, release the
   726  			// reference to the xdsChannels.
   727  			if len(a.resources) == 0 {
   728  				if a.logger.V(2) {
   729  					a.logger.Infof("Removing last watch for for any resource type, releasing reference to the xdsChannel")
   730  				}
   731  				a.closeXDSChannels()
   732  			}
   733  		}, func() { close(done) })
   734  		<-done
   735  	})
   736  }
   737  
   738  // xdsChannelToUse returns the xdsChannel to use for communicating with the
   739  // management server. If an active channel is available, it returns that.
   740  // Otherwise, it creates a new channel using the first server configuration in
   741  // the list of configurations, and returns that.
   742  //
   743  // A non-nil error is returned if the channel creation fails.
   744  //
   745  // Only executed in the context of a serializer callback.
   746  func (a *authority) xdsChannelToUse() (*xdsChannelWithConfig, error) {
   747  	if a.activeXDSChannel != nil {
   748  		return a.activeXDSChannel, nil
   749  	}
   750  
   751  	sc := a.xdsChannelConfigs[0].serverConfig
   752  	xc, cleanup, err := a.getChannelForADS(sc, a)
   753  	if err != nil {
   754  		return nil, err
   755  	}
   756  	a.xdsChannelConfigs[0].channel = xc
   757  	a.xdsChannelConfigs[0].cleanup = cleanup
   758  	a.activeXDSChannel = a.xdsChannelConfigs[0]
   759  	return a.activeXDSChannel, nil
   760  }
   761  
   762  // closeXDSChannels closes all the xDS channels associated with this authority,
   763  // when there are no more watchers for any resource type.
   764  //
   765  // Only executed in the context of a serializer callback.
   766  func (a *authority) closeXDSChannels() {
   767  	for _, xcc := range a.xdsChannelConfigs {
   768  		if xcc.cleanup != nil {
   769  			xcc.cleanup()
   770  			xcc.cleanup = nil
   771  		}
   772  		xcc.channel = nil
   773  	}
   774  	a.activeXDSChannel = nil
   775  }
   776  
   777  // watcherExistsForUncachedResource returns true if there is at least one
   778  // watcher for a resource that has not yet been cached.
   779  //
   780  // Only executed in the context of a serializer callback.
   781  func (a *authority) watcherExistsForUncachedResource() bool {
   782  	for _, resourceStates := range a.resources {
   783  		for _, state := range resourceStates {
   784  			if state.md.Status == xdsresource.ServiceStatusRequested {
   785  				return true
   786  			}
   787  		}
   788  	}
   789  	return false
   790  }
   791  
   792  // dumpResources returns a dump of the resource configuration cached by this
   793  // authority, for CSDS purposes.
   794  func (a *authority) dumpResources() []*v3statuspb.ClientConfig_GenericXdsConfig {
   795  	var ret []*v3statuspb.ClientConfig_GenericXdsConfig
   796  	done := make(chan struct{})
   797  
   798  	a.xdsClientSerializer.ScheduleOr(func(context.Context) {
   799  		defer close(done)
   800  		ret = a.resourceConfig()
   801  	}, func() { close(done) })
   802  	<-done
   803  	return ret
   804  }
   805  
   806  // resourceConfig returns a slice of GenericXdsConfig objects representing the
   807  // current state of all resources managed by this authority. This is used for
   808  // reporting the current state of the xDS client.
   809  //
   810  // Only executed in the context of a serializer callback.
   811  func (a *authority) resourceConfig() []*v3statuspb.ClientConfig_GenericXdsConfig {
   812  	var ret []*v3statuspb.ClientConfig_GenericXdsConfig
   813  	for rType, resourceStates := range a.resources {
   814  		typeURL := rType.TypeURL()
   815  		for name, state := range resourceStates {
   816  			var raw *anypb.Any
   817  			if state.cache != nil {
   818  				raw = state.cache.Raw()
   819  			}
   820  			config := &v3statuspb.ClientConfig_GenericXdsConfig{
   821  				TypeUrl:      typeURL,
   822  				Name:         name,
   823  				VersionInfo:  state.md.Version,
   824  				XdsConfig:    raw,
   825  				LastUpdated:  timestamppb.New(state.md.Timestamp),
   826  				ClientStatus: serviceStatusToProto(state.md.Status),
   827  			}
   828  			if errState := state.md.ErrState; errState != nil {
   829  				config.ErrorState = &v3adminpb.UpdateFailureState{
   830  					LastUpdateAttempt: timestamppb.New(errState.Timestamp),
   831  					Details:           errState.Err.Error(),
   832  					VersionInfo:       errState.Version,
   833  				}
   834  			}
   835  			ret = append(ret, config)
   836  		}
   837  	}
   838  	return ret
   839  }
   840  
   841  func (a *authority) close() {
   842  	a.xdsClientSerializerClose()
   843  	<-a.xdsClientSerializer.Done()
   844  	if a.logger.V(2) {
   845  		a.logger.Infof("Closed")
   846  	}
   847  }
   848  
   849  func serviceStatusToProto(serviceStatus xdsresource.ServiceStatus) v3adminpb.ClientResourceStatus {
   850  	switch serviceStatus {
   851  	case xdsresource.ServiceStatusUnknown:
   852  		return v3adminpb.ClientResourceStatus_UNKNOWN
   853  	case xdsresource.ServiceStatusRequested:
   854  		return v3adminpb.ClientResourceStatus_REQUESTED
   855  	case xdsresource.ServiceStatusNotExist:
   856  		return v3adminpb.ClientResourceStatus_DOES_NOT_EXIST
   857  	case xdsresource.ServiceStatusACKed:
   858  		return v3adminpb.ClientResourceStatus_ACKED
   859  	case xdsresource.ServiceStatusNACKed:
   860  		return v3adminpb.ClientResourceStatus_NACKED
   861  	default:
   862  		return v3adminpb.ClientResourceStatus_UNKNOWN
   863  	}
   864  }