google.golang.org/grpc@v1.74.2/xds/internal/balancer/cdsbalancer/cdsbalancer_test.go (about)

     1  /*
     2   * Copyright 2019 gRPC authors.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cdsbalancer
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/google/go-cmp/cmp"
    29  	"github.com/google/uuid"
    30  	"google.golang.org/grpc"
    31  	"google.golang.org/grpc/balancer"
    32  	"google.golang.org/grpc/codes"
    33  	"google.golang.org/grpc/connectivity"
    34  	"google.golang.org/grpc/credentials/insecure"
    35  	"google.golang.org/grpc/internal"
    36  	"google.golang.org/grpc/internal/balancer/stub"
    37  	"google.golang.org/grpc/internal/grpctest"
    38  	"google.golang.org/grpc/internal/stubserver"
    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/resolver"
    43  	"google.golang.org/grpc/resolver/manual"
    44  	"google.golang.org/grpc/serviceconfig"
    45  	"google.golang.org/grpc/status"
    46  	xdsinternal "google.golang.org/grpc/xds/internal"
    47  	"google.golang.org/grpc/xds/internal/balancer/clusterresolver"
    48  	"google.golang.org/grpc/xds/internal/xdsclient"
    49  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource"
    50  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"
    51  	"google.golang.org/protobuf/types/known/durationpb"
    52  	"google.golang.org/protobuf/types/known/wrapperspb"
    53  
    54  	v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
    55  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    56  	v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
    57  	v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    58  	testgrpc "google.golang.org/grpc/interop/grpc_testing"
    59  	testpb "google.golang.org/grpc/interop/grpc_testing"
    60  
    61  	_ "google.golang.org/grpc/balancer/ringhash" // Register the ring_hash LB policy
    62  )
    63  
    64  const (
    65  	clusterName             = "cluster1"
    66  	edsClusterName          = clusterName + "-eds"
    67  	dnsClusterName          = clusterName + "-dns"
    68  	serviceName             = "service1"
    69  	dnsHostName             = "dns_host"
    70  	dnsPort                 = uint32(8080)
    71  	defaultTestTimeout      = 5 * time.Second
    72  	defaultTestShortTimeout = 10 * time.Millisecond // For events expected to *not* happen.
    73  )
    74  
    75  type s struct {
    76  	grpctest.Tester
    77  }
    78  
    79  func Test(t *testing.T) {
    80  	grpctest.RunSubTests(t, s{})
    81  }
    82  
    83  func waitForResourceNames(ctx context.Context, resourceNamesCh chan []string, wantNames []string) error {
    84  	for ctx.Err() == nil {
    85  		select {
    86  		case <-ctx.Done():
    87  		case gotNames := <-resourceNamesCh:
    88  			if cmp.Equal(gotNames, wantNames) {
    89  				return nil
    90  			}
    91  		}
    92  	}
    93  	if ctx.Err() != nil {
    94  		return fmt.Errorf("Timeout when waiting for appropriate Cluster resources to be requested")
    95  	}
    96  	return nil
    97  }
    98  
    99  // Registers a wrapped cluster_resolver LB policy (child policy of the cds LB
   100  // policy) for the duration of this test that retains all the functionality of
   101  // the former, but makes certain events available for inspection by the test.
   102  //
   103  // Returns the following:
   104  // - a channel to read received load balancing configuration
   105  // - a channel to read received resolver error
   106  // - a channel that is closed when ExitIdle() is called
   107  // - a channel that is closed when the balancer is closed
   108  func registerWrappedClusterResolverPolicy(t *testing.T) (chan serviceconfig.LoadBalancingConfig, chan error, chan struct{}, chan struct{}) {
   109  	clusterresolverBuilder := balancer.Get(clusterresolver.Name)
   110  	internal.BalancerUnregister(clusterresolverBuilder.Name())
   111  
   112  	lbCfgCh := make(chan serviceconfig.LoadBalancingConfig, 1)
   113  	resolverErrCh := make(chan error, 1)
   114  	exitIdleCh := make(chan struct{})
   115  	closeCh := make(chan struct{})
   116  
   117  	stub.Register(clusterresolver.Name, stub.BalancerFuncs{
   118  		Init: func(bd *stub.BalancerData) {
   119  			bd.ChildBalancer = clusterresolverBuilder.Build(bd.ClientConn, bd.BuildOptions)
   120  		},
   121  		ParseConfig: func(lbCfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
   122  			return clusterresolverBuilder.(balancer.ConfigParser).ParseConfig(lbCfg)
   123  		},
   124  		UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
   125  			select {
   126  			case lbCfgCh <- ccs.BalancerConfig:
   127  			default:
   128  			}
   129  			return bd.ChildBalancer.UpdateClientConnState(ccs)
   130  		},
   131  		ResolverError: func(bd *stub.BalancerData, err error) {
   132  			select {
   133  			case resolverErrCh <- err:
   134  			default:
   135  			}
   136  			bd.ChildBalancer.ResolverError(err)
   137  		},
   138  		ExitIdle: func(bd *stub.BalancerData) {
   139  			bd.ChildBalancer.ExitIdle()
   140  			close(exitIdleCh)
   141  		},
   142  		Close: func(bd *stub.BalancerData) {
   143  			bd.ChildBalancer.Close()
   144  			close(closeCh)
   145  		},
   146  	})
   147  	t.Cleanup(func() { balancer.Register(clusterresolverBuilder) })
   148  
   149  	return lbCfgCh, resolverErrCh, exitIdleCh, closeCh
   150  }
   151  
   152  // Registers a wrapped cds LB policy for the duration of this test that retains
   153  // all the functionality of the original cds LB policy, but makes the newly
   154  // built policy available to the test to directly invoke any balancer methods.
   155  //
   156  // Returns a channel on which the newly built cds LB policy is written to.
   157  func registerWrappedCDSPolicy(t *testing.T) chan balancer.Balancer {
   158  	cdsBuilder := balancer.Get(cdsName)
   159  	internal.BalancerUnregister(cdsBuilder.Name())
   160  	cdsBalancerCh := make(chan balancer.Balancer, 1)
   161  	stub.Register(cdsBuilder.Name(), stub.BalancerFuncs{
   162  		Init: func(bd *stub.BalancerData) {
   163  			bal := cdsBuilder.Build(bd.ClientConn, bd.BuildOptions)
   164  			bd.ChildBalancer = bal
   165  			cdsBalancerCh <- bal
   166  		},
   167  		ParseConfig: func(lbCfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
   168  			return cdsBuilder.(balancer.ConfigParser).ParseConfig(lbCfg)
   169  		},
   170  		UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
   171  			return bd.ChildBalancer.UpdateClientConnState(ccs)
   172  		},
   173  		Close: func(bd *stub.BalancerData) {
   174  			bd.ChildBalancer.Close()
   175  		},
   176  	})
   177  	t.Cleanup(func() { balancer.Register(cdsBuilder) })
   178  
   179  	return cdsBalancerCh
   180  }
   181  
   182  // Performs the following setup required for tests:
   183  //   - Spins up an xDS management server
   184  //   - Creates an xDS client talking to this management server
   185  //   - Creates a manual resolver that configures the cds LB policy as the
   186  //     top-level policy, and pushes an initial configuration to it
   187  //   - Creates a gRPC channel with the above manual resolver
   188  //
   189  // Returns the following:
   190  //   - the xDS management server
   191  //   - the nodeID expected by the management server
   192  //   - the grpc channel to the test backend service
   193  //   - the manual resolver configured on the channel
   194  //   - the xDS client used the grpc channel
   195  //   - a channel on which requested cluster resource names are sent
   196  //   - a channel used to signal that previously requested cluster resources are
   197  //     no longer requested
   198  func setupWithManagementServer(t *testing.T) (*e2e.ManagementServer, string, *grpc.ClientConn, *manual.Resolver, xdsclient.XDSClient, chan []string, chan struct{}) {
   199  	return setupWithManagementServerAndListener(t, nil)
   200  }
   201  
   202  // Same as setupWithManagementServer, but also allows the caller to specify
   203  // a listener to be used by the management server.
   204  func setupWithManagementServerAndListener(t *testing.T, lis net.Listener) (*e2e.ManagementServer, string, *grpc.ClientConn, *manual.Resolver, xdsclient.XDSClient, chan []string, chan struct{}) {
   205  	t.Helper()
   206  
   207  	cdsResourceRequestedCh := make(chan []string, 1)
   208  	cdsResourceCanceledCh := make(chan struct{}, 1)
   209  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{
   210  		Listener: lis,
   211  		OnStreamRequest: func(_ int64, req *v3discoverypb.DiscoveryRequest) error {
   212  			if req.GetTypeUrl() == version.V3ClusterURL {
   213  				switch len(req.GetResourceNames()) {
   214  				case 0:
   215  					select {
   216  					case cdsResourceCanceledCh <- struct{}{}:
   217  					default:
   218  					}
   219  				default:
   220  					select {
   221  					case cdsResourceRequestedCh <- req.GetResourceNames():
   222  					default:
   223  					}
   224  				}
   225  			}
   226  			return nil
   227  		},
   228  		// Required for aggregate clusters as all resources cannot be requested
   229  		// at once.
   230  		AllowResourceSubset: true,
   231  	})
   232  
   233  	// Create bootstrap configuration pointing to the above management server.
   234  	nodeID := uuid.New().String()
   235  	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
   236  
   237  	config, err := bootstrap.NewConfigFromContents(bc)
   238  	if err != nil {
   239  		t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err)
   240  	}
   241  	pool := xdsclient.NewPool(config)
   242  	xdsC, xdsClose, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{
   243  		Name: t.Name(),
   244  	})
   245  	if err != nil {
   246  		t.Fatalf("Failed to create xDS client: %v", err)
   247  	}
   248  	t.Cleanup(xdsClose)
   249  
   250  	r := manual.NewBuilderWithScheme("whatever")
   251  	jsonSC := fmt.Sprintf(`{
   252  			"loadBalancingConfig":[{
   253  				"cds_experimental":{
   254  					"cluster": "%s"
   255  				}
   256  			}]
   257  		}`, clusterName)
   258  	scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC)
   259  	r.InitialState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, xdsC))
   260  
   261  	cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r))
   262  	if err != nil {
   263  		t.Fatalf("grpc.NewClient(%q) = %v", lis.Addr().String(), err)
   264  	}
   265  	cc.Connect()
   266  	t.Cleanup(func() { cc.Close() })
   267  
   268  	return mgmtServer, nodeID, cc, r, xdsC, cdsResourceRequestedCh, cdsResourceCanceledCh
   269  }
   270  
   271  // Helper function to compare the load balancing configuration received on the
   272  // channel with the expected one. Both configs are marshalled to JSON and then
   273  // compared.
   274  //
   275  // Returns an error if marshalling to JSON fails, or if the load balancing
   276  // configurations don't match, or if the context deadline expires before reading
   277  // a child policy configuration off of the lbCfgCh.
   278  func compareLoadBalancingConfig(ctx context.Context, lbCfgCh chan serviceconfig.LoadBalancingConfig, wantChildCfg serviceconfig.LoadBalancingConfig) error {
   279  	wantJSON, err := json.Marshal(wantChildCfg)
   280  	if err != nil {
   281  		return fmt.Errorf("failed to marshal expected child config to JSON: %v", err)
   282  	}
   283  	select {
   284  	case lbCfg := <-lbCfgCh:
   285  		gotJSON, err := json.Marshal(lbCfg)
   286  		if err != nil {
   287  			return fmt.Errorf("failed to marshal received LB config into JSON: %v", err)
   288  		}
   289  		if diff := cmp.Diff(wantJSON, gotJSON); diff != "" {
   290  			return fmt.Errorf("child policy received unexpected diff in config (-want +got):\n%s", diff)
   291  		}
   292  	case <-ctx.Done():
   293  		return fmt.Errorf("timeout when waiting for child policy to receive its configuration")
   294  	}
   295  	return nil
   296  }
   297  
   298  func verifyRPCError(gotErr error, wantCode codes.Code, wantErr, wantNodeID string) error {
   299  	if gotErr == nil {
   300  		return fmt.Errorf("RPC succeeded when expecting an error with code %v, message %q and nodeID %q", wantCode, wantErr, wantNodeID)
   301  	}
   302  	if gotCode := status.Code(gotErr); gotCode != wantCode {
   303  		return fmt.Errorf("RPC failed with code: %v, want code %v", gotCode, wantCode)
   304  	}
   305  	if !strings.Contains(gotErr.Error(), wantErr) {
   306  		return fmt.Errorf("RPC failed with error: %v, want %q", gotErr, wantErr)
   307  	}
   308  	if !strings.Contains(gotErr.Error(), wantNodeID) {
   309  		return fmt.Errorf("RPC failed with error: %v, want nodeID %q", gotErr, wantNodeID)
   310  	}
   311  	return nil
   312  }
   313  
   314  // Tests the functionality that handles LB policy configuration. Verifies that
   315  // the appropriate xDS resource is requested corresponding to the provided LB
   316  // policy configuration. Also verifies that when the LB policy receives the same
   317  // configuration again, it does not send out a new request, and when the
   318  // configuration changes, it stops requesting the old cluster resource and
   319  // starts requesting the new one.
   320  func (s) TestConfigurationUpdate_Success(t *testing.T) {
   321  	_, _, _, r, xdsClient, cdsResourceRequestedCh, _ := setupWithManagementServer(t)
   322  
   323  	// Verify that the specified cluster resource is requested.
   324  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   325  	defer cancel()
   326  	wantNames := []string{clusterName}
   327  	if err := waitForResourceNames(ctx, cdsResourceRequestedCh, wantNames); err != nil {
   328  		t.Fatal(err)
   329  	}
   330  
   331  	// Push the same configuration again.
   332  	jsonSC := fmt.Sprintf(`{
   333  			"loadBalancingConfig":[{
   334  				"cds_experimental":{
   335  					"cluster": "%s"
   336  				}
   337  			}]
   338  		}`, clusterName)
   339  	scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC)
   340  	r.UpdateState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, xdsClient))
   341  
   342  	// Verify that a new CDS request is not sent.
   343  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   344  	defer sCancel()
   345  	select {
   346  	case <-sCtx.Done():
   347  	case gotNames := <-cdsResourceRequestedCh:
   348  		t.Fatalf("CDS resources %v requested when none expected", gotNames)
   349  	}
   350  
   351  	// Push an updated configuration with a different cluster name.
   352  	newClusterName := clusterName + "-new"
   353  	jsonSC = fmt.Sprintf(`{
   354  			"loadBalancingConfig":[{
   355  				"cds_experimental":{
   356  					"cluster": "%s"
   357  				}
   358  			}]
   359  		}`, newClusterName)
   360  	scpr = internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC)
   361  	r.UpdateState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, xdsClient))
   362  
   363  	// Verify that the new cluster name is requested and the old one is no
   364  	// longer requested.
   365  	wantNames = []string{newClusterName}
   366  	if err := waitForResourceNames(ctx, cdsResourceRequestedCh, wantNames); err != nil {
   367  		t.Fatal(err)
   368  	}
   369  }
   370  
   371  // Tests the case where a configuration with an empty cluster name is pushed to
   372  // the CDS LB policy. Verifies that ErrBadResolverState is returned.
   373  func (s) TestConfigurationUpdate_EmptyCluster(t *testing.T) {
   374  	// Setup a management server and an xDS client to talk to it.
   375  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
   376  
   377  	// Create bootstrap configuration pointing to the above management server.
   378  	nodeID := uuid.New().String()
   379  	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
   380  
   381  	config, err := bootstrap.NewConfigFromContents(bc)
   382  	if err != nil {
   383  		t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err)
   384  	}
   385  	pool := xdsclient.NewPool(config)
   386  	xdsClient, xdsClose, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{
   387  		Name: t.Name(),
   388  	})
   389  	if err != nil {
   390  		t.Fatalf("Failed to create xDS client: %v", err)
   391  	}
   392  	t.Cleanup(xdsClose)
   393  
   394  	// Create a manual resolver that configures the CDS LB policy as the
   395  	// top-level LB policy on the channel, and pushes a configuration with an
   396  	// empty cluster name. Also, register a callback with the manual resolver to
   397  	// receive the error returned by the balancer when a configuration with an
   398  	// empty cluster name is pushed.
   399  	r := manual.NewBuilderWithScheme("whatever")
   400  	updateStateErrCh := make(chan error, 1)
   401  	r.UpdateStateCallback = func(err error) { updateStateErrCh <- err }
   402  	jsonSC := `{
   403  			"loadBalancingConfig":[{
   404  				"cds_experimental":{
   405  					"cluster": ""
   406  				}
   407  			}]
   408  		}`
   409  	scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC)
   410  	r.InitialState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, xdsClient))
   411  
   412  	// Create a ClientConn with the above manual resolver.
   413  	cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r))
   414  	if err != nil {
   415  		t.Fatalf("grpc.NewClient() failed: %v", err)
   416  	}
   417  	cc.Connect()
   418  	t.Cleanup(func() { cc.Close() })
   419  
   420  	select {
   421  	case <-time.After(defaultTestTimeout):
   422  		t.Fatalf("Timed out waiting for error from the LB policy")
   423  	case err := <-updateStateErrCh:
   424  		if err != balancer.ErrBadResolverState {
   425  			t.Fatalf("For a configuration update with an empty cluster name, got error %v from the LB policy, want %v", err, balancer.ErrBadResolverState)
   426  		}
   427  	}
   428  }
   429  
   430  // Tests the case where a configuration with a missing xDS client is pushed to
   431  // the CDS LB policy. Verifies that ErrBadResolverState is returned.
   432  func (s) TestConfigurationUpdate_MissingXdsClient(t *testing.T) {
   433  	// Create a manual resolver that configures the CDS LB policy as the
   434  	// top-level LB policy on the channel, and pushes a configuration that is
   435  	// missing the xDS client.  Also, register a callback with the manual
   436  	// resolver to receive the error returned by the balancer.
   437  	r := manual.NewBuilderWithScheme("whatever")
   438  	updateStateErrCh := make(chan error, 1)
   439  	r.UpdateStateCallback = func(err error) { updateStateErrCh <- err }
   440  	jsonSC := `{
   441  			"loadBalancingConfig":[{
   442  				"cds_experimental":{
   443  					"cluster": "foo"
   444  				}
   445  			}]
   446  		}`
   447  	scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC)
   448  	r.InitialState(resolver.State{ServiceConfig: scpr})
   449  
   450  	// Create a ClientConn with the above manual resolver.
   451  	cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r))
   452  	if err != nil {
   453  		t.Fatalf("grpc.NewClient() failed: %v", err)
   454  	}
   455  	cc.Connect()
   456  	t.Cleanup(func() { cc.Close() })
   457  
   458  	select {
   459  	case <-time.After(defaultTestTimeout):
   460  		t.Fatalf("Timed out waiting for error from the LB policy")
   461  	case err := <-updateStateErrCh:
   462  		if err != balancer.ErrBadResolverState {
   463  			t.Fatalf("For a configuration update missing the xDS client, got error %v from the LB policy, want %v", err, balancer.ErrBadResolverState)
   464  		}
   465  	}
   466  }
   467  
   468  // Tests success scenarios where the cds LB policy receives a cluster resource
   469  // from the management server. Verifies that the load balancing configuration
   470  // pushed to the child is as expected.
   471  func (s) TestClusterUpdate_Success(t *testing.T) {
   472  	tests := []struct {
   473  		name            string
   474  		clusterResource *v3clusterpb.Cluster
   475  		wantChildCfg    serviceconfig.LoadBalancingConfig
   476  	}{
   477  		{
   478  			name: "happy-case-with-circuit-breakers",
   479  			clusterResource: func() *v3clusterpb.Cluster {
   480  				c := e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)
   481  				c.CircuitBreakers = &v3clusterpb.CircuitBreakers{
   482  					Thresholds: []*v3clusterpb.CircuitBreakers_Thresholds{
   483  						{
   484  							Priority:    v3corepb.RoutingPriority_DEFAULT,
   485  							MaxRequests: wrapperspb.UInt32(512),
   486  						},
   487  						{
   488  							Priority:    v3corepb.RoutingPriority_HIGH,
   489  							MaxRequests: nil,
   490  						},
   491  					},
   492  				}
   493  				return c
   494  			}(),
   495  			wantChildCfg: &clusterresolver.LBConfig{
   496  				DiscoveryMechanisms: []clusterresolver.DiscoveryMechanism{{
   497  					Cluster:               clusterName,
   498  					Type:                  clusterresolver.DiscoveryMechanismTypeEDS,
   499  					EDSServiceName:        serviceName,
   500  					MaxConcurrentRequests: newUint32(512),
   501  					OutlierDetection:      json.RawMessage(`{}`),
   502  					TelemetryLabels:       xdsinternal.UnknownCSMLabels,
   503  				}},
   504  				XDSLBPolicy: json.RawMessage(`[{"xds_wrr_locality_experimental": {"childPolicy": [{"round_robin": {}}]}}]`),
   505  			},
   506  		},
   507  		{
   508  			name: "happy-case-with-ring-hash-lb-policy",
   509  			clusterResource: func() *v3clusterpb.Cluster {
   510  				c := e2e.ClusterResourceWithOptions(e2e.ClusterOptions{
   511  					ClusterName:   clusterName,
   512  					ServiceName:   serviceName,
   513  					SecurityLevel: e2e.SecurityLevelNone,
   514  					Policy:        e2e.LoadBalancingPolicyRingHash,
   515  				})
   516  				c.LbConfig = &v3clusterpb.Cluster_RingHashLbConfig_{
   517  					RingHashLbConfig: &v3clusterpb.Cluster_RingHashLbConfig{
   518  						MinimumRingSize: &wrapperspb.UInt64Value{Value: 100},
   519  						MaximumRingSize: &wrapperspb.UInt64Value{Value: 1000},
   520  					},
   521  				}
   522  				return c
   523  			}(),
   524  			wantChildCfg: &clusterresolver.LBConfig{
   525  				DiscoveryMechanisms: []clusterresolver.DiscoveryMechanism{{
   526  					Cluster:          clusterName,
   527  					Type:             clusterresolver.DiscoveryMechanismTypeEDS,
   528  					EDSServiceName:   serviceName,
   529  					OutlierDetection: json.RawMessage(`{}`),
   530  					TelemetryLabels:  xdsinternal.UnknownCSMLabels,
   531  				}},
   532  				XDSLBPolicy: json.RawMessage(`[{"ring_hash_experimental": {"minRingSize":100, "maxRingSize":1000}}]`),
   533  			},
   534  		},
   535  		{
   536  			name: "happy-case-outlier-detection-xds-defaults", // OD proto set but no proto fields set
   537  			clusterResource: func() *v3clusterpb.Cluster {
   538  				c := e2e.ClusterResourceWithOptions(e2e.ClusterOptions{
   539  					ClusterName:   clusterName,
   540  					ServiceName:   serviceName,
   541  					SecurityLevel: e2e.SecurityLevelNone,
   542  					Policy:        e2e.LoadBalancingPolicyRingHash,
   543  				})
   544  				c.OutlierDetection = &v3clusterpb.OutlierDetection{}
   545  				return c
   546  			}(),
   547  			wantChildCfg: &clusterresolver.LBConfig{
   548  				DiscoveryMechanisms: []clusterresolver.DiscoveryMechanism{{
   549  					Cluster:          clusterName,
   550  					Type:             clusterresolver.DiscoveryMechanismTypeEDS,
   551  					EDSServiceName:   serviceName,
   552  					OutlierDetection: json.RawMessage(`{"successRateEjection":{}}`),
   553  					TelemetryLabels:  xdsinternal.UnknownCSMLabels,
   554  				}},
   555  				XDSLBPolicy: json.RawMessage(`[{"ring_hash_experimental": {"minRingSize":1024, "maxRingSize":8388608}}]`),
   556  			},
   557  		},
   558  		{
   559  			name: "happy-case-outlier-detection-all-fields-set",
   560  			clusterResource: func() *v3clusterpb.Cluster {
   561  				c := e2e.ClusterResourceWithOptions(e2e.ClusterOptions{
   562  					ClusterName:   clusterName,
   563  					ServiceName:   serviceName,
   564  					SecurityLevel: e2e.SecurityLevelNone,
   565  					Policy:        e2e.LoadBalancingPolicyRingHash,
   566  				})
   567  				c.OutlierDetection = &v3clusterpb.OutlierDetection{
   568  					Interval:                       durationpb.New(10 * time.Second),
   569  					BaseEjectionTime:               durationpb.New(30 * time.Second),
   570  					MaxEjectionTime:                durationpb.New(300 * time.Second),
   571  					MaxEjectionPercent:             wrapperspb.UInt32(10),
   572  					SuccessRateStdevFactor:         wrapperspb.UInt32(1900),
   573  					EnforcingSuccessRate:           wrapperspb.UInt32(100),
   574  					SuccessRateMinimumHosts:        wrapperspb.UInt32(5),
   575  					SuccessRateRequestVolume:       wrapperspb.UInt32(100),
   576  					FailurePercentageThreshold:     wrapperspb.UInt32(85),
   577  					EnforcingFailurePercentage:     wrapperspb.UInt32(5),
   578  					FailurePercentageMinimumHosts:  wrapperspb.UInt32(5),
   579  					FailurePercentageRequestVolume: wrapperspb.UInt32(50),
   580  				}
   581  				return c
   582  			}(),
   583  			wantChildCfg: &clusterresolver.LBConfig{
   584  				DiscoveryMechanisms: []clusterresolver.DiscoveryMechanism{{
   585  					Cluster:        clusterName,
   586  					Type:           clusterresolver.DiscoveryMechanismTypeEDS,
   587  					EDSServiceName: serviceName,
   588  					OutlierDetection: json.RawMessage(`{
   589  						"interval": "10s",
   590  						"baseEjectionTime": "30s",
   591  						"maxEjectionTime": "300s",
   592  						"maxEjectionPercent": 10,
   593  						"successRateEjection": {
   594  							"stdevFactor": 1900,
   595  							"enforcementPercentage": 100,
   596  							"minimumHosts": 5,
   597  							"requestVolume": 100
   598  						},
   599  						"failurePercentageEjection": {
   600  							"threshold": 85,
   601  							"enforcementPercentage": 5,
   602  							"minimumHosts": 5,
   603  							"requestVolume": 50
   604  						}
   605  					}`),
   606  					TelemetryLabels: xdsinternal.UnknownCSMLabels,
   607  				}},
   608  				XDSLBPolicy: json.RawMessage(`[{"ring_hash_experimental": {"minRingSize":1024, "maxRingSize":8388608}}]`),
   609  			},
   610  		},
   611  	}
   612  
   613  	for _, test := range tests {
   614  		t.Run(test.name, func(t *testing.T) {
   615  			lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
   616  			mgmtServer, nodeID, _, _, _, _, _ := setupWithManagementServer(t)
   617  
   618  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   619  			defer cancel()
   620  			if err := mgmtServer.Update(ctx, e2e.UpdateOptions{
   621  				NodeID:         nodeID,
   622  				Clusters:       []*v3clusterpb.Cluster{test.clusterResource},
   623  				SkipValidation: true,
   624  			}); err != nil {
   625  				t.Fatal(err)
   626  			}
   627  
   628  			if err := compareLoadBalancingConfig(ctx, lbCfgCh, test.wantChildCfg); err != nil {
   629  				t.Fatal(err)
   630  			}
   631  		})
   632  	}
   633  }
   634  
   635  // Tests a single success scenario where the cds LB policy receives a cluster
   636  // resource from the management server with LRS enabled. Verifies that the load
   637  // balancing configuration pushed to the child is as expected.
   638  func (s) TestClusterUpdate_SuccessWithLRS(t *testing.T) {
   639  	lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
   640  	mgmtServer, nodeID, _, _, _, _, _ := setupWithManagementServer(t)
   641  
   642  	clusterResource := e2e.ClusterResourceWithOptions(e2e.ClusterOptions{
   643  		ClusterName: clusterName,
   644  		ServiceName: serviceName,
   645  		EnableLRS:   true,
   646  	})
   647  	lrsServerCfg, err := bootstrap.ServerConfigForTesting(bootstrap.ServerConfigTestingOptions{URI: fmt.Sprintf("passthrough:///%s", mgmtServer.Address)})
   648  	if err != nil {
   649  		t.Fatalf("Failed to create LRS server config for testing: %v", err)
   650  	}
   651  
   652  	wantChildCfg := &clusterresolver.LBConfig{
   653  		DiscoveryMechanisms: []clusterresolver.DiscoveryMechanism{{
   654  			Cluster:             clusterName,
   655  			Type:                clusterresolver.DiscoveryMechanismTypeEDS,
   656  			EDSServiceName:      serviceName,
   657  			LoadReportingServer: lrsServerCfg,
   658  			OutlierDetection:    json.RawMessage(`{}`),
   659  			TelemetryLabels:     xdsinternal.UnknownCSMLabels,
   660  		}},
   661  		XDSLBPolicy: json.RawMessage(`[{"xds_wrr_locality_experimental": {"childPolicy": [{"round_robin": {}}]}}]`),
   662  	}
   663  
   664  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   665  	defer cancel()
   666  	if err := mgmtServer.Update(ctx, e2e.UpdateOptions{
   667  		NodeID:         nodeID,
   668  		Clusters:       []*v3clusterpb.Cluster{clusterResource},
   669  		SkipValidation: true,
   670  	}); err != nil {
   671  		t.Fatal(err)
   672  	}
   673  
   674  	if err := compareLoadBalancingConfig(ctx, lbCfgCh, wantChildCfg); err != nil {
   675  		t.Fatal(err)
   676  	}
   677  }
   678  
   679  // Tests scenarios for a bad cluster update received from the management server.
   680  //
   681  //   - when a bad cluster resource update is received without any previous good
   682  //     update from the management server, the cds LB policy is expected to put
   683  //     the channel in TRANSIENT_FAILURE.
   684  //   - when a bad cluster resource update is received after a previous good
   685  //     update from the management server, the cds LB policy is expected to
   686  //     continue using the previous good update.
   687  func (s) TestClusterUpdate_Failure(t *testing.T) {
   688  	_, resolverErrCh, _, _ := registerWrappedClusterResolverPolicy(t)
   689  	mgmtServer, nodeID, cc, _, _, cdsResourceRequestedCh, cdsResourceCanceledCh := setupWithManagementServer(t)
   690  
   691  	// Verify that the specified cluster resource is requested.
   692  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   693  	defer cancel()
   694  	wantNames := []string{clusterName}
   695  	if err := waitForResourceNames(ctx, cdsResourceRequestedCh, wantNames); err != nil {
   696  		t.Fatal(err)
   697  	}
   698  
   699  	// Configure the management server to return a cluster resource that
   700  	// contains a config_source_specifier for the `lrs_server` field which is not
   701  	// set to `self`, and hence is expected to be NACKed by the client.
   702  	cluster := e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)
   703  	cluster.LrsServer = &v3corepb.ConfigSource{ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{}}
   704  	resources := e2e.UpdateOptions{
   705  		NodeID:         nodeID,
   706  		Clusters:       []*v3clusterpb.Cluster{cluster},
   707  		SkipValidation: true,
   708  	}
   709  	if err := mgmtServer.Update(ctx, resources); err != nil {
   710  		t.Fatal(err)
   711  	}
   712  
   713  	// Verify that the watch for the cluster resource is not cancelled.
   714  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   715  	defer sCancel()
   716  	select {
   717  	case <-sCtx.Done():
   718  	case <-cdsResourceCanceledCh:
   719  		t.Fatal("Watch for cluster resource is cancelled when not expected to")
   720  	}
   721  
   722  	testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
   723  
   724  	// Ensure that the NACK error and the xDS node ID are propagated to the RPC
   725  	// caller.
   726  	const wantClusterNACKErr = "unsupported config_source_specifier"
   727  	client := testgrpc.NewTestServiceClient(cc)
   728  	_, err := client.EmptyCall(ctx, &testpb.Empty{})
   729  	if err := verifyRPCError(err, codes.Unavailable, wantClusterNACKErr, nodeID); err != nil {
   730  		t.Fatal(err)
   731  	}
   732  
   733  	// Start a test service backend.
   734  	server := stubserver.StartTestService(t, nil)
   735  	t.Cleanup(server.Stop)
   736  
   737  	// Configure cluster and endpoints resources in the management server.
   738  	resources = e2e.UpdateOptions{
   739  		NodeID:         nodeID,
   740  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
   741  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
   742  		SkipValidation: true,
   743  	}
   744  	if err := mgmtServer.Update(ctx, resources); err != nil {
   745  		t.Fatal(err)
   746  	}
   747  
   748  	// Verify that a successful RPC can be made.
   749  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   750  		t.Fatalf("EmptyCall() failed: %v", err)
   751  	}
   752  
   753  	// Send the bad cluster resource again.
   754  	resources = e2e.UpdateOptions{
   755  		NodeID:         nodeID,
   756  		Clusters:       []*v3clusterpb.Cluster{cluster},
   757  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
   758  		SkipValidation: true,
   759  	}
   760  	if err := mgmtServer.Update(ctx, resources); err != nil {
   761  		t.Fatal(err)
   762  	}
   763  
   764  	// Verify that the watch for the cluster resource is not cancelled.
   765  	sCtx, sCancel = context.WithTimeout(ctx, defaultTestShortTimeout)
   766  	defer sCancel()
   767  	select {
   768  	case <-sCtx.Done():
   769  	case <-cdsResourceCanceledCh:
   770  		t.Fatal("Watch for cluster resource is cancelled when not expected to")
   771  	}
   772  
   773  	// Verify that a successful RPC can be made, using the previously received
   774  	// good configuration.
   775  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   776  		t.Fatalf("EmptyCall() failed: %v", err)
   777  	}
   778  
   779  	// Verify that the resolver error is pushed to the child policy.
   780  	select {
   781  	case err := <-resolverErrCh:
   782  		if !strings.Contains(err.Error(), wantClusterNACKErr) {
   783  			t.Fatalf("Error pushed to child policy is %v, want %v", err, wantClusterNACKErr)
   784  		}
   785  	case <-ctx.Done():
   786  		t.Fatal("Timeout when waiting for resolver error to be pushed to the child policy")
   787  	}
   788  }
   789  
   790  // Tests the following scenarios for resolver errors:
   791  //   - when a resolver error is received without any previous good update from the
   792  //     management server, the cds LB policy is expected to put the channel in
   793  //     TRANSIENT_FAILURE.
   794  //   - when a resolver error is received (one that is not a resource-not-found
   795  //     error), with a previous good update from the management server, the cds LB
   796  //     policy is expected to push the error down the child policy, but is expected
   797  //     to continue to use the previously received good configuration.
   798  //   - when a resolver error is received (one that is a resource-not-found
   799  //     error, which is usually the case when the LDS resource is removed),
   800  //     with a previous good update from the management server, the cds LB policy
   801  //     is expected to push the error down the child policy and put the channel in
   802  //     TRANSIENT_FAILURE. It is also expected to cancel the CDS watch.
   803  func (s) TestResolverError(t *testing.T) {
   804  	_, resolverErrCh, _, childPolicyCloseCh := registerWrappedClusterResolverPolicy(t)
   805  	lis := testutils.NewListenerWrapper(t, nil)
   806  	mgmtServer, nodeID, cc, r, _, cdsResourceRequestedCh, cdsResourceCanceledCh := setupWithManagementServerAndListener(t, lis)
   807  
   808  	// Grab the wrapped connection from the listener wrapper. This will be used
   809  	// to verify the connection is closed.
   810  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   811  	defer cancel()
   812  	val, err := lis.NewConnCh.Receive(ctx)
   813  	if err != nil {
   814  		t.Fatalf("Failed to receive new connection from wrapped listener: %v", err)
   815  	}
   816  	conn := val.(*testutils.ConnWrapper)
   817  
   818  	// Verify that the specified cluster resource is requested.
   819  	wantNames := []string{clusterName}
   820  	if err := waitForResourceNames(ctx, cdsResourceRequestedCh, wantNames); err != nil {
   821  		t.Fatal(err)
   822  	}
   823  
   824  	// Push a resolver error that is not a resource-not-found error. Here, we
   825  	// assume that errors from the xDS client or from the xDS resolver contain
   826  	// the xDS node ID.
   827  	resolverErr := fmt.Errorf("[xds node id: %s]: resolver-error-not-a-resource-not-found-error", nodeID)
   828  	r.CC().ReportError(resolverErr)
   829  
   830  	testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
   831  
   832  	// Drain the resolver error channel.
   833  	select {
   834  	case <-resolverErrCh:
   835  	default:
   836  	}
   837  
   838  	// Ensure that the resolver error is propagated to the RPC caller.
   839  	client := testgrpc.NewTestServiceClient(cc)
   840  	_, err = client.EmptyCall(ctx, &testpb.Empty{})
   841  	if err := verifyRPCError(err, codes.Unavailable, resolverErr.Error(), nodeID); err != nil {
   842  		t.Fatal(err)
   843  	}
   844  
   845  	// Also verify that the watch for the cluster resource is not cancelled.
   846  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   847  	defer sCancel()
   848  	select {
   849  	case <-sCtx.Done():
   850  	case <-cdsResourceCanceledCh:
   851  		t.Fatal("Watch for cluster resource is cancelled when not expected to")
   852  	}
   853  
   854  	// Start a test service backend.
   855  	server := stubserver.StartTestService(t, nil)
   856  	t.Cleanup(server.Stop)
   857  
   858  	// Configure good cluster and endpoints resources in the management server.
   859  	resources := e2e.UpdateOptions{
   860  		NodeID:         nodeID,
   861  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
   862  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
   863  		SkipValidation: true,
   864  	}
   865  	if err := mgmtServer.Update(ctx, resources); err != nil {
   866  		t.Fatal(err)
   867  	}
   868  
   869  	// Verify that a successful RPC can be made.
   870  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   871  		t.Fatalf("EmptyCall() failed: %v", err)
   872  	}
   873  
   874  	// Again push a resolver error that is not a resource-not-found error.
   875  	r.CC().ReportError(resolverErr)
   876  
   877  	// And again verify that the watch for the cluster resource is not
   878  	// cancelled.
   879  	sCtx, sCancel = context.WithTimeout(ctx, defaultTestShortTimeout)
   880  	defer sCancel()
   881  	select {
   882  	case <-sCtx.Done():
   883  	case <-cdsResourceCanceledCh:
   884  		t.Fatal("Watch for cluster resource is cancelled when not expected to")
   885  	}
   886  
   887  	// Verify that a successful RPC can be made, using the previously received
   888  	// good configuration.
   889  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   890  		t.Fatalf("EmptyCall() failed: %v", err)
   891  	}
   892  
   893  	// Verify that the resolver error is pushed to the child policy.
   894  	select {
   895  	case err := <-resolverErrCh:
   896  		if err != resolverErr {
   897  			t.Fatalf("Error pushed to child policy is %v, want %v", err, resolverErr)
   898  		}
   899  	case <-ctx.Done():
   900  		t.Fatal("Timeout when waiting for resolver error to be pushed to the child policy")
   901  	}
   902  
   903  	// Push a resource-not-found-error this time around. Our xDS resolver does
   904  	// not send this error though. When an LDS or RDS resource is missing, the
   905  	// xDS resolver instead sends an erroring config selector which returns an
   906  	// error at RPC time with the xDS node ID, for new RPCs. Once ongoing RPCs
   907  	// complete, the xDS resolver will send an empty service config with no
   908  	// addresses, which will result in pick_first being configured on the
   909  	// channel. And pick_first will put the channel in TRANSIENT_FAILURE since
   910  	// it would have received an update with no addresses.
   911  	resolverErr = fmt.Errorf("[xds node id: %s]: %w", nodeID, xdsresource.NewError(xdsresource.ErrorTypeResourceNotFound, "xds resource not found error"))
   912  	r.CC().ReportError(resolverErr)
   913  
   914  	// Wait for the CDS resource to be not requested anymore, or the connection
   915  	// to the management server to be closed (which happens as part of the last
   916  	// resource watch being canceled).
   917  	select {
   918  	case <-ctx.Done():
   919  		t.Fatal("Timeout when waiting for CDS resource to be not requested")
   920  	case <-cdsResourceCanceledCh:
   921  	case <-conn.CloseCh.C:
   922  	}
   923  
   924  	// Verify that the resolver error is pushed to the child policy.
   925  	select {
   926  	case <-childPolicyCloseCh:
   927  	case <-ctx.Done():
   928  		t.Fatal("Timeout when waiting for child policy to be closed")
   929  	}
   930  
   931  	testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
   932  
   933  	// Ensure that the resolver error is propagated to the RPC caller.
   934  	_, err = client.EmptyCall(ctx, &testpb.Empty{})
   935  	if err := verifyRPCError(err, codes.Unavailable, resolverErr.Error(), nodeID); err != nil {
   936  		t.Fatal(err)
   937  	}
   938  }
   939  
   940  // Tests scenarios involving removal of a cluster resource from the management
   941  // server.
   942  //
   943  //   - when the cluster resource is removed after a previous good
   944  //     update from the management server, the cds LB policy is expected to put
   945  //     the channel in TRANSIENT_FAILURE.
   946  //   - when the cluster resource is re-sent by the management server, RPCs
   947  //     should start succeeding.
   948  func (s) TestClusterUpdate_ResourceNotFound(t *testing.T) {
   949  	mgmtServer, nodeID, cc, _, _, cdsResourceRequestedCh, cdsResourceCanceledCh := setupWithManagementServer(t)
   950  
   951  	// Verify that the specified cluster resource is requested.
   952  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   953  	defer cancel()
   954  	wantNames := []string{clusterName}
   955  	if err := waitForResourceNames(ctx, cdsResourceRequestedCh, wantNames); err != nil {
   956  		t.Fatal(err)
   957  	}
   958  
   959  	// Start a test service backend.
   960  	server := stubserver.StartTestService(t, nil)
   961  	t.Cleanup(server.Stop)
   962  
   963  	// Configure cluster and endpoints resources in the management server.
   964  	resources := e2e.UpdateOptions{
   965  		NodeID:         nodeID,
   966  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
   967  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
   968  		SkipValidation: true,
   969  	}
   970  	if err := mgmtServer.Update(ctx, resources); err != nil {
   971  		t.Fatal(err)
   972  	}
   973  
   974  	// Verify that a successful RPC can be made.
   975  	client := testgrpc.NewTestServiceClient(cc)
   976  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   977  		t.Fatalf("EmptyCall() failed: %v", err)
   978  	}
   979  
   980  	// Remove the cluster resource from the management server, triggering a
   981  	// resource-not-found error.
   982  	resources.Clusters = nil
   983  	if err := mgmtServer.Update(ctx, resources); err != nil {
   984  		t.Fatal(err)
   985  	}
   986  
   987  	// Verify that the watch for the cluster resource is not cancelled.
   988  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   989  	defer sCancel()
   990  	select {
   991  	case <-sCtx.Done():
   992  	case <-cdsResourceCanceledCh:
   993  		t.Fatal("Watch for cluster resource is cancelled when not expected to")
   994  	}
   995  
   996  	testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
   997  
   998  	// Ensure RPC fails with Unavailable status code and the error message is
   999  	// meaningful and contains the xDS node ID.
  1000  	wantErr := fmt.Sprintf("resource %q of type %q has been removed", clusterName, "ClusterResource")
  1001  	_, err := client.EmptyCall(ctx, &testpb.Empty{})
  1002  	if err := verifyRPCError(err, codes.Unavailable, wantErr, nodeID); err != nil {
  1003  		t.Fatal(err)
  1004  	}
  1005  
  1006  	// Re-add the cluster resource to the management server.
  1007  	resources = e2e.UpdateOptions{
  1008  		NodeID:         nodeID,
  1009  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
  1010  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
  1011  		SkipValidation: true,
  1012  	}
  1013  	if err := mgmtServer.Update(ctx, resources); err != nil {
  1014  		t.Fatal(err)
  1015  	}
  1016  
  1017  	// Verify that a successful RPC can be made.
  1018  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
  1019  		t.Fatalf("EmptyCall() failed: %v", err)
  1020  	}
  1021  }
  1022  
  1023  // Tests that closing the cds LB policy results in the the child policy being
  1024  // closed.
  1025  func (s) TestClose(t *testing.T) {
  1026  	cdsBalancerCh := registerWrappedCDSPolicy(t)
  1027  	_, _, _, childPolicyCloseCh := registerWrappedClusterResolverPolicy(t)
  1028  	mgmtServer, nodeID, cc, _, _, _, _ := setupWithManagementServer(t)
  1029  
  1030  	// Start a test service backend.
  1031  	server := stubserver.StartTestService(t, nil)
  1032  	t.Cleanup(server.Stop)
  1033  
  1034  	// Configure cluster and endpoints resources in the management server.
  1035  	resources := e2e.UpdateOptions{
  1036  		NodeID:         nodeID,
  1037  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
  1038  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
  1039  		SkipValidation: true,
  1040  	}
  1041  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1042  	defer cancel()
  1043  	if err := mgmtServer.Update(ctx, resources); err != nil {
  1044  		t.Fatal(err)
  1045  	}
  1046  
  1047  	// Verify that a successful RPC can be made.
  1048  	client := testgrpc.NewTestServiceClient(cc)
  1049  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
  1050  		t.Fatalf("EmptyCall() failed: %v", err)
  1051  	}
  1052  
  1053  	// Retrieve the cds LB policy and close it.
  1054  	var cdsBal balancer.Balancer
  1055  	select {
  1056  	case cdsBal = <-cdsBalancerCh:
  1057  	case <-ctx.Done():
  1058  		t.Fatal("Timeout when waiting for cds LB policy to be created")
  1059  	}
  1060  	cdsBal.Close()
  1061  
  1062  	// Wait for the child policy to be closed.
  1063  	select {
  1064  	case <-ctx.Done():
  1065  		t.Fatal("Timeout when waiting for the child policy to be closed")
  1066  	case <-childPolicyCloseCh:
  1067  	}
  1068  }
  1069  
  1070  // Tests that calling ExitIdle on the cds LB policy results in the call being
  1071  // propagated to the child policy.
  1072  func (s) TestExitIdle(t *testing.T) {
  1073  	cdsBalancerCh := registerWrappedCDSPolicy(t)
  1074  	_, _, exitIdleCh, _ := registerWrappedClusterResolverPolicy(t)
  1075  	mgmtServer, nodeID, cc, _, _, _, _ := setupWithManagementServer(t)
  1076  
  1077  	// Start a test service backend.
  1078  	server := stubserver.StartTestService(t, nil)
  1079  	t.Cleanup(server.Stop)
  1080  
  1081  	// Configure cluster and endpoints resources in the management server.
  1082  	resources := e2e.UpdateOptions{
  1083  		NodeID:         nodeID,
  1084  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
  1085  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
  1086  		SkipValidation: true,
  1087  	}
  1088  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1089  	defer cancel()
  1090  	if err := mgmtServer.Update(ctx, resources); err != nil {
  1091  		t.Fatal(err)
  1092  	}
  1093  
  1094  	// Verify that a successful RPC can be made.
  1095  	client := testgrpc.NewTestServiceClient(cc)
  1096  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
  1097  		t.Fatalf("EmptyCall() failed: %v", err)
  1098  	}
  1099  
  1100  	// Retrieve the cds LB policy and call ExitIdle() on it.
  1101  	var cdsBal balancer.Balancer
  1102  	select {
  1103  	case cdsBal = <-cdsBalancerCh:
  1104  	case <-ctx.Done():
  1105  		t.Fatal("Timeout when waiting for cds LB policy to be created")
  1106  	}
  1107  	cdsBal.ExitIdle()
  1108  
  1109  	// Wait for ExitIdle to be called on the child policy.
  1110  	select {
  1111  	case <-ctx.Done():
  1112  		t.Fatal("Timeout when waiting for the child policy to be closed")
  1113  	case <-exitIdleCh:
  1114  	}
  1115  }
  1116  
  1117  // TestParseConfig verifies the ParseConfig() method in the CDS balancer.
  1118  func (s) TestParseConfig(t *testing.T) {
  1119  	bb := balancer.Get(cdsName)
  1120  	if bb == nil {
  1121  		t.Fatalf("balancer.Get(%q) returned nil", cdsName)
  1122  	}
  1123  	parser, ok := bb.(balancer.ConfigParser)
  1124  	if !ok {
  1125  		t.Fatalf("balancer %q does not implement the ConfigParser interface", cdsName)
  1126  	}
  1127  
  1128  	tests := []struct {
  1129  		name    string
  1130  		input   json.RawMessage
  1131  		wantCfg serviceconfig.LoadBalancingConfig
  1132  		wantErr bool
  1133  	}{
  1134  		{
  1135  			name:    "good-config",
  1136  			input:   json.RawMessage(`{"Cluster": "cluster1"}`),
  1137  			wantCfg: &lbConfig{ClusterName: "cluster1"},
  1138  		},
  1139  		{
  1140  			name:    "unknown-fields-in-config",
  1141  			input:   json.RawMessage(`{"Unknown": "foobar"}`),
  1142  			wantCfg: &lbConfig{ClusterName: ""},
  1143  		},
  1144  		{
  1145  			name:    "empty-config",
  1146  			input:   json.RawMessage(""),
  1147  			wantErr: true,
  1148  		},
  1149  		{
  1150  			name:    "bad-config",
  1151  			input:   json.RawMessage(`{"Cluster": 5}`),
  1152  			wantErr: true,
  1153  		},
  1154  	}
  1155  
  1156  	for _, test := range tests {
  1157  		t.Run(test.name, func(t *testing.T) {
  1158  			gotCfg, gotErr := parser.ParseConfig(test.input)
  1159  			if (gotErr != nil) != test.wantErr {
  1160  				t.Fatalf("ParseConfig(%v) = %v, wantErr %v", string(test.input), gotErr, test.wantErr)
  1161  			}
  1162  			if test.wantErr {
  1163  				return
  1164  			}
  1165  			if !cmp.Equal(gotCfg, test.wantCfg) {
  1166  				t.Fatalf("ParseConfig(%v) = %v, want %v", string(test.input), gotCfg, test.wantCfg)
  1167  			}
  1168  		})
  1169  	}
  1170  }
  1171  
  1172  func newUint32(i uint32) *uint32 {
  1173  	return &i
  1174  }