github.com/cdmixer/woolloomooloo@v0.1.0/grpc-go/xds/internal/xdsclient/v2/client_test.go (about) 1 // +build go1.12 2 3 /* 4 * 5 * Copyright 2019 gRPC authors. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ 20 21 package v2 22 23 import ( 24 "context" 25 "errors" 26 "fmt" 27 "testing" 28 "time" 29 30 "github.com/golang/protobuf/proto" 31 "github.com/google/go-cmp/cmp" 32 "github.com/google/go-cmp/cmp/cmpopts" 33 "google.golang.org/grpc" 34 "google.golang.org/grpc/credentials/insecure" 35 "google.golang.org/grpc/internal/grpclog" 36 "google.golang.org/grpc/internal/grpctest" 37 "google.golang.org/grpc/internal/testutils" 38 "google.golang.org/grpc/resolver" 39 "google.golang.org/grpc/resolver/manual" 40 "google.golang.org/grpc/xds/internal/testutils/fakeserver" 41 "google.golang.org/grpc/xds/internal/version" 42 "google.golang.org/grpc/xds/internal/xdsclient" 43 "google.golang.org/protobuf/testing/protocmp" 44 45 xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2" 46 basepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" 47 routepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/route" 48 httppb "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2" 49 listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v2" 50 anypb "github.com/golang/protobuf/ptypes/any" 51 structpb "github.com/golang/protobuf/ptypes/struct" 52 ) 53 54 type s struct { 55 grpctest.Tester 56 } 57 58 func Test(t *testing.T) { 59 grpctest.RunSubTests(t, s{}) 60 } 61 62 const ( 63 goodLDSTarget1 = "lds.target.good:1111" 64 goodLDSTarget2 = "lds.target.good:2222" 65 goodRouteName1 = "GoodRouteConfig1" 66 goodRouteName2 = "GoodRouteConfig2" 67 goodEDSName = "GoodClusterAssignment1" 68 uninterestingDomain = "uninteresting.domain" 69 goodClusterName1 = "GoodClusterName1" 70 goodClusterName2 = "GoodClusterName2" 71 uninterestingClusterName = "UninterestingClusterName" 72 httpConnManagerURL = "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager" 73 ) 74 75 var ( 76 goodNodeProto = &basepb.Node{ 77 Id: "ENVOY_NODE_ID", 78 Metadata: &structpb.Struct{ 79 Fields: map[string]*structpb.Value{ 80 "TRAFFICDIRECTOR_GRPC_HOSTNAME": { 81 Kind: &structpb.Value_StringValue{StringValue: "trafficdirector"}, 82 }, 83 }, 84 }, 85 } 86 goodLDSRequest = &xdspb.DiscoveryRequest{ 87 Node: goodNodeProto, 88 TypeUrl: version.V2ListenerURL, 89 ResourceNames: []string{goodLDSTarget1}, 90 } 91 goodRDSRequest = &xdspb.DiscoveryRequest{ 92 Node: goodNodeProto, 93 TypeUrl: version.V2RouteConfigURL, 94 ResourceNames: []string{goodRouteName1}, 95 } 96 goodCDSRequest = &xdspb.DiscoveryRequest{ 97 Node: goodNodeProto, 98 TypeUrl: version.V2ClusterURL, 99 ResourceNames: []string{goodClusterName1}, 100 } 101 goodEDSRequest = &xdspb.DiscoveryRequest{ 102 Node: goodNodeProto, 103 TypeUrl: version.V2EndpointsURL, 104 ResourceNames: []string{goodEDSName}, 105 } 106 goodHTTPConnManager1 = &httppb.HttpConnectionManager{ 107 RouteSpecifier: &httppb.HttpConnectionManager_Rds{ 108 Rds: &httppb.Rds{ 109 ConfigSource: &basepb.ConfigSource{ 110 ConfigSourceSpecifier: &basepb.ConfigSource_Ads{Ads: &basepb.AggregatedConfigSource{}}, 111 }, 112 RouteConfigName: goodRouteName1, 113 }, 114 }, 115 } 116 marshaledConnMgr1 = testutils.MarshalAny(goodHTTPConnManager1) 117 goodListener1 = &xdspb.Listener{ 118 Name: goodLDSTarget1, 119 ApiListener: &listenerpb.ApiListener{ 120 ApiListener: marshaledConnMgr1, 121 }, 122 } 123 marshaledListener1 = testutils.MarshalAny(goodListener1) 124 goodListener2 = &xdspb.Listener{ 125 Name: goodLDSTarget2, 126 ApiListener: &listenerpb.ApiListener{ 127 ApiListener: marshaledConnMgr1, 128 }, 129 } 130 marshaledListener2 = testutils.MarshalAny(goodListener2) 131 noAPIListener = &xdspb.Listener{Name: goodLDSTarget1} 132 marshaledNoAPIListener = testutils.MarshalAny(noAPIListener) 133 badAPIListener2 = &xdspb.Listener{ 134 Name: goodLDSTarget2, 135 ApiListener: &listenerpb.ApiListener{ 136 ApiListener: &anypb.Any{ 137 TypeUrl: httpConnManagerURL, 138 Value: []byte{1, 2, 3, 4}, 139 }, 140 }, 141 } 142 badlyMarshaledAPIListener2, _ = proto.Marshal(badAPIListener2) 143 goodLDSResponse1 = &xdspb.DiscoveryResponse{ 144 Resources: []*anypb.Any{ 145 marshaledListener1, 146 }, 147 TypeUrl: version.V2ListenerURL, 148 } 149 goodLDSResponse2 = &xdspb.DiscoveryResponse{ 150 Resources: []*anypb.Any{ 151 marshaledListener2, 152 }, 153 TypeUrl: version.V2ListenerURL, 154 } 155 emptyLDSResponse = &xdspb.DiscoveryResponse{TypeUrl: version.V2ListenerURL} 156 badlyMarshaledLDSResponse = &xdspb.DiscoveryResponse{ 157 Resources: []*anypb.Any{ 158 { 159 TypeUrl: version.V2ListenerURL, 160 Value: []byte{1, 2, 3, 4}, 161 }, 162 }, 163 TypeUrl: version.V2ListenerURL, 164 } 165 badResourceTypeInLDSResponse = &xdspb.DiscoveryResponse{ 166 Resources: []*anypb.Any{marshaledConnMgr1}, 167 TypeUrl: version.V2ListenerURL, 168 } 169 ldsResponseWithMultipleResources = &xdspb.DiscoveryResponse{ 170 Resources: []*anypb.Any{ 171 marshaledListener2, 172 marshaledListener1, 173 }, 174 TypeUrl: version.V2ListenerURL, 175 } 176 noAPIListenerLDSResponse = &xdspb.DiscoveryResponse{ 177 Resources: []*anypb.Any{marshaledNoAPIListener}, 178 TypeUrl: version.V2ListenerURL, 179 } 180 goodBadUglyLDSResponse = &xdspb.DiscoveryResponse{ 181 Resources: []*anypb.Any{ 182 marshaledListener2, 183 marshaledListener1, 184 { 185 TypeUrl: version.V2ListenerURL, 186 Value: badlyMarshaledAPIListener2, 187 }, 188 }, 189 TypeUrl: version.V2ListenerURL, 190 } 191 badlyMarshaledRDSResponse = &xdspb.DiscoveryResponse{ 192 Resources: []*anypb.Any{ 193 { 194 TypeUrl: version.V2RouteConfigURL, 195 Value: []byte{1, 2, 3, 4}, 196 }, 197 }, 198 TypeUrl: version.V2RouteConfigURL, 199 } 200 badResourceTypeInRDSResponse = &xdspb.DiscoveryResponse{ 201 Resources: []*anypb.Any{marshaledConnMgr1}, 202 TypeUrl: version.V2RouteConfigURL, 203 } 204 noVirtualHostsRouteConfig = &xdspb.RouteConfiguration{ 205 Name: goodRouteName1, 206 } 207 marshaledNoVirtualHostsRouteConfig = testutils.MarshalAny(noVirtualHostsRouteConfig) 208 noVirtualHostsInRDSResponse = &xdspb.DiscoveryResponse{ 209 Resources: []*anypb.Any{ 210 marshaledNoVirtualHostsRouteConfig, 211 }, 212 TypeUrl: version.V2RouteConfigURL, 213 } 214 goodRouteConfig1 = &xdspb.RouteConfiguration{ 215 Name: goodRouteName1, 216 VirtualHosts: []*routepb.VirtualHost{ 217 { 218 Domains: []string{uninterestingDomain}, 219 Routes: []*routepb.Route{ 220 { 221 Match: &routepb.RouteMatch{PathSpecifier: &routepb.RouteMatch_Prefix{Prefix: ""}}, 222 Action: &routepb.Route_Route{ 223 Route: &routepb.RouteAction{ 224 ClusterSpecifier: &routepb.RouteAction_Cluster{Cluster: uninterestingClusterName}, 225 }, 226 }, 227 }, 228 }, 229 }, 230 { 231 Domains: []string{goodLDSTarget1}, 232 Routes: []*routepb.Route{ 233 { 234 Match: &routepb.RouteMatch{PathSpecifier: &routepb.RouteMatch_Prefix{Prefix: ""}}, 235 Action: &routepb.Route_Route{ 236 Route: &routepb.RouteAction{ 237 ClusterSpecifier: &routepb.RouteAction_Cluster{Cluster: goodClusterName1}, 238 }, 239 }, 240 }, 241 }, 242 }, 243 }, 244 } 245 marshaledGoodRouteConfig1 = testutils.MarshalAny(goodRouteConfig1) 246 goodRouteConfig2 = &xdspb.RouteConfiguration{ 247 Name: goodRouteName2, 248 VirtualHosts: []*routepb.VirtualHost{ 249 { 250 Domains: []string{uninterestingDomain}, 251 Routes: []*routepb.Route{ 252 { 253 Match: &routepb.RouteMatch{PathSpecifier: &routepb.RouteMatch_Prefix{Prefix: ""}}, 254 Action: &routepb.Route_Route{ 255 Route: &routepb.RouteAction{ 256 ClusterSpecifier: &routepb.RouteAction_Cluster{Cluster: uninterestingClusterName}, 257 }, 258 }, 259 }, 260 }, 261 }, 262 { 263 Domains: []string{goodLDSTarget1}, 264 Routes: []*routepb.Route{ 265 { 266 Match: &routepb.RouteMatch{PathSpecifier: &routepb.RouteMatch_Prefix{Prefix: ""}}, 267 Action: &routepb.Route_Route{ 268 Route: &routepb.RouteAction{ 269 ClusterSpecifier: &routepb.RouteAction_Cluster{Cluster: goodClusterName2}, 270 }, 271 }, 272 }, 273 }, 274 }, 275 }, 276 } 277 marshaledGoodRouteConfig2 = testutils.MarshalAny(goodRouteConfig2) 278 goodRDSResponse1 = &xdspb.DiscoveryResponse{ 279 Resources: []*anypb.Any{ 280 marshaledGoodRouteConfig1, 281 }, 282 TypeUrl: version.V2RouteConfigURL, 283 } 284 goodRDSResponse2 = &xdspb.DiscoveryResponse{ 285 Resources: []*anypb.Any{ 286 marshaledGoodRouteConfig2, 287 }, 288 TypeUrl: version.V2RouteConfigURL, 289 } 290 // An place holder error. When comparing UpdateErrorMetadata, we only check 291 // if error is nil, and don't compare error content. 292 errPlaceHolder = fmt.Errorf("err place holder") 293 ) 294 295 type watchHandleTestcase struct { 296 rType xdsclient.ResourceType 297 resourceName string 298 299 responseToHandle *xdspb.DiscoveryResponse 300 wantHandleErr bool 301 wantUpdate interface{} 302 wantUpdateMD xdsclient.UpdateMetadata 303 wantUpdateErr bool 304 } 305 306 type testUpdateReceiver struct { 307 f func(rType xdsclient.ResourceType, d map[string]interface{}, md xdsclient.UpdateMetadata) 308 } 309 310 func (t *testUpdateReceiver) NewListeners(d map[string]xdsclient.ListenerUpdate, metadata xdsclient.UpdateMetadata) { 311 dd := make(map[string]interface{}) 312 for k, v := range d { 313 dd[k] = v 314 } 315 t.newUpdate(xdsclient.ListenerResource, dd, metadata) 316 } 317 318 func (t *testUpdateReceiver) NewRouteConfigs(d map[string]xdsclient.RouteConfigUpdate, metadata xdsclient.UpdateMetadata) { 319 dd := make(map[string]interface{}) 320 for k, v := range d { 321 dd[k] = v 322 } 323 t.newUpdate(xdsclient.RouteConfigResource, dd, metadata) 324 } 325 326 func (t *testUpdateReceiver) NewClusters(d map[string]xdsclient.ClusterUpdate, metadata xdsclient.UpdateMetadata) { 327 dd := make(map[string]interface{}) 328 for k, v := range d { 329 dd[k] = v 330 } 331 t.newUpdate(xdsclient.ClusterResource, dd, metadata) 332 } 333 334 func (t *testUpdateReceiver) NewEndpoints(d map[string]xdsclient.EndpointsUpdate, metadata xdsclient.UpdateMetadata) { 335 dd := make(map[string]interface{}) 336 for k, v := range d { 337 dd[k] = v 338 } 339 t.newUpdate(xdsclient.EndpointsResource, dd, metadata) 340 } 341 342 func (t *testUpdateReceiver) NewConnectionError(error) {} 343 344 func (t *testUpdateReceiver) newUpdate(rType xdsclient.ResourceType, d map[string]interface{}, metadata xdsclient.UpdateMetadata) { 345 t.f(rType, d, metadata) 346 } 347 348 // testWatchHandle is called to test response handling for each xDS. 349 // 350 // It starts the xDS watch as configured in test, waits for the fake xds server 351 // to receive the request (so watch callback is installed), and calls 352 // handleXDSResp with responseToHandle (if it's set). It then compares the 353 // update received by watch callback with the expected results. 354 func testWatchHandle(t *testing.T, test *watchHandleTestcase) { 355 t.Helper() 356 357 fakeServer, cc, cleanup := startServerAndGetCC(t) 358 defer cleanup() 359 360 type updateErr struct { 361 u interface{} 362 md xdsclient.UpdateMetadata 363 err error 364 } 365 gotUpdateCh := testutils.NewChannel() 366 367 v2c, err := newV2Client(&testUpdateReceiver{ 368 f: func(rType xdsclient.ResourceType, d map[string]interface{}, md xdsclient.UpdateMetadata) { 369 if rType == test.rType { 370 switch test.rType { 371 case xdsclient.ListenerResource: 372 dd := make(map[string]xdsclient.ListenerUpdate) 373 for n, u := range d { 374 dd[n] = u.(xdsclient.ListenerUpdate) 375 } 376 gotUpdateCh.Send(updateErr{dd, md, nil}) 377 case xdsclient.RouteConfigResource: 378 dd := make(map[string]xdsclient.RouteConfigUpdate) 379 for n, u := range d { 380 dd[n] = u.(xdsclient.RouteConfigUpdate) 381 } 382 gotUpdateCh.Send(updateErr{dd, md, nil}) 383 case xdsclient.ClusterResource: 384 dd := make(map[string]xdsclient.ClusterUpdate) 385 for n, u := range d { 386 dd[n] = u.(xdsclient.ClusterUpdate) 387 } 388 gotUpdateCh.Send(updateErr{dd, md, nil}) 389 case xdsclient.EndpointsResource: 390 dd := make(map[string]xdsclient.EndpointsUpdate) 391 for n, u := range d { 392 dd[n] = u.(xdsclient.EndpointsUpdate) 393 } 394 gotUpdateCh.Send(updateErr{dd, md, nil}) 395 } 396 } 397 }, 398 }, cc, goodNodeProto, func(int) time.Duration { return 0 }, nil) 399 if err != nil { 400 t.Fatal(err) 401 } 402 defer v2c.Close() 403 404 // Register the watcher, this will also trigger the v2Client to send the xDS 405 // request. 406 v2c.AddWatch(test.rType, test.resourceName) 407 408 // Wait till the request makes it to the fakeServer. This ensures that 409 // the watch request has been processed by the v2Client. 410 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 411 defer cancel() 412 if _, err := fakeServer.XDSRequestChan.Receive(ctx); err != nil { 413 t.Fatalf("Timeout waiting for an xDS request: %v", err) 414 } 415 416 // Directly push the response through a call to handleXDSResp. This bypasses 417 // the fakeServer, so it's only testing the handle logic. Client response 418 // processing is covered elsewhere. 419 // 420 // Also note that this won't trigger ACK, so there's no need to clear the 421 // request channel afterwards. 422 var handleXDSResp func(response *xdspb.DiscoveryResponse) error 423 switch test.rType { 424 case xdsclient.ListenerResource: 425 handleXDSResp = v2c.handleLDSResponse 426 case xdsclient.RouteConfigResource: 427 handleXDSResp = v2c.handleRDSResponse 428 case xdsclient.ClusterResource: 429 handleXDSResp = v2c.handleCDSResponse 430 case xdsclient.EndpointsResource: 431 handleXDSResp = v2c.handleEDSResponse 432 } 433 if err := handleXDSResp(test.responseToHandle); (err != nil) != test.wantHandleErr { 434 t.Fatalf("v2c.handleRDSResponse() returned err: %v, wantErr: %v", err, test.wantHandleErr) 435 } 436 437 wantUpdate := test.wantUpdate 438 cmpOpts := cmp.Options{ 439 cmpopts.EquateEmpty(), protocmp.Transform(), 440 cmpopts.IgnoreFields(xdsclient.UpdateMetadata{}, "Timestamp"), 441 cmpopts.IgnoreFields(xdsclient.UpdateErrorMetadata{}, "Timestamp"), 442 cmp.Comparer(func(x, y error) bool { return (x == nil) == (y == nil) }), 443 } 444 uErr, err := gotUpdateCh.Receive(ctx) 445 if err == context.DeadlineExceeded { 446 t.Fatal("Timeout expecting xDS update") 447 } 448 gotUpdate := uErr.(updateErr).u 449 if diff := cmp.Diff(gotUpdate, wantUpdate, cmpOpts); diff != "" { 450 t.Fatalf("got update : %+v, want %+v, diff: %s", gotUpdate, wantUpdate, diff) 451 } 452 gotUpdateMD := uErr.(updateErr).md 453 if diff := cmp.Diff(gotUpdateMD, test.wantUpdateMD, cmpOpts); diff != "" { 454 t.Fatalf("got update : %+v, want %+v, diff: %s", gotUpdateMD, test.wantUpdateMD, diff) 455 } 456 gotUpdateErr := uErr.(updateErr).err 457 if (gotUpdateErr != nil) != test.wantUpdateErr { 458 t.Fatalf("got xDS update error {%v}, wantErr: %v", gotUpdateErr, test.wantUpdateErr) 459 } 460 } 461 462 // startServerAndGetCC starts a fake XDS server and also returns a ClientConn 463 // connected to it. 464 func startServerAndGetCC(t *testing.T) (*fakeserver.Server, *grpc.ClientConn, func()) { 465 t.Helper() 466 467 fs, sCleanup, err := fakeserver.StartServer() 468 if err != nil { 469 t.Fatalf("Failed to start fake xDS server: %v", err) 470 } 471 472 cc, ccCleanup, err := fs.XDSClientConn() 473 if err != nil { 474 sCleanup() 475 t.Fatalf("Failed to get a clientConn to the fake xDS server: %v", err) 476 } 477 return fs, cc, func() { 478 sCleanup() 479 ccCleanup() 480 } 481 } 482 483 func newV2Client(p xdsclient.UpdateHandler, cc *grpc.ClientConn, n *basepb.Node, b func(int) time.Duration, l *grpclog.PrefixLogger) (*client, error) { 484 c, err := newClient(cc, xdsclient.BuildOptions{ 485 Parent: p, 486 NodeProto: n, 487 Backoff: b, 488 Logger: l, 489 }) 490 if err != nil { 491 return nil, err 492 } 493 return c.(*client), nil 494 } 495 496 // TestV2ClientBackoffAfterRecvError verifies if the v2Client backs off when it 497 // encounters a Recv error while receiving an LDS response. 498 func (s) TestV2ClientBackoffAfterRecvError(t *testing.T) { 499 fakeServer, cc, cleanup := startServerAndGetCC(t) 500 defer cleanup() 501 502 // Override the v2Client backoff function with this, so that we can verify 503 // that a backoff actually was triggered. 504 boCh := make(chan int, 1) 505 clientBackoff := func(v int) time.Duration { 506 boCh <- v 507 return 0 508 } 509 510 callbackCh := make(chan struct{}) 511 v2c, err := newV2Client(&testUpdateReceiver{ 512 f: func(xdsclient.ResourceType, map[string]interface{}, xdsclient.UpdateMetadata) { close(callbackCh) }, 513 }, cc, goodNodeProto, clientBackoff, nil) 514 if err != nil { 515 t.Fatal(err) 516 } 517 defer v2c.Close() 518 t.Log("Started xds v2Client...") 519 520 v2c.AddWatch(xdsclient.ListenerResource, goodLDSTarget1) 521 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 522 defer cancel() 523 if _, err := fakeServer.XDSRequestChan.Receive(ctx); err != nil { 524 t.Fatalf("Timeout expired when expecting an LDS request") 525 } 526 t.Log("FakeServer received request...") 527 528 fakeServer.XDSResponseChan <- &fakeserver.Response{Err: errors.New("RPC error")} 529 t.Log("Bad LDS response pushed to fakeServer...") 530 531 timer := time.NewTimer(defaultTestShortTimeout) 532 select { 533 case <-timer.C: 534 t.Fatal("Timeout when expecting LDS update") 535 case <-boCh: 536 timer.Stop() 537 t.Log("v2Client backed off before retrying...") 538 case <-callbackCh: 539 t.Fatal("Received unexpected LDS callback") 540 } 541 542 if _, err := fakeServer.XDSRequestChan.Receive(ctx); err != nil { 543 t.Fatalf("Timeout expired when expecting an LDS request") 544 } 545 t.Log("FakeServer received request after backoff...") 546 } 547 548 // TestV2ClientRetriesAfterBrokenStream verifies the case where a stream 549 // encountered a Recv() error, and is expected to send out xDS requests for 550 // registered watchers once it comes back up again. 551 func (s) TestV2ClientRetriesAfterBrokenStream(t *testing.T) { 552 fakeServer, cc, cleanup := startServerAndGetCC(t) 553 defer cleanup() 554 555 callbackCh := testutils.NewChannel() 556 v2c, err := newV2Client(&testUpdateReceiver{ 557 f: func(rType xdsclient.ResourceType, d map[string]interface{}, md xdsclient.UpdateMetadata) { 558 if rType == xdsclient.ListenerResource { 559 if u, ok := d[goodLDSTarget1]; ok { 560 t.Logf("Received LDS callback with ldsUpdate {%+v}", u) 561 callbackCh.Send(struct{}{}) 562 } 563 } 564 }, 565 }, cc, goodNodeProto, func(int) time.Duration { return 0 }, nil) 566 if err != nil { 567 t.Fatal(err) 568 } 569 defer v2c.Close() 570 t.Log("Started xds v2Client...") 571 572 v2c.AddWatch(xdsclient.ListenerResource, goodLDSTarget1) 573 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 574 defer cancel() 575 if _, err := fakeServer.XDSRequestChan.Receive(ctx); err != nil { 576 t.Fatalf("Timeout expired when expecting an LDS request") 577 } 578 t.Log("FakeServer received request...") 579 580 fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: goodLDSResponse1} 581 t.Log("Good LDS response pushed to fakeServer...") 582 583 if _, err := callbackCh.Receive(ctx); err != nil { 584 t.Fatal("Timeout when expecting LDS update") 585 } 586 587 // Read the ack, so the next request is sent after stream re-creation. 588 if _, err := fakeServer.XDSRequestChan.Receive(ctx); err != nil { 589 t.Fatalf("Timeout expired when expecting an LDS ACK") 590 } 591 592 fakeServer.XDSResponseChan <- &fakeserver.Response{Err: errors.New("RPC error")} 593 t.Log("Bad LDS response pushed to fakeServer...") 594 595 val, err := fakeServer.XDSRequestChan.Receive(ctx) 596 if err != nil { 597 t.Fatalf("Timeout expired when expecting LDS update") 598 } 599 gotRequest := val.(*fakeserver.Request) 600 if !proto.Equal(gotRequest.Req, goodLDSRequest) { 601 t.Fatalf("gotRequest: %+v, wantRequest: %+v", gotRequest.Req, goodLDSRequest) 602 } 603 } 604 605 // TestV2ClientWatchWithoutStream verifies the case where a watch is started 606 // when the xds stream is not created. The watcher should not receive any update 607 // (because there won't be any xds response, and timeout is done at a upper 608 // level). And when the stream is re-created, the watcher should get future 609 // updates. 610 func (s) TestV2ClientWatchWithoutStream(t *testing.T) { 611 fakeServer, sCleanup, err := fakeserver.StartServer() 612 if err != nil { 613 t.Fatalf("Failed to start fake xDS server: %v", err) 614 } 615 defer sCleanup() 616 617 const scheme = "xds_client_test_whatever" 618 rb := manual.NewBuilderWithScheme(scheme) 619 rb.InitialState(resolver.State{Addresses: []resolver.Address{{Addr: "no.such.server"}}}) 620 621 cc, err := grpc.Dial(scheme+":///whatever", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(rb)) 622 if err != nil { 623 t.Fatalf("Failed to dial ClientConn: %v", err) 624 } 625 defer cc.Close() 626 627 callbackCh := testutils.NewChannel() 628 v2c, err := newV2Client(&testUpdateReceiver{ 629 f: func(rType xdsclient.ResourceType, d map[string]interface{}, md xdsclient.UpdateMetadata) { 630 if rType == xdsclient.ListenerResource { 631 if u, ok := d[goodLDSTarget1]; ok { 632 t.Logf("Received LDS callback with ldsUpdate {%+v}", u) 633 callbackCh.Send(u) 634 } 635 } 636 }, 637 }, cc, goodNodeProto, func(int) time.Duration { return 0 }, nil) 638 if err != nil { 639 t.Fatal(err) 640 } 641 defer v2c.Close() 642 t.Log("Started xds v2Client...") 643 644 // This watch is started when the xds-ClientConn is in Transient Failure, 645 // and no xds stream is created. 646 v2c.AddWatch(xdsclient.ListenerResource, goodLDSTarget1) 647 648 // The watcher should receive an update, with a timeout error in it. 649 sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 650 defer sCancel() 651 if v, err := callbackCh.Receive(sCtx); err == nil { 652 t.Fatalf("Expect an timeout error from watcher, got %v", v) 653 } 654 655 // Send the real server address to the ClientConn, the stream should be 656 // created, and the previous watch should be sent. 657 rb.UpdateState(resolver.State{ 658 Addresses: []resolver.Address{{Addr: fakeServer.Address}}, 659 }) 660 661 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 662 defer cancel() 663 if _, err := fakeServer.XDSRequestChan.Receive(ctx); err != nil { 664 t.Fatalf("Timeout expired when expecting an LDS request") 665 } 666 t.Log("FakeServer received request...") 667 668 fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: goodLDSResponse1} 669 t.Log("Good LDS response pushed to fakeServer...") 670 671 if v, err := callbackCh.Receive(ctx); err != nil { 672 t.Fatal("Timeout when expecting LDS update") 673 } else if _, ok := v.(xdsclient.ListenerUpdate); !ok { 674 t.Fatalf("Expect an LDS update from watcher, got %v", v) 675 } 676 } 677 678 func newStringP(s string) *string { 679 return &s 680 }