gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/xds/internal/server/rds_handler_test.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 "context" 23 "errors" 24 "fmt" 25 "testing" 26 27 "gitee.com/ks-custle/core-gm/grpc/xds/internal/testutils/fakeclient" 28 "gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource" 29 "github.com/google/go-cmp/cmp" 30 ) 31 32 const ( 33 route1 = "route1" 34 route2 = "route2" 35 route3 = "route3" 36 ) 37 38 // setupTests creates a rds handler with a fake xds client for control over the 39 // xds client. 40 func setupTests() (*rdsHandler, *fakeclient.Client, chan rdsHandlerUpdate) { 41 xdsC := fakeclient.NewClient() 42 ch := make(chan rdsHandlerUpdate, 1) 43 rh := newRDSHandler(xdsC, ch) 44 return rh, xdsC, ch 45 } 46 47 // waitForFuncWithNames makes sure that a blocking function returns the correct 48 // set of names, where order doesn't matter. This takes away nondeterminism from 49 // ranging through a map. 50 func waitForFuncWithNames(ctx context.Context, f func(context.Context) (string, error), names ...string) error { 51 wantNames := make(map[string]bool, len(names)) 52 for _, name := range names { 53 wantNames[name] = true 54 } 55 gotNames := make(map[string]bool, len(names)) 56 for range wantNames { 57 name, err := f(ctx) 58 if err != nil { 59 return err 60 } 61 gotNames[name] = true 62 } 63 if !cmp.Equal(gotNames, wantNames) { 64 return fmt.Errorf("got routeNames %v, want %v", gotNames, wantNames) 65 } 66 return nil 67 } 68 69 // TestSuccessCaseOneRDSWatch tests the simplest scenario: the rds handler 70 // receives a single route name, starts a watch for that route name, gets a 71 // successful update, and then writes an update to the update channel for 72 // listener to pick up. 73 func (s) TestSuccessCaseOneRDSWatch(t *testing.T) { 74 rh, fakeClient, ch := setupTests() 75 // When you first update the rds handler with a list of a single Route names 76 // that needs dynamic RDS Configuration, this Route name has not been seen 77 // before, so the RDS Handler should start a watch on that RouteName. 78 rh.updateRouteNamesToWatch(map[string]bool{route1: true}) 79 // The RDS Handler should start a watch for that routeName. 80 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 81 defer cancel() 82 gotRoute, err := fakeClient.WaitForWatchRouteConfig(ctx) 83 if err != nil { 84 t.Fatalf("xdsClient.WatchRDS failed with error: %v", err) 85 } 86 if gotRoute != route1 { 87 t.Fatalf("xdsClient.WatchRDS called for route: %v, want %v", gotRoute, route1) 88 } 89 rdsUpdate := xdsresource.RouteConfigUpdate{} 90 // Invoke callback with the xds client with a certain route update. Due to 91 // this route update updating every route name that rds handler handles, 92 // this should write to the update channel to send to the listener. 93 fakeClient.InvokeWatchRouteConfigCallback(route1, rdsUpdate, nil) 94 rhuWant := map[string]xdsresource.RouteConfigUpdate{route1: rdsUpdate} 95 select { 96 case rhu := <-ch: 97 if diff := cmp.Diff(rhu.updates, rhuWant); diff != "" { 98 t.Fatalf("got unexpected route update, diff (-got, +want): %v", diff) 99 } 100 case <-ctx.Done(): 101 t.Fatal("Timed out waiting for update from update channel.") 102 } 103 // Close the rds handler. This is meant to be called when the lis wrapper is 104 // closed, and the call should cancel all the watches present (for this 105 // test, a single watch). 106 rh.close() 107 routeNameDeleted, err := fakeClient.WaitForCancelRouteConfigWatch(ctx) 108 if err != nil { 109 t.Fatalf("xdsClient.CancelRDS failed with error: %v", err) 110 } 111 if routeNameDeleted != route1 { 112 t.Fatalf("xdsClient.CancelRDS called for route %v, want %v", routeNameDeleted, route1) 113 } 114 } 115 116 // TestSuccessCaseTwoUpdates tests the case where the rds handler receives an 117 // update with a single Route, then receives a second update with two routes. 118 // The handler should start a watch for the added route, and if received a RDS 119 // update for that route it should send an update with both RDS updates present. 120 func (s) TestSuccessCaseTwoUpdates(t *testing.T) { 121 rh, fakeClient, ch := setupTests() 122 123 rh.updateRouteNamesToWatch(map[string]bool{route1: true}) 124 125 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 126 defer cancel() 127 gotRoute, err := fakeClient.WaitForWatchRouteConfig(ctx) 128 if err != nil { 129 t.Fatalf("xdsClient.WatchRDS failed with error: %v", err) 130 } 131 if gotRoute != route1 { 132 t.Fatalf("xdsClient.WatchRDS called for route: %v, want %v", gotRoute, route1) 133 } 134 135 // Update the RDSHandler with route names which adds a route name to watch. 136 // This should trigger the RDSHandler to start a watch for the added route 137 // name to watch. 138 rh.updateRouteNamesToWatch(map[string]bool{route1: true, route2: true}) 139 gotRoute, err = fakeClient.WaitForWatchRouteConfig(ctx) 140 if err != nil { 141 t.Fatalf("xdsClient.WatchRDS failed with error: %v", err) 142 } 143 if gotRoute != route2 { 144 t.Fatalf("xdsClient.WatchRDS called for route: %v, want %v", gotRoute, route2) 145 } 146 147 // Invoke the callback with an update for route 1. This shouldn't cause the 148 // handler to write an update, as it has not received RouteConfigurations 149 // for every RouteName. 150 rdsUpdate1 := xdsresource.RouteConfigUpdate{} 151 fakeClient.InvokeWatchRouteConfigCallback(route1, rdsUpdate1, nil) 152 153 // The RDS Handler should not send an update. 154 sCtx, sCtxCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 155 defer sCtxCancel() 156 select { 157 case <-ch: 158 t.Fatal("RDS Handler wrote an update to updateChannel when it shouldn't have, as each route name has not received an update yet") 159 case <-sCtx.Done(): 160 } 161 162 // Invoke the callback with an update for route 2. This should cause the 163 // handler to write an update, as it has received RouteConfigurations for 164 // every RouteName. 165 rdsUpdate2 := xdsresource.RouteConfigUpdate{} 166 fakeClient.InvokeWatchRouteConfigCallback(route2, rdsUpdate2, nil) 167 // The RDS Handler should then update the listener wrapper with an update 168 // with two route configurations, as both route names the RDS Handler handles 169 // have received an update. 170 rhuWant := map[string]xdsresource.RouteConfigUpdate{route1: rdsUpdate1, route2: rdsUpdate2} 171 select { 172 case rhu := <-ch: 173 if diff := cmp.Diff(rhu.updates, rhuWant); diff != "" { 174 t.Fatalf("got unexpected route update, diff (-got, +want): %v", diff) 175 } 176 case <-ctx.Done(): 177 t.Fatal("Timed out waiting for the rds handler update to be written to the update buffer.") 178 } 179 180 // Close the rds handler. This is meant to be called when the lis wrapper is 181 // closed, and the call should cancel all the watches present (for this 182 // test, two watches on route1 and route2). 183 rh.close() 184 if err = waitForFuncWithNames(ctx, fakeClient.WaitForCancelRouteConfigWatch, route1, route2); err != nil { 185 t.Fatalf("Error while waiting for names: %v", err) 186 } 187 } 188 189 // TestSuccessCaseDeletedRoute tests the case where the rds handler receives an 190 // update with two routes, then receives an update with only one route. The RDS 191 // Handler is expected to cancel the watch for the route no longer present. 192 func (s) TestSuccessCaseDeletedRoute(t *testing.T) { 193 rh, fakeClient, ch := setupTests() 194 195 rh.updateRouteNamesToWatch(map[string]bool{route1: true, route2: true}) 196 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 197 defer cancel() 198 // Will start two watches. 199 if err := waitForFuncWithNames(ctx, fakeClient.WaitForWatchRouteConfig, route1, route2); err != nil { 200 t.Fatalf("Error while waiting for names: %v", err) 201 } 202 203 // Update the RDSHandler with route names which deletes a route name to 204 // watch. This should trigger the RDSHandler to cancel the watch for the 205 // deleted route name to watch. 206 rh.updateRouteNamesToWatch(map[string]bool{route1: true}) 207 // This should delete the watch for route2. 208 routeNameDeleted, err := fakeClient.WaitForCancelRouteConfigWatch(ctx) 209 if err != nil { 210 t.Fatalf("xdsClient.CancelRDS failed with error %v", err) 211 } 212 if routeNameDeleted != route2 { 213 t.Fatalf("xdsClient.CancelRDS called for route %v, want %v", routeNameDeleted, route2) 214 } 215 216 rdsUpdate := xdsresource.RouteConfigUpdate{} 217 // Invoke callback with the xds client with a certain route update. Due to 218 // this route update updating every route name that rds handler handles, 219 // this should write to the update channel to send to the listener. 220 fakeClient.InvokeWatchRouteConfigCallback(route1, rdsUpdate, nil) 221 rhuWant := map[string]xdsresource.RouteConfigUpdate{route1: rdsUpdate} 222 select { 223 case rhu := <-ch: 224 if diff := cmp.Diff(rhu.updates, rhuWant); diff != "" { 225 t.Fatalf("got unexpected route update, diff (-got, +want): %v", diff) 226 } 227 case <-ctx.Done(): 228 t.Fatal("Timed out waiting for update from update channel.") 229 } 230 231 rh.close() 232 routeNameDeleted, err = fakeClient.WaitForCancelRouteConfigWatch(ctx) 233 if err != nil { 234 t.Fatalf("xdsClient.CancelRDS failed with error: %v", err) 235 } 236 if routeNameDeleted != route1 { 237 t.Fatalf("xdsClient.CancelRDS called for route %v, want %v", routeNameDeleted, route1) 238 } 239 } 240 241 // TestSuccessCaseTwoUpdatesAddAndDeleteRoute tests the case where the rds 242 // handler receives an update with two routes, and then receives an update with 243 // two routes, one previously there and one added (i.e. 12 -> 23). This should 244 // cause the route that is no longer there to be deleted and cancelled, and the 245 // route that was added should have a watch started for it. 246 func (s) TestSuccessCaseTwoUpdatesAddAndDeleteRoute(t *testing.T) { 247 rh, fakeClient, ch := setupTests() 248 249 rh.updateRouteNamesToWatch(map[string]bool{route1: true, route2: true}) 250 251 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 252 defer cancel() 253 if err := waitForFuncWithNames(ctx, fakeClient.WaitForWatchRouteConfig, route1, route2); err != nil { 254 t.Fatalf("Error while waiting for names: %v", err) 255 } 256 257 // Update the rds handler with two routes, one which was already there and a new route. 258 // This should cause the rds handler to delete/cancel watch for route 1 and start a watch 259 // for route 3. 260 rh.updateRouteNamesToWatch(map[string]bool{route2: true, route3: true}) 261 262 // Start watch comes first, which should be for route3 as was just added. 263 gotRoute, err := fakeClient.WaitForWatchRouteConfig(ctx) 264 if err != nil { 265 t.Fatalf("xdsClient.WatchRDS failed with error: %v", err) 266 } 267 if gotRoute != route3 { 268 t.Fatalf("xdsClient.WatchRDS called for route: %v, want %v", gotRoute, route3) 269 } 270 271 // Then route 1 should be deleted/cancelled watch for, as it is no longer present 272 // in the new RouteName to watch map. 273 routeNameDeleted, err := fakeClient.WaitForCancelRouteConfigWatch(ctx) 274 if err != nil { 275 t.Fatalf("xdsClient.CancelRDS failed with error: %v", err) 276 } 277 if routeNameDeleted != route1 { 278 t.Fatalf("xdsClient.CancelRDS called for route %v, want %v", routeNameDeleted, route1) 279 } 280 281 // Invoke the callback with an update for route 2. This shouldn't cause the 282 // handler to write an update, as it has not received RouteConfigurations 283 // for every RouteName. 284 rdsUpdate2 := xdsresource.RouteConfigUpdate{} 285 fakeClient.InvokeWatchRouteConfigCallback(route2, rdsUpdate2, nil) 286 287 // The RDS Handler should not send an update. 288 sCtx, sCtxCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 289 defer sCtxCancel() 290 select { 291 case <-ch: 292 t.Fatalf("RDS Handler wrote an update to updateChannel when it shouldn't have, as each route name has not received an update yet") 293 case <-sCtx.Done(): 294 } 295 296 // Invoke the callback with an update for route 3. This should cause the 297 // handler to write an update, as it has received RouteConfigurations for 298 // every RouteName. 299 rdsUpdate3 := xdsresource.RouteConfigUpdate{} 300 fakeClient.InvokeWatchRouteConfigCallback(route3, rdsUpdate3, nil) 301 // The RDS Handler should then update the listener wrapper with an update 302 // with two route configurations, as both route names the RDS Handler handles 303 // have received an update. 304 rhuWant := map[string]xdsresource.RouteConfigUpdate{route2: rdsUpdate2, route3: rdsUpdate3} 305 select { 306 case rhu := <-rh.updateChannel: 307 if diff := cmp.Diff(rhu.updates, rhuWant); diff != "" { 308 t.Fatalf("got unexpected route update, diff (-got, +want): %v", diff) 309 } 310 case <-ctx.Done(): 311 t.Fatal("Timed out waiting for the rds handler update to be written to the update buffer.") 312 } 313 // Close the rds handler. This is meant to be called when the lis wrapper is 314 // closed, and the call should cancel all the watches present (for this 315 // test, two watches on route2 and route3). 316 rh.close() 317 if err = waitForFuncWithNames(ctx, fakeClient.WaitForCancelRouteConfigWatch, route2, route3); err != nil { 318 t.Fatalf("Error while waiting for names: %v", err) 319 } 320 } 321 322 // TestSuccessCaseSecondUpdateMakesRouteFull tests the scenario where the rds handler gets 323 // told to watch three rds configurations, gets two successful updates, then gets told to watch 324 // only those two. The rds handler should then write an update to update buffer. 325 func (s) TestSuccessCaseSecondUpdateMakesRouteFull(t *testing.T) { 326 rh, fakeClient, ch := setupTests() 327 328 rh.updateRouteNamesToWatch(map[string]bool{route1: true, route2: true, route3: true}) 329 330 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 331 defer cancel() 332 if err := waitForFuncWithNames(ctx, fakeClient.WaitForWatchRouteConfig, route1, route2, route3); err != nil { 333 t.Fatalf("Error while waiting for names: %v", err) 334 } 335 336 // Invoke the callbacks for two of the three watches. Since RDS is not full, 337 // this shouldn't trigger rds handler to write an update to update buffer. 338 fakeClient.InvokeWatchRouteConfigCallback(route1, xdsresource.RouteConfigUpdate{}, nil) 339 fakeClient.InvokeWatchRouteConfigCallback(route2, xdsresource.RouteConfigUpdate{}, nil) 340 341 // The RDS Handler should not send an update. 342 sCtx, sCtxCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 343 defer sCtxCancel() 344 select { 345 case <-rh.updateChannel: 346 t.Fatalf("RDS Handler wrote an update to updateChannel when it shouldn't have, as each route name has not received an update yet") 347 case <-sCtx.Done(): 348 } 349 350 // Tell the rds handler to now only watch Route 1 and Route 2. This should 351 // trigger the rds handler to write an update to the update buffer as it now 352 // has full rds configuration. 353 rh.updateRouteNamesToWatch(map[string]bool{route1: true, route2: true}) 354 // Route 3 should be deleted/cancelled watch for, as it is no longer present 355 // in the new RouteName to watch map. 356 routeNameDeleted, err := fakeClient.WaitForCancelRouteConfigWatch(ctx) 357 if err != nil { 358 t.Fatalf("xdsClient.CancelRDS failed with error: %v", err) 359 } 360 if routeNameDeleted != route3 { 361 t.Fatalf("xdsClient.CancelRDS called for route %v, want %v", routeNameDeleted, route1) 362 } 363 rhuWant := map[string]xdsresource.RouteConfigUpdate{route1: {}, route2: {}} 364 select { 365 case rhu := <-ch: 366 if diff := cmp.Diff(rhu.updates, rhuWant); diff != "" { 367 t.Fatalf("got unexpected route update, diff (-got, +want): %v", diff) 368 } 369 case <-ctx.Done(): 370 t.Fatal("Timed out waiting for the rds handler update to be written to the update buffer.") 371 } 372 } 373 374 // TestErrorReceived tests the case where the rds handler receives a route name 375 // to watch, then receives an update with an error. This error should be then 376 // written to the update channel. 377 func (s) TestErrorReceived(t *testing.T) { 378 rh, fakeClient, ch := setupTests() 379 380 rh.updateRouteNamesToWatch(map[string]bool{route1: true}) 381 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 382 defer cancel() 383 gotRoute, err := fakeClient.WaitForWatchRouteConfig(ctx) 384 if err != nil { 385 t.Fatalf("xdsClient.WatchRDS failed with error %v", err) 386 } 387 if gotRoute != route1 { 388 t.Fatalf("xdsClient.WatchRDS called for route: %v, want %v", gotRoute, route1) 389 } 390 391 rdsErr := errors.New("some error") 392 fakeClient.InvokeWatchRouteConfigCallback(route1, xdsresource.RouteConfigUpdate{}, rdsErr) 393 select { 394 case rhu := <-ch: 395 if rhu.err.Error() != "some error" { 396 t.Fatalf("Did not receive the expected error, instead received: %v", rhu.err.Error()) 397 } 398 case <-ctx.Done(): 399 t.Fatal("Timed out waiting for update from update channel") 400 } 401 }