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