google.golang.org/grpc@v1.72.2/xds/internal/xdsclient/tests/lds_watchers_test.go (about) 1 /* 2 * 3 * Copyright 2022 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 xdsclient_test 20 21 import ( 22 "context" 23 "encoding/json" 24 "fmt" 25 "strings" 26 "testing" 27 "time" 28 29 "github.com/envoyproxy/go-control-plane/pkg/wellknown" 30 "github.com/google/go-cmp/cmp" 31 "github.com/google/go-cmp/cmp/cmpopts" 32 "github.com/google/uuid" 33 "google.golang.org/grpc/internal/grpcsync" 34 "google.golang.org/grpc/internal/testutils" 35 "google.golang.org/grpc/internal/testutils/xds/e2e" 36 "google.golang.org/grpc/internal/xds/bootstrap" 37 "google.golang.org/grpc/xds/internal/xdsclient" 38 "google.golang.org/grpc/xds/internal/xdsclient/xdsresource" 39 40 v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 41 v3routerpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" 42 v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" 43 v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" 44 45 _ "google.golang.org/grpc/xds" // To ensure internal.NewXDSResolverWithConfigForTesting is set. 46 _ "google.golang.org/grpc/xds/internal/httpfilter/router" // Register the router filter. 47 ) 48 49 type noopListenerWatcher struct{} 50 51 func (noopListenerWatcher) OnUpdate(update *xdsresource.ListenerResourceData, onDone xdsresource.OnDoneFunc) { 52 onDone() 53 } 54 func (noopListenerWatcher) OnError(err error, onDone xdsresource.OnDoneFunc) { 55 onDone() 56 } 57 func (noopListenerWatcher) OnResourceDoesNotExist(onDone xdsresource.OnDoneFunc) { 58 onDone() 59 } 60 61 type listenerUpdateErrTuple struct { 62 update xdsresource.ListenerUpdate 63 err error 64 } 65 66 type listenerWatcher struct { 67 updateCh *testutils.Channel 68 } 69 70 func newListenerWatcher() *listenerWatcher { 71 return &listenerWatcher{updateCh: testutils.NewChannel()} 72 } 73 74 func (lw *listenerWatcher) OnUpdate(update *xdsresource.ListenerResourceData, onDone xdsresource.OnDoneFunc) { 75 lw.updateCh.Send(listenerUpdateErrTuple{update: update.Resource}) 76 onDone() 77 } 78 79 func (lw *listenerWatcher) OnError(err error, onDone xdsresource.OnDoneFunc) { 80 // When used with a go-control-plane management server that continuously 81 // resends resources which are NACKed by the xDS client, using a `Replace()` 82 // here and in OnResourceDoesNotExist() simplifies tests which will have 83 // access to the most recently received error. 84 lw.updateCh.Replace(listenerUpdateErrTuple{err: err}) 85 onDone() 86 } 87 88 func (lw *listenerWatcher) OnResourceDoesNotExist(onDone xdsresource.OnDoneFunc) { 89 lw.updateCh.Replace(listenerUpdateErrTuple{err: xdsresource.NewError(xdsresource.ErrorTypeResourceNotFound, "Listener not found in received response")}) 90 onDone() 91 } 92 93 type listenerWatcherMultiple struct { 94 updateCh *testutils.Channel 95 } 96 97 // TODO: delete this once `newListenerWatcher` is modified to handle multiple 98 // updates (https://github.com/grpc/grpc-go/issues/7864). 99 func newListenerWatcherMultiple(size int) *listenerWatcherMultiple { 100 return &listenerWatcherMultiple{updateCh: testutils.NewChannelWithSize(size)} 101 } 102 103 func (lw *listenerWatcherMultiple) OnUpdate(update *xdsresource.ListenerResourceData, onDone xdsresource.OnDoneFunc) { 104 lw.updateCh.Send(listenerUpdateErrTuple{update: update.Resource}) 105 onDone() 106 } 107 108 func (lw *listenerWatcherMultiple) OnError(err error, onDone xdsresource.OnDoneFunc) { 109 lw.updateCh.Send(listenerUpdateErrTuple{err: err}) 110 onDone() 111 } 112 113 func (lw *listenerWatcherMultiple) OnResourceDoesNotExist(onDone xdsresource.OnDoneFunc) { 114 lw.updateCh.Send(listenerUpdateErrTuple{err: xdsresource.NewError(xdsresource.ErrorTypeResourceNotFound, "Listener not found in received response")}) 115 onDone() 116 } 117 118 // badListenerResource returns a listener resource for the given name which does 119 // not contain the `RouteSpecifier` field in the HTTPConnectionManager, and 120 // hence is expected to be NACKed by the client. 121 func badListenerResource(t *testing.T, name string) *v3listenerpb.Listener { 122 hcm := testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{ 123 HttpFilters: []*v3httppb.HttpFilter{e2e.HTTPFilter("router", &v3routerpb.Router{})}, 124 }) 125 return &v3listenerpb.Listener{ 126 Name: name, 127 ApiListener: &v3listenerpb.ApiListener{ApiListener: hcm}, 128 FilterChains: []*v3listenerpb.FilterChain{{ 129 Name: "filter-chain-name", 130 Filters: []*v3listenerpb.Filter{{ 131 Name: wellknown.HTTPConnectionManager, 132 ConfigType: &v3listenerpb.Filter_TypedConfig{TypedConfig: hcm}, 133 }}, 134 }}, 135 } 136 } 137 138 // verifyNoListenerUpdate verifies that no listener update is received on the 139 // provided update channel, and returns an error if an update is received. 140 // 141 // A very short deadline is used while waiting for the update, as this function 142 // is intended to be used when an update is not expected. 143 func verifyNoListenerUpdate(ctx context.Context, updateCh *testutils.Channel) error { 144 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 145 defer sCancel() 146 if u, err := updateCh.Receive(sCtx); err != context.DeadlineExceeded { 147 return fmt.Errorf("unexpected ListenerUpdate: %v", u) 148 } 149 return nil 150 } 151 152 // verifyListenerUpdate waits for an update to be received on the provided 153 // update channel and verifies that it matches the expected update. 154 // 155 // Returns an error if no update is received before the context deadline expires 156 // or the received update does not match the expected one. 157 func verifyListenerUpdate(ctx context.Context, updateCh *testutils.Channel, wantUpdate listenerUpdateErrTuple) error { 158 u, err := updateCh.Receive(ctx) 159 if err != nil { 160 return fmt.Errorf("timeout when waiting for a listener resource from the management server: %v", err) 161 } 162 got := u.(listenerUpdateErrTuple) 163 if wantUpdate.err != nil { 164 if gotType, wantType := xdsresource.ErrType(got.err), xdsresource.ErrType(wantUpdate.err); gotType != wantType { 165 return fmt.Errorf("received update with error type %v, want %v", gotType, wantType) 166 } 167 } 168 cmpOpts := []cmp.Option{ 169 cmpopts.EquateEmpty(), 170 cmpopts.IgnoreFields(xdsresource.HTTPFilter{}, "Filter", "Config"), 171 cmpopts.IgnoreFields(xdsresource.ListenerUpdate{}, "Raw"), 172 } 173 if diff := cmp.Diff(wantUpdate.update, got.update, cmpOpts...); diff != "" { 174 return fmt.Errorf("received unexpected diff in the listener resource update: (-want, got):\n%s", diff) 175 } 176 return nil 177 } 178 179 func verifyListenerError(ctx context.Context, updateCh *testutils.Channel, wantErr, wantNodeID string) error { 180 u, err := updateCh.Receive(ctx) 181 if err != nil { 182 return fmt.Errorf("timeout when waiting for a listener error from the management server: %v", err) 183 } 184 gotErr := u.(listenerUpdateErrTuple).err 185 if gotErr == nil || !strings.Contains(gotErr.Error(), wantErr) { 186 return fmt.Errorf("update received with error: %v, want %q", gotErr, wantErr) 187 } 188 if !strings.Contains(gotErr.Error(), wantNodeID) { 189 return fmt.Errorf("update received with error: %v, want error with node ID: %q", gotErr, wantNodeID) 190 } 191 return nil 192 } 193 194 func verifyErrorType(ctx context.Context, updateCh *testutils.Channel, wantErrType xdsresource.ErrorType, wantNodeID string) error { 195 u, err := updateCh.Receive(ctx) 196 if err != nil { 197 return fmt.Errorf("timeout when waiting for a listener error from the management server: %v", err) 198 } 199 gotErr := u.(listenerUpdateErrTuple).err 200 if got, want := xdsresource.ErrType(gotErr), wantErrType; got != want { 201 return fmt.Errorf("update received with error %v of type: %v, want %v", gotErr, got, want) 202 } 203 if !strings.Contains(gotErr.Error(), wantNodeID) { 204 return fmt.Errorf("update received with error: %v, want error with node ID: %q", gotErr, wantNodeID) 205 } 206 return nil 207 } 208 209 // TestLDSWatch covers the case where a single watcher exists for a single 210 // listener resource. The test verifies the following scenarios: 211 // 1. An update from the management server containing the resource being 212 // watched should result in the invocation of the watch callback. 213 // 2. An update from the management server containing a resource *not* being 214 // watched should not result in the invocation of the watch callback. 215 // 3. After the watch is cancelled, an update from the management server 216 // containing the resource that was being watched should not result in the 217 // invocation of the watch callback. 218 // 219 // The test is run for old and new style names. 220 func (s) TestLDSWatch(t *testing.T) { 221 tests := []struct { 222 desc string 223 resourceName string 224 watchedResource *v3listenerpb.Listener // The resource being watched. 225 updatedWatchedResource *v3listenerpb.Listener // The watched resource after an update. 226 notWatchedResource *v3listenerpb.Listener // A resource which is not being watched. 227 wantUpdate listenerUpdateErrTuple 228 }{ 229 { 230 desc: "old style resource", 231 resourceName: ldsName, 232 watchedResource: e2e.DefaultClientListener(ldsName, rdsName), 233 updatedWatchedResource: e2e.DefaultClientListener(ldsName, "new-rds-resource"), 234 notWatchedResource: e2e.DefaultClientListener("unsubscribed-lds-resource", rdsName), 235 wantUpdate: listenerUpdateErrTuple{ 236 update: xdsresource.ListenerUpdate{ 237 RouteConfigName: rdsName, 238 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 239 }, 240 }, 241 }, 242 { 243 desc: "new style resource", 244 resourceName: ldsNameNewStyle, 245 watchedResource: e2e.DefaultClientListener(ldsNameNewStyle, rdsNameNewStyle), 246 updatedWatchedResource: e2e.DefaultClientListener(ldsNameNewStyle, "new-rds-resource"), 247 notWatchedResource: e2e.DefaultClientListener("unsubscribed-lds-resource", rdsNameNewStyle), 248 wantUpdate: listenerUpdateErrTuple{ 249 update: xdsresource.ListenerUpdate{ 250 RouteConfigName: rdsNameNewStyle, 251 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 252 }, 253 }, 254 }, 255 } 256 257 for _, test := range tests { 258 t.Run(test.desc, func(t *testing.T) { 259 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 260 261 nodeID := uuid.New().String() 262 bc, err := bootstrap.NewContentsForTesting(bootstrap.ConfigOptionsForTesting{ 263 Servers: []byte(fmt.Sprintf(`[{ 264 "server_uri": %q, 265 "channel_creds": [{"type": "insecure"}] 266 }]`, mgmtServer.Address)), 267 Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)), 268 Authorities: map[string]json.RawMessage{ 269 // Xdstp resource names used in this test do not specify an 270 // authority. These will end up looking up an entry with the 271 // empty key in the authorities map. Having an entry with an 272 // empty key and empty configuration, results in these 273 // resources also using the top-level configuration. 274 "": []byte(`{}`), 275 }, 276 }) 277 if err != nil { 278 t.Fatalf("Failed to create bootstrap configuration: %v", err) 279 } 280 281 // Create an xDS client with the above bootstrap contents. 282 config, err := bootstrap.NewConfigFromContents(bc) 283 if err != nil { 284 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 285 } 286 pool := xdsclient.NewPool(config) 287 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 288 Name: t.Name(), 289 }) 290 if err != nil { 291 t.Fatalf("Failed to create xDS client: %v", err) 292 } 293 defer close() 294 295 // Register a watch for a listener resource and have the watch 296 // callback push the received update on to a channel. 297 lw := newListenerWatcher() 298 ldsCancel := xdsresource.WatchListener(client, test.resourceName, lw) 299 300 // Configure the management server to return a single listener 301 // resource, corresponding to the one we registered a watch for. 302 resources := e2e.UpdateOptions{ 303 NodeID: nodeID, 304 Listeners: []*v3listenerpb.Listener{test.watchedResource}, 305 SkipValidation: true, 306 } 307 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 308 defer cancel() 309 if err := mgmtServer.Update(ctx, resources); err != nil { 310 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 311 } 312 313 // Verify the contents of the received update. 314 if err := verifyListenerUpdate(ctx, lw.updateCh, test.wantUpdate); err != nil { 315 t.Fatal(err) 316 } 317 318 // Configure the management server to return an additional listener 319 // resource, one that we are not interested in. 320 resources = e2e.UpdateOptions{ 321 NodeID: nodeID, 322 Listeners: []*v3listenerpb.Listener{test.watchedResource, test.notWatchedResource}, 323 SkipValidation: true, 324 } 325 if err := mgmtServer.Update(ctx, resources); err != nil { 326 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 327 } 328 if err := verifyNoListenerUpdate(ctx, lw.updateCh); err != nil { 329 t.Fatal(err) 330 } 331 332 // Cancel the watch and update the resource corresponding to the original 333 // watch. Ensure that the cancelled watch callback is not invoked. 334 ldsCancel() 335 resources = e2e.UpdateOptions{ 336 NodeID: nodeID, 337 Listeners: []*v3listenerpb.Listener{test.updatedWatchedResource, test.notWatchedResource}, 338 SkipValidation: true, 339 } 340 if err := mgmtServer.Update(ctx, resources); err != nil { 341 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 342 } 343 if err := verifyNoListenerUpdate(ctx, lw.updateCh); err != nil { 344 t.Fatal(err) 345 } 346 }) 347 } 348 } 349 350 // TestLDSWatch_TwoWatchesForSameResourceName covers the case where two watchers 351 // exist for a single listener resource. The test verifies the following 352 // scenarios: 353 // 1. An update from the management server containing the resource being 354 // watched should result in the invocation of both watch callbacks. 355 // 2. After one of the watches is cancelled, a redundant update from the 356 // management server should not result in the invocation of either of the 357 // watch callbacks. 358 // 3. An update from the management server containing the resource being 359 // watched should result in the invocation of the un-cancelled watch 360 // callback. 361 // 362 // The test is run for old and new style names. 363 func (s) TestLDSWatch_TwoWatchesForSameResourceName(t *testing.T) { 364 tests := []struct { 365 desc string 366 resourceName string 367 watchedResource *v3listenerpb.Listener // The resource being watched. 368 updatedWatchedResource *v3listenerpb.Listener // The watched resource after an update. 369 wantUpdateV1 listenerUpdateErrTuple 370 wantUpdateV2 listenerUpdateErrTuple 371 }{ 372 { 373 desc: "old style resource", 374 resourceName: ldsName, 375 watchedResource: e2e.DefaultClientListener(ldsName, rdsName), 376 updatedWatchedResource: e2e.DefaultClientListener(ldsName, "new-rds-resource"), 377 wantUpdateV1: listenerUpdateErrTuple{ 378 update: xdsresource.ListenerUpdate{ 379 RouteConfigName: rdsName, 380 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 381 }, 382 }, 383 wantUpdateV2: listenerUpdateErrTuple{ 384 update: xdsresource.ListenerUpdate{ 385 RouteConfigName: "new-rds-resource", 386 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 387 }, 388 }, 389 }, 390 { 391 desc: "new style resource", 392 resourceName: ldsNameNewStyle, 393 watchedResource: e2e.DefaultClientListener(ldsNameNewStyle, rdsNameNewStyle), 394 updatedWatchedResource: e2e.DefaultClientListener(ldsNameNewStyle, "new-rds-resource"), 395 wantUpdateV1: listenerUpdateErrTuple{ 396 update: xdsresource.ListenerUpdate{ 397 RouteConfigName: rdsNameNewStyle, 398 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 399 }, 400 }, 401 wantUpdateV2: listenerUpdateErrTuple{ 402 update: xdsresource.ListenerUpdate{ 403 RouteConfigName: "new-rds-resource", 404 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 405 }, 406 }, 407 }, 408 } 409 410 for _, test := range tests { 411 t.Run(test.desc, func(t *testing.T) { 412 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 413 414 nodeID := uuid.New().String() 415 bc, err := bootstrap.NewContentsForTesting(bootstrap.ConfigOptionsForTesting{ 416 Servers: []byte(fmt.Sprintf(`[{ 417 "server_uri": %q, 418 "channel_creds": [{"type": "insecure"}] 419 }]`, mgmtServer.Address)), 420 Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)), 421 Authorities: map[string]json.RawMessage{ 422 // Xdstp resource names used in this test do not specify an 423 // authority. These will end up looking up an entry with the 424 // empty key in the authorities map. Having an entry with an 425 // empty key and empty configuration, results in these 426 // resources also using the top-level configuration. 427 "": []byte(`{}`), 428 }, 429 }) 430 if err != nil { 431 t.Fatalf("Failed to create bootstrap configuration: %v", err) 432 } 433 434 // Create an xDS client with the above bootstrap contents. 435 config, err := bootstrap.NewConfigFromContents(bc) 436 if err != nil { 437 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 438 } 439 pool := xdsclient.NewPool(config) 440 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 441 Name: t.Name(), 442 }) 443 if err != nil { 444 t.Fatalf("Failed to create xDS client: %v", err) 445 } 446 defer close() 447 448 // Register two watches for the same listener resource and have the 449 // callbacks push the received updates on to a channel. 450 lw1 := newListenerWatcher() 451 ldsCancel1 := xdsresource.WatchListener(client, test.resourceName, lw1) 452 defer ldsCancel1() 453 lw2 := newListenerWatcher() 454 ldsCancel2 := xdsresource.WatchListener(client, test.resourceName, lw2) 455 456 // Configure the management server to return a single listener 457 // resource, corresponding to the one we registered watches for. 458 resources := e2e.UpdateOptions{ 459 NodeID: nodeID, 460 Listeners: []*v3listenerpb.Listener{test.watchedResource}, 461 SkipValidation: true, 462 } 463 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 464 defer cancel() 465 if err := mgmtServer.Update(ctx, resources); err != nil { 466 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 467 } 468 469 // Verify the contents of the received update. 470 if err := verifyListenerUpdate(ctx, lw1.updateCh, test.wantUpdateV1); err != nil { 471 t.Fatal(err) 472 } 473 if err := verifyListenerUpdate(ctx, lw2.updateCh, test.wantUpdateV1); err != nil { 474 t.Fatal(err) 475 } 476 477 // Cancel the second watch and force the management server to push a 478 // redundant update for the resource being watched. Neither of the 479 // two watch callbacks should be invoked. 480 ldsCancel2() 481 if err := mgmtServer.Update(ctx, resources); err != nil { 482 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 483 } 484 if err := verifyNoListenerUpdate(ctx, lw1.updateCh); err != nil { 485 t.Fatal(err) 486 } 487 if err := verifyNoListenerUpdate(ctx, lw2.updateCh); err != nil { 488 t.Fatal(err) 489 } 490 491 // Update to the resource being watched. The un-cancelled callback 492 // should be invoked while the cancelled one should not be. 493 resources = e2e.UpdateOptions{ 494 NodeID: nodeID, 495 Listeners: []*v3listenerpb.Listener{test.updatedWatchedResource}, 496 SkipValidation: true, 497 } 498 if err := mgmtServer.Update(ctx, resources); err != nil { 499 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 500 } 501 if err := verifyListenerUpdate(ctx, lw1.updateCh, test.wantUpdateV2); err != nil { 502 t.Fatal(err) 503 } 504 if err := verifyNoListenerUpdate(ctx, lw2.updateCh); err != nil { 505 t.Fatal(err) 506 } 507 }) 508 } 509 } 510 511 // TestLDSWatch_ThreeWatchesForDifferentResourceNames covers the case with three 512 // watchers (two watchers for one resource, and the third watcher for another 513 // resource), exist across two listener resources. The test verifies that an 514 // update from the management server containing both resources results in the 515 // invocation of all watch callbacks. 516 // 517 // The test is run with both old and new style names. 518 func (s) TestLDSWatch_ThreeWatchesForDifferentResourceNames(t *testing.T) { 519 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 520 521 nodeID := uuid.New().String() 522 authority := makeAuthorityName(t.Name()) 523 bc, err := bootstrap.NewContentsForTesting(bootstrap.ConfigOptionsForTesting{ 524 Servers: []byte(fmt.Sprintf(`[{ 525 "server_uri": %q, 526 "channel_creds": [{"type": "insecure"}] 527 }]`, mgmtServer.Address)), 528 Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)), 529 Authorities: map[string]json.RawMessage{ 530 // Xdstp style resource names used in this test use a slash removed 531 // version of t.Name as their authority, and the empty config 532 // results in the top-level xds server configuration being used for 533 // this authority. 534 authority: []byte(`{}`), 535 }, 536 }) 537 if err != nil { 538 t.Fatalf("Failed to create bootstrap configuration: %v", err) 539 } 540 541 // Create an xDS client with the above bootstrap contents. 542 config, err := bootstrap.NewConfigFromContents(bc) 543 if err != nil { 544 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 545 } 546 pool := xdsclient.NewPool(config) 547 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 548 Name: t.Name(), 549 }) 550 if err != nil { 551 t.Fatalf("Failed to create xDS client: %v", err) 552 } 553 defer close() 554 555 // Register two watches for the same listener resource and have the 556 // callbacks push the received updates on to a channel. 557 lw1 := newListenerWatcher() 558 ldsCancel1 := xdsresource.WatchListener(client, ldsName, lw1) 559 defer ldsCancel1() 560 lw2 := newListenerWatcher() 561 ldsCancel2 := xdsresource.WatchListener(client, ldsName, lw2) 562 defer ldsCancel2() 563 564 // Register the third watch for a different listener resource. 565 ldsNameNewStyle := makeNewStyleLDSName(authority) 566 lw3 := newListenerWatcher() 567 ldsCancel3 := xdsresource.WatchListener(client, ldsNameNewStyle, lw3) 568 defer ldsCancel3() 569 570 // Configure the management server to return two listener resources, 571 // corresponding to the registered watches. 572 resources := e2e.UpdateOptions{ 573 NodeID: nodeID, 574 Listeners: []*v3listenerpb.Listener{ 575 e2e.DefaultClientListener(ldsName, rdsName), 576 e2e.DefaultClientListener(ldsNameNewStyle, rdsName), 577 }, 578 SkipValidation: true, 579 } 580 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 581 defer cancel() 582 if err := mgmtServer.Update(ctx, resources); err != nil { 583 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 584 } 585 586 // Verify the contents of the received update for the all watchers. The two 587 // resources returned differ only in the resource name. Therefore the 588 // expected update is the same for all the watchers. 589 wantUpdate := listenerUpdateErrTuple{ 590 update: xdsresource.ListenerUpdate{ 591 RouteConfigName: rdsName, 592 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 593 }, 594 } 595 if err := verifyListenerUpdate(ctx, lw1.updateCh, wantUpdate); err != nil { 596 t.Fatal(err) 597 } 598 if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil { 599 t.Fatal(err) 600 } 601 if err := verifyListenerUpdate(ctx, lw3.updateCh, wantUpdate); err != nil { 602 t.Fatal(err) 603 } 604 } 605 606 // TestLDSWatch_ResourceCaching covers the case where a watch is registered for 607 // a resource which is already present in the cache. The test verifies that the 608 // watch callback is invoked with the contents from the cache, instead of a 609 // request being sent to the management server. 610 func (s) TestLDSWatch_ResourceCaching(t *testing.T) { 611 firstRequestReceived := false 612 firstAckReceived := grpcsync.NewEvent() 613 secondRequestReceived := grpcsync.NewEvent() 614 615 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{ 616 OnStreamRequest: func(id int64, req *v3discoverypb.DiscoveryRequest) error { 617 // The first request has an empty version string. 618 if !firstRequestReceived && req.GetVersionInfo() == "" { 619 firstRequestReceived = true 620 return nil 621 } 622 // The first ack has a non-empty version string. 623 if !firstAckReceived.HasFired() && req.GetVersionInfo() != "" { 624 firstAckReceived.Fire() 625 return nil 626 } 627 // Any requests after the first request and ack, are not expected. 628 secondRequestReceived.Fire() 629 return nil 630 }, 631 }) 632 633 nodeID := uuid.New().String() 634 bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address) 635 636 // Create an xDS client with the above bootstrap contents. 637 config, err := bootstrap.NewConfigFromContents(bc) 638 if err != nil { 639 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 640 } 641 pool := xdsclient.NewPool(config) 642 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 643 Name: t.Name(), 644 }) 645 if err != nil { 646 t.Fatalf("Failed to create xDS client: %v", err) 647 } 648 defer close() 649 650 // Register a watch for a listener resource and have the watch 651 // callback push the received update on to a channel. 652 lw1 := newListenerWatcher() 653 ldsCancel1 := xdsresource.WatchListener(client, ldsName, lw1) 654 defer ldsCancel1() 655 656 // Configure the management server to return a single listener 657 // resource, corresponding to the one we registered a watch for. 658 resources := e2e.UpdateOptions{ 659 NodeID: nodeID, 660 Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(ldsName, rdsName)}, 661 SkipValidation: true, 662 } 663 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 664 defer cancel() 665 if err := mgmtServer.Update(ctx, resources); err != nil { 666 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 667 } 668 669 // Verify the contents of the received update. 670 wantUpdate := listenerUpdateErrTuple{ 671 update: xdsresource.ListenerUpdate{ 672 RouteConfigName: rdsName, 673 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 674 }, 675 } 676 if err := verifyListenerUpdate(ctx, lw1.updateCh, wantUpdate); err != nil { 677 t.Fatal(err) 678 } 679 select { 680 case <-ctx.Done(): 681 t.Fatal("timeout when waiting for receipt of ACK at the management server") 682 case <-firstAckReceived.Done(): 683 } 684 685 // Register another watch for the same resource. This should get the update 686 // from the cache. 687 lw2 := newListenerWatcher() 688 ldsCancel2 := xdsresource.WatchListener(client, ldsName, lw2) 689 defer ldsCancel2() 690 if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil { 691 t.Fatal(err) 692 } 693 // No request should get sent out as part of this watch. 694 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 695 defer sCancel() 696 select { 697 case <-sCtx.Done(): 698 case <-secondRequestReceived.Done(): 699 t.Fatal("xdsClient sent out request instead of using update from cache") 700 } 701 } 702 703 // TestLDSWatch_ExpiryTimerFiresBeforeResponse tests the case where the client 704 // does not receive an LDS response for the request that it sends. The test 705 // verifies that the watch callback is invoked with an error once the 706 // watchExpiryTimer fires. 707 func (s) TestLDSWatch_ExpiryTimerFiresBeforeResponse(t *testing.T) { 708 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 709 710 nodeID := uuid.New().String() 711 bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address) 712 713 // Create an xDS client talking to the above management server. 714 config, err := bootstrap.NewConfigFromContents(bc) 715 if err != nil { 716 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 717 } 718 pool := xdsclient.NewPool(config) 719 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 720 Name: t.Name(), 721 WatchExpiryTimeout: defaultTestWatchExpiryTimeout, 722 }) 723 if err != nil { 724 t.Fatalf("Failed to create an xDS client: %v", err) 725 } 726 defer close() 727 728 // Register a watch for a resource which is expected to fail with an error 729 // after the watch expiry timer fires. 730 lw := newListenerWatcher() 731 ldsCancel := xdsresource.WatchListener(client, ldsName, lw) 732 defer ldsCancel() 733 734 // Wait for the watch expiry timer to fire. 735 <-time.After(defaultTestWatchExpiryTimeout) 736 737 // Verify that an empty update with the expected error is received. 738 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 739 defer cancel() 740 wantErr := xdsresource.NewError(xdsresource.ErrorTypeResourceNotFound, "") 741 if err := verifyListenerUpdate(ctx, lw.updateCh, listenerUpdateErrTuple{err: wantErr}); err != nil { 742 t.Fatal(err) 743 } 744 } 745 746 // TestLDSWatch_ValidResponseCancelsExpiryTimerBehavior tests the case where the 747 // client receives a valid LDS response for the request that it sends. The test 748 // verifies that the behavior associated with the expiry timer (i.e, callback 749 // invocation with error) does not take place. 750 func (s) TestLDSWatch_ValidResponseCancelsExpiryTimerBehavior(t *testing.T) { 751 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 752 753 nodeID := uuid.New().String() 754 bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address) 755 756 // Create an xDS client talking to the above management server. 757 config, err := bootstrap.NewConfigFromContents(bc) 758 if err != nil { 759 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 760 } 761 pool := xdsclient.NewPool(config) 762 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 763 Name: t.Name(), 764 WatchExpiryTimeout: defaultTestWatchExpiryTimeout, 765 }) 766 if err != nil { 767 t.Fatalf("Failed to create an xDS client: %v", err) 768 } 769 defer close() 770 771 // Register a watch for a listener resource and have the watch 772 // callback push the received update on to a channel. 773 lw := newListenerWatcher() 774 ldsCancel := xdsresource.WatchListener(client, ldsName, lw) 775 defer ldsCancel() 776 777 // Configure the management server to return a single listener 778 // resource, corresponding to the one we registered a watch for. 779 resources := e2e.UpdateOptions{ 780 NodeID: nodeID, 781 Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(ldsName, rdsName)}, 782 SkipValidation: true, 783 } 784 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 785 defer cancel() 786 if err := mgmtServer.Update(ctx, resources); err != nil { 787 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 788 } 789 790 // Verify the contents of the received update. 791 wantUpdate := listenerUpdateErrTuple{ 792 update: xdsresource.ListenerUpdate{ 793 RouteConfigName: rdsName, 794 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 795 }, 796 } 797 if err := verifyListenerUpdate(ctx, lw.updateCh, wantUpdate); err != nil { 798 t.Fatal(err) 799 } 800 801 // Wait for the watch expiry timer to fire, and verify that the callback is 802 // not invoked. 803 <-time.After(defaultTestWatchExpiryTimeout) 804 if err := verifyNoListenerUpdate(ctx, lw.updateCh); err != nil { 805 t.Fatal(err) 806 } 807 } 808 809 // TestLDSWatch_ResourceRemoved covers the cases where a resource being watched 810 // is removed from the management server. The test verifies the following 811 // scenarios: 812 // 1. Removing a resource should trigger the watch callback with a resource 813 // removed error. It should not trigger the watch callback for an unrelated 814 // resource. 815 // 2. An update to another resource should result in the invocation of the watch 816 // callback associated with that resource. It should not result in the 817 // invocation of the watch callback associated with the deleted resource. 818 // 819 // The test is run with both old and new style names. 820 func (s) TestLDSWatch_ResourceRemoved(t *testing.T) { 821 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 822 823 nodeID := uuid.New().String() 824 authority := makeAuthorityName(t.Name()) 825 bc, err := bootstrap.NewContentsForTesting(bootstrap.ConfigOptionsForTesting{ 826 Servers: []byte(fmt.Sprintf(`[{ 827 "server_uri": %q, 828 "channel_creds": [{"type": "insecure"}] 829 }]`, mgmtServer.Address)), 830 Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)), 831 Authorities: map[string]json.RawMessage{ 832 // Xdstp style resource names used in this test use a slash removed 833 // version of t.Name as their authority, and the empty config 834 // results in the top-level xds server configuration being used for 835 // this authority. 836 authority: []byte(`{}`), 837 }, 838 }) 839 if err != nil { 840 t.Fatalf("Failed to create bootstrap configuration: %v", err) 841 } 842 843 // Create an xDS client with the above bootstrap contents. 844 config, err := bootstrap.NewConfigFromContents(bc) 845 if err != nil { 846 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 847 } 848 pool := xdsclient.NewPool(config) 849 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 850 Name: t.Name(), 851 }) 852 if err != nil { 853 t.Fatalf("Failed to create xDS client: %v", err) 854 } 855 defer close() 856 857 // Register two watches for two listener resources and have the 858 // callbacks push the received updates on to a channel. 859 resourceName1 := ldsName 860 lw1 := newListenerWatcher() 861 ldsCancel1 := xdsresource.WatchListener(client, resourceName1, lw1) 862 defer ldsCancel1() 863 864 resourceName2 := makeNewStyleLDSName(authority) 865 lw2 := newListenerWatcher() 866 ldsCancel2 := xdsresource.WatchListener(client, resourceName2, lw2) 867 defer ldsCancel2() 868 869 // Configure the management server to return two listener resources, 870 // corresponding to the registered watches. 871 resources := e2e.UpdateOptions{ 872 NodeID: nodeID, 873 Listeners: []*v3listenerpb.Listener{ 874 e2e.DefaultClientListener(resourceName1, rdsName), 875 e2e.DefaultClientListener(resourceName2, rdsName), 876 }, 877 SkipValidation: true, 878 } 879 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 880 defer cancel() 881 if err := mgmtServer.Update(ctx, resources); err != nil { 882 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 883 } 884 885 // Verify the contents of the received update for both watchers. The two 886 // resources returned differ only in the resource name. Therefore the 887 // expected update is the same for both watchers. 888 wantUpdate := listenerUpdateErrTuple{ 889 update: xdsresource.ListenerUpdate{ 890 RouteConfigName: rdsName, 891 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 892 }, 893 } 894 if err := verifyListenerUpdate(ctx, lw1.updateCh, wantUpdate); err != nil { 895 t.Fatal(err) 896 } 897 if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil { 898 t.Fatal(err) 899 } 900 901 // Remove the first listener resource on the management server. 902 resources = e2e.UpdateOptions{ 903 NodeID: nodeID, 904 Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(resourceName2, rdsName)}, 905 SkipValidation: true, 906 } 907 if err := mgmtServer.Update(ctx, resources); err != nil { 908 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 909 } 910 911 // The first watcher should receive a resource removed error, while the 912 // second watcher should not see an update. 913 if err := verifyListenerUpdate(ctx, lw1.updateCh, listenerUpdateErrTuple{ 914 err: xdsresource.NewError(xdsresource.ErrorTypeResourceNotFound, ""), 915 }); err != nil { 916 t.Fatal(err) 917 } 918 if err := verifyNoListenerUpdate(ctx, lw2.updateCh); err != nil { 919 t.Fatal(err) 920 } 921 922 // Update the second listener resource on the management server. The first 923 // watcher should not see an update, while the second watcher should. 924 resources = e2e.UpdateOptions{ 925 NodeID: nodeID, 926 Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(resourceName2, "new-rds-resource")}, 927 SkipValidation: true, 928 } 929 if err := mgmtServer.Update(ctx, resources); err != nil { 930 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 931 } 932 if err := verifyNoListenerUpdate(ctx, lw1.updateCh); err != nil { 933 t.Fatal(err) 934 } 935 wantUpdate = listenerUpdateErrTuple{ 936 update: xdsresource.ListenerUpdate{ 937 RouteConfigName: "new-rds-resource", 938 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 939 }, 940 } 941 if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil { 942 t.Fatal(err) 943 } 944 } 945 946 // TestLDSWatch_NewWatcherForRemovedResource covers the case where a new 947 // watcher registers for a resource that has been removed. The test verifies 948 // the following scenarios: 949 // 1. When a resource is deleted by the management server, any active 950 // watchers of that resource should be notified with a "resource removed" 951 // error through their watch callback. 952 // 2. If a new watcher attempts to register for a resource that has already 953 // been deleted, its watch callback should be immediately invoked with a 954 // "resource removed" error. 955 func (s) TestLDSWatch_NewWatcherForRemovedResource(t *testing.T) { 956 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 957 958 nodeID := uuid.New().String() 959 bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address) 960 961 // Create an xDS client with the above bootstrap contents. 962 config, err := bootstrap.NewConfigFromContents(bc) 963 if err != nil { 964 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 965 } 966 pool := xdsclient.NewPool(config) 967 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 968 Name: t.Name(), 969 }) 970 if err != nil { 971 t.Fatalf("Failed to create xDS client: %v", err) 972 } 973 defer close() 974 975 // Register watch for the listener resource and have the 976 // callbacks push the received updates on to a channel. 977 lw1 := newListenerWatcher() 978 ldsCancel1 := xdsresource.WatchListener(client, ldsName, lw1) 979 defer ldsCancel1() 980 981 // Configure the management server to return listener resource, 982 // corresponding to the registered watch. 983 resource := e2e.UpdateOptions{ 984 NodeID: nodeID, 985 Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(ldsName, rdsName)}, 986 SkipValidation: true, 987 } 988 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 989 defer cancel() 990 if err := mgmtServer.Update(ctx, resource); err != nil { 991 t.Fatalf("Failed to update management server with resource: %v, err: %v", resource, err) 992 } 993 994 // Verify the contents of the received update for existing watch. 995 wantUpdate := listenerUpdateErrTuple{ 996 update: xdsresource.ListenerUpdate{ 997 RouteConfigName: rdsName, 998 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 999 }, 1000 } 1001 if err := verifyListenerUpdate(ctx, lw1.updateCh, wantUpdate); err != nil { 1002 t.Fatal(err) 1003 } 1004 1005 // Remove the listener resource on the management server. 1006 resource = e2e.UpdateOptions{ 1007 NodeID: nodeID, 1008 Listeners: []*v3listenerpb.Listener{}, 1009 SkipValidation: true, 1010 } 1011 if err := mgmtServer.Update(ctx, resource); err != nil { 1012 t.Fatalf("Failed to update management server with resource: %v, err: %v", resource, err) 1013 } 1014 1015 // The existing watcher should receive a resource removed error. 1016 updateError := listenerUpdateErrTuple{err: xdsresource.NewError(xdsresource.ErrorTypeResourceNotFound, "")} 1017 if err := verifyListenerUpdate(ctx, lw1.updateCh, updateError); err != nil { 1018 t.Fatal(err) 1019 } 1020 1021 // New watchers attempting to register for a deleted resource should also 1022 // receive a "resource removed" error. 1023 lw2 := newListenerWatcher() 1024 ldsCancel2 := xdsresource.WatchListener(client, ldsName, lw2) 1025 defer ldsCancel2() 1026 if err := verifyListenerUpdate(ctx, lw2.updateCh, updateError); err != nil { 1027 t.Fatal(err) 1028 } 1029 } 1030 1031 // TestLDSWatch_NACKError covers the case where an update from the management 1032 // server is NACKed by the xdsclient. The test verifies that the error is 1033 // propagated to the existing watcher. After NACK, if a new watcher registers 1034 // for the resource, error is propagated to the new watcher as well. 1035 func (s) TestLDSWatch_NACKError(t *testing.T) { 1036 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 1037 1038 nodeID := uuid.New().String() 1039 bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address) 1040 1041 // Create an xDS client with the above bootstrap contents. 1042 config, err := bootstrap.NewConfigFromContents(bc) 1043 if err != nil { 1044 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 1045 } 1046 pool := xdsclient.NewPool(config) 1047 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 1048 Name: t.Name(), 1049 }) 1050 if err != nil { 1051 t.Fatalf("Failed to create xDS client: %v", err) 1052 } 1053 defer close() 1054 1055 // Register a watch for a listener resource and have the watch 1056 // callback push the received update on to a channel. 1057 lw := newListenerWatcher() 1058 ldsCancel := xdsresource.WatchListener(client, ldsName, lw) 1059 defer ldsCancel() 1060 1061 // Configure the management server to return a single listener resource 1062 // which is expected to be NACKed by the client. 1063 resources := e2e.UpdateOptions{ 1064 NodeID: nodeID, 1065 Listeners: []*v3listenerpb.Listener{badListenerResource(t, ldsName)}, 1066 SkipValidation: true, 1067 } 1068 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1069 defer cancel() 1070 if err := mgmtServer.Update(ctx, resources); err != nil { 1071 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 1072 } 1073 1074 // Verify that the expected error is propagated to the existing watcher. 1075 if err := verifyErrorType(ctx, lw.updateCh, xdsresource.ErrorTypeNACKed, nodeID); err != nil { 1076 t.Fatal(err) 1077 } 1078 1079 // Verify that the expected error is propagated to the new watcher as well. 1080 lw2 := newListenerWatcher() 1081 ldsCancel2 := xdsresource.WatchListener(client, ldsName, lw2) 1082 defer ldsCancel2() 1083 if err := verifyErrorType(ctx, lw2.updateCh, xdsresource.ErrorTypeNACKed, nodeID); err != nil { 1084 t.Fatal(err) 1085 } 1086 } 1087 1088 // Tests the scenario where a watch registered for a resource results in a good 1089 // update followed by a bad update. This results in the resource cache 1090 // containing both the old good update and the latest NACK error. The test 1091 // verifies that a when a new watch is registered for the same resource, the new 1092 // watcher receives the good update followed by the NACK error. 1093 func (s) TestLDSWatch_ResourceCaching_NACKError(t *testing.T) { 1094 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 1095 1096 nodeID := uuid.New().String() 1097 bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address) 1098 1099 // Create an xDS client with the above bootstrap contents. 1100 config, err := bootstrap.NewConfigFromContents(bc) 1101 if err != nil { 1102 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 1103 } 1104 pool := xdsclient.NewPool(config) 1105 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 1106 Name: t.Name(), 1107 }) 1108 if err != nil { 1109 t.Fatalf("Failed to create xDS client: %v", err) 1110 } 1111 defer close() 1112 1113 // Register a watch for a listener resource and have the watch 1114 // callback push the received update on to a channel. 1115 lw1 := newListenerWatcher() 1116 ldsCancel1 := xdsresource.WatchListener(client, ldsName, lw1) 1117 defer ldsCancel1() 1118 1119 // Configure the management server to return a single listener 1120 // resource, corresponding to the one we registered a watch for. 1121 resources := e2e.UpdateOptions{ 1122 NodeID: nodeID, 1123 Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(ldsName, rdsName)}, 1124 SkipValidation: true, 1125 } 1126 ctx, cancel := context.WithTimeout(context.Background(), 1000*defaultTestTimeout) 1127 defer cancel() 1128 if err := mgmtServer.Update(ctx, resources); err != nil { 1129 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 1130 } 1131 1132 // Verify the contents of the received update. 1133 wantUpdate := listenerUpdateErrTuple{ 1134 update: xdsresource.ListenerUpdate{ 1135 RouteConfigName: rdsName, 1136 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 1137 }, 1138 } 1139 if err := verifyListenerUpdate(ctx, lw1.updateCh, wantUpdate); err != nil { 1140 t.Fatal(err) 1141 } 1142 1143 // Configure the management server to return a single listener resource 1144 // which is expected to be NACKed by the client. 1145 resources = e2e.UpdateOptions{ 1146 NodeID: nodeID, 1147 Listeners: []*v3listenerpb.Listener{badListenerResource(t, ldsName)}, 1148 SkipValidation: true, 1149 } 1150 if err := mgmtServer.Update(ctx, resources); err != nil { 1151 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 1152 } 1153 1154 // Verify that the expected error is propagated to the existing watcher. 1155 if err := verifyErrorType(ctx, lw1.updateCh, xdsresource.ErrorTypeNACKed, nodeID); err != nil { 1156 t.Fatal(err) 1157 } 1158 1159 // Register another watch for the same resource. This should get the update 1160 // and error from the cache. 1161 lw2 := newListenerWatcherMultiple(2) 1162 ldsCancel2 := xdsresource.WatchListener(client, ldsName, lw2) 1163 defer ldsCancel2() 1164 if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil { 1165 t.Fatal(err) 1166 } 1167 // Verify that the expected error is propagated to the existing watcher. 1168 if err := verifyErrorType(ctx, lw2.updateCh, xdsresource.ErrorTypeNACKed, nodeID); err != nil { 1169 t.Fatal(err) 1170 } 1171 } 1172 1173 // TestLDSWatch_PartialValid covers the case where a response from the 1174 // management server contains both valid and invalid resources and is expected 1175 // to be NACKed by the xdsclient. The test verifies that watchers corresponding 1176 // to the valid resource receive the update, while watchers corresponding to the 1177 // invalid resource receive an error. 1178 func (s) TestLDSWatch_PartialValid(t *testing.T) { 1179 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 1180 1181 nodeID := uuid.New().String() 1182 authority := makeAuthorityName(t.Name()) 1183 bc, err := bootstrap.NewContentsForTesting(bootstrap.ConfigOptionsForTesting{ 1184 Servers: []byte(fmt.Sprintf(`[{ 1185 "server_uri": %q, 1186 "channel_creds": [{"type": "insecure"}] 1187 }]`, mgmtServer.Address)), 1188 Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)), 1189 Authorities: map[string]json.RawMessage{ 1190 // Xdstp style resource names used in this test use a slash removed 1191 // version of t.Name as their authority, and the empty config 1192 // results in the top-level xds server configuration being used for 1193 // this authority. 1194 authority: []byte(`{}`), 1195 }, 1196 }) 1197 if err != nil { 1198 t.Fatalf("Failed to create bootstrap configuration: %v", err) 1199 } 1200 1201 // Create an xDS client with the above bootstrap contents. 1202 config, err := bootstrap.NewConfigFromContents(bc) 1203 if err != nil { 1204 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 1205 } 1206 pool := xdsclient.NewPool(config) 1207 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 1208 Name: t.Name(), 1209 }) 1210 if err != nil { 1211 t.Fatalf("Failed to create xDS client: %v", err) 1212 } 1213 defer close() 1214 1215 // Register two watches for listener resources. The first watch is expected 1216 // to receive an error because the received resource is NACKed. The second 1217 // watch is expected to get a good update. 1218 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1219 defer cancel() 1220 badResourceName := ldsName 1221 lw1 := newListenerWatcher() 1222 ldsCancel1 := xdsresource.WatchListener(client, badResourceName, lw1) 1223 defer ldsCancel1() 1224 goodResourceName := makeNewStyleLDSName(authority) 1225 lw2 := newListenerWatcher() 1226 ldsCancel2 := xdsresource.WatchListener(client, goodResourceName, lw2) 1227 defer ldsCancel2() 1228 1229 // Configure the management server with two listener resources. One of these 1230 // is a bad resource causing the update to be NACKed. 1231 resources := e2e.UpdateOptions{ 1232 NodeID: nodeID, 1233 Listeners: []*v3listenerpb.Listener{ 1234 badListenerResource(t, badResourceName), 1235 e2e.DefaultClientListener(goodResourceName, rdsName), 1236 }, 1237 SkipValidation: true, 1238 } 1239 if err := mgmtServer.Update(ctx, resources); err != nil { 1240 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 1241 } 1242 1243 // Verify that the expected error is propagated to the watcher which 1244 // requested for the bad resource. 1245 // Verify that the expected error is propagated to the existing watcher. 1246 if err := verifyErrorType(ctx, lw1.updateCh, xdsresource.ErrorTypeNACKed, nodeID); err != nil { 1247 t.Fatal(err) 1248 } 1249 1250 // Verify that the watcher watching the good resource receives a good 1251 // update. 1252 wantUpdate := listenerUpdateErrTuple{ 1253 update: xdsresource.ListenerUpdate{ 1254 RouteConfigName: rdsName, 1255 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 1256 }, 1257 } 1258 if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil { 1259 t.Fatal(err) 1260 } 1261 } 1262 1263 // TestLDSWatch_PartialResponse covers the case where a response from the 1264 // management server does not contain all requested resources. LDS responses are 1265 // supposed to contain all requested resources, and the absence of one usually 1266 // indicates that the management server does not know about it. In cases where 1267 // the server has never responded with this resource before, the xDS client is 1268 // expected to wait for the watch timeout to expire before concluding that the 1269 // resource does not exist on the server 1270 func (s) TestLDSWatch_PartialResponse(t *testing.T) { 1271 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 1272 1273 nodeID := uuid.New().String() 1274 authority := makeAuthorityName(t.Name()) 1275 bc, err := bootstrap.NewContentsForTesting(bootstrap.ConfigOptionsForTesting{ 1276 Servers: []byte(fmt.Sprintf(`[{ 1277 "server_uri": %q, 1278 "channel_creds": [{"type": "insecure"}] 1279 }]`, mgmtServer.Address)), 1280 Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)), 1281 Authorities: map[string]json.RawMessage{ 1282 // Xdstp style resource names used in this test use a slash removed 1283 // version of t.Name as their authority, and the empty config 1284 // results in the top-level xds server configuration being used for 1285 // this authority. 1286 authority: []byte(`{}`), 1287 }, 1288 }) 1289 if err != nil { 1290 t.Fatalf("Failed to create bootstrap configuration: %v", err) 1291 } 1292 1293 // Create an xDS client with the above bootstrap contents. 1294 config, err := bootstrap.NewConfigFromContents(bc) 1295 if err != nil { 1296 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 1297 } 1298 pool := xdsclient.NewPool(config) 1299 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 1300 Name: t.Name(), 1301 }) 1302 if err != nil { 1303 t.Fatalf("Failed to create xDS client: %v", err) 1304 } 1305 defer close() 1306 1307 // Register two watches for two listener resources and have the 1308 // callbacks push the received updates on to a channel. 1309 resourceName1 := ldsName 1310 lw1 := newListenerWatcher() 1311 ldsCancel1 := xdsresource.WatchListener(client, resourceName1, lw1) 1312 defer ldsCancel1() 1313 1314 resourceName2 := makeNewStyleLDSName(authority) 1315 lw2 := newListenerWatcher() 1316 ldsCancel2 := xdsresource.WatchListener(client, resourceName2, lw2) 1317 defer ldsCancel2() 1318 1319 // Configure the management server to return only one of the two listener 1320 // resources, corresponding to the registered watches. 1321 resources := e2e.UpdateOptions{ 1322 NodeID: nodeID, 1323 Listeners: []*v3listenerpb.Listener{ 1324 e2e.DefaultClientListener(resourceName1, rdsName), 1325 }, 1326 SkipValidation: true, 1327 } 1328 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1329 defer cancel() 1330 if err := mgmtServer.Update(ctx, resources); err != nil { 1331 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 1332 } 1333 1334 // Verify the contents of the received update for first watcher. 1335 wantUpdate1 := listenerUpdateErrTuple{ 1336 update: xdsresource.ListenerUpdate{ 1337 RouteConfigName: rdsName, 1338 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 1339 }, 1340 } 1341 if err := verifyListenerUpdate(ctx, lw1.updateCh, wantUpdate1); err != nil { 1342 t.Fatal(err) 1343 } 1344 1345 // Verify that the second watcher does not get an update with an error. 1346 if err := verifyNoListenerUpdate(ctx, lw2.updateCh); err != nil { 1347 t.Fatal(err) 1348 } 1349 1350 // Configure the management server to return two listener resources, 1351 // corresponding to the registered watches. 1352 resources = e2e.UpdateOptions{ 1353 NodeID: nodeID, 1354 Listeners: []*v3listenerpb.Listener{ 1355 e2e.DefaultClientListener(resourceName1, rdsName), 1356 e2e.DefaultClientListener(resourceName2, rdsName), 1357 }, 1358 SkipValidation: true, 1359 } 1360 if err := mgmtServer.Update(ctx, resources); err != nil { 1361 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 1362 } 1363 1364 // Verify the contents of the received update for the second watcher. 1365 wantUpdate2 := listenerUpdateErrTuple{ 1366 update: xdsresource.ListenerUpdate{ 1367 RouteConfigName: rdsName, 1368 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 1369 }, 1370 } 1371 if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate2); err != nil { 1372 t.Fatal(err) 1373 } 1374 1375 // Verify that the first watcher gets no update, as the first resource did 1376 // not change. 1377 if err := verifyNoListenerUpdate(ctx, lw1.updateCh); err != nil { 1378 t.Fatal(err) 1379 } 1380 }