gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/xds/internal/xdsclient/controller/v2_ack_test.go (about)

     1  /*
     2   *
     3   * Copyright 2019 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  package controller
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"strconv"
    24  	"testing"
    25  	"time"
    26  
    27  	xdspb "gitee.com/ks-custle/core-gm/go-control-plane/envoy/api/v2"
    28  	"gitee.com/ks-custle/core-gm/grpc/codes"
    29  	"gitee.com/ks-custle/core-gm/grpc/internal/testutils"
    30  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/testutils/fakeserver"
    31  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource"
    32  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource/version"
    33  	"github.com/golang/protobuf/proto"
    34  	anypb "github.com/golang/protobuf/ptypes/any"
    35  	"github.com/google/go-cmp/cmp"
    36  )
    37  
    38  const (
    39  	defaultTestTimeout      = 5 * time.Second
    40  	defaultTestShortTimeout = 10 * time.Millisecond
    41  )
    42  
    43  func startXDSV2Client(t *testing.T, controlPlaneAddr string) (v2c *Controller, cbLDS, cbRDS, cbCDS, cbEDS *testutils.Channel, cleanup func()) {
    44  	cbLDS = testutils.NewChannel()
    45  	cbRDS = testutils.NewChannel()
    46  	cbCDS = testutils.NewChannel()
    47  	cbEDS = testutils.NewChannel()
    48  	v2c, err := newTestController(&testUpdateReceiver{
    49  		f: func(rType xdsresource.ResourceType, d map[string]interface{}, md xdsresource.UpdateMetadata) {
    50  			t.Logf("Received %v callback with {%+v}", rType, d)
    51  			switch rType {
    52  			case xdsresource.ListenerResource:
    53  				if _, ok := d[goodLDSTarget1]; ok {
    54  					cbLDS.Send(struct{}{})
    55  				}
    56  			case xdsresource.RouteConfigResource:
    57  				if _, ok := d[goodRouteName1]; ok {
    58  					cbRDS.Send(struct{}{})
    59  				}
    60  			case xdsresource.ClusterResource:
    61  				if _, ok := d[goodClusterName1]; ok {
    62  					cbCDS.Send(struct{}{})
    63  				}
    64  			case xdsresource.EndpointsResource:
    65  				if _, ok := d[goodEDSName]; ok {
    66  					cbEDS.Send(struct{}{})
    67  				}
    68  			}
    69  		},
    70  	}, controlPlaneAddr, goodNodeProto, func(int) time.Duration { return 0 }, nil)
    71  	if err != nil {
    72  		t.Fatal(err)
    73  	}
    74  	t.Log("Started xds client...")
    75  	return v2c, cbLDS, cbRDS, cbCDS, cbEDS, v2c.Close
    76  }
    77  
    78  // compareXDSRequest reads requests from channel, compare it with want.
    79  func compareXDSRequest(ctx context.Context, ch *testutils.Channel, want *xdspb.DiscoveryRequest, ver, nonce string, wantErr bool) error {
    80  	val, err := ch.Receive(ctx)
    81  	if err != nil {
    82  		return err
    83  	}
    84  	req := val.(*fakeserver.Request)
    85  	if req.Err != nil {
    86  		return fmt.Errorf("unexpected error from request: %v", req.Err)
    87  	}
    88  
    89  	xdsReq := req.Req.(*xdspb.DiscoveryRequest)
    90  	if (xdsReq.ErrorDetail != nil) != wantErr {
    91  		return fmt.Errorf("received request with error details: %v, wantErr: %v", xdsReq.ErrorDetail, wantErr)
    92  	}
    93  	// All NACK request.ErrorDetails have hardcoded status code InvalidArguments.
    94  	if xdsReq.ErrorDetail != nil && xdsReq.ErrorDetail.Code != int32(codes.InvalidArgument) {
    95  		return fmt.Errorf("received request with error details: %v, want status with code: %v", xdsReq.ErrorDetail, codes.InvalidArgument)
    96  	}
    97  
    98  	xdsReq.ErrorDetail = nil // Clear the error details field before comparing.
    99  	wantClone := proto.Clone(want).(*xdspb.DiscoveryRequest)
   100  	wantClone.VersionInfo = ver
   101  	wantClone.ResponseNonce = nonce
   102  	if !cmp.Equal(xdsReq, wantClone, cmp.Comparer(proto.Equal)) {
   103  		return fmt.Errorf("received request different from want, diff: %s", cmp.Diff(req.Req, wantClone, cmp.Comparer(proto.Equal)))
   104  	}
   105  	return nil
   106  }
   107  
   108  func sendXDSRespWithVersion(ch chan<- *fakeserver.Response, respWithoutVersion *xdspb.DiscoveryResponse, ver int) (nonce string) {
   109  	respToSend := proto.Clone(respWithoutVersion).(*xdspb.DiscoveryResponse)
   110  	respToSend.VersionInfo = strconv.Itoa(ver)
   111  	nonce = strconv.Itoa(int(time.Now().UnixNano()))
   112  	respToSend.Nonce = nonce
   113  	ch <- &fakeserver.Response{Resp: respToSend}
   114  	return
   115  }
   116  
   117  // startXDS calls watch to send the first request. It then sends a good response
   118  // and checks for ack.
   119  func startXDS(ctx context.Context, t *testing.T, rType xdsresource.ResourceType, v2c *Controller, reqChan *testutils.Channel, req *xdspb.DiscoveryRequest, preVersion string, preNonce string) {
   120  	nameToWatch := ""
   121  	switch rType {
   122  	case xdsresource.ListenerResource:
   123  		nameToWatch = goodLDSTarget1
   124  	case xdsresource.RouteConfigResource:
   125  		nameToWatch = goodRouteName1
   126  	case xdsresource.ClusterResource:
   127  		nameToWatch = goodClusterName1
   128  	case xdsresource.EndpointsResource:
   129  		nameToWatch = goodEDSName
   130  	}
   131  	v2c.AddWatch(rType, nameToWatch)
   132  
   133  	if err := compareXDSRequest(ctx, reqChan, req, preVersion, preNonce, false); err != nil {
   134  		t.Fatalf("Failed to receive %v request: %v", rType, err)
   135  	}
   136  	t.Logf("FakeServer received %v request...", rType)
   137  }
   138  
   139  // sendGoodResp sends the good response, with the given version, and a random
   140  // nonce.
   141  //
   142  // It also waits and checks that the ack request contains the given version, and
   143  // the generated nonce.
   144  func sendGoodResp(ctx context.Context, t *testing.T, rType xdsresource.ResourceType, fakeServer *fakeserver.Server, ver int, goodResp *xdspb.DiscoveryResponse, wantReq *xdspb.DiscoveryRequest, callbackCh *testutils.Channel) (string, error) {
   145  	nonce := sendXDSRespWithVersion(fakeServer.XDSResponseChan, goodResp, ver)
   146  	t.Logf("Good %v response pushed to fakeServer...", rType)
   147  
   148  	if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, wantReq, strconv.Itoa(ver), nonce, false); err != nil {
   149  		return "", fmt.Errorf("failed to receive %v request: %v", rType, err)
   150  	}
   151  	t.Logf("Good %v response acked", rType)
   152  
   153  	if _, err := callbackCh.Receive(ctx); err != nil {
   154  		return "", fmt.Errorf("timeout when expecting %v update", rType)
   155  	}
   156  	t.Logf("Good %v response callback executed", rType)
   157  	return nonce, nil
   158  }
   159  
   160  // sendBadResp sends a bad response with the given version. This response will
   161  // be nacked, so we expect a request with the previous version (version-1).
   162  //
   163  // But the nonce in request should be the new nonce.
   164  func sendBadResp(ctx context.Context, t *testing.T, rType xdsresource.ResourceType, fakeServer *fakeserver.Server, ver int, wantReq *xdspb.DiscoveryRequest) error {
   165  	var typeURL string
   166  	switch rType {
   167  	case xdsresource.ListenerResource:
   168  		typeURL = version.V2ListenerURL
   169  	case xdsresource.RouteConfigResource:
   170  		typeURL = version.V2RouteConfigURL
   171  	case xdsresource.ClusterResource:
   172  		typeURL = version.V2ClusterURL
   173  	case xdsresource.EndpointsResource:
   174  		typeURL = version.V2EndpointsURL
   175  	}
   176  	nonce := sendXDSRespWithVersion(fakeServer.XDSResponseChan, &xdspb.DiscoveryResponse{
   177  		Resources: []*anypb.Any{{}},
   178  		TypeUrl:   typeURL,
   179  	}, ver)
   180  	t.Logf("Bad %v response pushed to fakeServer...", rType)
   181  	if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, wantReq, strconv.Itoa(ver-1), nonce, true); err != nil {
   182  		return fmt.Errorf("failed to receive %v request: %v", rType, err)
   183  	}
   184  	t.Logf("Bad %v response nacked", rType)
   185  	return nil
   186  }
   187  
   188  // TestV2ClientAck verifies that valid responses are acked, and invalid ones
   189  // are nacked.
   190  //
   191  // This test also verifies the version for different types are independent.
   192  func (s) TestV2ClientAck(t *testing.T) {
   193  	var (
   194  		versionLDS = 1000
   195  		versionRDS = 2000
   196  		versionCDS = 3000
   197  		versionEDS = 4000
   198  	)
   199  
   200  	fakeServer, cleanup := startServer(t)
   201  	defer cleanup()
   202  
   203  	v2c, cbLDS, cbRDS, cbCDS, cbEDS, v2cCleanup := startXDSV2Client(t, fakeServer.Address)
   204  	defer v2cCleanup()
   205  
   206  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   207  	defer cancel()
   208  
   209  	// Start the watch, send a good response, and check for ack.
   210  	startXDS(ctx, t, xdsresource.ListenerResource, v2c, fakeServer.XDSRequestChan, goodLDSRequest, "", "")
   211  	if _, err := sendGoodResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS); err != nil {
   212  		t.Fatal(err)
   213  	}
   214  	versionLDS++
   215  	startXDS(ctx, t, xdsresource.RouteConfigResource, v2c, fakeServer.XDSRequestChan, goodRDSRequest, "", "")
   216  	if _, err := sendGoodResp(ctx, t, xdsresource.RouteConfigResource, fakeServer, versionRDS, goodRDSResponse1, goodRDSRequest, cbRDS); err != nil {
   217  		t.Fatal(err)
   218  	}
   219  	versionRDS++
   220  	startXDS(ctx, t, xdsresource.ClusterResource, v2c, fakeServer.XDSRequestChan, goodCDSRequest, "", "")
   221  	if _, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS); err != nil {
   222  		t.Fatal(err)
   223  	}
   224  	versionCDS++
   225  	startXDS(ctx, t, xdsresource.EndpointsResource, v2c, fakeServer.XDSRequestChan, goodEDSRequest, "", "")
   226  	if _, err := sendGoodResp(ctx, t, xdsresource.EndpointsResource, fakeServer, versionEDS, goodEDSResponse1, goodEDSRequest, cbEDS); err != nil {
   227  		t.Fatal(err)
   228  	}
   229  	versionEDS++
   230  
   231  	// Send a bad response, and check for nack.
   232  	if err := sendBadResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSRequest); err != nil {
   233  		t.Fatal(err)
   234  	}
   235  	versionLDS++
   236  	if err := sendBadResp(ctx, t, xdsresource.RouteConfigResource, fakeServer, versionRDS, goodRDSRequest); err != nil {
   237  		t.Fatal(err)
   238  	}
   239  	versionRDS++
   240  	if err := sendBadResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSRequest); err != nil {
   241  		t.Fatal(err)
   242  	}
   243  	versionCDS++
   244  	if err := sendBadResp(ctx, t, xdsresource.EndpointsResource, fakeServer, versionEDS, goodEDSRequest); err != nil {
   245  		t.Fatal(err)
   246  	}
   247  	versionEDS++
   248  
   249  	// send another good response, and check for ack, with the new version.
   250  	if _, err := sendGoodResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS); err != nil {
   251  		t.Fatal(err)
   252  	}
   253  	versionLDS++
   254  	if _, err := sendGoodResp(ctx, t, xdsresource.RouteConfigResource, fakeServer, versionRDS, goodRDSResponse1, goodRDSRequest, cbRDS); err != nil {
   255  		t.Fatal(err)
   256  	}
   257  	versionRDS++
   258  	if _, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS); err != nil {
   259  		t.Fatal(err)
   260  	}
   261  	versionCDS++
   262  	if _, err := sendGoodResp(ctx, t, xdsresource.EndpointsResource, fakeServer, versionEDS, goodEDSResponse1, goodEDSRequest, cbEDS); err != nil {
   263  		t.Fatal(err)
   264  	}
   265  	versionEDS++
   266  }
   267  
   268  // Test when the first response is invalid, and is nacked, the nack requests
   269  // should have an empty version string.
   270  func (s) TestV2ClientAckFirstIsNack(t *testing.T) {
   271  	var versionLDS = 1000
   272  
   273  	fakeServer, cleanup := startServer(t)
   274  	defer cleanup()
   275  
   276  	v2c, cbLDS, _, _, _, v2cCleanup := startXDSV2Client(t, fakeServer.Address)
   277  	defer v2cCleanup()
   278  
   279  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   280  	defer cancel()
   281  
   282  	// Start the watch, send a good response, and check for ack.
   283  	startXDS(ctx, t, xdsresource.ListenerResource, v2c, fakeServer.XDSRequestChan, goodLDSRequest, "", "")
   284  
   285  	nonce := sendXDSRespWithVersion(fakeServer.XDSResponseChan, &xdspb.DiscoveryResponse{
   286  		Resources: []*anypb.Any{{}},
   287  		TypeUrl:   version.V2ListenerURL,
   288  	}, versionLDS)
   289  	t.Logf("Bad response pushed to fakeServer...")
   290  
   291  	// The expected version string is an empty string, because this is the first
   292  	// response, and it's nacked (so there's no previous ack version).
   293  	if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodLDSRequest, "", nonce, true); err != nil {
   294  		t.Errorf("Failed to receive request: %v", err)
   295  	}
   296  	t.Logf("Bad response nacked")
   297  	versionLDS++
   298  
   299  	sendGoodResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS)
   300  	versionLDS++
   301  }
   302  
   303  // Test when a nack is sent after a new watch, we nack with the previous acked
   304  // version (instead of resetting to empty string).
   305  func (s) TestV2ClientAckNackAfterNewWatch(t *testing.T) {
   306  	var versionLDS = 1000
   307  
   308  	fakeServer, cleanup := startServer(t)
   309  	defer cleanup()
   310  
   311  	v2c, cbLDS, _, _, _, v2cCleanup := startXDSV2Client(t, fakeServer.Address)
   312  	defer v2cCleanup()
   313  
   314  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   315  	defer cancel()
   316  
   317  	// Start the watch, send a good response, and check for ack.
   318  	startXDS(ctx, t, xdsresource.ListenerResource, v2c, fakeServer.XDSRequestChan, goodLDSRequest, "", "")
   319  	nonce, err := sendGoodResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS)
   320  	if err != nil {
   321  		t.Fatal(err)
   322  	}
   323  	// Start a new watch. The version in the new request should be the version
   324  	// from the previous response, thus versionLDS before ++.
   325  	startXDS(ctx, t, xdsresource.ListenerResource, v2c, fakeServer.XDSRequestChan, goodLDSRequest, strconv.Itoa(versionLDS), nonce)
   326  	versionLDS++
   327  
   328  	// This is an invalid response after the new watch.
   329  	nonce = sendXDSRespWithVersion(fakeServer.XDSResponseChan, &xdspb.DiscoveryResponse{
   330  		Resources: []*anypb.Any{{}},
   331  		TypeUrl:   version.V2ListenerURL,
   332  	}, versionLDS)
   333  	t.Logf("Bad response pushed to fakeServer...")
   334  
   335  	// The expected version string is the previous acked version.
   336  	if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodLDSRequest, strconv.Itoa(versionLDS-1), nonce, true); err != nil {
   337  		t.Errorf("Failed to receive request: %v", err)
   338  	}
   339  	t.Logf("Bad response nacked")
   340  	versionLDS++
   341  
   342  	if _, err := sendGoodResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS); err != nil {
   343  		t.Fatal(err)
   344  	}
   345  	versionLDS++
   346  }
   347  
   348  // TestV2ClientAckNewWatchAfterCancel verifies the new request for a new watch
   349  // after the previous watch is canceled, has the right version.
   350  func (s) TestV2ClientAckNewWatchAfterCancel(t *testing.T) {
   351  	var versionCDS = 3000
   352  
   353  	fakeServer, cleanup := startServer(t)
   354  	defer cleanup()
   355  
   356  	v2c, _, _, cbCDS, _, v2cCleanup := startXDSV2Client(t, fakeServer.Address)
   357  	defer v2cCleanup()
   358  
   359  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   360  	defer cancel()
   361  
   362  	// Start a CDS watch.
   363  	v2c.AddWatch(xdsresource.ClusterResource, goodClusterName1)
   364  	if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodCDSRequest, "", "", false); err != nil {
   365  		t.Fatal(err)
   366  	}
   367  	t.Logf("FakeServer received %v request...", xdsresource.ClusterResource)
   368  
   369  	// Send a good CDS response, this function waits for the ACK with the right
   370  	// version.
   371  	nonce, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS)
   372  	if err != nil {
   373  		t.Fatal(err)
   374  	}
   375  	// Cancel the CDS watch, and start a new one. The new watch should have the
   376  	// version from the response above.
   377  	v2c.RemoveWatch(xdsresource.ClusterResource, goodClusterName1)
   378  	// Wait for a request with no resource names, because the only watch was
   379  	// removed.
   380  	emptyReq := &xdspb.DiscoveryRequest{Node: goodNodeProto, TypeUrl: version.V2ClusterURL}
   381  	if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, emptyReq, strconv.Itoa(versionCDS), nonce, false); err != nil {
   382  		t.Fatalf("Failed to receive %v request: %v", xdsresource.ClusterResource, err)
   383  	}
   384  	v2c.AddWatch(xdsresource.ClusterResource, goodClusterName1)
   385  	// Wait for a request with correct resource names and version.
   386  	if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodCDSRequest, strconv.Itoa(versionCDS), nonce, false); err != nil {
   387  		t.Fatalf("Failed to receive %v request: %v", xdsresource.ClusterResource, err)
   388  	}
   389  	versionCDS++
   390  
   391  	// Send a bad response with the next version.
   392  	if err := sendBadResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSRequest); err != nil {
   393  		t.Fatal(err)
   394  	}
   395  	versionCDS++
   396  
   397  	// send another good response, and check for ack, with the new version.
   398  	if _, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS); err != nil {
   399  		t.Fatal(err)
   400  	}
   401  	versionCDS++
   402  }
   403  
   404  // TestV2ClientAckCancelResponseRace verifies if the response and ACK request
   405  // race with cancel (which means the ACK request will not be sent on wire,
   406  // because there's no active watch), the nonce will still be updated, and the
   407  // new request with the new watch will have the correct nonce.
   408  func (s) TestV2ClientAckCancelResponseRace(t *testing.T) {
   409  	var versionCDS = 3000
   410  
   411  	fakeServer, cleanup := startServer(t)
   412  	defer cleanup()
   413  
   414  	v2c, _, _, cbCDS, _, v2cCleanup := startXDSV2Client(t, fakeServer.Address)
   415  	defer v2cCleanup()
   416  
   417  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   418  	defer cancel()
   419  
   420  	// Start a CDS watch.
   421  	v2c.AddWatch(xdsresource.ClusterResource, goodClusterName1)
   422  	if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodCDSRequest, "", "", false); err != nil {
   423  		t.Fatalf("Failed to receive %v request: %v", xdsresource.ClusterResource, err)
   424  	}
   425  	t.Logf("FakeServer received %v request...", xdsresource.ClusterResource)
   426  
   427  	// send a good response, and check for ack, with the new version.
   428  	nonce, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS)
   429  	if err != nil {
   430  		t.Fatal(err)
   431  	}
   432  	// Cancel the watch before the next response is sent. This mimics the case
   433  	// watch is canceled while response is on wire.
   434  	v2c.RemoveWatch(xdsresource.ClusterResource, goodClusterName1)
   435  	// Wait for a request with no resource names, because the only watch was
   436  	// removed.
   437  	emptyReq := &xdspb.DiscoveryRequest{Node: goodNodeProto, TypeUrl: version.V2ClusterURL}
   438  	if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, emptyReq, strconv.Itoa(versionCDS), nonce, false); err != nil {
   439  		t.Fatalf("Failed to receive %v request: %v", xdsresource.ClusterResource, err)
   440  	}
   441  	versionCDS++
   442  
   443  	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   444  	defer sCancel()
   445  	if req, err := fakeServer.XDSRequestChan.Receive(sCtx); err != context.DeadlineExceeded {
   446  		t.Fatalf("Got unexpected xds request after watch is canceled: %v", req)
   447  	}
   448  
   449  	// Send a good response.
   450  	nonce = sendXDSRespWithVersion(fakeServer.XDSResponseChan, goodCDSResponse1, versionCDS)
   451  	t.Logf("Good %v response pushed to fakeServer...", xdsresource.ClusterResource)
   452  
   453  	// Expect no ACK because watch was canceled.
   454  	sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
   455  	defer sCancel()
   456  	if req, err := fakeServer.XDSRequestChan.Receive(sCtx); err != context.DeadlineExceeded {
   457  		t.Fatalf("Got unexpected xds request after watch is canceled: %v", req)
   458  	}
   459  
   460  	// Still expected an callback update, because response was good.
   461  	if _, err := cbCDS.Receive(ctx); err != nil {
   462  		t.Fatalf("Timeout when expecting %v update", xdsresource.ClusterResource)
   463  	}
   464  
   465  	// Start a new watch. The new watch should have the nonce from the response
   466  	// above, and version from the first good response.
   467  	v2c.AddWatch(xdsresource.ClusterResource, goodClusterName1)
   468  	if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodCDSRequest, strconv.Itoa(versionCDS-1), nonce, false); err != nil {
   469  		t.Fatalf("Failed to receive %v request: %v", xdsresource.ClusterResource, err)
   470  	}
   471  
   472  	// Send a bad response with the next version.
   473  	if err := sendBadResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSRequest); err != nil {
   474  		t.Fatal(err)
   475  	}
   476  	versionCDS++
   477  
   478  	// send another good response, and check for ack, with the new version.
   479  	if _, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS); err != nil {
   480  		t.Fatal(err)
   481  	}
   482  	versionCDS++
   483  }