github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/resolver/xds_resolver_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  
    19  package resolver
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"net/url"
    25  	"reflect"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	xxhash "github.com/cespare/xxhash/v2"
    31  	"github.com/google/go-cmp/cmp"
    32  	"github.com/hxx258456/ccgo/grpc/codes"
    33  	"github.com/hxx258456/ccgo/grpc/credentials/insecure"
    34  	xdscreds "github.com/hxx258456/ccgo/grpc/credentials/xds"
    35  	"github.com/hxx258456/ccgo/grpc/internal"
    36  	"github.com/hxx258456/ccgo/grpc/internal/envconfig"
    37  	"github.com/hxx258456/ccgo/grpc/internal/grpcrand"
    38  	"github.com/hxx258456/ccgo/grpc/internal/grpctest"
    39  	iresolver "github.com/hxx258456/ccgo/grpc/internal/resolver"
    40  	"github.com/hxx258456/ccgo/grpc/internal/testutils"
    41  	"github.com/hxx258456/ccgo/grpc/internal/wrr"
    42  	"github.com/hxx258456/ccgo/grpc/metadata"
    43  	"github.com/hxx258456/ccgo/grpc/resolver"
    44  	"github.com/hxx258456/ccgo/grpc/serviceconfig"
    45  	"github.com/hxx258456/ccgo/grpc/status"
    46  	_ "github.com/hxx258456/ccgo/grpc/xds/internal/balancer/cdsbalancer" // To parse LB config
    47  	"github.com/hxx258456/ccgo/grpc/xds/internal/balancer/clustermanager"
    48  	"github.com/hxx258456/ccgo/grpc/xds/internal/balancer/ringhash"
    49  	"github.com/hxx258456/ccgo/grpc/xds/internal/httpfilter"
    50  	"github.com/hxx258456/ccgo/grpc/xds/internal/httpfilter/router"
    51  	"github.com/hxx258456/ccgo/grpc/xds/internal/testutils/fakeclient"
    52  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient"
    53  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/bootstrap"
    54  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource"
    55  )
    56  
    57  const (
    58  	targetStr               = "target"
    59  	routeStr                = "route"
    60  	cluster                 = "cluster"
    61  	defaultTestTimeout      = 1 * time.Second
    62  	defaultTestShortTimeout = 100 * time.Microsecond
    63  )
    64  
    65  var target = resolver.Target{Endpoint: targetStr, URL: url.URL{Scheme: "xds", Path: "/" + targetStr}}
    66  
    67  var routerFilter = xdsresource.HTTPFilter{Name: "rtr", Filter: httpfilter.Get(router.TypeURL)}
    68  var routerFilterList = []xdsresource.HTTPFilter{routerFilter}
    69  
    70  type s struct {
    71  	grpctest.Tester
    72  }
    73  
    74  func Test(t *testing.T) {
    75  	grpctest.RunSubTests(t, s{})
    76  }
    77  
    78  func (s) TestRegister(t *testing.T) {
    79  	b := resolver.Get(xdsScheme)
    80  	if b == nil {
    81  		t.Errorf("scheme %v is not registered", xdsScheme)
    82  	}
    83  }
    84  
    85  // testClientConn is a fake implemetation of resolver.ClientConn. All is does
    86  // is to store the state received from the resolver locally and signal that
    87  // event through a channel.
    88  type testClientConn struct {
    89  	resolver.ClientConn
    90  	stateCh *testutils.Channel
    91  	errorCh *testutils.Channel
    92  }
    93  
    94  func (t *testClientConn) UpdateState(s resolver.State) error {
    95  	t.stateCh.Send(s)
    96  	return nil
    97  }
    98  
    99  func (t *testClientConn) ReportError(err error) {
   100  	t.errorCh.Send(err)
   101  }
   102  
   103  func (t *testClientConn) ParseServiceConfig(jsonSC string) *serviceconfig.ParseResult {
   104  	return internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(jsonSC)
   105  }
   106  
   107  func newTestClientConn() *testClientConn {
   108  	return &testClientConn{
   109  		stateCh: testutils.NewChannel(),
   110  		errorCh: testutils.NewChannel(),
   111  	}
   112  }
   113  
   114  // TestResolverBuilder tests the xdsResolverBuilder's Build method with
   115  // different parameters.
   116  func (s) TestResolverBuilder(t *testing.T) {
   117  	tests := []struct {
   118  		name          string
   119  		xdsClientFunc func() (xdsclient.XDSClient, error)
   120  		target        resolver.Target
   121  		wantErr       bool
   122  	}{
   123  		{
   124  			name: "simple-good",
   125  			xdsClientFunc: func() (xdsclient.XDSClient, error) {
   126  				return fakeclient.NewClient(), nil
   127  			},
   128  			target:  target,
   129  			wantErr: false,
   130  		},
   131  		{
   132  			name: "newXDSClient-throws-error",
   133  			xdsClientFunc: func() (xdsclient.XDSClient, error) {
   134  				return nil, errors.New("newXDSClient-throws-error")
   135  			},
   136  			target:  target,
   137  			wantErr: true,
   138  		},
   139  		{
   140  			name: "authority not defined in bootstrap",
   141  			xdsClientFunc: func() (xdsclient.XDSClient, error) {
   142  				c := fakeclient.NewClient()
   143  				c.SetBootstrapConfig(&bootstrap.Config{
   144  					ClientDefaultListenerResourceNameTemplate: "%s",
   145  					Authorities: map[string]*bootstrap.Authority{
   146  						"test-authority": {
   147  							ClientListenerResourceNameTemplate: "xdstp://test-authority/%s",
   148  						},
   149  					},
   150  				})
   151  				return c, nil
   152  			},
   153  			target: resolver.Target{
   154  				URL: url.URL{
   155  					Host: "non-existing-authority",
   156  					Path: "/" + targetStr,
   157  				},
   158  			},
   159  			wantErr: true,
   160  		},
   161  	}
   162  	for _, test := range tests {
   163  		t.Run(test.name, func(t *testing.T) {
   164  			// Fake out the xdsClient creation process by providing a fake.
   165  			oldClientMaker := newXDSClient
   166  			newXDSClient = test.xdsClientFunc
   167  			defer func() {
   168  				newXDSClient = oldClientMaker
   169  			}()
   170  
   171  			builder := resolver.Get(xdsScheme)
   172  			if builder == nil {
   173  				t.Fatalf("resolver.Get(%v) returned nil", xdsScheme)
   174  			}
   175  
   176  			r, err := builder.Build(test.target, newTestClientConn(), resolver.BuildOptions{})
   177  			if (err != nil) != test.wantErr {
   178  				t.Fatalf("builder.Build(%v) returned err: %v, wantErr: %v", target, err, test.wantErr)
   179  			}
   180  			if err != nil {
   181  				// This is the case where we expect an error and got it.
   182  				return
   183  			}
   184  			r.Close()
   185  		})
   186  	}
   187  }
   188  
   189  // TestResolverBuilder_xdsCredsBootstrapMismatch tests the case where an xds
   190  // resolver is built with xds credentials being specified by the user. The
   191  // bootstrap file does not contain any certificate provider configuration
   192  // though, and therefore we expect the resolver build to fail.
   193  func (s) TestResolverBuilder_xdsCredsBootstrapMismatch(t *testing.T) {
   194  	// Fake out the xdsClient creation process by providing a fake, which does
   195  	// not have any certificate provider configuration.
   196  	fc := fakeclient.NewClient()
   197  	fc.SetBootstrapConfig(&bootstrap.Config{})
   198  	oldClientMaker := newXDSClient
   199  	newXDSClient = func() (xdsclient.XDSClient, error) {
   200  		return fc, nil
   201  	}
   202  	defer func() { newXDSClient = oldClientMaker }()
   203  	defer func() {
   204  		select {
   205  		case <-time.After(defaultTestTimeout):
   206  			t.Fatalf("timeout waiting for close")
   207  		case <-fc.Closed.Done():
   208  		}
   209  	}()
   210  
   211  	builder := resolver.Get(xdsScheme)
   212  	if builder == nil {
   213  		t.Fatalf("resolver.Get(%v) returned nil", xdsScheme)
   214  	}
   215  
   216  	// Create xds credentials to be passed to resolver.Build().
   217  	creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
   218  	if err != nil {
   219  		t.Fatalf("xds.NewClientCredentials() failed: %v", err)
   220  	}
   221  
   222  	// Since the fake xds client is not configured with any certificate provider
   223  	// configs, and we are specifying xds credentials in the call to
   224  	// resolver.Build(), we expect it to fail.
   225  	if _, err := builder.Build(target, newTestClientConn(), resolver.BuildOptions{DialCreds: creds}); err == nil {
   226  		t.Fatal("builder.Build() succeeded when expected to fail")
   227  	}
   228  }
   229  
   230  type setupOpts struct {
   231  	bootstrapC *bootstrap.Config
   232  	target     resolver.Target
   233  }
   234  
   235  func testSetup(t *testing.T, opts setupOpts) (*xdsResolver, *fakeclient.Client, *testClientConn, func()) {
   236  	t.Helper()
   237  
   238  	fc := fakeclient.NewClient()
   239  	if opts.bootstrapC != nil {
   240  		fc.SetBootstrapConfig(opts.bootstrapC)
   241  	}
   242  	oldClientMaker := newXDSClient
   243  	newXDSClient = func() (xdsclient.XDSClient, error) {
   244  		return fc, nil
   245  	}
   246  	cancel := func() {
   247  		// Make sure the xDS client is closed, in all (successful or failed)
   248  		// cases.
   249  		select {
   250  		case <-time.After(defaultTestTimeout):
   251  			t.Fatalf("timeout waiting for close")
   252  		case <-fc.Closed.Done():
   253  		}
   254  		newXDSClient = oldClientMaker
   255  	}
   256  	builder := resolver.Get(xdsScheme)
   257  	if builder == nil {
   258  		t.Fatalf("resolver.Get(%v) returned nil", xdsScheme)
   259  	}
   260  
   261  	tcc := newTestClientConn()
   262  	r, err := builder.Build(opts.target, tcc, resolver.BuildOptions{})
   263  	if err != nil {
   264  		t.Fatalf("builder.Build(%v) returned err: %v", target, err)
   265  	}
   266  	return r.(*xdsResolver), fc, tcc, func() {
   267  		r.Close()
   268  		cancel()
   269  	}
   270  }
   271  
   272  // waitForWatchListener waits for the WatchListener method to be called on the
   273  // xdsClient within a reasonable amount of time, and also verifies that the
   274  // watch is called with the expected target.
   275  func waitForWatchListener(ctx context.Context, t *testing.T, xdsC *fakeclient.Client, wantTarget string) {
   276  	t.Helper()
   277  
   278  	gotTarget, err := xdsC.WaitForWatchListener(ctx)
   279  	if err != nil {
   280  		t.Fatalf("xdsClient.WatchService failed with error: %v", err)
   281  	}
   282  	if gotTarget != wantTarget {
   283  		t.Fatalf("xdsClient.WatchService() called with target: %v, want %v", gotTarget, wantTarget)
   284  	}
   285  }
   286  
   287  // waitForWatchRouteConfig waits for the WatchRoute method to be called on the
   288  // xdsClient within a reasonable amount of time, and also verifies that the
   289  // watch is called with the expected target.
   290  func waitForWatchRouteConfig(ctx context.Context, t *testing.T, xdsC *fakeclient.Client, wantTarget string) {
   291  	t.Helper()
   292  
   293  	gotTarget, err := xdsC.WaitForWatchRouteConfig(ctx)
   294  	if err != nil {
   295  		t.Fatalf("xdsClient.WatchService failed with error: %v", err)
   296  	}
   297  	if gotTarget != wantTarget {
   298  		t.Fatalf("xdsClient.WatchService() called with target: %v, want %v", gotTarget, wantTarget)
   299  	}
   300  }
   301  
   302  // TestXDSResolverResourceNameToWatch tests that the correct resource name is
   303  // used to watch for the service. This covers cases with different bootstrap
   304  // config, and different authority.
   305  func (s) TestXDSResolverResourceNameToWatch(t *testing.T) {
   306  	tests := []struct {
   307  		name   string
   308  		bc     *bootstrap.Config
   309  		target resolver.Target
   310  		want   string
   311  	}{
   312  		{
   313  			name: "default %s old style",
   314  			bc: &bootstrap.Config{
   315  				ClientDefaultListenerResourceNameTemplate: "%s",
   316  			},
   317  			target: resolver.Target{
   318  				URL: url.URL{Path: "/" + targetStr},
   319  			},
   320  			want: targetStr,
   321  		},
   322  		{
   323  			name: "old style no percent encoding",
   324  			bc: &bootstrap.Config{
   325  				ClientDefaultListenerResourceNameTemplate: "/path/to/%s",
   326  			},
   327  			target: resolver.Target{
   328  				URL: url.URL{Path: "/" + targetStr},
   329  			},
   330  			want: "/path/to/" + targetStr,
   331  		},
   332  		{
   333  			name: "new style with %s",
   334  			bc: &bootstrap.Config{
   335  				ClientDefaultListenerResourceNameTemplate: "xdstp://authority.com/%s",
   336  				Authorities: nil,
   337  			},
   338  			target: resolver.Target{
   339  				URL: url.URL{Path: "/0.0.0.0:8080"},
   340  			},
   341  			want: "xdstp://authority.com/0.0.0.0:8080",
   342  		},
   343  		{
   344  			name: "new style percent encoding",
   345  			bc: &bootstrap.Config{
   346  				ClientDefaultListenerResourceNameTemplate: "xdstp://authority.com/%s",
   347  				Authorities: nil,
   348  			},
   349  			target: resolver.Target{
   350  				URL: url.URL{Path: "/[::1]:8080"},
   351  			},
   352  			want: "xdstp://authority.com/%5B::1%5D:8080",
   353  		},
   354  		{
   355  			name: "new style different authority",
   356  			bc: &bootstrap.Config{
   357  				ClientDefaultListenerResourceNameTemplate: "xdstp://authority.com/%s",
   358  				Authorities: map[string]*bootstrap.Authority{
   359  					"test-authority": {
   360  						ClientListenerResourceNameTemplate: "xdstp://test-authority/%s",
   361  					},
   362  				},
   363  			},
   364  			target: resolver.Target{
   365  				URL: url.URL{
   366  					Host: "test-authority",
   367  					Path: "/" + targetStr,
   368  				},
   369  			},
   370  			want: "xdstp://test-authority/" + targetStr,
   371  		},
   372  	}
   373  	for _, tt := range tests {
   374  		t.Run(tt.name, func(t *testing.T) {
   375  			xdsR, xdsC, _, cancel := testSetup(t, setupOpts{
   376  				bootstrapC: tt.bc,
   377  				target:     tt.target,
   378  			})
   379  			defer cancel()
   380  			defer xdsR.Close()
   381  
   382  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   383  			defer cancel()
   384  			waitForWatchListener(ctx, t, xdsC, tt.want)
   385  		})
   386  	}
   387  }
   388  
   389  // TestXDSResolverWatchCallbackAfterClose tests the case where a service update
   390  // from the underlying xdsClient is received after the resolver is closed.
   391  func (s) TestXDSResolverWatchCallbackAfterClose(t *testing.T) {
   392  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   393  	defer cancel()
   394  
   395  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   396  	defer cancel()
   397  	waitForWatchListener(ctx, t, xdsC, targetStr)
   398  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   399  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   400  
   401  	// Call the watchAPI callback after closing the resolver, and make sure no
   402  	// update is triggerred on the ClientConn.
   403  	xdsR.Close()
   404  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   405  		VirtualHosts: []*xdsresource.VirtualHost{
   406  			{
   407  				Domains: []string{targetStr},
   408  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
   409  			},
   410  		},
   411  	}, nil)
   412  
   413  	if gotVal, gotErr := tcc.stateCh.Receive(ctx); gotErr != context.DeadlineExceeded {
   414  		t.Fatalf("ClientConn.UpdateState called after xdsResolver is closed: %v", gotVal)
   415  	}
   416  }
   417  
   418  // TestXDSResolverCloseClosesXDSClient tests that the XDS resolver's Close
   419  // method closes the XDS client.
   420  func (s) TestXDSResolverCloseClosesXDSClient(t *testing.T) {
   421  	xdsR, xdsC, _, cancel := testSetup(t, setupOpts{target: target})
   422  	defer cancel()
   423  	xdsR.Close()
   424  	if !xdsC.Closed.HasFired() {
   425  		t.Fatalf("xds client not closed by xds resolver Close method")
   426  	}
   427  }
   428  
   429  // TestXDSResolverBadServiceUpdate tests the case the xdsClient returns a bad
   430  // service update.
   431  func (s) TestXDSResolverBadServiceUpdate(t *testing.T) {
   432  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   433  	defer xdsR.Close()
   434  	defer cancel()
   435  
   436  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   437  	defer cancel()
   438  	waitForWatchListener(ctx, t, xdsC, targetStr)
   439  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   440  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   441  
   442  	// Invoke the watchAPI callback with a bad service update and wait for the
   443  	// ReportError method to be called on the ClientConn.
   444  	suErr := errors.New("bad serviceupdate")
   445  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr)
   446  
   447  	if gotErrVal, gotErr := tcc.errorCh.Receive(ctx); gotErr != nil || gotErrVal != suErr {
   448  		t.Fatalf("ClientConn.ReportError() received %v, want %v", gotErrVal, suErr)
   449  	}
   450  }
   451  
   452  // TestXDSResolverGoodServiceUpdate tests the happy case where the resolver
   453  // gets a good service update from the xdsClient.
   454  func (s) TestXDSResolverGoodServiceUpdate(t *testing.T) {
   455  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   456  	defer xdsR.Close()
   457  	defer cancel()
   458  
   459  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   460  	defer cancel()
   461  	waitForWatchListener(ctx, t, xdsC, targetStr)
   462  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   463  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   464  	defer replaceRandNumGenerator(0)()
   465  
   466  	for _, tt := range []struct {
   467  		routes       []*xdsresource.Route
   468  		wantJSON     string
   469  		wantClusters map[string]bool
   470  	}{
   471  		{
   472  			routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"test-cluster-1": {Weight: 1}}}},
   473  			wantJSON: `{"loadBalancingConfig":[{
   474      "xds_cluster_manager_experimental":{
   475        "children":{
   476          "cluster:test-cluster-1":{
   477            "childPolicy":[{"cds_experimental":{"cluster":"test-cluster-1"}}]
   478          }
   479        }
   480      }}]}`,
   481  			wantClusters: map[string]bool{"cluster:test-cluster-1": true},
   482  		},
   483  		{
   484  			routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{
   485  				"cluster_1": {Weight: 75},
   486  				"cluster_2": {Weight: 25},
   487  			}}},
   488  			// This update contains the cluster from the previous update as
   489  			// well as this update, as the previous config selector still
   490  			// references the old cluster when the new one is pushed.
   491  			wantJSON: `{"loadBalancingConfig":[{
   492      "xds_cluster_manager_experimental":{
   493        "children":{
   494          "cluster:test-cluster-1":{
   495            "childPolicy":[{"cds_experimental":{"cluster":"test-cluster-1"}}]
   496          },
   497          "cluster:cluster_1":{
   498            "childPolicy":[{"cds_experimental":{"cluster":"cluster_1"}}]
   499          },
   500          "cluster:cluster_2":{
   501            "childPolicy":[{"cds_experimental":{"cluster":"cluster_2"}}]
   502          }
   503        }
   504      }}]}`,
   505  			wantClusters: map[string]bool{"cluster:cluster_1": true, "cluster:cluster_2": true},
   506  		},
   507  		{
   508  			routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{
   509  				"cluster_1": {Weight: 75},
   510  				"cluster_2": {Weight: 25},
   511  			}}},
   512  			// With this redundant update, the old config selector has been
   513  			// stopped, so there are no more references to the first cluster.
   514  			// Only the second update's clusters should remain.
   515  			wantJSON: `{"loadBalancingConfig":[{
   516      "xds_cluster_manager_experimental":{
   517        "children":{
   518          "cluster:cluster_1":{
   519            "childPolicy":[{"cds_experimental":{"cluster":"cluster_1"}}]
   520          },
   521          "cluster:cluster_2":{
   522            "childPolicy":[{"cds_experimental":{"cluster":"cluster_2"}}]
   523          }
   524        }
   525      }}]}`,
   526  			wantClusters: map[string]bool{"cluster:cluster_1": true, "cluster:cluster_2": true},
   527  		},
   528  	} {
   529  		// Invoke the watchAPI callback with a good service update and wait for the
   530  		// UpdateState method to be called on the ClientConn.
   531  		xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   532  			VirtualHosts: []*xdsresource.VirtualHost{
   533  				{
   534  					Domains: []string{targetStr},
   535  					Routes:  tt.routes,
   536  				},
   537  			},
   538  		}, nil)
   539  
   540  		ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   541  		defer cancel()
   542  		gotState, err := tcc.stateCh.Receive(ctx)
   543  		if err != nil {
   544  			t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   545  		}
   546  		rState := gotState.(resolver.State)
   547  		if err := rState.ServiceConfig.Err; err != nil {
   548  			t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   549  		}
   550  
   551  		wantSCParsed := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(tt.wantJSON)
   552  		if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed.Config) {
   553  			t.Errorf("ClientConn.UpdateState received different service config")
   554  			t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
   555  			t.Error("want: ", cmp.Diff(nil, wantSCParsed.Config))
   556  		}
   557  
   558  		cs := iresolver.GetConfigSelector(rState)
   559  		if cs == nil {
   560  			t.Error("received nil config selector")
   561  			continue
   562  		}
   563  
   564  		pickedClusters := make(map[string]bool)
   565  		// Odds of picking 75% cluster 100 times in a row: 1 in 3E-13.  And
   566  		// with the random number generator stubbed out, we can rely on this
   567  		// to be 100% reproducible.
   568  		for i := 0; i < 100; i++ {
   569  			res, err := cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
   570  			if err != nil {
   571  				t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
   572  			}
   573  			cluster := clustermanager.GetPickedClusterForTesting(res.Context)
   574  			pickedClusters[cluster] = true
   575  			res.OnCommitted()
   576  		}
   577  		if !reflect.DeepEqual(pickedClusters, tt.wantClusters) {
   578  			t.Errorf("Picked clusters: %v; want: %v", pickedClusters, tt.wantClusters)
   579  		}
   580  	}
   581  }
   582  
   583  // TestXDSResolverRequestHash tests a case where a resolver receives a RouteConfig update
   584  // with a HashPolicy specifying to generate a hash. The configSelector generated should
   585  // successfully generate a Hash.
   586  func (s) TestXDSResolverRequestHash(t *testing.T) {
   587  	oldRH := envconfig.XDSRingHash
   588  	envconfig.XDSRingHash = true
   589  	defer func() { envconfig.XDSRingHash = oldRH }()
   590  
   591  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   592  	defer xdsR.Close()
   593  	defer cancel()
   594  
   595  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   596  	defer cancel()
   597  	waitForWatchListener(ctx, t, xdsC, targetStr)
   598  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   599  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   600  	// Invoke watchAPI callback with a good service update (with hash policies
   601  	// specified) and wait for UpdateState method to be called on ClientConn.
   602  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   603  		VirtualHosts: []*xdsresource.VirtualHost{
   604  			{
   605  				Domains: []string{targetStr},
   606  				Routes: []*xdsresource.Route{{
   607  					Prefix: newStringP(""),
   608  					WeightedClusters: map[string]xdsresource.WeightedCluster{
   609  						"cluster_1": {Weight: 75},
   610  						"cluster_2": {Weight: 25},
   611  					},
   612  					HashPolicies: []*xdsresource.HashPolicy{{
   613  						HashPolicyType: xdsresource.HashPolicyTypeHeader,
   614  						HeaderName:     ":path",
   615  					}},
   616  				}},
   617  			},
   618  		},
   619  	}, nil)
   620  
   621  	ctx, cancel = context.WithTimeout(context.Background(), defaultTestTimeout)
   622  	defer cancel()
   623  	gotState, err := tcc.stateCh.Receive(ctx)
   624  	if err != nil {
   625  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   626  	}
   627  	rState := gotState.(resolver.State)
   628  	cs := iresolver.GetConfigSelector(rState)
   629  	if cs == nil {
   630  		t.Error("received nil config selector")
   631  	}
   632  	// Selecting a config when there was a hash policy specified in the route
   633  	// that will be selected should put a request hash in the config's context.
   634  	res, err := cs.SelectConfig(iresolver.RPCInfo{Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs(":path", "/products"))})
   635  	if err != nil {
   636  		t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
   637  	}
   638  	requestHashGot := ringhash.GetRequestHashForTesting(res.Context)
   639  	requestHashWant := xxhash.Sum64String("/products")
   640  	if requestHashGot != requestHashWant {
   641  		t.Fatalf("requestHashGot = %v, requestHashWant = %v", requestHashGot, requestHashWant)
   642  	}
   643  }
   644  
   645  // TestXDSResolverRemovedWithRPCs tests the case where a config selector sends
   646  // an empty update to the resolver after the resource is removed.
   647  func (s) TestXDSResolverRemovedWithRPCs(t *testing.T) {
   648  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   649  	defer cancel()
   650  	defer xdsR.Close()
   651  
   652  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   653  	defer cancel()
   654  	waitForWatchListener(ctx, t, xdsC, targetStr)
   655  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   656  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   657  
   658  	// Invoke the watchAPI callback with a good service update and wait for the
   659  	// UpdateState method to be called on the ClientConn.
   660  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   661  		VirtualHosts: []*xdsresource.VirtualHost{
   662  			{
   663  				Domains: []string{targetStr},
   664  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"test-cluster-1": {Weight: 1}}}},
   665  			},
   666  		},
   667  	}, nil)
   668  
   669  	gotState, err := tcc.stateCh.Receive(ctx)
   670  	if err != nil {
   671  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   672  	}
   673  	rState := gotState.(resolver.State)
   674  	if err := rState.ServiceConfig.Err; err != nil {
   675  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   676  	}
   677  
   678  	// "Make an RPC" by invoking the config selector.
   679  	cs := iresolver.GetConfigSelector(rState)
   680  	if cs == nil {
   681  		t.Fatalf("received nil config selector")
   682  	}
   683  
   684  	res, err := cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
   685  	if err != nil {
   686  		t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
   687  	}
   688  
   689  	// Delete the resource
   690  	suErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "resource removed error")
   691  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr)
   692  
   693  	if _, err = tcc.stateCh.Receive(ctx); err != nil {
   694  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   695  	}
   696  
   697  	// "Finish the RPC"; this could cause a panic if the resolver doesn't
   698  	// handle it correctly.
   699  	res.OnCommitted()
   700  }
   701  
   702  // TestXDSResolverRemovedResource tests for proper behavior after a resource is
   703  // removed.
   704  func (s) TestXDSResolverRemovedResource(t *testing.T) {
   705  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   706  	defer cancel()
   707  	defer xdsR.Close()
   708  
   709  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   710  	defer cancel()
   711  	waitForWatchListener(ctx, t, xdsC, targetStr)
   712  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   713  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   714  
   715  	// Invoke the watchAPI callback with a good service update and wait for the
   716  	// UpdateState method to be called on the ClientConn.
   717  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   718  		VirtualHosts: []*xdsresource.VirtualHost{
   719  			{
   720  				Domains: []string{targetStr},
   721  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"test-cluster-1": {Weight: 1}}}},
   722  			},
   723  		},
   724  	}, nil)
   725  	wantJSON := `{"loadBalancingConfig":[{
   726      "xds_cluster_manager_experimental":{
   727        "children":{
   728          "cluster:test-cluster-1":{
   729            "childPolicy":[{"cds_experimental":{"cluster":"test-cluster-1"}}]
   730          }
   731        }
   732      }}]}`
   733  	wantSCParsed := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(wantJSON)
   734  
   735  	gotState, err := tcc.stateCh.Receive(ctx)
   736  	if err != nil {
   737  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   738  	}
   739  	rState := gotState.(resolver.State)
   740  	if err := rState.ServiceConfig.Err; err != nil {
   741  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   742  	}
   743  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed.Config) {
   744  		t.Errorf("ClientConn.UpdateState received different service config")
   745  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
   746  		t.Error("want: ", cmp.Diff(nil, wantSCParsed.Config))
   747  	}
   748  
   749  	// "Make an RPC" by invoking the config selector.
   750  	cs := iresolver.GetConfigSelector(rState)
   751  	if cs == nil {
   752  		t.Fatalf("received nil config selector")
   753  	}
   754  
   755  	res, err := cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
   756  	if err != nil {
   757  		t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
   758  	}
   759  
   760  	// "Finish the RPC"; this could cause a panic if the resolver doesn't
   761  	// handle it correctly.
   762  	res.OnCommitted()
   763  
   764  	// Delete the resource.  The channel should receive a service config with the
   765  	// original cluster but with an erroring config selector.
   766  	suErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "resource removed error")
   767  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr)
   768  
   769  	if gotState, err = tcc.stateCh.Receive(ctx); err != nil {
   770  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   771  	}
   772  	rState = gotState.(resolver.State)
   773  	if err := rState.ServiceConfig.Err; err != nil {
   774  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   775  	}
   776  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed.Config) {
   777  		t.Errorf("ClientConn.UpdateState received different service config")
   778  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
   779  		t.Error("want: ", cmp.Diff(nil, wantSCParsed.Config))
   780  	}
   781  
   782  	// "Make another RPC" by invoking the config selector.
   783  	cs = iresolver.GetConfigSelector(rState)
   784  	if cs == nil {
   785  		t.Fatalf("received nil config selector")
   786  	}
   787  
   788  	res, err = cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
   789  	if err == nil || status.Code(err) != codes.Unavailable {
   790  		t.Fatalf("Expected UNAVAILABLE error from cs.SelectConfig(_); got %v, %v", res, err)
   791  	}
   792  
   793  	// In the meantime, an empty ServiceConfig update should have been sent.
   794  	if gotState, err = tcc.stateCh.Receive(ctx); err != nil {
   795  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   796  	}
   797  	rState = gotState.(resolver.State)
   798  	if err := rState.ServiceConfig.Err; err != nil {
   799  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   800  	}
   801  	wantSCParsed = internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)("{}")
   802  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed.Config) {
   803  		t.Errorf("ClientConn.UpdateState received different service config")
   804  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
   805  		t.Error("want: ", cmp.Diff(nil, wantSCParsed.Config))
   806  	}
   807  }
   808  
   809  func (s) TestXDSResolverWRR(t *testing.T) {
   810  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   811  	defer xdsR.Close()
   812  	defer cancel()
   813  
   814  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   815  	defer cancel()
   816  	waitForWatchListener(ctx, t, xdsC, targetStr)
   817  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   818  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   819  
   820  	defer func(oldNewWRR func() wrr.WRR) { newWRR = oldNewWRR }(newWRR)
   821  	newWRR = testutils.NewTestWRR
   822  
   823  	// Invoke the watchAPI callback with a good service update and wait for the
   824  	// UpdateState method to be called on the ClientConn.
   825  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   826  		VirtualHosts: []*xdsresource.VirtualHost{
   827  			{
   828  				Domains: []string{targetStr},
   829  				Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{
   830  					"A": {Weight: 5},
   831  					"B": {Weight: 10},
   832  				}}},
   833  			},
   834  		},
   835  	}, nil)
   836  
   837  	gotState, err := tcc.stateCh.Receive(ctx)
   838  	if err != nil {
   839  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   840  	}
   841  	rState := gotState.(resolver.State)
   842  	if err := rState.ServiceConfig.Err; err != nil {
   843  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   844  	}
   845  
   846  	cs := iresolver.GetConfigSelector(rState)
   847  	if cs == nil {
   848  		t.Fatal("received nil config selector")
   849  	}
   850  
   851  	picks := map[string]int{}
   852  	for i := 0; i < 30; i++ {
   853  		res, err := cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
   854  		if err != nil {
   855  			t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
   856  		}
   857  		picks[clustermanager.GetPickedClusterForTesting(res.Context)]++
   858  		res.OnCommitted()
   859  	}
   860  	want := map[string]int{"cluster:A": 10, "cluster:B": 20}
   861  	if !reflect.DeepEqual(picks, want) {
   862  		t.Errorf("picked clusters = %v; want %v", picks, want)
   863  	}
   864  }
   865  
   866  func (s) TestXDSResolverMaxStreamDuration(t *testing.T) {
   867  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   868  	defer xdsR.Close()
   869  	defer cancel()
   870  
   871  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   872  	defer cancel()
   873  	waitForWatchListener(ctx, t, xdsC, targetStr)
   874  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, MaxStreamDuration: time.Second, HTTPFilters: routerFilterList}, nil)
   875  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   876  
   877  	defer func(oldNewWRR func() wrr.WRR) { newWRR = oldNewWRR }(newWRR)
   878  	newWRR = testutils.NewTestWRR
   879  
   880  	// Invoke the watchAPI callback with a good service update and wait for the
   881  	// UpdateState method to be called on the ClientConn.
   882  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   883  		VirtualHosts: []*xdsresource.VirtualHost{
   884  			{
   885  				Domains: []string{targetStr},
   886  				Routes: []*xdsresource.Route{{
   887  					Prefix:            newStringP("/foo"),
   888  					WeightedClusters:  map[string]xdsresource.WeightedCluster{"A": {Weight: 1}},
   889  					MaxStreamDuration: newDurationP(5 * time.Second),
   890  				}, {
   891  					Prefix:            newStringP("/bar"),
   892  					WeightedClusters:  map[string]xdsresource.WeightedCluster{"B": {Weight: 1}},
   893  					MaxStreamDuration: newDurationP(0),
   894  				}, {
   895  					Prefix:           newStringP(""),
   896  					WeightedClusters: map[string]xdsresource.WeightedCluster{"C": {Weight: 1}},
   897  				}},
   898  			},
   899  		},
   900  	}, nil)
   901  
   902  	gotState, err := tcc.stateCh.Receive(ctx)
   903  	if err != nil {
   904  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   905  	}
   906  	rState := gotState.(resolver.State)
   907  	if err := rState.ServiceConfig.Err; err != nil {
   908  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   909  	}
   910  
   911  	cs := iresolver.GetConfigSelector(rState)
   912  	if cs == nil {
   913  		t.Fatal("received nil config selector")
   914  	}
   915  
   916  	testCases := []struct {
   917  		name   string
   918  		method string
   919  		want   *time.Duration
   920  	}{{
   921  		name:   "RDS setting",
   922  		method: "/foo/method",
   923  		want:   newDurationP(5 * time.Second),
   924  	}, {
   925  		name:   "explicit zero in RDS; ignore LDS",
   926  		method: "/bar/method",
   927  		want:   nil,
   928  	}, {
   929  		name:   "no config in RDS; fallback to LDS",
   930  		method: "/baz/method",
   931  		want:   newDurationP(time.Second),
   932  	}}
   933  
   934  	for _, tc := range testCases {
   935  		t.Run(tc.name, func(t *testing.T) {
   936  			req := iresolver.RPCInfo{
   937  				Method:  tc.method,
   938  				Context: context.Background(),
   939  			}
   940  			res, err := cs.SelectConfig(req)
   941  			if err != nil {
   942  				t.Errorf("Unexpected error from cs.SelectConfig(%v): %v", req, err)
   943  				return
   944  			}
   945  			res.OnCommitted()
   946  			got := res.MethodConfig.Timeout
   947  			if !reflect.DeepEqual(got, tc.want) {
   948  				t.Errorf("For method %q: res.MethodConfig.Timeout = %v; want %v", tc.method, got, tc.want)
   949  			}
   950  		})
   951  	}
   952  }
   953  
   954  // TestXDSResolverDelayedOnCommitted tests that clusters remain in service
   955  // config if RPCs are in flight.
   956  func (s) TestXDSResolverDelayedOnCommitted(t *testing.T) {
   957  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   958  	defer xdsR.Close()
   959  	defer cancel()
   960  
   961  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   962  	defer cancel()
   963  	waitForWatchListener(ctx, t, xdsC, targetStr)
   964  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   965  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   966  
   967  	// Invoke the watchAPI callback with a good service update and wait for the
   968  	// UpdateState method to be called on the ClientConn.
   969  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   970  		VirtualHosts: []*xdsresource.VirtualHost{
   971  			{
   972  				Domains: []string{targetStr},
   973  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"test-cluster-1": {Weight: 1}}}},
   974  			},
   975  		},
   976  	}, nil)
   977  
   978  	gotState, err := tcc.stateCh.Receive(ctx)
   979  	if err != nil {
   980  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   981  	}
   982  	rState := gotState.(resolver.State)
   983  	if err := rState.ServiceConfig.Err; err != nil {
   984  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   985  	}
   986  
   987  	wantJSON := `{"loadBalancingConfig":[{
   988      "xds_cluster_manager_experimental":{
   989        "children":{
   990          "cluster:test-cluster-1":{
   991            "childPolicy":[{"cds_experimental":{"cluster":"test-cluster-1"}}]
   992          }
   993        }
   994      }}]}`
   995  	wantSCParsed := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(wantJSON)
   996  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed.Config) {
   997  		t.Errorf("ClientConn.UpdateState received different service config")
   998  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
   999  		t.Fatal("want: ", cmp.Diff(nil, wantSCParsed.Config))
  1000  	}
  1001  
  1002  	cs := iresolver.GetConfigSelector(rState)
  1003  	if cs == nil {
  1004  		t.Fatal("received nil config selector")
  1005  	}
  1006  
  1007  	res, err := cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
  1008  	if err != nil {
  1009  		t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
  1010  	}
  1011  	cluster := clustermanager.GetPickedClusterForTesting(res.Context)
  1012  	if cluster != "cluster:test-cluster-1" {
  1013  		t.Fatalf("")
  1014  	}
  1015  	// delay res.OnCommitted()
  1016  
  1017  	// Perform TWO updates to ensure the old config selector does not hold a
  1018  	// reference to test-cluster-1.
  1019  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
  1020  		VirtualHosts: []*xdsresource.VirtualHost{
  1021  			{
  1022  				Domains: []string{targetStr},
  1023  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"NEW": {Weight: 1}}}},
  1024  			},
  1025  		},
  1026  	}, nil)
  1027  	tcc.stateCh.Receive(ctx) // Ignore the first update.
  1028  
  1029  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
  1030  		VirtualHosts: []*xdsresource.VirtualHost{
  1031  			{
  1032  				Domains: []string{targetStr},
  1033  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"NEW": {Weight: 1}}}},
  1034  			},
  1035  		},
  1036  	}, nil)
  1037  
  1038  	gotState, err = tcc.stateCh.Receive(ctx)
  1039  	if err != nil {
  1040  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
  1041  	}
  1042  	rState = gotState.(resolver.State)
  1043  	if err := rState.ServiceConfig.Err; err != nil {
  1044  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
  1045  	}
  1046  	wantJSON2 := `{"loadBalancingConfig":[{
  1047      "xds_cluster_manager_experimental":{
  1048        "children":{
  1049          "cluster:test-cluster-1":{
  1050            "childPolicy":[{"cds_experimental":{"cluster":"test-cluster-1"}}]
  1051          },
  1052          "cluster:NEW":{
  1053            "childPolicy":[{"cds_experimental":{"cluster":"NEW"}}]
  1054          }
  1055        }
  1056      }}]}`
  1057  	wantSCParsed2 := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(wantJSON2)
  1058  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed2.Config) {
  1059  		t.Errorf("ClientConn.UpdateState received different service config")
  1060  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
  1061  		t.Fatal("want: ", cmp.Diff(nil, wantSCParsed2.Config))
  1062  	}
  1063  
  1064  	// Invoke OnCommitted; should lead to a service config update that deletes
  1065  	// test-cluster-1.
  1066  	res.OnCommitted()
  1067  
  1068  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
  1069  		VirtualHosts: []*xdsresource.VirtualHost{
  1070  			{
  1071  				Domains: []string{targetStr},
  1072  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"NEW": {Weight: 1}}}},
  1073  			},
  1074  		},
  1075  	}, nil)
  1076  	gotState, err = tcc.stateCh.Receive(ctx)
  1077  	if err != nil {
  1078  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
  1079  	}
  1080  	rState = gotState.(resolver.State)
  1081  	if err := rState.ServiceConfig.Err; err != nil {
  1082  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
  1083  	}
  1084  	wantJSON3 := `{"loadBalancingConfig":[{
  1085      "xds_cluster_manager_experimental":{
  1086        "children":{
  1087          "cluster:NEW":{
  1088            "childPolicy":[{"cds_experimental":{"cluster":"NEW"}}]
  1089          }
  1090        }
  1091      }}]}`
  1092  	wantSCParsed3 := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(wantJSON3)
  1093  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed3.Config) {
  1094  		t.Errorf("ClientConn.UpdateState received different service config")
  1095  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
  1096  		t.Fatal("want: ", cmp.Diff(nil, wantSCParsed3.Config))
  1097  	}
  1098  }
  1099  
  1100  // TestXDSResolverUpdates tests the cases where the resolver gets a good update
  1101  // after an error, and an error after the good update.
  1102  func (s) TestXDSResolverGoodUpdateAfterError(t *testing.T) {
  1103  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
  1104  	defer xdsR.Close()
  1105  	defer cancel()
  1106  
  1107  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1108  	defer cancel()
  1109  	waitForWatchListener(ctx, t, xdsC, targetStr)
  1110  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
  1111  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
  1112  
  1113  	// Invoke the watchAPI callback with a bad service update and wait for the
  1114  	// ReportError method to be called on the ClientConn.
  1115  	suErr := errors.New("bad serviceupdate")
  1116  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr)
  1117  
  1118  	if gotErrVal, gotErr := tcc.errorCh.Receive(ctx); gotErr != nil || gotErrVal != suErr {
  1119  		t.Fatalf("ClientConn.ReportError() received %v, want %v", gotErrVal, suErr)
  1120  	}
  1121  
  1122  	// Invoke the watchAPI callback with a good service update and wait for the
  1123  	// UpdateState method to be called on the ClientConn.
  1124  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
  1125  		VirtualHosts: []*xdsresource.VirtualHost{
  1126  			{
  1127  				Domains: []string{targetStr},
  1128  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
  1129  			},
  1130  		},
  1131  	}, nil)
  1132  	gotState, err := tcc.stateCh.Receive(ctx)
  1133  	if err != nil {
  1134  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
  1135  	}
  1136  	rState := gotState.(resolver.State)
  1137  	if err := rState.ServiceConfig.Err; err != nil {
  1138  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
  1139  	}
  1140  
  1141  	// Invoke the watchAPI callback with a bad service update and wait for the
  1142  	// ReportError method to be called on the ClientConn.
  1143  	suErr2 := errors.New("bad serviceupdate 2")
  1144  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr2)
  1145  	if gotErrVal, gotErr := tcc.errorCh.Receive(ctx); gotErr != nil || gotErrVal != suErr2 {
  1146  		t.Fatalf("ClientConn.ReportError() received %v, want %v", gotErrVal, suErr2)
  1147  	}
  1148  }
  1149  
  1150  // TestXDSResolverResourceNotFoundError tests the cases where the resolver gets
  1151  // a ResourceNotFoundError. It should generate a service config picking
  1152  // weighted_target, but no child balancers.
  1153  func (s) TestXDSResolverResourceNotFoundError(t *testing.T) {
  1154  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
  1155  	defer xdsR.Close()
  1156  	defer cancel()
  1157  
  1158  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1159  	defer cancel()
  1160  	waitForWatchListener(ctx, t, xdsC, targetStr)
  1161  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
  1162  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
  1163  
  1164  	// Invoke the watchAPI callback with a bad service update and wait for the
  1165  	// ReportError method to be called on the ClientConn.
  1166  	suErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "resource removed error")
  1167  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr)
  1168  
  1169  	if gotErrVal, gotErr := tcc.errorCh.Receive(ctx); gotErr != context.DeadlineExceeded {
  1170  		t.Fatalf("ClientConn.ReportError() received %v, %v, want channel recv timeout", gotErrVal, gotErr)
  1171  	}
  1172  
  1173  	ctx, cancel = context.WithTimeout(context.Background(), defaultTestTimeout)
  1174  	defer cancel()
  1175  	gotState, err := tcc.stateCh.Receive(ctx)
  1176  	if err != nil {
  1177  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
  1178  	}
  1179  	rState := gotState.(resolver.State)
  1180  	wantParsedConfig := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)("{}")
  1181  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantParsedConfig.Config) {
  1182  		t.Error("ClientConn.UpdateState got wrong service config")
  1183  		t.Errorf("gotParsed: %s", cmp.Diff(nil, rState.ServiceConfig.Config))
  1184  		t.Errorf("wantParsed: %s", cmp.Diff(nil, wantParsedConfig.Config))
  1185  	}
  1186  	if err := rState.ServiceConfig.Err; err != nil {
  1187  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
  1188  	}
  1189  }
  1190  
  1191  // TestXDSResolverMultipleLDSUpdates tests the case where two LDS updates with
  1192  // the same RDS name to watch are received without an RDS in between. Those LDS
  1193  // updates shouldn't trigger service config update.
  1194  //
  1195  // This test case also makes sure the resolver doesn't panic.
  1196  func (s) TestXDSResolverMultipleLDSUpdates(t *testing.T) {
  1197  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
  1198  	defer xdsR.Close()
  1199  	defer cancel()
  1200  
  1201  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1202  	defer cancel()
  1203  	waitForWatchListener(ctx, t, xdsC, targetStr)
  1204  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
  1205  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
  1206  	defer replaceRandNumGenerator(0)()
  1207  
  1208  	// Send a new LDS update, with the same fields.
  1209  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
  1210  	ctx, cancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
  1211  	defer cancel()
  1212  	// Should NOT trigger a state update.
  1213  	gotState, err := tcc.stateCh.Receive(ctx)
  1214  	if err == nil {
  1215  		t.Fatalf("ClientConn.UpdateState received %v, want timeout error", gotState)
  1216  	}
  1217  
  1218  	// Send a new LDS update, with the same RDS name, but different fields.
  1219  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, MaxStreamDuration: time.Second, HTTPFilters: routerFilterList}, nil)
  1220  	ctx, cancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
  1221  	defer cancel()
  1222  	gotState, err = tcc.stateCh.Receive(ctx)
  1223  	if err == nil {
  1224  		t.Fatalf("ClientConn.UpdateState received %v, want timeout error", gotState)
  1225  	}
  1226  }
  1227  
  1228  type filterBuilder struct {
  1229  	httpfilter.Filter // embedded as we do not need to implement registry / parsing in this test.
  1230  	path              *[]string
  1231  }
  1232  
  1233  var _ httpfilter.ClientInterceptorBuilder = &filterBuilder{}
  1234  
  1235  func (fb *filterBuilder) BuildClientInterceptor(config, override httpfilter.FilterConfig) (iresolver.ClientInterceptor, error) {
  1236  	if config == nil {
  1237  		panic("unexpected missing config")
  1238  	}
  1239  	*fb.path = append(*fb.path, "build:"+config.(filterCfg).s)
  1240  	err := config.(filterCfg).newStreamErr
  1241  	if override != nil {
  1242  		*fb.path = append(*fb.path, "override:"+override.(filterCfg).s)
  1243  		err = override.(filterCfg).newStreamErr
  1244  	}
  1245  
  1246  	return &filterInterceptor{path: fb.path, s: config.(filterCfg).s, err: err}, nil
  1247  }
  1248  
  1249  type filterInterceptor struct {
  1250  	path *[]string
  1251  	s    string
  1252  	err  error
  1253  }
  1254  
  1255  func (fi *filterInterceptor) NewStream(ctx context.Context, ri iresolver.RPCInfo, done func(), newStream func(ctx context.Context, done func()) (iresolver.ClientStream, error)) (iresolver.ClientStream, error) {
  1256  	*fi.path = append(*fi.path, "newstream:"+fi.s)
  1257  	if fi.err != nil {
  1258  		return nil, fi.err
  1259  	}
  1260  	d := func() {
  1261  		*fi.path = append(*fi.path, "done:"+fi.s)
  1262  		done()
  1263  	}
  1264  	cs, err := newStream(ctx, d)
  1265  	if err != nil {
  1266  		return nil, err
  1267  	}
  1268  	return &clientStream{ClientStream: cs, path: fi.path, s: fi.s}, nil
  1269  }
  1270  
  1271  type clientStream struct {
  1272  	iresolver.ClientStream
  1273  	path *[]string
  1274  	s    string
  1275  }
  1276  
  1277  type filterCfg struct {
  1278  	httpfilter.FilterConfig
  1279  	s            string
  1280  	newStreamErr error
  1281  }
  1282  
  1283  func (s) TestXDSResolverHTTPFilters(t *testing.T) {
  1284  	var path []string
  1285  	testCases := []struct {
  1286  		name         string
  1287  		ldsFilters   []xdsresource.HTTPFilter
  1288  		vhOverrides  map[string]httpfilter.FilterConfig
  1289  		rtOverrides  map[string]httpfilter.FilterConfig
  1290  		clOverrides  map[string]httpfilter.FilterConfig
  1291  		rpcRes       map[string][][]string
  1292  		selectErr    string
  1293  		newStreamErr string
  1294  	}{
  1295  		{
  1296  			name: "no router filter",
  1297  			ldsFilters: []xdsresource.HTTPFilter{
  1298  				{Name: "foo", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo1"}},
  1299  			},
  1300  			rpcRes: map[string][][]string{
  1301  				"1": {
  1302  					{"build:foo1", "override:foo2", "build:bar1", "override:bar2", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1303  				},
  1304  			},
  1305  			selectErr: "no router filter present",
  1306  		},
  1307  		{
  1308  			name: "ignored after router filter",
  1309  			ldsFilters: []xdsresource.HTTPFilter{
  1310  				{Name: "foo", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo1"}},
  1311  				routerFilter,
  1312  				{Name: "foo2", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo2"}},
  1313  			},
  1314  			rpcRes: map[string][][]string{
  1315  				"1": {
  1316  					{"build:foo1", "newstream:foo1", "done:foo1"},
  1317  				},
  1318  				"2": {
  1319  					{"build:foo1", "newstream:foo1", "done:foo1"},
  1320  					{"build:foo1", "newstream:foo1", "done:foo1"},
  1321  					{"build:foo1", "newstream:foo1", "done:foo1"},
  1322  				},
  1323  			},
  1324  		},
  1325  		{
  1326  			name: "NewStream error; ensure earlier interceptor Done is still called",
  1327  			ldsFilters: []xdsresource.HTTPFilter{
  1328  				{Name: "foo", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo1"}},
  1329  				{Name: "bar", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "bar1", newStreamErr: errors.New("bar newstream err")}},
  1330  				routerFilter,
  1331  			},
  1332  			rpcRes: map[string][][]string{
  1333  				"1": {
  1334  					{"build:foo1", "build:bar1", "newstream:foo1", "newstream:bar1" /* <err in bar1 NewStream> */, "done:foo1"},
  1335  				},
  1336  				"2": {
  1337  					{"build:foo1", "build:bar1", "newstream:foo1", "newstream:bar1" /* <err in bar1 NewSteam> */, "done:foo1"},
  1338  				},
  1339  			},
  1340  			newStreamErr: "bar newstream err",
  1341  		},
  1342  		{
  1343  			name: "all overrides",
  1344  			ldsFilters: []xdsresource.HTTPFilter{
  1345  				{Name: "foo", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo1", newStreamErr: errors.New("this is overridden to nil")}},
  1346  				{Name: "bar", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "bar1"}},
  1347  				routerFilter,
  1348  			},
  1349  			vhOverrides: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo2"}, "bar": filterCfg{s: "bar2"}},
  1350  			rtOverrides: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo3"}, "bar": filterCfg{s: "bar3"}},
  1351  			clOverrides: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo4"}, "bar": filterCfg{s: "bar4"}},
  1352  			rpcRes: map[string][][]string{
  1353  				"1": {
  1354  					{"build:foo1", "override:foo2", "build:bar1", "override:bar2", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1355  					{"build:foo1", "override:foo2", "build:bar1", "override:bar2", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1356  				},
  1357  				"2": {
  1358  					{"build:foo1", "override:foo3", "build:bar1", "override:bar3", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1359  					{"build:foo1", "override:foo4", "build:bar1", "override:bar4", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1360  					{"build:foo1", "override:foo3", "build:bar1", "override:bar3", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1361  					{"build:foo1", "override:foo4", "build:bar1", "override:bar4", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1362  				},
  1363  			},
  1364  		},
  1365  	}
  1366  
  1367  	for i, tc := range testCases {
  1368  		t.Run(tc.name, func(t *testing.T) {
  1369  			xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
  1370  			defer xdsR.Close()
  1371  			defer cancel()
  1372  
  1373  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1374  			defer cancel()
  1375  			waitForWatchListener(ctx, t, xdsC, targetStr)
  1376  
  1377  			xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{
  1378  				RouteConfigName: routeStr,
  1379  				HTTPFilters:     tc.ldsFilters,
  1380  			}, nil)
  1381  			if i == 0 {
  1382  				waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
  1383  			}
  1384  
  1385  			defer func(oldNewWRR func() wrr.WRR) { newWRR = oldNewWRR }(newWRR)
  1386  			newWRR = testutils.NewTestWRR
  1387  
  1388  			// Invoke the watchAPI callback with a good service update and wait for the
  1389  			// UpdateState method to be called on the ClientConn.
  1390  			xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
  1391  				VirtualHosts: []*xdsresource.VirtualHost{
  1392  					{
  1393  						Domains: []string{targetStr},
  1394  						Routes: []*xdsresource.Route{{
  1395  							Prefix: newStringP("1"), WeightedClusters: map[string]xdsresource.WeightedCluster{
  1396  								"A": {Weight: 1},
  1397  								"B": {Weight: 1},
  1398  							},
  1399  						}, {
  1400  							Prefix: newStringP("2"), WeightedClusters: map[string]xdsresource.WeightedCluster{
  1401  								"A": {Weight: 1},
  1402  								"B": {Weight: 1, HTTPFilterConfigOverride: tc.clOverrides},
  1403  							},
  1404  							HTTPFilterConfigOverride: tc.rtOverrides,
  1405  						}},
  1406  						HTTPFilterConfigOverride: tc.vhOverrides,
  1407  					},
  1408  				},
  1409  			}, nil)
  1410  
  1411  			gotState, err := tcc.stateCh.Receive(ctx)
  1412  			if err != nil {
  1413  				t.Fatalf("Error waiting for UpdateState to be called: %v", err)
  1414  			}
  1415  			rState := gotState.(resolver.State)
  1416  			if err := rState.ServiceConfig.Err; err != nil {
  1417  				t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
  1418  			}
  1419  
  1420  			cs := iresolver.GetConfigSelector(rState)
  1421  			if cs == nil {
  1422  				t.Fatal("received nil config selector")
  1423  			}
  1424  
  1425  			for method, wants := range tc.rpcRes {
  1426  				// Order of wants is non-deterministic.
  1427  				remainingWant := make([][]string, len(wants))
  1428  				copy(remainingWant, wants)
  1429  				for n := range wants {
  1430  					path = nil
  1431  
  1432  					res, err := cs.SelectConfig(iresolver.RPCInfo{Method: method, Context: context.Background()})
  1433  					if tc.selectErr != "" {
  1434  						if err == nil || !strings.Contains(err.Error(), tc.selectErr) {
  1435  							t.Errorf("SelectConfig(_) = _, %v; want _, Contains(%v)", err, tc.selectErr)
  1436  						}
  1437  						if err == nil {
  1438  							res.OnCommitted()
  1439  						}
  1440  						continue
  1441  					}
  1442  					if err != nil {
  1443  						t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
  1444  					}
  1445  					var doneFunc func()
  1446  					_, err = res.Interceptor.NewStream(context.Background(), iresolver.RPCInfo{}, func() {}, func(ctx context.Context, done func()) (iresolver.ClientStream, error) {
  1447  						doneFunc = done
  1448  						return nil, nil
  1449  					})
  1450  					if tc.newStreamErr != "" {
  1451  						if err == nil || !strings.Contains(err.Error(), tc.newStreamErr) {
  1452  							t.Errorf("NewStream(...) = _, %v; want _, Contains(%v)", err, tc.newStreamErr)
  1453  						}
  1454  						if err == nil {
  1455  							res.OnCommitted()
  1456  							doneFunc()
  1457  						}
  1458  						continue
  1459  					}
  1460  					if err != nil {
  1461  						t.Fatalf("unexpected error from Interceptor.NewStream: %v", err)
  1462  
  1463  					}
  1464  					res.OnCommitted()
  1465  					doneFunc()
  1466  
  1467  					// Confirm the desired path is found in remainingWant, and remove it.
  1468  					pass := false
  1469  					for i := range remainingWant {
  1470  						if reflect.DeepEqual(path, remainingWant[i]) {
  1471  							remainingWant[i] = remainingWant[len(remainingWant)-1]
  1472  							remainingWant = remainingWant[:len(remainingWant)-1]
  1473  							pass = true
  1474  							break
  1475  						}
  1476  					}
  1477  					if !pass {
  1478  						t.Errorf("%q:%v - path:\n%v\nwant one of:\n%v", method, n, path, remainingWant)
  1479  					}
  1480  				}
  1481  			}
  1482  		})
  1483  	}
  1484  }
  1485  
  1486  func replaceRandNumGenerator(start int64) func() {
  1487  	nextInt := start
  1488  	xdsresource.RandInt63n = func(int64) (ret int64) {
  1489  		ret = nextInt
  1490  		nextInt++
  1491  		return
  1492  	}
  1493  	return func() {
  1494  		xdsresource.RandInt63n = grpcrand.Int63n
  1495  	}
  1496  }
  1497  
  1498  func newDurationP(d time.Duration) *time.Duration {
  1499  	return &d
  1500  }