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 }