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

     1  /*
     2   *
     3   * Copyright 2021 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 xdsclient
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"testing"
    24  
    25  	"gitee.com/ks-custle/core-gm/grpc/internal/testutils"
    26  	xdstestutils "gitee.com/ks-custle/core-gm/grpc/xds/internal/testutils"
    27  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/bootstrap"
    28  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/pubsub"
    29  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource"
    30  	"google.golang.org/protobuf/types/known/anypb"
    31  )
    32  
    33  // testClientSetup sets up the client and controller for the test. It returns a
    34  // newly created client, and a channel where new controllers will be sent to.
    35  func testClientSetup(t *testing.T, overrideWatchExpiryTimeout bool) (*clientImpl, *testutils.Channel) {
    36  	t.Helper()
    37  	ctrlCh := overrideNewController(t)
    38  
    39  	watchExpiryTimeout := defaultWatchExpiryTimeout
    40  	if overrideWatchExpiryTimeout {
    41  		watchExpiryTimeout = defaultTestWatchExpiryTimeout
    42  	}
    43  
    44  	client, err := newWithConfig(clientOpts(), watchExpiryTimeout, defaultIdleAuthorityDeleteTimeout)
    45  	if err != nil {
    46  		t.Fatalf("failed to create client: %v", err)
    47  	}
    48  	t.Cleanup(client.Close)
    49  	return client, ctrlCh
    50  }
    51  
    52  // newWatch starts a new watch on the client.
    53  func newWatch(t *testing.T, client *clientImpl, typ xdsresource.ResourceType, resourceName string) (updateCh *testutils.Channel, cancelWatch func()) {
    54  	newWatchF, _, _ := typeToTestFuncs(typ)
    55  	updateCh, cancelWatch = newWatchF(client, resourceName)
    56  	t.Cleanup(cancelWatch)
    57  
    58  	if u, ok := updateCh.ReceiveOrFail(); ok {
    59  		t.Fatalf("received unexpected update immediately after watch: %+v", u)
    60  	}
    61  	return
    62  }
    63  
    64  // getControllerAndPubsub returns the controller and pubsub for the given
    65  // type+resourceName from the client.
    66  func getControllerAndPubsub(ctx context.Context, t *testing.T, client *clientImpl, ctrlCh *testutils.Channel, typ xdsresource.ResourceType, resourceName string) (*testController, pubsub.UpdateHandler) {
    67  	c, err := ctrlCh.Receive(ctx)
    68  	if err != nil {
    69  		t.Fatalf("timeout when waiting for API client to be created: %v", err)
    70  	}
    71  	ctrl := c.(*testController)
    72  
    73  	if _, err := ctrl.addWatches[typ].Receive(ctx); err != nil {
    74  		t.Fatalf("want new watch to start, got error %v", err)
    75  	}
    76  
    77  	updateHandler := findPubsubForTest(t, client, xdsresource.ParseName(resourceName).Authority)
    78  
    79  	return ctrl, updateHandler
    80  }
    81  
    82  // findPubsubForTest returns the pubsub for the given authority, to send updates
    83  // to. If authority is "", the default is returned. If the authority is not
    84  // found, the test will fail.
    85  func findPubsubForTest(t *testing.T, c *clientImpl, authority string) pubsub.UpdateHandler {
    86  	t.Helper()
    87  	var config *bootstrap.ServerConfig
    88  	if authority == "" {
    89  		config = c.config.XDSServer
    90  	} else {
    91  		authConfig, ok := c.config.Authorities[authority]
    92  		if !ok {
    93  			t.Fatalf("failed to find authority %q", authority)
    94  		}
    95  		config = authConfig.XDSServer
    96  	}
    97  	a := c.authorities[config.String()]
    98  	if a == nil {
    99  		t.Fatalf("authority for %q is not created", authority)
   100  	}
   101  	return a.pubsub
   102  }
   103  
   104  var (
   105  	newLDSWatchF = func(client *clientImpl, resourceName string) (*testutils.Channel, func()) {
   106  		updateCh := testutils.NewChannel()
   107  		cancelLastWatch := client.WatchListener(resourceName, func(update xdsresource.ListenerUpdate, err error) {
   108  			updateCh.Send(xdsresource.ListenerUpdateErrTuple{Update: update, Err: err})
   109  		})
   110  		return updateCh, cancelLastWatch
   111  	}
   112  	newLDSUpdateF = func(updateHandler pubsub.UpdateHandler, updates map[string]interface{}) {
   113  		wantUpdates := map[string]xdsresource.ListenerUpdateErrTuple{}
   114  		for n, u := range updates {
   115  			wantUpdate := u.(xdsresource.ListenerUpdate)
   116  			wantUpdates[n] = xdsresource.ListenerUpdateErrTuple{Update: wantUpdate}
   117  		}
   118  		updateHandler.NewListeners(wantUpdates, xdsresource.UpdateMetadata{})
   119  	}
   120  	verifyLDSUpdateF = func(ctx context.Context, t *testing.T, updateCh *testutils.Channel, update interface{}, err error) {
   121  		t.Helper()
   122  		wantUpdate := update.(xdsresource.ListenerUpdate)
   123  		if err := verifyListenerUpdate(ctx, updateCh, wantUpdate, err); err != nil {
   124  			t.Fatal(err)
   125  		}
   126  	}
   127  
   128  	newRDSWatchF = func(client *clientImpl, resourceName string) (*testutils.Channel, func()) {
   129  		updateCh := testutils.NewChannel()
   130  		cancelLastWatch := client.WatchRouteConfig(resourceName, func(update xdsresource.RouteConfigUpdate, err error) {
   131  			updateCh.Send(xdsresource.RouteConfigUpdateErrTuple{Update: update, Err: err})
   132  		})
   133  		return updateCh, cancelLastWatch
   134  	}
   135  	newRDSUpdateF = func(updateHandler pubsub.UpdateHandler, updates map[string]interface{}) {
   136  		wantUpdates := map[string]xdsresource.RouteConfigUpdateErrTuple{}
   137  		for n, u := range updates {
   138  			wantUpdate := u.(xdsresource.RouteConfigUpdate)
   139  			wantUpdates[n] = xdsresource.RouteConfigUpdateErrTuple{Update: wantUpdate}
   140  		}
   141  		updateHandler.NewRouteConfigs(wantUpdates, xdsresource.UpdateMetadata{})
   142  	}
   143  	verifyRDSUpdateF = func(ctx context.Context, t *testing.T, updateCh *testutils.Channel, update interface{}, err error) {
   144  		t.Helper()
   145  		wantUpdate := update.(xdsresource.RouteConfigUpdate)
   146  		if err := verifyRouteConfigUpdate(ctx, updateCh, wantUpdate, err); err != nil {
   147  			t.Fatal(err)
   148  		}
   149  	}
   150  
   151  	newCDSWatchF = func(client *clientImpl, resourceName string) (*testutils.Channel, func()) {
   152  		updateCh := testutils.NewChannel()
   153  		cancelLastWatch := client.WatchCluster(resourceName, func(update xdsresource.ClusterUpdate, err error) {
   154  			updateCh.Send(xdsresource.ClusterUpdateErrTuple{Update: update, Err: err})
   155  		})
   156  		return updateCh, cancelLastWatch
   157  	}
   158  	newCDSUpdateF = func(updateHandler pubsub.UpdateHandler, updates map[string]interface{}) {
   159  		wantUpdates := map[string]xdsresource.ClusterUpdateErrTuple{}
   160  		for n, u := range updates {
   161  			wantUpdate := u.(xdsresource.ClusterUpdate)
   162  			wantUpdates[n] = xdsresource.ClusterUpdateErrTuple{Update: wantUpdate}
   163  		}
   164  		updateHandler.NewClusters(wantUpdates, xdsresource.UpdateMetadata{})
   165  	}
   166  	verifyCDSUpdateF = func(ctx context.Context, t *testing.T, updateCh *testutils.Channel, update interface{}, err error) {
   167  		t.Helper()
   168  		wantUpdate := update.(xdsresource.ClusterUpdate)
   169  		if err := verifyClusterUpdate(ctx, updateCh, wantUpdate, err); err != nil {
   170  			t.Fatal(err)
   171  		}
   172  	}
   173  
   174  	newEDSWatchF = func(client *clientImpl, resourceName string) (*testutils.Channel, func()) {
   175  		updateCh := testutils.NewChannel()
   176  		cancelLastWatch := client.WatchEndpoints(resourceName, func(update xdsresource.EndpointsUpdate, err error) {
   177  			updateCh.Send(xdsresource.EndpointsUpdateErrTuple{Update: update, Err: err})
   178  		})
   179  		return updateCh, cancelLastWatch
   180  	}
   181  	newEDSUpdateF = func(updateHandler pubsub.UpdateHandler, updates map[string]interface{}) {
   182  		wantUpdates := map[string]xdsresource.EndpointsUpdateErrTuple{}
   183  		for n, u := range updates {
   184  			wantUpdate := u.(xdsresource.EndpointsUpdate)
   185  			wantUpdates[n] = xdsresource.EndpointsUpdateErrTuple{Update: wantUpdate}
   186  		}
   187  		updateHandler.NewEndpoints(wantUpdates, xdsresource.UpdateMetadata{})
   188  	}
   189  	verifyEDSUpdateF = func(ctx context.Context, t *testing.T, updateCh *testutils.Channel, update interface{}, err error) {
   190  		t.Helper()
   191  		wantUpdate := update.(xdsresource.EndpointsUpdate)
   192  		if err := verifyEndpointsUpdate(ctx, updateCh, wantUpdate, err); err != nil {
   193  			t.Fatal(err)
   194  		}
   195  	}
   196  )
   197  
   198  func typeToTestFuncs(typ xdsresource.ResourceType) (
   199  	newWatchF func(client *clientImpl, resourceName string) (*testutils.Channel, func()),
   200  	newUpdateF func(updateHandler pubsub.UpdateHandler, updates map[string]interface{}),
   201  	verifyUpdateF func(ctx context.Context, t *testing.T, updateCh *testutils.Channel, update interface{}, err error),
   202  ) {
   203  	switch typ {
   204  	case xdsresource.ListenerResource:
   205  		newWatchF = newLDSWatchF
   206  		newUpdateF = newLDSUpdateF
   207  		verifyUpdateF = verifyLDSUpdateF
   208  	case xdsresource.RouteConfigResource:
   209  		newWatchF = newRDSWatchF
   210  		newUpdateF = newRDSUpdateF
   211  		verifyUpdateF = verifyRDSUpdateF
   212  	case xdsresource.ClusterResource:
   213  		newWatchF = newCDSWatchF
   214  		newUpdateF = newCDSUpdateF
   215  		verifyUpdateF = verifyCDSUpdateF
   216  	case xdsresource.EndpointsResource:
   217  		newWatchF = newEDSWatchF
   218  		newUpdateF = newEDSUpdateF
   219  		verifyUpdateF = verifyEDSUpdateF
   220  	}
   221  	return
   222  }
   223  
   224  // TestClusterWatch covers the cases:
   225  // - an update is received after a watch()
   226  // - an update for another resource name
   227  // - an update is received after cancel()
   228  func testWatch(t *testing.T, typ xdsresource.ResourceType, update interface{}, resourceName string) {
   229  	overrideFedEnvVar(t)
   230  	for _, rName := range []string{resourceName, xdstestutils.BuildResourceName(typ, testAuthority, resourceName, nil)} {
   231  		t.Run(rName, func(t *testing.T) {
   232  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   233  			defer cancel()
   234  			client, ctrlCh := testClientSetup(t, false)
   235  			updateCh, cancelWatch := newWatch(t, client, typ, rName)
   236  			_, updateHandler := getControllerAndPubsub(ctx, t, client, ctrlCh, typ, rName)
   237  			_, newUpdateF, verifyUpdateF := typeToTestFuncs(typ)
   238  
   239  			// Send an update, and check the result.
   240  			newUpdateF(updateHandler, map[string]interface{}{rName: update})
   241  			verifyUpdateF(ctx, t, updateCh, update, nil)
   242  
   243  			// Push an update, with an extra resource for a different resource name.
   244  			// Specify a non-nil raw proto in the original resource to ensure that the
   245  			// new update is not considered equal to the old one.
   246  			var newUpdate interface{}
   247  			switch typ {
   248  			case xdsresource.ListenerResource:
   249  				newU := update.(xdsresource.ListenerUpdate)
   250  				newU.Raw = &anypb.Any{}
   251  				newUpdate = newU
   252  			case xdsresource.RouteConfigResource:
   253  				newU := update.(xdsresource.RouteConfigUpdate)
   254  				newU.Raw = &anypb.Any{}
   255  				newUpdate = newU
   256  			case xdsresource.ClusterResource:
   257  				newU := update.(xdsresource.ClusterUpdate)
   258  				newU.Raw = &anypb.Any{}
   259  				newUpdate = newU
   260  			case xdsresource.EndpointsResource:
   261  				newU := update.(xdsresource.EndpointsUpdate)
   262  				newU.Raw = &anypb.Any{}
   263  				newUpdate = newU
   264  			}
   265  			newUpdateF(updateHandler, map[string]interface{}{rName: newUpdate})
   266  			verifyUpdateF(ctx, t, updateCh, newUpdate, nil)
   267  
   268  			// Cancel watch, and send update again.
   269  			cancelWatch()
   270  			newUpdateF(updateHandler, map[string]interface{}{rName: update})
   271  			sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   272  			defer sCancel()
   273  			if u, err := updateCh.Receive(sCtx); err != context.DeadlineExceeded {
   274  				t.Errorf("unexpected update: %v, %v, want channel recv timeout", u, err)
   275  			}
   276  		})
   277  	}
   278  }
   279  
   280  // testClusterTwoWatchSameResourceName covers the case where an update is
   281  // received after two watch() for the same resource name.
   282  func testTwoWatchSameResourceName(t *testing.T, typ xdsresource.ResourceType, update interface{}, resourceName string) {
   283  	overrideFedEnvVar(t)
   284  	for _, rName := range []string{resourceName, xdstestutils.BuildResourceName(typ, testAuthority, resourceName, nil)} {
   285  		t.Run(rName, func(t *testing.T) {
   286  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   287  			defer cancel()
   288  			client, ctrlCh := testClientSetup(t, false)
   289  			updateCh, _ := newWatch(t, client, typ, resourceName)
   290  			_, updateHandler := getControllerAndPubsub(ctx, t, client, ctrlCh, typ, resourceName)
   291  			newWatchF, newUpdateF, verifyUpdateF := typeToTestFuncs(typ)
   292  
   293  			updateChs := []*testutils.Channel{updateCh}
   294  			var cancelLastWatch func()
   295  			const count = 1
   296  			for i := 0; i < count; i++ {
   297  				var updateCh *testutils.Channel
   298  				updateCh, cancelLastWatch = newWatchF(client, resourceName)
   299  				updateChs = append(updateChs, updateCh)
   300  			}
   301  
   302  			newUpdateF(updateHandler, map[string]interface{}{resourceName: update})
   303  			for i := 0; i < count+1; i++ {
   304  				verifyUpdateF(ctx, t, updateChs[i], update, nil)
   305  			}
   306  
   307  			// Cancel the last watch, and send update again. None of the watchers should
   308  			// be notified because one has been cancelled, and the other is receiving
   309  			// the same update.
   310  			cancelLastWatch()
   311  			newUpdateF(updateHandler, map[string]interface{}{resourceName: update})
   312  			for i := 0; i < count+1; i++ {
   313  				func() {
   314  					sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   315  					defer sCancel()
   316  					if u, err := updateChs[i].Receive(sCtx); err != context.DeadlineExceeded {
   317  						t.Errorf("unexpected update: %v, %v, want channel recv timeout", u, err)
   318  					}
   319  				}()
   320  			}
   321  
   322  			// Push a new update and make sure the uncancelled watcher is invoked.
   323  			// Specify a non-nil raw proto to ensure that the new update is not
   324  			// considered equal to the old one.
   325  			var newUpdate interface{}
   326  			switch typ {
   327  			case xdsresource.ListenerResource:
   328  				newU := update.(xdsresource.ListenerUpdate)
   329  				newU.Raw = &anypb.Any{}
   330  				newUpdate = newU
   331  			case xdsresource.RouteConfigResource:
   332  				newU := update.(xdsresource.RouteConfigUpdate)
   333  				newU.Raw = &anypb.Any{}
   334  				newUpdate = newU
   335  			case xdsresource.ClusterResource:
   336  				newU := update.(xdsresource.ClusterUpdate)
   337  				newU.Raw = &anypb.Any{}
   338  				newUpdate = newU
   339  			case xdsresource.EndpointsResource:
   340  				newU := update.(xdsresource.EndpointsUpdate)
   341  				newU.Raw = &anypb.Any{}
   342  				newUpdate = newU
   343  			}
   344  			newUpdateF(updateHandler, map[string]interface{}{resourceName: newUpdate})
   345  			verifyUpdateF(ctx, t, updateCh, newUpdate, nil)
   346  		})
   347  	}
   348  }
   349  
   350  // testThreeWatchDifferentResourceName starts two watches for name1, and one
   351  // watch for name2. This test verifies that two watches for name1 receive the
   352  // same update, and name2 watch receives a different update.
   353  func testThreeWatchDifferentResourceName(t *testing.T, typ xdsresource.ResourceType, update1 interface{}, resourceName1 string, update2 interface{}, resourceName2 string) {
   354  	overrideFedEnvVar(t)
   355  	for _, rName := range [][]string{
   356  		{resourceName1, resourceName2},
   357  		{xdstestutils.BuildResourceName(typ, testAuthority, resourceName1, nil), xdstestutils.BuildResourceName(typ, testAuthority, resourceName2, nil)},
   358  	} {
   359  		t.Run(rName[0], func(t *testing.T) {
   360  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   361  			defer cancel()
   362  			client, ctrlCh := testClientSetup(t, false)
   363  			updateCh, _ := newWatch(t, client, typ, rName[0])
   364  			_, updateHandler := getControllerAndPubsub(ctx, t, client, ctrlCh, typ, rName[0])
   365  			newWatchF, newUpdateF, verifyUpdateF := typeToTestFuncs(typ)
   366  
   367  			// Two watches for the same name.
   368  			updateChs := []*testutils.Channel{updateCh}
   369  			const count = 1
   370  			for i := 0; i < count; i++ {
   371  				var updateCh *testutils.Channel
   372  				updateCh, _ = newWatchF(client, rName[0])
   373  				updateChs = append(updateChs, updateCh)
   374  			}
   375  			// Third watch for a different name.
   376  			updateCh2, _ := newWatchF(client, rName[1])
   377  
   378  			newUpdateF(updateHandler, map[string]interface{}{
   379  				rName[0]: update1,
   380  				rName[1]: update2,
   381  			})
   382  
   383  			// The first several watches for the same resource should all
   384  			// receive the first update.
   385  			for i := 0; i < count+1; i++ {
   386  				verifyUpdateF(ctx, t, updateChs[i], update1, nil)
   387  			}
   388  			// The last watch for the different resource should receive the
   389  			// second update.
   390  			verifyUpdateF(ctx, t, updateCh2, update2, nil)
   391  		})
   392  	}
   393  }
   394  
   395  // testWatchAfterCache covers the case where watch is called after the update is
   396  // in cache.
   397  func testWatchAfterCache(t *testing.T, typ xdsresource.ResourceType, update interface{}, resourceName string) {
   398  	overrideFedEnvVar(t)
   399  	for _, rName := range []string{resourceName, xdstestutils.BuildResourceName(typ, testAuthority, resourceName, nil)} {
   400  		t.Run(rName, func(t *testing.T) {
   401  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   402  			defer cancel()
   403  			client, ctrlCh := testClientSetup(t, false)
   404  			updateCh, _ := newWatch(t, client, typ, rName)
   405  			_, updateHandler := getControllerAndPubsub(ctx, t, client, ctrlCh, typ, rName)
   406  			newWatchF, newUpdateF, verifyUpdateF := typeToTestFuncs(typ)
   407  
   408  			newUpdateF(updateHandler, map[string]interface{}{rName: update})
   409  			verifyUpdateF(ctx, t, updateCh, update, nil)
   410  
   411  			// Another watch for the resource in cache.
   412  			updateCh2, _ := newWatchF(client, rName)
   413  
   414  			// New watch should receive the update.
   415  			verifyUpdateF(ctx, t, updateCh2, update, nil)
   416  
   417  			// Old watch should see nothing.
   418  			sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   419  			defer sCancel()
   420  			if u, err := updateCh.Receive(sCtx); err != context.DeadlineExceeded {
   421  				t.Errorf("unexpected update: %v, %v, want channel recv timeout", u, err)
   422  			}
   423  		})
   424  	}
   425  }
   426  
   427  // testResourceRemoved covers the cases:
   428  // - an update is received after a watch()
   429  // - another update is received, with one resource removed
   430  //   - this should trigger callback with resource removed error
   431  //
   432  // - one more update without the removed resource
   433  //   - the callback (above) shouldn't receive any update
   434  func testResourceRemoved(t *testing.T, typ xdsresource.ResourceType, update1 interface{}, resourceName1 string, update2 interface{}, resourceName2 string) {
   435  	overrideFedEnvVar(t)
   436  	for _, rName := range [][]string{
   437  		{resourceName1, resourceName2},
   438  		{xdstestutils.BuildResourceName(typ, testAuthority, resourceName1, nil), xdstestutils.BuildResourceName(typ, testAuthority, resourceName2, nil)},
   439  	} {
   440  		t.Run(rName[0], func(t *testing.T) {
   441  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   442  			defer cancel()
   443  			client, ctrlCh := testClientSetup(t, false)
   444  			updateCh, _ := newWatch(t, client, typ, rName[0])
   445  			_, updateHandler := getControllerAndPubsub(ctx, t, client, ctrlCh, typ, rName[0])
   446  			newWatchF, newUpdateF, verifyUpdateF := typeToTestFuncs(typ)
   447  
   448  			// Another watch for a different name.
   449  			updateCh2, _ := newWatchF(client, rName[1])
   450  
   451  			newUpdateF(updateHandler, map[string]interface{}{
   452  				rName[0]: update1,
   453  				rName[1]: update2,
   454  			})
   455  			verifyUpdateF(ctx, t, updateCh, update1, nil)
   456  			verifyUpdateF(ctx, t, updateCh2, update2, nil)
   457  
   458  			// Send another update to remove resource 1.
   459  			newUpdateF(updateHandler, map[string]interface{}{
   460  				rName[1]: update2,
   461  			})
   462  
   463  			// Watcher 1 should get an error.
   464  			if u, err := updateCh.Receive(ctx); err != nil {
   465  				t.Errorf("failed to receive update: %v", err)
   466  			} else {
   467  				var gotErr error
   468  				switch typ {
   469  				case xdsresource.ListenerResource:
   470  					newU := u.(xdsresource.ListenerUpdateErrTuple)
   471  					gotErr = newU.Err
   472  				case xdsresource.RouteConfigResource:
   473  					newU := u.(xdsresource.RouteConfigUpdateErrTuple)
   474  					gotErr = newU.Err
   475  				case xdsresource.ClusterResource:
   476  					newU := u.(xdsresource.ClusterUpdateErrTuple)
   477  					gotErr = newU.Err
   478  				case xdsresource.EndpointsResource:
   479  					newU := u.(xdsresource.EndpointsUpdateErrTuple)
   480  					gotErr = newU.Err
   481  				}
   482  				if xdsresource.ErrType(gotErr) != xdsresource.ErrorTypeResourceNotFound {
   483  					t.Errorf("unexpected clusterUpdate: %v, error receiving from channel: %v, want update with error resource not found", u, err)
   484  				}
   485  			}
   486  
   487  			// Watcher 2 should not see an update since the resource has not changed.
   488  			sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   489  			defer sCancel()
   490  			if u, err := updateCh2.Receive(sCtx); err != context.DeadlineExceeded {
   491  				t.Errorf("unexpected ClusterUpdate: %v, want receiving from channel timeout", u)
   492  			}
   493  
   494  			// Send another update with resource 2 modified. Specify a non-nil raw proto
   495  			// to ensure that the new update is not considered equal to the old one.
   496  			var newUpdate interface{}
   497  			switch typ {
   498  			case xdsresource.ListenerResource:
   499  				newU := update2.(xdsresource.ListenerUpdate)
   500  				newU.Raw = &anypb.Any{}
   501  				newUpdate = newU
   502  			case xdsresource.RouteConfigResource:
   503  				newU := update2.(xdsresource.RouteConfigUpdate)
   504  				newU.Raw = &anypb.Any{}
   505  				newUpdate = newU
   506  			case xdsresource.ClusterResource:
   507  				newU := update2.(xdsresource.ClusterUpdate)
   508  				newU.Raw = &anypb.Any{}
   509  				newUpdate = newU
   510  			case xdsresource.EndpointsResource:
   511  				newU := update2.(xdsresource.EndpointsUpdate)
   512  				newU.Raw = &anypb.Any{}
   513  				newUpdate = newU
   514  			}
   515  			newUpdateF(updateHandler, map[string]interface{}{
   516  				rName[1]: newUpdate,
   517  			})
   518  
   519  			// Watcher 1 should not see an update.
   520  			sCtx, sCancel = context.WithTimeout(ctx, defaultTestShortTimeout)
   521  			defer sCancel()
   522  			if u, err := updateCh.Receive(sCtx); err != context.DeadlineExceeded {
   523  				t.Errorf("unexpected Cluster: %v, want receiving from channel timeout", u)
   524  			}
   525  
   526  			// Watcher 2 should get the update.
   527  			verifyUpdateF(ctx, t, updateCh2, newUpdate, nil)
   528  		})
   529  	}
   530  }
   531  
   532  // testWatchPartialValid covers the case that a response contains both
   533  // valid and invalid resources. This response will be NACK'ed by the xdsclient.
   534  // But the watchers with valid resources should receive the update, those with
   535  // invalid resources should receive an error.
   536  func testWatchPartialValid(t *testing.T, typ xdsresource.ResourceType, update interface{}, resourceName string) {
   537  	overrideFedEnvVar(t)
   538  	const badResourceName = "bad-resource"
   539  
   540  	for _, rName := range [][]string{
   541  		{resourceName, badResourceName},
   542  		{xdstestutils.BuildResourceName(typ, testAuthority, resourceName, nil), xdstestutils.BuildResourceName(typ, testAuthority, badResourceName, nil)},
   543  	} {
   544  		t.Run(rName[0], func(t *testing.T) {
   545  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   546  			defer cancel()
   547  			client, ctrlCh := testClientSetup(t, false)
   548  			updateCh, _ := newWatch(t, client, typ, rName[0])
   549  			_, updateHandler := getControllerAndPubsub(ctx, t, client, ctrlCh, typ, rName[0])
   550  			newWatchF, _, verifyUpdateF := typeToTestFuncs(typ)
   551  
   552  			updateChs := map[string]*testutils.Channel{
   553  				rName[0]: updateCh,
   554  			}
   555  
   556  			for _, name := range []string{rName[1]} {
   557  				updateChT, _ := newWatchF(client, rName[1])
   558  				updateChs[name] = updateChT
   559  			}
   560  
   561  			wantError := fmt.Errorf("testing error")
   562  			wantError2 := fmt.Errorf("individual error")
   563  
   564  			switch typ {
   565  			case xdsresource.ListenerResource:
   566  				updateHandler.NewListeners(map[string]xdsresource.ListenerUpdateErrTuple{
   567  					rName[0]: {Update: update.(xdsresource.ListenerUpdate)},
   568  					rName[1]: {Err: wantError2},
   569  				}, xdsresource.UpdateMetadata{ErrState: &xdsresource.UpdateErrorMetadata{Err: wantError}})
   570  			case xdsresource.RouteConfigResource:
   571  				updateHandler.NewRouteConfigs(map[string]xdsresource.RouteConfigUpdateErrTuple{
   572  					rName[0]: {Update: update.(xdsresource.RouteConfigUpdate)},
   573  					rName[1]: {Err: wantError2},
   574  				}, xdsresource.UpdateMetadata{ErrState: &xdsresource.UpdateErrorMetadata{Err: wantError}})
   575  			case xdsresource.ClusterResource:
   576  				updateHandler.NewClusters(map[string]xdsresource.ClusterUpdateErrTuple{
   577  					rName[0]: {Update: update.(xdsresource.ClusterUpdate)},
   578  					rName[1]: {Err: wantError2},
   579  				}, xdsresource.UpdateMetadata{ErrState: &xdsresource.UpdateErrorMetadata{Err: wantError}})
   580  			case xdsresource.EndpointsResource:
   581  				updateHandler.NewEndpoints(map[string]xdsresource.EndpointsUpdateErrTuple{
   582  					rName[0]: {Update: update.(xdsresource.EndpointsUpdate)},
   583  					rName[1]: {Err: wantError2},
   584  				}, xdsresource.UpdateMetadata{ErrState: &xdsresource.UpdateErrorMetadata{Err: wantError}})
   585  			}
   586  
   587  			// The valid resource should be sent to the watcher.
   588  			verifyUpdateF(ctx, t, updateChs[rName[0]], update, nil)
   589  
   590  			// The failed watcher should receive an error.
   591  			verifyUpdateF(ctx, t, updateChs[rName[1]], update, wantError2)
   592  		})
   593  	}
   594  }