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  }