google.golang.org/grpc@v1.74.2/xds/internal/server/rds_handler.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  
    19  package server
    20  
    21  import (
    22  	"sync"
    23  
    24  	igrpclog "google.golang.org/grpc/internal/grpclog"
    25  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource"
    26  )
    27  
    28  // rdsHandler handles any RDS queries that need to be started for a given server
    29  // side listeners Filter Chains (i.e. not inline). It persists rdsWatcher
    30  // updates for later use and also determines whether all the rdsWatcher updates
    31  // needed have been received or not.
    32  type rdsHandler struct {
    33  	xdsC      XDSClient
    34  	xdsNodeID string
    35  	logger    *igrpclog.PrefixLogger
    36  
    37  	callback func(string, rdsWatcherUpdate)
    38  
    39  	// updates is a map from routeName to rdsWatcher update, including
    40  	// RouteConfiguration resources and any errors received. If not written in
    41  	// this map, no RouteConfiguration or error for that route name yet. If
    42  	// update set in value, use that as valid route configuration, otherwise
    43  	// treat as an error case and fail at L7 level.
    44  	updates map[string]rdsWatcherUpdate
    45  
    46  	mu      sync.Mutex
    47  	cancels map[string]func()
    48  }
    49  
    50  // newRDSHandler creates a new rdsHandler to watch for RouteConfiguration
    51  // resources. listenerWrapper updates the list of route names to watch by
    52  // calling updateRouteNamesToWatch() upon receipt of new Listener configuration.
    53  func newRDSHandler(cb func(string, rdsWatcherUpdate), xdsC XDSClient, logger *igrpclog.PrefixLogger) *rdsHandler {
    54  	r := &rdsHandler{
    55  		xdsC:     xdsC,
    56  		logger:   logger,
    57  		callback: cb,
    58  		updates:  make(map[string]rdsWatcherUpdate),
    59  		cancels:  make(map[string]func()),
    60  	}
    61  	r.xdsNodeID = xdsC.BootstrapConfig().Node().GetId()
    62  	return r
    63  }
    64  
    65  // updateRouteNamesToWatch handles a list of route names to watch for a given
    66  // server side listener (if a filter chain specifies dynamic
    67  // RouteConfiguration). This function handles all the logic with respect to any
    68  // routes that may have been added or deleted as compared to what was previously
    69  // present. Must be called within an xDS Client callback.
    70  func (rh *rdsHandler) updateRouteNamesToWatch(routeNamesToWatch map[string]bool) {
    71  	rh.mu.Lock()
    72  	defer rh.mu.Unlock()
    73  	// Add and start watches for any new routes in routeNamesToWatch.
    74  	for routeName := range routeNamesToWatch {
    75  		if _, ok := rh.cancels[routeName]; !ok {
    76  			// The xDS client keeps a reference to the watcher until the cancel
    77  			// func is invoked. So, we don't need to keep a reference for fear
    78  			// of it being garbage collected.
    79  			w := &rdsWatcher{parent: rh, routeName: routeName}
    80  			cancel := xdsresource.WatchRouteConfig(rh.xdsC, routeName, w)
    81  			// Set bit on cancel function to eat any RouteConfiguration calls
    82  			// for this watcher after it has been canceled.
    83  			rh.cancels[routeName] = func() {
    84  				w.mu.Lock()
    85  				w.canceled = true
    86  				w.mu.Unlock()
    87  				cancel()
    88  			}
    89  		}
    90  	}
    91  
    92  	// Delete and cancel watches for any routes from persisted routeNamesToWatch
    93  	// that are no longer present.
    94  	for routeName := range rh.cancels {
    95  		if _, ok := routeNamesToWatch[routeName]; !ok {
    96  			rh.cancels[routeName]()
    97  			delete(rh.cancels, routeName)
    98  			delete(rh.updates, routeName)
    99  		}
   100  	}
   101  }
   102  
   103  // determines if all dynamic RouteConfiguration needed has received
   104  // configuration or update. Must be called from an xDS Client Callback.
   105  func (rh *rdsHandler) determineRouteConfigurationReady() bool {
   106  	// Safe to read cancels because only written to in other parts of xDS Client
   107  	// Callbacks, which are sync.
   108  	return len(rh.updates) == len(rh.cancels)
   109  }
   110  
   111  // close() is meant to be called by wrapped listener when the wrapped listener
   112  // is closed, and it cleans up resources by canceling all the active RDS
   113  // watches.
   114  func (rh *rdsHandler) close() {
   115  	rh.mu.Lock()
   116  	defer rh.mu.Unlock()
   117  	for _, cancel := range rh.cancels {
   118  		cancel()
   119  	}
   120  }
   121  
   122  type rdsWatcherUpdate struct {
   123  	data *xdsresource.RouteConfigUpdate
   124  	err  error
   125  }
   126  
   127  // rdsWatcher implements the xdsresource.RouteConfigWatcher interface and is
   128  // passed to the WatchRouteConfig API.
   129  type rdsWatcher struct {
   130  	parent    *rdsHandler
   131  	logger    *igrpclog.PrefixLogger
   132  	routeName string
   133  
   134  	mu       sync.Mutex
   135  	canceled bool // eats callbacks if true
   136  }
   137  
   138  func (rw *rdsWatcher) ResourceChanged(update *xdsresource.RouteConfigResourceData, onDone func()) {
   139  	defer onDone()
   140  	rw.mu.Lock()
   141  	if rw.canceled {
   142  		rw.mu.Unlock()
   143  		return
   144  	}
   145  	rw.mu.Unlock()
   146  	if rw.logger.V(2) {
   147  		rw.logger.Infof("RDS watch for resource %q received update: %#v", rw.routeName, update.Resource)
   148  	}
   149  
   150  	routeName := rw.routeName
   151  	rwu := rdsWatcherUpdate{data: &update.Resource}
   152  	rw.parent.updates[routeName] = rwu
   153  	rw.parent.callback(routeName, rwu)
   154  }
   155  
   156  func (rw *rdsWatcher) ResourceError(err error, onDone func()) {
   157  	defer onDone()
   158  	rw.mu.Lock()
   159  	if rw.canceled {
   160  		rw.mu.Unlock()
   161  		return
   162  	}
   163  	rw.mu.Unlock()
   164  	if rw.logger.V(2) {
   165  		rw.logger.Infof("RDS watch for resource %q reported resource error", rw.routeName)
   166  	}
   167  
   168  	routeName := rw.routeName
   169  	rwu := rdsWatcherUpdate{err: err}
   170  	rw.parent.updates[routeName] = rwu
   171  	rw.parent.callback(routeName, rwu)
   172  }
   173  
   174  func (rw *rdsWatcher) AmbientError(err error, onDone func()) {
   175  	defer onDone()
   176  	rw.mu.Lock()
   177  	if rw.canceled {
   178  		rw.mu.Unlock()
   179  		return
   180  	}
   181  	rw.mu.Unlock()
   182  	if rw.logger.V(2) {
   183  		rw.logger.Infof("RDS watch for resource %q reported ambient error: %v", rw.routeName, err)
   184  	}
   185  	routeName := rw.routeName
   186  	rwu := rw.parent.updates[routeName]
   187  	rw.parent.callback(routeName, rwu)
   188  }