google.golang.org/grpc@v1.74.2/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_test
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  	"fmt"
    25  	"strings"
    26  	"sync"
    27  	"testing"
    28  	"time"
    29  
    30  	xxhash "github.com/cespare/xxhash/v2"
    31  	"github.com/envoyproxy/go-control-plane/pkg/wellknown"
    32  	"github.com/google/go-cmp/cmp"
    33  	"github.com/google/uuid"
    34  	"google.golang.org/grpc/codes"
    35  	estats "google.golang.org/grpc/experimental/stats"
    36  	"google.golang.org/grpc/internal"
    37  	iresolver "google.golang.org/grpc/internal/resolver"
    38  	iringhash "google.golang.org/grpc/internal/ringhash"
    39  	"google.golang.org/grpc/internal/testutils"
    40  	"google.golang.org/grpc/internal/testutils/xds/e2e"
    41  	"google.golang.org/grpc/internal/xds/bootstrap"
    42  	"google.golang.org/grpc/metadata"
    43  	"google.golang.org/grpc/resolver"
    44  	"google.golang.org/grpc/serviceconfig"
    45  	"google.golang.org/grpc/xds/internal/balancer/clustermanager"
    46  	"google.golang.org/grpc/xds/internal/httpfilter"
    47  	rinternal "google.golang.org/grpc/xds/internal/resolver/internal"
    48  	"google.golang.org/grpc/xds/internal/xdsclient"
    49  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"
    50  	"google.golang.org/protobuf/proto"
    51  	"google.golang.org/protobuf/types/known/anypb"
    52  	"google.golang.org/protobuf/types/known/durationpb"
    53  	"google.golang.org/protobuf/types/known/structpb"
    54  	"google.golang.org/protobuf/types/known/wrapperspb"
    55  
    56  	v3xdsxdstypepb "github.com/cncf/xds/go/xds/type/v3"
    57  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    58  	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
    59  	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
    60  	v3routerpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"
    61  	v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
    62  	v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    63  
    64  	_ "google.golang.org/grpc/xds/internal/balancer/cdsbalancer" // Register the cds LB policy
    65  	_ "google.golang.org/grpc/xds/internal/httpfilter/router"    // Register the router filter
    66  )
    67  
    68  // Tests the case where xDS client creation is expected to fail because the
    69  // bootstrap configuration for the xDS client pool is not specified. The test
    70  // verifies that xDS resolver build fails as well.
    71  func (s) TestResolverBuilder_ClientCreationFails_NoBootstrap(t *testing.T) {
    72  	// Build an xDS resolver specifying nil for bootstrap configuration for the
    73  	// xDS client pool.
    74  	pool := xdsclient.NewPool(nil)
    75  	var xdsResolver resolver.Builder
    76  	if newResolver := internal.NewXDSResolverWithPoolForTesting; newResolver != nil {
    77  		var err error
    78  		xdsResolver, err = newResolver.(func(*xdsclient.Pool) (resolver.Builder, error))(pool)
    79  		if err != nil {
    80  			t.Fatalf("Failed to create xDS resolver for testing: %v", err)
    81  		}
    82  	}
    83  
    84  	target := resolver.Target{URL: *testutils.MustParseURL("xds:///target")}
    85  	if _, err := xdsResolver.Build(target, nil, resolver.BuildOptions{}); err == nil {
    86  		t.Fatalf("xds Resolver Build(%v) succeeded when expected to fail, because there is no bootstrap configuration for the xDS client pool", pool)
    87  	}
    88  }
    89  
    90  // Tests the case where the specified dial target contains an authority that is
    91  // not specified in the bootstrap file. Verifies that the resolver.Build method
    92  // fails with the expected error string.
    93  func (s) TestResolverBuilder_AuthorityNotDefinedInBootstrap(t *testing.T) {
    94  	contents := e2e.DefaultBootstrapContents(t, "node-id", "dummy-management-server")
    95  
    96  	// Create an xDS resolver with the above bootstrap configuration.
    97  	if internal.NewXDSResolverWithConfigForTesting == nil {
    98  		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
    99  	}
   100  	xdsResolver, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(contents)
   101  	if err != nil {
   102  		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
   103  	}
   104  
   105  	target := resolver.Target{URL: *testutils.MustParseURL("xds://non-existing-authority/target")}
   106  	const wantErr = `authority "non-existing-authority" specified in dial target "xds://non-existing-authority/target" is not found in the bootstrap file`
   107  	r, err := xdsResolver.Build(target, &testutils.ResolverClientConn{Logger: t}, resolver.BuildOptions{})
   108  	if r != nil {
   109  		r.Close()
   110  	}
   111  	if err == nil {
   112  		t.Fatalf("xds Resolver Build(%v) succeeded for target with authority not specified in bootstrap", target)
   113  	}
   114  	if !strings.Contains(err.Error(), wantErr) {
   115  		t.Fatalf("xds Resolver Build(%v) returned err: %v, wantErr: %v", target, err, wantErr)
   116  	}
   117  }
   118  
   119  // Test builds an xDS resolver and verifies that the resource name specified in
   120  // the discovery request matches expectations.
   121  func (s) TestResolverResourceName(t *testing.T) {
   122  	tests := []struct {
   123  		name                         string
   124  		listenerResourceNameTemplate string
   125  		extraAuthority               string
   126  		dialTarget                   string
   127  		wantResourceNames            []string
   128  	}{
   129  		{
   130  			name:                         "default %s old style",
   131  			listenerResourceNameTemplate: "%s",
   132  			dialTarget:                   "xds:///target",
   133  			wantResourceNames:            []string{"target"},
   134  		},
   135  		{
   136  			name:                         "old style no percent encoding",
   137  			listenerResourceNameTemplate: "/path/to/%s",
   138  			dialTarget:                   "xds:///target",
   139  			wantResourceNames:            []string{"/path/to/target"},
   140  		},
   141  		{
   142  			name:                         "new style with %s",
   143  			listenerResourceNameTemplate: "xdstp://authority.com/%s",
   144  			dialTarget:                   "xds:///0.0.0.0:8080",
   145  			wantResourceNames:            []string{"xdstp://authority.com/0.0.0.0:8080"},
   146  		},
   147  		{
   148  			name:                         "new style percent encoding",
   149  			listenerResourceNameTemplate: "xdstp://authority.com/%s",
   150  			dialTarget:                   "xds:///[::1]:8080",
   151  			wantResourceNames:            []string{"xdstp://authority.com/%5B::1%5D:8080"},
   152  		},
   153  		{
   154  			name:                         "new style different authority",
   155  			listenerResourceNameTemplate: "xdstp://authority.com/%s",
   156  			extraAuthority:               "test-authority",
   157  			dialTarget:                   "xds://test-authority/target",
   158  			wantResourceNames:            []string{"xdstp://test-authority/envoy.config.listener.v3.Listener/target"},
   159  		},
   160  	}
   161  	for _, tt := range tests {
   162  		t.Run(tt.name, func(t *testing.T) {
   163  			// Spin up an xDS management server for the test.
   164  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   165  			defer cancel()
   166  			nodeID := uuid.New().String()
   167  			mgmtServer, lisCh, _, _ := setupManagementServerForTest(t, nodeID)
   168  
   169  			// Create a bootstrap configuration with test options.
   170  			opts := bootstrap.ConfigOptionsForTesting{
   171  				Servers: []byte(fmt.Sprintf(`[{
   172  					"server_uri": %q,
   173  					"channel_creds": [{"type": "insecure"}]
   174  				}]`, mgmtServer.Address)),
   175  				ClientDefaultListenerResourceNameTemplate: tt.listenerResourceNameTemplate,
   176  				Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)),
   177  			}
   178  			if tt.extraAuthority != "" {
   179  				// In this test, we really don't care about having multiple
   180  				// management servers. All we need to verify is whether the
   181  				// resource name matches expectation.
   182  				opts.Authorities = map[string]json.RawMessage{
   183  					tt.extraAuthority: []byte(fmt.Sprintf(`{
   184  						"server_uri": %q,
   185  						"channel_creds": [{"type": "insecure"}]
   186  					}`, mgmtServer.Address)),
   187  				}
   188  			}
   189  			contents, err := bootstrap.NewContentsForTesting(opts)
   190  			if err != nil {
   191  				t.Fatalf("Failed to create bootstrap configuration: %v", err)
   192  			}
   193  
   194  			buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL(tt.dialTarget)}, contents)
   195  			waitForResourceNames(ctx, t, lisCh, tt.wantResourceNames)
   196  		})
   197  	}
   198  }
   199  
   200  // Tests the case where a service update from the underlying xDS client is
   201  // received after the resolver is closed, and verifies that the update is not
   202  // propagated to the ClientConn.
   203  func (s) TestResolverWatchCallbackAfterClose(t *testing.T) {
   204  	// Setup the management server that synchronizes with the test goroutine
   205  	// using two channels. The management server signals the test goroutine when
   206  	// it receives a discovery request for a route configuration resource. And
   207  	// the test goroutine signals the management server when the resolver is
   208  	// closed.
   209  	routeConfigResourceNamesCh := make(chan []string, 1)
   210  	waitForResolverCloseCh := make(chan struct{})
   211  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{
   212  		OnStreamRequest: func(_ int64, req *v3discoverypb.DiscoveryRequest) error {
   213  			if req.GetTypeUrl() == version.V3RouteConfigURL {
   214  				select {
   215  				case <-routeConfigResourceNamesCh:
   216  				default:
   217  				}
   218  				select {
   219  				case routeConfigResourceNamesCh <- req.GetResourceNames():
   220  				default:
   221  				}
   222  				<-waitForResolverCloseCh
   223  			}
   224  			return nil
   225  		},
   226  	})
   227  
   228  	// Create a bootstrap configuration specifying the above management server.
   229  	nodeID := uuid.New().String()
   230  	contents := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
   231  
   232  	// Configure resources on the management server.
   233  	listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
   234  	routes := []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(defaultTestRouteConfigName, defaultTestServiceName, defaultTestClusterName)}
   235  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   236  	defer cancel()
   237  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
   238  
   239  	// Wait for a discovery request for a route configuration resource.
   240  	stateCh, _, r := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, contents)
   241  	waitForResourceNames(ctx, t, routeConfigResourceNamesCh, []string{defaultTestRouteConfigName})
   242  
   243  	// Close the resolver and unblock the management server.
   244  	r.Close()
   245  	close(waitForResolverCloseCh)
   246  
   247  	// Verify that the update from the management server is not propagated to
   248  	// the ClientConn. The xDS resolver, once closed, is expected to drop
   249  	// updates from the xDS client.
   250  	verifyNoUpdateFromResolver(ctx, t, stateCh)
   251  }
   252  
   253  // Tests that the xDS resolver's Close method closes the xDS client.
   254  func (s) TestResolverCloseClosesXDSClient(t *testing.T) {
   255  	// Override xDS client creation to use bootstrap configuration pointing to a
   256  	// dummy management server. Also close a channel when the returned xDS
   257  	// client is closed.
   258  	origNewClient := rinternal.NewXDSClient
   259  	closeCh := make(chan struct{})
   260  	rinternal.NewXDSClient = func(string, estats.MetricsRecorder) (xdsclient.XDSClient, func(), error) {
   261  		bc := e2e.DefaultBootstrapContents(t, uuid.New().String(), "dummy-management-server-address")
   262  		config, err := bootstrap.NewConfigFromContents(bc)
   263  		if err != nil {
   264  			t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err)
   265  		}
   266  		pool := xdsclient.NewPool(config)
   267  		if err != nil {
   268  			t.Fatalf("Failed to create an xDS client pool: %v", err)
   269  		}
   270  		c, cancel, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{
   271  			Name:               t.Name(),
   272  			WatchExpiryTimeout: defaultTestTimeout,
   273  		})
   274  		return c, sync.OnceFunc(func() {
   275  			close(closeCh)
   276  			cancel()
   277  		}), err
   278  	}
   279  	defer func() { rinternal.NewXDSClient = origNewClient }()
   280  
   281  	_, _, r := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///my-service-client-side-xds")}, nil)
   282  	r.Close()
   283  
   284  	select {
   285  	case <-closeCh:
   286  	case <-time.After(defaultTestTimeout):
   287  		t.Fatal("Timeout when waiting for xDS client to be closed")
   288  	}
   289  }
   290  
   291  // Tests the case where a resource, not present in cache, returned by the
   292  // management server is NACKed by the xDS client, which then returns an update
   293  // containing a resource error to the resolver. It tests the case where the
   294  // resolver gets an error update without any previous good update. The test
   295  // also verifies that these are propagated to the ClientConn.
   296  func (s) TestResolverBadServiceUpdate_NACKedWithoutCache(t *testing.T) {
   297  	// Spin up an xDS management server for the test.
   298  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   299  	defer cancel()
   300  	nodeID := uuid.New().String()
   301  	mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
   302  
   303  	// Configure a listener resource that is expected to be NACKed because it
   304  	// does not contain the `RouteSpecifier` field in the HTTPConnectionManager.
   305  	hcm := testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   306  		HttpFilters: []*v3httppb.HttpFilter{e2e.HTTPFilter("router", &v3routerpb.Router{})},
   307  	})
   308  	lis := &v3listenerpb.Listener{
   309  		Name:        defaultTestServiceName,
   310  		ApiListener: &v3listenerpb.ApiListener{ApiListener: hcm},
   311  		FilterChains: []*v3listenerpb.FilterChain{{
   312  			Name: "filter-chain-name",
   313  			Filters: []*v3listenerpb.Filter{{
   314  				Name:       wellknown.HTTPConnectionManager,
   315  				ConfigType: &v3listenerpb.Filter_TypedConfig{TypedConfig: hcm},
   316  			}},
   317  		}},
   318  	}
   319  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, []*v3listenerpb.Listener{lis}, nil)
   320  
   321  	// Build the resolver and expect an error update from it. Since the
   322  	// resource is not cached, it should be received as resource error.
   323  	_, errCh, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
   324  	if err := waitForErrorFromResolver(ctx, errCh, "no RouteSpecifier", nodeID); err != nil {
   325  		t.Fatal(err)
   326  	}
   327  }
   328  
   329  // Tests the case where a resource, present in cache, returned by the
   330  // management server is NACKed by the xDS client, which then returns
   331  // an update containing an ambient error to the resolver. Verifies that the
   332  // update is propagated to the ClientConn by the resolver. It tests the
   333  // case where the resolver gets a good update first, and an error
   334  // after the good update. The test also verifies that these are propagated to
   335  // the ClientConn and that RPC succeeds as expected after receiving good update
   336  // as well as ambient error.
   337  func (s) TestResolverBadServiceUpdate_NACKedWithCache(t *testing.T) {
   338  	// Spin up an xDS management server for the test.
   339  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   340  	defer cancel()
   341  	nodeID := uuid.New().String()
   342  	mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
   343  
   344  	stateCh, errCh, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
   345  
   346  	// Configure good listener and route configuration resources on the
   347  	// management server.
   348  	listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
   349  	routes := []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(defaultTestRouteConfigName, defaultTestServiceName, defaultTestClusterName)}
   350  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
   351  
   352  	// Expect a good update from the resolver.
   353  	cs := verifyUpdateFromResolver(ctx, t, stateCh, wantDefaultServiceConfig)
   354  
   355  	// "Make an RPC" by invoking the config selector.
   356  	_, err := cs.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: "/service/method"})
   357  	if err != nil {
   358  		t.Fatalf("cs.SelectConfig(): %v", err)
   359  	}
   360  
   361  	// Configure a listener resource that is expected to be NACKed because it
   362  	// does not contain the `RouteSpecifier` field in the HTTPConnectionManager.
   363  	hcm := testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   364  		HttpFilters: []*v3httppb.HttpFilter{e2e.HTTPFilter("router", &v3routerpb.Router{})},
   365  	})
   366  	lis := &v3listenerpb.Listener{
   367  		Name:        defaultTestServiceName,
   368  		ApiListener: &v3listenerpb.ApiListener{ApiListener: hcm},
   369  		FilterChains: []*v3listenerpb.FilterChain{{
   370  			Name: "filter-chain-name",
   371  			Filters: []*v3listenerpb.Filter{{
   372  				Name:       wellknown.HTTPConnectionManager,
   373  				ConfigType: &v3listenerpb.Filter_TypedConfig{TypedConfig: hcm},
   374  			}},
   375  		}},
   376  	}
   377  
   378  	// Expect an error update from the resolver. Since the resource is cached,
   379  	// it should be received as an ambient error.
   380  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, []*v3listenerpb.Listener{lis}, nil)
   381  	if err := waitForErrorFromResolver(ctx, errCh, "no RouteSpecifier", nodeID); err != nil {
   382  		t.Fatal(err)
   383  	}
   384  
   385  	// "Make an RPC" by invoking the config selector which should succeed by
   386  	// continuing to use the previously cached resource.
   387  	_, err = cs.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: "/service/method"})
   388  	if err != nil {
   389  		t.Fatalf("cs.SelectConfig(): %v", err)
   390  	}
   391  }
   392  
   393  // TestResolverGoodServiceUpdate tests the case where the resource returned by
   394  // the management server is ACKed by the xDS client, which then returns a good
   395  // service update to the resolver. The test verifies that the service config
   396  // returned by the resolver matches expectations, and that the config selector
   397  // returned by the resolver picks clusters based on the route configuration
   398  // received from the management server.
   399  func (s) TestResolverGoodServiceUpdate(t *testing.T) {
   400  	for _, tt := range []struct {
   401  		name              string
   402  		routeConfig       *v3routepb.RouteConfiguration
   403  		wantServiceConfig string
   404  		wantClusters      map[string]bool
   405  	}{
   406  		{
   407  			name: "single cluster",
   408  			routeConfig: e2e.RouteConfigResourceWithOptions(e2e.RouteConfigOptions{
   409  				RouteConfigName:      defaultTestRouteConfigName,
   410  				ListenerName:         defaultTestServiceName,
   411  				ClusterSpecifierType: e2e.RouteConfigClusterSpecifierTypeCluster,
   412  				ClusterName:          defaultTestClusterName,
   413  			}),
   414  			wantServiceConfig: wantDefaultServiceConfig,
   415  			wantClusters:      map[string]bool{fmt.Sprintf("cluster:%s", defaultTestClusterName): true},
   416  		},
   417  		{
   418  			name: "two clusters",
   419  			routeConfig: e2e.RouteConfigResourceWithOptions(e2e.RouteConfigOptions{
   420  				RouteConfigName:      defaultTestRouteConfigName,
   421  				ListenerName:         defaultTestServiceName,
   422  				ClusterSpecifierType: e2e.RouteConfigClusterSpecifierTypeWeightedCluster,
   423  				WeightedClusters:     map[string]int{"cluster_1": 75, "cluster_2": 25},
   424  			}),
   425  			// This update contains the cluster from the previous update as well
   426  			// as this update, as the previous config selector still references
   427  			// the old cluster when the new one is pushed.
   428  			wantServiceConfig: `{
   429    "loadBalancingConfig": [{
   430      "xds_cluster_manager_experimental": {
   431        "children": {
   432          "cluster:cluster_1": {
   433            "childPolicy": [{
   434  			"cds_experimental": {
   435  			  "cluster": "cluster_1"
   436  			}
   437  		  }]
   438          },
   439          "cluster:cluster_2": {
   440            "childPolicy": [{
   441  			"cds_experimental": {
   442  			  "cluster": "cluster_2"
   443  			}
   444  		  }]
   445          }
   446        }
   447      }
   448    }]}`,
   449  			wantClusters: map[string]bool{"cluster:cluster_1": true, "cluster:cluster_2": true},
   450  		},
   451  	} {
   452  		t.Run(tt.name, func(t *testing.T) {
   453  			// Spin up an xDS management server for the test.
   454  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   455  			defer cancel()
   456  			nodeID := uuid.New().String()
   457  			mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
   458  
   459  			// Configure the management server with a good listener resource and a
   460  			// route configuration resource, as specified by the test case.
   461  			listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
   462  			routes := []*v3routepb.RouteConfiguration{tt.routeConfig}
   463  			configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
   464  
   465  			stateCh, _, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
   466  
   467  			// Read the update pushed by the resolver to the ClientConn.
   468  			cs := verifyUpdateFromResolver(ctx, t, stateCh, tt.wantServiceConfig)
   469  
   470  			pickedClusters := make(map[string]bool)
   471  			// Odds of picking 75% cluster 100 times in a row: 1 in 3E-13.  And
   472  			// with the random number generator stubbed out, we can rely on this
   473  			// to be 100% reproducible.
   474  			for i := 0; i < 100; i++ {
   475  				res, err := cs.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: "/service/method"})
   476  				if err != nil {
   477  					t.Fatalf("cs.SelectConfig(): %v", err)
   478  				}
   479  				cluster := clustermanager.GetPickedClusterForTesting(res.Context)
   480  				pickedClusters[cluster] = true
   481  				res.OnCommitted()
   482  			}
   483  			if !cmp.Equal(pickedClusters, tt.wantClusters) {
   484  				t.Errorf("Picked clusters: %v; want: %v", pickedClusters, tt.wantClusters)
   485  			}
   486  		})
   487  	}
   488  }
   489  
   490  // Tests a case where a resolver receives a RouteConfig update with a HashPolicy
   491  // specifying to generate a hash. The configSelector generated should
   492  // successfully generate a Hash.
   493  func (s) TestResolverRequestHash(t *testing.T) {
   494  	// Spin up an xDS management server for the test.
   495  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   496  	defer cancel()
   497  	nodeID := uuid.New().String()
   498  	mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
   499  
   500  	// Configure the management server with a good listener resource and a
   501  	// route configuration resource that specifies a hash policy.
   502  	listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
   503  	routes := []*v3routepb.RouteConfiguration{{
   504  		Name: defaultTestRouteConfigName,
   505  		VirtualHosts: []*v3routepb.VirtualHost{{
   506  			Domains: []string{defaultTestServiceName},
   507  			Routes: []*v3routepb.Route{{
   508  				Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}},
   509  				Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{
   510  					ClusterSpecifier: &v3routepb.RouteAction_WeightedClusters{WeightedClusters: &v3routepb.WeightedCluster{
   511  						Clusters: []*v3routepb.WeightedCluster_ClusterWeight{
   512  							{
   513  								Name:   defaultTestClusterName,
   514  								Weight: &wrapperspb.UInt32Value{Value: 100},
   515  							},
   516  						},
   517  					}},
   518  					HashPolicy: []*v3routepb.RouteAction_HashPolicy{{
   519  						PolicySpecifier: &v3routepb.RouteAction_HashPolicy_Header_{
   520  							Header: &v3routepb.RouteAction_HashPolicy_Header{
   521  								HeaderName: ":path",
   522  							},
   523  						},
   524  						Terminal: true,
   525  					}},
   526  				}},
   527  			}},
   528  		}},
   529  	}}
   530  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
   531  
   532  	// Build the resolver and read the config selector out of it.
   533  	stateCh, _, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
   534  	cs := verifyUpdateFromResolver(ctx, t, stateCh, "")
   535  
   536  	// Selecting a config when there was a hash policy specified in the route
   537  	// that will be selected should put a request hash in the config's context.
   538  	res, err := cs.SelectConfig(iresolver.RPCInfo{
   539  		Context: metadata.NewOutgoingContext(ctx, metadata.Pairs(":path", "/products")),
   540  		Method:  "/service/method",
   541  	})
   542  	if err != nil {
   543  		t.Fatalf("cs.SelectConfig(): %v", err)
   544  	}
   545  	wantHash := xxhash.Sum64String("/products")
   546  	gotHash, ok := iringhash.XDSRequestHash(res.Context)
   547  	if !ok {
   548  		t.Fatalf("Got no request hash, want: %v", wantHash)
   549  	}
   550  	if gotHash != wantHash {
   551  		t.Fatalf("Got request hash: %v, want: %v", gotHash, wantHash)
   552  	}
   553  }
   554  
   555  // Tests the case where resources are removed from the management server,
   556  // causing it to send an empty update to the xDS client, which returns a
   557  // resource-not-found error to the xDS resolver. The test verifies that an
   558  // ongoing RPC is handled to completion when this happens.
   559  func (s) TestResolverRemovedWithRPCs(t *testing.T) {
   560  	// Spin up an xDS management server for the test.
   561  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   562  	defer cancel()
   563  	nodeID := uuid.New().String()
   564  	mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
   565  
   566  	// Configure resources on the management server.
   567  	listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
   568  	routes := []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(defaultTestRouteConfigName, defaultTestServiceName, defaultTestClusterName)}
   569  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
   570  
   571  	stateCh, _, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
   572  
   573  	// Read the update pushed by the resolver to the ClientConn.
   574  	cs := verifyUpdateFromResolver(ctx, t, stateCh, wantDefaultServiceConfig)
   575  
   576  	res, err := cs.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: "/service/method"})
   577  	if err != nil {
   578  		t.Fatalf("cs.SelectConfig(): %v", err)
   579  	}
   580  
   581  	// Delete the resources on the management server. This should result in a
   582  	// resource-not-found error from the xDS client.
   583  	if err := mgmtServer.Update(ctx, e2e.UpdateOptions{NodeID: nodeID}); err != nil {
   584  		t.Fatal(err)
   585  	}
   586  
   587  	// The RPC started earlier is still in progress. So, the xDS resolver will
   588  	// not produce an empty service config at this point. Instead it will retain
   589  	// the cluster to which the RPC is ongoing in the service config, but will
   590  	// return an erroring config selector which will fail new RPCs.
   591  	cs = verifyUpdateFromResolver(ctx, t, stateCh, wantDefaultServiceConfig)
   592  	_, err = cs.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: "/service/method"})
   593  	if err := verifyResolverError(err, codes.Unavailable, "has been removed", nodeID); err != nil {
   594  		t.Fatal(err)
   595  	}
   596  
   597  	// "Finish the RPC"; this could cause a panic if the resolver doesn't
   598  	// handle it correctly.
   599  	res.OnCommitted()
   600  
   601  	// Now that the RPC is committed, the xDS resolver is expected to send an
   602  	// update with an empty service config.
   603  	var state resolver.State
   604  	select {
   605  	case <-ctx.Done():
   606  		t.Fatalf("Timeout waiting for an update from the resolver: %v", ctx.Err())
   607  	case state = <-stateCh:
   608  		if err := state.ServiceConfig.Err; err != nil {
   609  			t.Fatalf("Received error in service config: %v", state.ServiceConfig.Err)
   610  		}
   611  		wantSCParsed := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)("{}")
   612  		if !internal.EqualServiceConfigForTesting(state.ServiceConfig.Config, wantSCParsed.Config) {
   613  			t.Fatalf("Got service config:\n%s \nWant service config:\n%s", cmp.Diff(nil, state.ServiceConfig.Config), cmp.Diff(nil, wantSCParsed.Config))
   614  		}
   615  	}
   616  
   617  	// Workaround for https://github.com/envoyproxy/go-control-plane/issues/431.
   618  	//
   619  	// The xDS client can miss route configurations due to a race condition
   620  	// between resource removal and re-addition. To avoid this, continuously
   621  	// push new versions of the resources to the server, ensuring the client
   622  	// eventually receives the configuration.
   623  	//
   624  	// TODO(https://github.com/grpc/grpc-go/issues/7807): Remove this workaround
   625  	// once the issue is fixed.
   626  waitForStateUpdate:
   627  	for {
   628  		sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   629  		defer sCancel()
   630  
   631  		configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
   632  
   633  		select {
   634  		case state = <-stateCh:
   635  			if err := state.ServiceConfig.Err; err != nil {
   636  				t.Fatalf("Received error in service config: %v", state.ServiceConfig.Err)
   637  			}
   638  			wantSCParsed := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(wantDefaultServiceConfig)
   639  			if !internal.EqualServiceConfigForTesting(state.ServiceConfig.Config, wantSCParsed.Config) {
   640  				t.Fatalf("Got service config:\n%s \nWant service config:\n%s", cmp.Diff(nil, state.ServiceConfig.Config), cmp.Diff(nil, wantSCParsed.Config))
   641  			}
   642  			break waitForStateUpdate
   643  		case <-sCtx.Done():
   644  		}
   645  	}
   646  	cs = iresolver.GetConfigSelector(state)
   647  	if cs == nil {
   648  		t.Fatal("Received nil config selector in update from resolver")
   649  	}
   650  
   651  	res, err = cs.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: "/service/method"})
   652  	if err != nil {
   653  		t.Fatalf("cs.SelectConfig(): %v", err)
   654  	}
   655  	res.OnCommitted()
   656  }
   657  
   658  // Tests the case where resources returned by the management server are removed.
   659  // The test verifies that the resolver pushes the expected config selector and
   660  // service config in this case.
   661  func (s) TestResolverRemovedResource(t *testing.T) {
   662  	// Spin up an xDS management server for the test.
   663  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   664  	defer cancel()
   665  	nodeID := uuid.New().String()
   666  	mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
   667  
   668  	// Configure resources on the management server.
   669  	listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
   670  	routes := []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(defaultTestRouteConfigName, defaultTestServiceName, defaultTestClusterName)}
   671  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
   672  
   673  	stateCh, errCh, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
   674  
   675  	// Read the update pushed by the resolver to the ClientConn.
   676  	cs := verifyUpdateFromResolver(ctx, t, stateCh, wantDefaultServiceConfig)
   677  
   678  	// "Make an RPC" by invoking the config selector.
   679  	res, err := cs.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: "/service/method"})
   680  	if err != nil {
   681  		t.Fatalf("cs.SelectConfig(): %v", err)
   682  	}
   683  
   684  	// "Finish the RPC"; this could cause a panic if the resolver doesn't
   685  	// handle it correctly.
   686  	res.OnCommitted()
   687  
   688  	// Delete the resources on the management server, resulting in a
   689  	// resource-not-found error from the xDS client.
   690  	if err := mgmtServer.Update(ctx, e2e.UpdateOptions{NodeID: nodeID}); err != nil {
   691  		t.Fatal(err)
   692  	}
   693  
   694  	// The channel should receive the existing service config with the original
   695  	// cluster but with an erroring config selector.
   696  	cs = verifyUpdateFromResolver(ctx, t, stateCh, wantDefaultServiceConfig)
   697  
   698  	// "Make another RPC" by invoking the config selector.
   699  	_, err = cs.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: "/service/method"})
   700  	if err := verifyResolverError(err, codes.Unavailable, "has been removed", nodeID); err != nil {
   701  		t.Fatal(err)
   702  	}
   703  
   704  	// In the meantime, an empty ServiceConfig update should have been sent.
   705  	var state resolver.State
   706  	select {
   707  	case <-ctx.Done():
   708  		t.Fatalf("Timeout waiting for an update from the resolver: %v", ctx.Err())
   709  	case state = <-stateCh:
   710  		if err := state.ServiceConfig.Err; err != nil {
   711  			t.Fatalf("Received error in service config: %v", state.ServiceConfig.Err)
   712  		}
   713  		wantSCParsed := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)("{}")
   714  		if !internal.EqualServiceConfigForTesting(state.ServiceConfig.Config, wantSCParsed.Config) {
   715  			t.Fatalf("Got service config:\n%s \nWant service config:\n%s", cmp.Diff(nil, state.ServiceConfig.Config), cmp.Diff(nil, wantSCParsed.Config))
   716  		}
   717  	}
   718  
   719  	// The xDS resolver is expected to report an error to the channel.
   720  	select {
   721  	case <-ctx.Done():
   722  		t.Fatalf("Timeout waiting for an error from the resolver: %v", ctx.Err())
   723  	case err := <-errCh:
   724  		if err := verifyResolverError(err, codes.Unavailable, "has been removed", nodeID); err != nil {
   725  			t.Fatal(err)
   726  		}
   727  	}
   728  }
   729  
   730  // Tests the case where the resolver receives max stream duration as part of the
   731  // listener and route configuration resources.  The test verifies that the RPC
   732  // timeout returned by the config selector matches expectations. A non-nil max
   733  // stream duration (this includes an explicit zero value) in a matching route
   734  // overrides the value specified in the listener resource.
   735  func (s) TestResolverMaxStreamDuration(t *testing.T) {
   736  	// Spin up an xDS management server for the test.
   737  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   738  	defer cancel()
   739  	nodeID := uuid.New().String()
   740  	mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
   741  
   742  	stateCh, _, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
   743  
   744  	// Configure the management server with a listener resource that specifies a
   745  	// max stream duration as part of its HTTP connection manager. Also
   746  	// configure a route configuration resource, which has multiple routes with
   747  	// different values of max stream duration.
   748  	hcm := testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   749  		RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{Rds: &v3httppb.Rds{
   750  			ConfigSource: &v3corepb.ConfigSource{
   751  				ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
   752  			},
   753  			RouteConfigName: defaultTestRouteConfigName,
   754  		}},
   755  		HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
   756  		CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
   757  			MaxStreamDuration: durationpb.New(1 * time.Second),
   758  		},
   759  	})
   760  	listeners := []*v3listenerpb.Listener{{
   761  		Name:        defaultTestServiceName,
   762  		ApiListener: &v3listenerpb.ApiListener{ApiListener: hcm},
   763  		FilterChains: []*v3listenerpb.FilterChain{{
   764  			Name: "filter-chain-name",
   765  			Filters: []*v3listenerpb.Filter{{
   766  				Name:       wellknown.HTTPConnectionManager,
   767  				ConfigType: &v3listenerpb.Filter_TypedConfig{TypedConfig: hcm},
   768  			}},
   769  		}},
   770  	}}
   771  	routes := []*v3routepb.RouteConfiguration{{
   772  		Name: defaultTestRouteConfigName,
   773  		VirtualHosts: []*v3routepb.VirtualHost{{
   774  			Domains: []string{defaultTestServiceName},
   775  			Routes: []*v3routepb.Route{
   776  				{
   777  					Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/foo"}},
   778  					Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{
   779  						ClusterSpecifier: &v3routepb.RouteAction_WeightedClusters{WeightedClusters: &v3routepb.WeightedCluster{
   780  							Clusters: []*v3routepb.WeightedCluster_ClusterWeight{
   781  								{
   782  									Name:   "A",
   783  									Weight: &wrapperspb.UInt32Value{Value: 100},
   784  								},
   785  							}},
   786  						},
   787  						MaxStreamDuration: &v3routepb.RouteAction_MaxStreamDuration{
   788  							MaxStreamDuration: durationpb.New(5 * time.Second),
   789  						},
   790  					}},
   791  				},
   792  				{
   793  					Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/bar"}},
   794  					Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{
   795  						ClusterSpecifier: &v3routepb.RouteAction_WeightedClusters{WeightedClusters: &v3routepb.WeightedCluster{
   796  							Clusters: []*v3routepb.WeightedCluster_ClusterWeight{
   797  								{
   798  									Name:   "B",
   799  									Weight: &wrapperspb.UInt32Value{Value: 100},
   800  								},
   801  							}},
   802  						},
   803  						MaxStreamDuration: &v3routepb.RouteAction_MaxStreamDuration{
   804  							MaxStreamDuration: durationpb.New(0 * time.Second),
   805  						},
   806  					}},
   807  				},
   808  				{
   809  					Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}},
   810  					Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{
   811  						ClusterSpecifier: &v3routepb.RouteAction_WeightedClusters{WeightedClusters: &v3routepb.WeightedCluster{
   812  							Clusters: []*v3routepb.WeightedCluster_ClusterWeight{
   813  								{
   814  									Name:   "C",
   815  									Weight: &wrapperspb.UInt32Value{Value: 100},
   816  								},
   817  							}},
   818  						},
   819  					}},
   820  				},
   821  			},
   822  		}},
   823  	}}
   824  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
   825  
   826  	// Read the update pushed by the resolver to the ClientConn.
   827  	cs := verifyUpdateFromResolver(ctx, t, stateCh, "")
   828  
   829  	testCases := []struct {
   830  		name   string
   831  		method string
   832  		want   *time.Duration
   833  	}{{
   834  		name:   "RDS setting",
   835  		method: "/foo/method",
   836  		want:   newDurationP(5 * time.Second),
   837  	}, {
   838  		name:   "explicit zero in RDS; ignore LDS",
   839  		method: "/bar/method",
   840  		want:   nil,
   841  	}, {
   842  		name:   "no config in RDS; fallback to LDS",
   843  		method: "/baz/method",
   844  		want:   newDurationP(time.Second),
   845  	}}
   846  
   847  	for _, tc := range testCases {
   848  		t.Run(tc.name, func(t *testing.T) {
   849  			req := iresolver.RPCInfo{
   850  				Method:  tc.method,
   851  				Context: ctx,
   852  			}
   853  			res, err := cs.SelectConfig(req)
   854  			if err != nil {
   855  				t.Errorf("cs.SelectConfig(%v): %v", req, err)
   856  				return
   857  			}
   858  			res.OnCommitted()
   859  			got := res.MethodConfig.Timeout
   860  			if !cmp.Equal(got, tc.want) {
   861  				t.Errorf("For method %q: res.MethodConfig.Timeout = %v; want %v", tc.method, got, tc.want)
   862  			}
   863  		})
   864  	}
   865  }
   866  
   867  // Tests that clusters remain in service config if RPCs are in flight.
   868  func (s) TestResolverDelayedOnCommitted(t *testing.T) {
   869  	// Spin up an xDS management server for the test.
   870  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   871  	defer cancel()
   872  	nodeID := uuid.New().String()
   873  	mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
   874  
   875  	// Configure resources on the management server.
   876  	listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
   877  	routes := []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(defaultTestRouteConfigName, defaultTestServiceName, defaultTestClusterName)}
   878  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
   879  
   880  	stateCh, _, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
   881  
   882  	// Read the update pushed by the resolver to the ClientConn.
   883  	cs := verifyUpdateFromResolver(ctx, t, stateCh, wantDefaultServiceConfig)
   884  
   885  	// Make an RPC, but do not commit it yet.
   886  	resOld, err := cs.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: "/service/method"})
   887  	if err != nil {
   888  		t.Fatalf("cs.SelectConfig(): %v", err)
   889  	}
   890  	wantClusterName := fmt.Sprintf("cluster:%s", defaultTestClusterName)
   891  	if cluster := clustermanager.GetPickedClusterForTesting(resOld.Context); cluster != wantClusterName {
   892  		t.Fatalf("Picked cluster is %q, want %q", cluster, wantClusterName)
   893  	}
   894  
   895  	// Delay resOld.OnCommitted(). As long as there are pending RPCs to removed
   896  	// clusters, they still appear in the service config.
   897  
   898  	// Update the route configuration resource on the management server to
   899  	// return a new cluster.
   900  	newClusterName := "new-" + defaultTestClusterName
   901  	routes = []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(defaultTestRouteConfigName, defaultTestServiceName, newClusterName)}
   902  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
   903  
   904  	// Read the update pushed by the resolver to the ClientConn and ensure the
   905  	// old cluster is present in the service config. Also ensure that the newly
   906  	// returned config selector does not hold a reference to the old cluster.
   907  	wantSC := fmt.Sprintf(`
   908  {
   909  	"loadBalancingConfig": [
   910  		{
   911  		  "xds_cluster_manager_experimental": {
   912  			"children": {
   913  			  "cluster:%s": {
   914  				"childPolicy": [
   915  				  {
   916  					"cds_experimental": {
   917  					  "cluster": "%s"
   918  					}
   919  				  }
   920  				]
   921  			  },
   922  			  "cluster:%s": {
   923  				"childPolicy": [
   924  				  {
   925  					"cds_experimental": {
   926  					  "cluster": "%s"
   927  					}
   928  				  }
   929  				]
   930  			  }
   931  			}
   932  		  }
   933  		}
   934  	  ]
   935  }`, defaultTestClusterName, defaultTestClusterName, newClusterName, newClusterName)
   936  	cs = verifyUpdateFromResolver(ctx, t, stateCh, wantSC)
   937  
   938  	resNew, err := cs.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: "/service/method"})
   939  	if err != nil {
   940  		t.Fatalf("cs.SelectConfig(): %v", err)
   941  	}
   942  	wantClusterName = fmt.Sprintf("cluster:%s", newClusterName)
   943  	if cluster := clustermanager.GetPickedClusterForTesting(resNew.Context); cluster != wantClusterName {
   944  		t.Fatalf("Picked cluster is %q, want %q", cluster, wantClusterName)
   945  	}
   946  
   947  	// Invoke OnCommitted on the old RPC; should lead to a service config update
   948  	// that deletes the old cluster, as the old cluster no longer has any
   949  	// pending RPCs.
   950  	resOld.OnCommitted()
   951  
   952  	wantSC = fmt.Sprintf(`
   953  {
   954  	"loadBalancingConfig": [
   955  		{
   956  		  "xds_cluster_manager_experimental": {
   957  			"children": {
   958  			  "cluster:%s": {
   959  				"childPolicy": [
   960  				  {
   961  					"cds_experimental": {
   962  					  "cluster": "%s"
   963  					}
   964  				  }
   965  				]
   966  			  }
   967  			}
   968  		  }
   969  		}
   970  	  ]
   971  }`, newClusterName, newClusterName)
   972  	verifyUpdateFromResolver(ctx, t, stateCh, wantSC)
   973  }
   974  
   975  // Tests the case where two LDS updates with the same RDS name to watch are
   976  // received without an RDS in between. Those LDS updates shouldn't trigger a
   977  // service config update.
   978  func (s) TestResolverMultipleLDSUpdates(t *testing.T) {
   979  	// Spin up an xDS management server for the test.
   980  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   981  	defer cancel()
   982  	nodeID := uuid.New().String()
   983  	mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
   984  
   985  	// Configure the management server with a listener resource, but no route
   986  	// configuration resource.
   987  	listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
   988  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, nil)
   989  
   990  	stateCh, _, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
   991  
   992  	// Ensure there is no update from the resolver.
   993  	verifyNoUpdateFromResolver(ctx, t, stateCh)
   994  
   995  	// Configure the management server with a listener resource that points to
   996  	// the same route configuration resource but has different values for max
   997  	// stream duration field. There is still no route configuration resource on
   998  	// the management server.
   999  	hcm := testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1000  		RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{Rds: &v3httppb.Rds{
  1001  			ConfigSource: &v3corepb.ConfigSource{
  1002  				ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
  1003  			},
  1004  			RouteConfigName: defaultTestRouteConfigName,
  1005  		}},
  1006  		HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
  1007  		CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
  1008  			MaxStreamDuration: durationpb.New(1 * time.Second),
  1009  		},
  1010  	})
  1011  	listeners = []*v3listenerpb.Listener{{
  1012  		Name:        defaultTestServiceName,
  1013  		ApiListener: &v3listenerpb.ApiListener{ApiListener: hcm},
  1014  		FilterChains: []*v3listenerpb.FilterChain{{
  1015  			Name: "filter-chain-name",
  1016  			Filters: []*v3listenerpb.Filter{{
  1017  				Name:       wellknown.HTTPConnectionManager,
  1018  				ConfigType: &v3listenerpb.Filter_TypedConfig{TypedConfig: hcm},
  1019  			}},
  1020  		}},
  1021  	}}
  1022  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, nil)
  1023  
  1024  	// Ensure that there is no update from the resolver.
  1025  	verifyNoUpdateFromResolver(ctx, t, stateCh)
  1026  }
  1027  
  1028  // TestResolverWRR tests the case where the route configuration returned by the
  1029  // management server contains a set of weighted clusters. The test performs a
  1030  // bunch of RPCs using the cluster specifier returned by the resolver, and
  1031  // verifies the cluster distribution.
  1032  func (s) TestResolverWRR(t *testing.T) {
  1033  	origNewWRR := rinternal.NewWRR
  1034  	rinternal.NewWRR = testutils.NewTestWRR
  1035  	defer func() { rinternal.NewWRR = origNewWRR }()
  1036  
  1037  	// Spin up an xDS management server for the test.
  1038  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1039  	defer cancel()
  1040  	nodeID := uuid.New().String()
  1041  	mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
  1042  
  1043  	stateCh, _, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
  1044  
  1045  	// Configure resources on the management server.
  1046  	listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
  1047  	routes := []*v3routepb.RouteConfiguration{e2e.RouteConfigResourceWithOptions(e2e.RouteConfigOptions{
  1048  		RouteConfigName:      defaultTestRouteConfigName,
  1049  		ListenerName:         defaultTestServiceName,
  1050  		ClusterSpecifierType: e2e.RouteConfigClusterSpecifierTypeWeightedCluster,
  1051  		WeightedClusters:     map[string]int{"A": 75, "B": 25},
  1052  	})}
  1053  	configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
  1054  
  1055  	// Read the update pushed by the resolver to the ClientConn.
  1056  	cs := verifyUpdateFromResolver(ctx, t, stateCh, "")
  1057  
  1058  	// Make RPCs to verify WRR behavior in the cluster specifier.
  1059  	picks := map[string]int{}
  1060  	for i := 0; i < 100; i++ {
  1061  		res, err := cs.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: "/service/method"})
  1062  		if err != nil {
  1063  			t.Fatalf("cs.SelectConfig(): %v", err)
  1064  		}
  1065  		picks[clustermanager.GetPickedClusterForTesting(res.Context)]++
  1066  		res.OnCommitted()
  1067  	}
  1068  	want := map[string]int{"cluster:A": 75, "cluster:B": 25}
  1069  	if !cmp.Equal(picks, want) {
  1070  		t.Errorf("Picked clusters: %v; want: %v", picks, want)
  1071  	}
  1072  }
  1073  
  1074  const filterCfgPathFieldName = "path"
  1075  const filterCfgErrorFieldName = "new_stream_error"
  1076  
  1077  type filterCfg struct {
  1078  	httpfilter.FilterConfig
  1079  	path         string
  1080  	newStreamErr error
  1081  }
  1082  
  1083  type filterBuilder struct {
  1084  	paths   []string
  1085  	typeURL string
  1086  }
  1087  
  1088  func (fb *filterBuilder) TypeURLs() []string { return []string{fb.typeURL} }
  1089  
  1090  func filterConfigFromProto(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1091  	ts, ok := cfg.(*v3xdsxdstypepb.TypedStruct)
  1092  	if !ok {
  1093  		return nil, fmt.Errorf("unsupported filter config type: %T, want %T", cfg, &v3xdsxdstypepb.TypedStruct{})
  1094  	}
  1095  
  1096  	if ts.GetValue() == nil {
  1097  		return filterCfg{}, nil
  1098  	}
  1099  	ret := filterCfg{}
  1100  	if v := ts.GetValue().GetFields()[filterCfgPathFieldName]; v != nil {
  1101  		ret.path = v.GetStringValue()
  1102  	}
  1103  	if v := ts.GetValue().GetFields()[filterCfgErrorFieldName]; v != nil {
  1104  		if v.GetStringValue() == "" {
  1105  			ret.newStreamErr = nil
  1106  		} else {
  1107  			ret.newStreamErr = fmt.Errorf("%s", v.GetStringValue())
  1108  		}
  1109  	}
  1110  	return ret, nil
  1111  }
  1112  
  1113  func (*filterBuilder) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1114  	return filterConfigFromProto(cfg)
  1115  }
  1116  
  1117  func (*filterBuilder) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1118  	return filterConfigFromProto(override)
  1119  }
  1120  
  1121  func (*filterBuilder) IsTerminal() bool { return false }
  1122  
  1123  var _ httpfilter.ClientInterceptorBuilder = &filterBuilder{}
  1124  
  1125  func (fb *filterBuilder) BuildClientInterceptor(config, override httpfilter.FilterConfig) (iresolver.ClientInterceptor, error) {
  1126  	if config == nil {
  1127  		panic("unexpected missing config")
  1128  	}
  1129  
  1130  	fi := &filterInterceptor{
  1131  		parent: fb,
  1132  		pathCh: make(chan string, 10),
  1133  	}
  1134  
  1135  	fb.paths = append(fb.paths, "build:"+config.(filterCfg).path)
  1136  	err := config.(filterCfg).newStreamErr
  1137  	if override != nil {
  1138  		fb.paths = append(fb.paths, "override:"+override.(filterCfg).path)
  1139  		err = override.(filterCfg).newStreamErr
  1140  	}
  1141  
  1142  	fi.cfgPath = config.(filterCfg).path
  1143  	fi.err = err
  1144  	return fi, nil
  1145  }
  1146  
  1147  type filterInterceptor struct {
  1148  	parent  *filterBuilder
  1149  	pathCh  chan string
  1150  	cfgPath string
  1151  	err     error
  1152  }
  1153  
  1154  func (fi *filterInterceptor) NewStream(ctx context.Context, _ iresolver.RPCInfo, done func(), newStream func(ctx context.Context, done func()) (iresolver.ClientStream, error)) (iresolver.ClientStream, error) {
  1155  	fi.parent.paths = append(fi.parent.paths, "newstream:"+fi.cfgPath)
  1156  	if fi.err != nil {
  1157  		return nil, fi.err
  1158  	}
  1159  	d := func() {
  1160  		fi.parent.paths = append(fi.parent.paths, "done:"+fi.cfgPath)
  1161  		done()
  1162  	}
  1163  	cs, err := newStream(ctx, d)
  1164  	if err != nil {
  1165  		return nil, err
  1166  	}
  1167  	return &clientStream{ClientStream: cs}, nil
  1168  }
  1169  
  1170  type clientStream struct {
  1171  	iresolver.ClientStream
  1172  }
  1173  
  1174  func (s) TestConfigSelector_FailureCases(t *testing.T) {
  1175  	const methodName = "1"
  1176  
  1177  	tests := []struct {
  1178  		name     string
  1179  		listener *v3listenerpb.Listener
  1180  		wantErr  string
  1181  	}{
  1182  		{
  1183  			name: "route type RouteActionUnsupported invalid for client",
  1184  			listener: &v3listenerpb.Listener{
  1185  				Name: defaultTestServiceName,
  1186  				ApiListener: &v3listenerpb.ApiListener{
  1187  					ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1188  						RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1189  							RouteConfig: &v3routepb.RouteConfiguration{
  1190  								Name: defaultTestRouteConfigName,
  1191  								VirtualHosts: []*v3routepb.VirtualHost{{
  1192  									Domains: []string{defaultTestServiceName},
  1193  									Routes: []*v3routepb.Route{{
  1194  										Match: &v3routepb.RouteMatch{
  1195  											PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: methodName},
  1196  										},
  1197  										Action: &v3routepb.Route_FilterAction{},
  1198  									}},
  1199  								}},
  1200  							}},
  1201  						HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
  1202  					}),
  1203  				},
  1204  			},
  1205  			wantErr: "matched route does not have a supported route action type",
  1206  		},
  1207  		{
  1208  			name: "route type RouteActionNonForwardingAction invalid for client",
  1209  			listener: &v3listenerpb.Listener{
  1210  				Name: defaultTestServiceName,
  1211  				ApiListener: &v3listenerpb.ApiListener{
  1212  					ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1213  						RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1214  							RouteConfig: &v3routepb.RouteConfiguration{
  1215  								Name: defaultTestRouteConfigName,
  1216  								VirtualHosts: []*v3routepb.VirtualHost{{
  1217  									Domains: []string{defaultTestServiceName},
  1218  									Routes: []*v3routepb.Route{{
  1219  										Match: &v3routepb.RouteMatch{
  1220  											PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: methodName},
  1221  										},
  1222  										Action: &v3routepb.Route_NonForwardingAction{},
  1223  									}},
  1224  								}},
  1225  							}},
  1226  						HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
  1227  					}),
  1228  				},
  1229  			},
  1230  			wantErr: "matched route does not have a supported route action type",
  1231  		},
  1232  	}
  1233  
  1234  	for _, test := range tests {
  1235  		t.Run(test.name, func(t *testing.T) {
  1236  			// Spin up an xDS management server.
  1237  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1238  			defer cancel()
  1239  			nodeID := uuid.New().String()
  1240  			mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
  1241  
  1242  			// Build an xDS resolver.
  1243  			stateCh, _, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
  1244  
  1245  			// Update the management server with a listener resource that
  1246  			// contains inline route configuration.
  1247  			configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, []*v3listenerpb.Listener{test.listener}, nil)
  1248  
  1249  			// Ensure that the resolver pushes a state update to the channel.
  1250  			cs := verifyUpdateFromResolver(ctx, t, stateCh, "")
  1251  
  1252  			// Ensure that it returns the expected error.
  1253  			_, err := cs.SelectConfig(iresolver.RPCInfo{Method: methodName, Context: ctx})
  1254  			if err := verifyResolverError(err, codes.Unavailable, test.wantErr, nodeID); err != nil {
  1255  				t.Fatal(err)
  1256  			}
  1257  		})
  1258  	}
  1259  }
  1260  
  1261  func newHTTPFilter(t *testing.T, name, typeURL, path, err string) *v3httppb.HttpFilter {
  1262  	return &v3httppb.HttpFilter{
  1263  		Name: name,
  1264  		ConfigType: &v3httppb.HttpFilter_TypedConfig{
  1265  			TypedConfig: testutils.MarshalAny(t, &v3xdsxdstypepb.TypedStruct{
  1266  				TypeUrl: typeURL,
  1267  				Value: &structpb.Struct{
  1268  					Fields: map[string]*structpb.Value{
  1269  						filterCfgPathFieldName:  {Kind: &structpb.Value_StringValue{StringValue: path}},
  1270  						filterCfgErrorFieldName: {Kind: &structpb.Value_StringValue{StringValue: err}},
  1271  					},
  1272  				},
  1273  			}),
  1274  		},
  1275  	}
  1276  }
  1277  
  1278  func (s) TestXDSResolverHTTPFilters(t *testing.T) {
  1279  	const methodName1 = "1"
  1280  	const methodName2 = "2"
  1281  	testFilterName := t.Name()
  1282  
  1283  	testCases := []struct {
  1284  		name          string
  1285  		listener      *v3listenerpb.Listener
  1286  		rpcRes        map[string][][]string
  1287  		wantStreamErr string
  1288  	}{
  1289  		{
  1290  			name: "NewStream error - ensure earlier interceptor Done is still called",
  1291  			listener: &v3listenerpb.Listener{
  1292  				Name: defaultTestServiceName,
  1293  				ApiListener: &v3listenerpb.ApiListener{
  1294  					ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1295  						RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1296  							RouteConfig: &v3routepb.RouteConfiguration{
  1297  								Name: defaultTestRouteConfigName,
  1298  								VirtualHosts: []*v3routepb.VirtualHost{{
  1299  									Domains: []string{defaultTestServiceName},
  1300  									Routes: []*v3routepb.Route{{
  1301  										Match: &v3routepb.RouteMatch{
  1302  											PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: methodName1},
  1303  										},
  1304  										Action: &v3routepb.Route_Route{
  1305  											Route: &v3routepb.RouteAction{
  1306  												ClusterSpecifier: &v3routepb.RouteAction_WeightedClusters{
  1307  													WeightedClusters: &v3routepb.WeightedCluster{
  1308  														Clusters: []*v3routepb.WeightedCluster_ClusterWeight{
  1309  															{Name: "A", Weight: wrapperspb.UInt32(1)},
  1310  															{Name: "B", Weight: wrapperspb.UInt32(1)},
  1311  														},
  1312  													},
  1313  												},
  1314  											},
  1315  										},
  1316  									}},
  1317  								}},
  1318  							}},
  1319  						HttpFilters: []*v3httppb.HttpFilter{
  1320  							newHTTPFilter(t, "foo", testFilterName, "foo1", ""),
  1321  							newHTTPFilter(t, "bar", testFilterName, "bar1", "bar newstream err"),
  1322  							e2e.RouterHTTPFilter,
  1323  						},
  1324  					}),
  1325  				},
  1326  			},
  1327  			rpcRes: map[string][][]string{
  1328  				methodName1: {
  1329  					{"build:foo1", "build:bar1", "newstream:foo1", "newstream:bar1", "done:foo1"}, // err in bar1 NewStream()
  1330  				},
  1331  			},
  1332  			wantStreamErr: "bar newstream err",
  1333  		},
  1334  		{
  1335  			name: "all overrides",
  1336  			listener: &v3listenerpb.Listener{
  1337  				Name: defaultTestServiceName,
  1338  				ApiListener: &v3listenerpb.ApiListener{
  1339  					ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1340  						RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1341  							RouteConfig: &v3routepb.RouteConfiguration{
  1342  								Name: defaultTestRouteConfigName,
  1343  								VirtualHosts: []*v3routepb.VirtualHost{{
  1344  									Domains: []string{defaultTestServiceName},
  1345  									Routes: []*v3routepb.Route{
  1346  										{
  1347  											Match: &v3routepb.RouteMatch{
  1348  												PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: methodName1},
  1349  											},
  1350  											Action: &v3routepb.Route_Route{
  1351  												Route: &v3routepb.RouteAction{
  1352  													ClusterSpecifier: &v3routepb.RouteAction_WeightedClusters{
  1353  														WeightedClusters: &v3routepb.WeightedCluster{
  1354  															Clusters: []*v3routepb.WeightedCluster_ClusterWeight{
  1355  																{Name: "A", Weight: wrapperspb.UInt32(1)},
  1356  																{Name: "B", Weight: wrapperspb.UInt32(1)},
  1357  															},
  1358  														},
  1359  													},
  1360  												},
  1361  											},
  1362  										},
  1363  										{
  1364  											Match: &v3routepb.RouteMatch{
  1365  												PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: methodName2},
  1366  											},
  1367  											Action: &v3routepb.Route_Route{
  1368  												Route: &v3routepb.RouteAction{
  1369  													ClusterSpecifier: &v3routepb.RouteAction_WeightedClusters{
  1370  														WeightedClusters: &v3routepb.WeightedCluster{
  1371  															Clusters: []*v3routepb.WeightedCluster_ClusterWeight{
  1372  																{Name: "A", Weight: wrapperspb.UInt32(1)},
  1373  																{
  1374  																	Name:   "B",
  1375  																	Weight: wrapperspb.UInt32(1),
  1376  																	TypedPerFilterConfig: map[string]*anypb.Any{
  1377  																		"foo": testutils.MarshalAny(t, &v3xdsxdstypepb.TypedStruct{
  1378  																			TypeUrl: testFilterName,
  1379  																			Value: &structpb.Struct{
  1380  																				Fields: map[string]*structpb.Value{
  1381  																					filterCfgPathFieldName: {Kind: &structpb.Value_StringValue{StringValue: "foo4"}},
  1382  																				},
  1383  																			},
  1384  																		}),
  1385  																		"bar": testutils.MarshalAny(t, &v3xdsxdstypepb.TypedStruct{
  1386  																			TypeUrl: testFilterName,
  1387  																			Value: &structpb.Struct{
  1388  																				Fields: map[string]*structpb.Value{
  1389  																					filterCfgPathFieldName: {Kind: &structpb.Value_StringValue{StringValue: "bar4"}},
  1390  																				},
  1391  																			},
  1392  																		}),
  1393  																	},
  1394  																},
  1395  															},
  1396  														},
  1397  													},
  1398  												},
  1399  											},
  1400  											TypedPerFilterConfig: map[string]*anypb.Any{
  1401  												"foo": testutils.MarshalAny(t, &v3xdsxdstypepb.TypedStruct{
  1402  													TypeUrl: testFilterName,
  1403  													Value: &structpb.Struct{
  1404  														Fields: map[string]*structpb.Value{
  1405  															filterCfgPathFieldName:  {Kind: &structpb.Value_StringValue{StringValue: "foo3"}},
  1406  															filterCfgErrorFieldName: {Kind: &structpb.Value_StringValue{StringValue: ""}},
  1407  														},
  1408  													},
  1409  												}),
  1410  												"bar": testutils.MarshalAny(t, &v3xdsxdstypepb.TypedStruct{
  1411  													TypeUrl: testFilterName,
  1412  													Value: &structpb.Struct{
  1413  														Fields: map[string]*structpb.Value{
  1414  															filterCfgPathFieldName: {Kind: &structpb.Value_StringValue{StringValue: "bar3"}},
  1415  														},
  1416  													},
  1417  												}),
  1418  											},
  1419  										},
  1420  									},
  1421  									TypedPerFilterConfig: map[string]*anypb.Any{
  1422  										"foo": testutils.MarshalAny(t, &v3xdsxdstypepb.TypedStruct{
  1423  											TypeUrl: testFilterName,
  1424  											Value: &structpb.Struct{
  1425  												Fields: map[string]*structpb.Value{
  1426  													filterCfgPathFieldName:  {Kind: &structpb.Value_StringValue{StringValue: "foo2"}},
  1427  													filterCfgErrorFieldName: {Kind: &structpb.Value_StringValue{StringValue: ""}},
  1428  												},
  1429  											},
  1430  										}),
  1431  										"bar": testutils.MarshalAny(t, &v3xdsxdstypepb.TypedStruct{
  1432  											TypeUrl: testFilterName,
  1433  											Value: &structpb.Struct{
  1434  												Fields: map[string]*structpb.Value{
  1435  													filterCfgPathFieldName: {Kind: &structpb.Value_StringValue{StringValue: "bar2"}},
  1436  												},
  1437  											},
  1438  										}),
  1439  									},
  1440  								}},
  1441  							}},
  1442  						HttpFilters: []*v3httppb.HttpFilter{
  1443  							newHTTPFilter(t, "foo", testFilterName, "foo1", "this is overridden to nil"),
  1444  							newHTTPFilter(t, "bar", testFilterName, "bar1", ""),
  1445  							e2e.RouterHTTPFilter,
  1446  						},
  1447  					}),
  1448  				},
  1449  			},
  1450  			rpcRes: map[string][][]string{
  1451  				methodName1: {
  1452  					{"build:foo1", "override:foo2", "build:bar1", "override:bar2", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1453  					{"build:foo1", "override:foo2", "build:bar1", "override:bar2", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1454  				},
  1455  				methodName2: {
  1456  					{"build:foo1", "override:foo3", "build:bar1", "override:bar3", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1457  					{"build:foo1", "override:foo4", "build:bar1", "override:bar4", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1458  					{"build:foo1", "override:foo3", "build:bar1", "override:bar3", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1459  					{"build:foo1", "override:foo4", "build:bar1", "override:bar4", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1460  				},
  1461  			},
  1462  		},
  1463  	}
  1464  
  1465  	for _, tc := range testCases {
  1466  		t.Run(tc.name, func(t *testing.T) {
  1467  			origNewWRR := rinternal.NewWRR
  1468  			rinternal.NewWRR = testutils.NewTestWRR
  1469  			defer func() { rinternal.NewWRR = origNewWRR }()
  1470  
  1471  			// Register a custom httpFilter builder for the test.
  1472  			fb := &filterBuilder{typeURL: testFilterName}
  1473  			httpfilter.Register(fb)
  1474  
  1475  			// Spin up an xDS management server.
  1476  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1477  			defer cancel()
  1478  			nodeID := uuid.New().String()
  1479  			mgmtServer, _, _, bc := setupManagementServerForTest(t, nodeID)
  1480  
  1481  			// Build an xDS resolver.
  1482  			stateCh, _, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
  1483  
  1484  			// Update the management server with a listener resource that
  1485  			// contains an inline route configuration.
  1486  			configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, []*v3listenerpb.Listener{tc.listener}, nil)
  1487  
  1488  			// Ensure that the resolver pushes a state update to the channel.
  1489  			cs := verifyUpdateFromResolver(ctx, t, stateCh, "")
  1490  
  1491  			for method, wants := range tc.rpcRes {
  1492  				// Order of wants is non-deterministic.
  1493  				remainingWant := make([][]string, len(wants))
  1494  				copy(remainingWant, wants)
  1495  				for n := range wants {
  1496  					res, err := cs.SelectConfig(iresolver.RPCInfo{Method: method, Context: ctx})
  1497  					if err != nil {
  1498  						t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
  1499  					}
  1500  
  1501  					var doneFunc func()
  1502  					_, err = res.Interceptor.NewStream(ctx, iresolver.RPCInfo{}, func() {}, func(_ context.Context, done func()) (iresolver.ClientStream, error) {
  1503  						doneFunc = done
  1504  						return nil, nil
  1505  					})
  1506  					if tc.wantStreamErr != "" {
  1507  						if err == nil || !strings.Contains(err.Error(), tc.wantStreamErr) {
  1508  							t.Errorf("NewStream(...) = _, %v; want _, Contains(%v)", err, tc.wantStreamErr)
  1509  						}
  1510  						if err == nil {
  1511  							res.OnCommitted()
  1512  							doneFunc()
  1513  						}
  1514  						continue
  1515  					}
  1516  					if err != nil {
  1517  						t.Fatalf("unexpected error from Interceptor.NewStream: %v", err)
  1518  
  1519  					}
  1520  					res.OnCommitted()
  1521  					doneFunc()
  1522  
  1523  					gotPaths := fb.paths
  1524  					fb.paths = []string{}
  1525  
  1526  					// Confirm the desired path is found in remainingWant, and remove it.
  1527  					pass := false
  1528  					for i := range remainingWant {
  1529  						if cmp.Equal(gotPaths, remainingWant[i]) {
  1530  							remainingWant[i] = remainingWant[len(remainingWant)-1]
  1531  							remainingWant = remainingWant[:len(remainingWant)-1]
  1532  							pass = true
  1533  							break
  1534  						}
  1535  					}
  1536  					if !pass {
  1537  						t.Errorf("%q:%v - path:\n%v\nwant one of:\n%v", method, n, gotPaths, remainingWant)
  1538  					}
  1539  				}
  1540  			}
  1541  		})
  1542  	}
  1543  }
  1544  
  1545  func newDurationP(d time.Duration) *time.Duration {
  1546  	return &d
  1547  }