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