google.golang.org/grpc@v1.62.1/xds/internal/balancer/clusterimpl/balancer_test.go (about)

     1  /*
     2   *
     3   * Copyright 2020 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 clusterimpl
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/google/go-cmp/cmp"
    30  	"github.com/google/go-cmp/cmp/cmpopts"
    31  	"google.golang.org/grpc/balancer"
    32  	"google.golang.org/grpc/balancer/base"
    33  	"google.golang.org/grpc/balancer/roundrobin"
    34  	"google.golang.org/grpc/connectivity"
    35  	"google.golang.org/grpc/internal"
    36  	"google.golang.org/grpc/internal/balancer/stub"
    37  	"google.golang.org/grpc/internal/grpctest"
    38  	internalserviceconfig "google.golang.org/grpc/internal/serviceconfig"
    39  	"google.golang.org/grpc/internal/testutils"
    40  	"google.golang.org/grpc/resolver"
    41  	xdsinternal "google.golang.org/grpc/xds/internal"
    42  	"google.golang.org/grpc/xds/internal/testutils/fakeclient"
    43  	"google.golang.org/grpc/xds/internal/xdsclient"
    44  	"google.golang.org/grpc/xds/internal/xdsclient/bootstrap"
    45  	"google.golang.org/grpc/xds/internal/xdsclient/load"
    46  )
    47  
    48  const (
    49  	defaultTestTimeout      = 5 * time.Second
    50  	defaultShortTestTimeout = 100 * time.Microsecond
    51  
    52  	testClusterName = "test-cluster"
    53  	testServiceName = "test-eds-service"
    54  )
    55  
    56  var (
    57  	testBackendAddrs = []resolver.Address{
    58  		{Addr: "1.1.1.1:1"},
    59  	}
    60  	testLRSServerConfig = &bootstrap.ServerConfig{
    61  		ServerURI: "trafficdirector.googleapis.com:443",
    62  		Creds: bootstrap.ChannelCreds{
    63  			Type: "google_default",
    64  		},
    65  	}
    66  
    67  	cmpOpts = cmp.Options{
    68  		cmpopts.EquateEmpty(),
    69  		cmpopts.IgnoreFields(load.Data{}, "ReportInterval"),
    70  	}
    71  )
    72  
    73  type s struct {
    74  	grpctest.Tester
    75  }
    76  
    77  func Test(t *testing.T) {
    78  	grpctest.RunSubTests(t, s{})
    79  }
    80  
    81  func init() {
    82  	NewRandomWRR = testutils.NewTestWRR
    83  }
    84  
    85  // TestDropByCategory verifies that the balancer correctly drops the picks, and
    86  // that the drops are reported.
    87  func (s) TestDropByCategory(t *testing.T) {
    88  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
    89  	defer cancel()
    90  
    91  	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
    92  	xdsC := fakeclient.NewClient()
    93  
    94  	builder := balancer.Get(Name)
    95  	cc := testutils.NewBalancerClientConn(t)
    96  	b := builder.Build(cc, balancer.BuildOptions{})
    97  	defer b.Close()
    98  
    99  	const (
   100  		dropReason      = "test-dropping-category"
   101  		dropNumerator   = 1
   102  		dropDenominator = 2
   103  	)
   104  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   105  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   106  		BalancerConfig: &LBConfig{
   107  			Cluster:             testClusterName,
   108  			EDSServiceName:      testServiceName,
   109  			LoadReportingServer: testLRSServerConfig,
   110  			DropCategories: []DropConfig{{
   111  				Category:           dropReason,
   112  				RequestsPerMillion: million * dropNumerator / dropDenominator,
   113  			}},
   114  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   115  				Name: roundrobin.Name,
   116  			},
   117  		},
   118  	}); err != nil {
   119  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   120  	}
   121  
   122  	got, err := xdsC.WaitForReportLoad(ctx)
   123  	if err != nil {
   124  		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
   125  	}
   126  	if got.Server != testLRSServerConfig {
   127  		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig)
   128  	}
   129  
   130  	sc1 := <-cc.NewSubConnCh
   131  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   132  	// This should get the connecting picker.
   133  	if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil {
   134  		t.Fatal(err.Error())
   135  	}
   136  
   137  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
   138  	// Test pick with one backend.
   139  
   140  	const rpcCount = 20
   141  	if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error {
   142  		for i := 0; i < rpcCount; i++ {
   143  			gotSCSt, err := p.Pick(balancer.PickInfo{})
   144  			// Even RPCs are dropped.
   145  			if i%2 == 0 {
   146  				if err == nil || !strings.Contains(err.Error(), "dropped") {
   147  					return fmt.Errorf("pick.Pick, got %v, %v, want error RPC dropped", gotSCSt, err)
   148  				}
   149  				continue
   150  			}
   151  			if err != nil || gotSCSt.SubConn != sc1 {
   152  				return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
   153  			}
   154  			if gotSCSt.Done != nil {
   155  				gotSCSt.Done(balancer.DoneInfo{})
   156  			}
   157  		}
   158  		return nil
   159  	}); err != nil {
   160  		t.Fatal(err.Error())
   161  	}
   162  
   163  	// Dump load data from the store and compare with expected counts.
   164  	loadStore := xdsC.LoadStore()
   165  	if loadStore == nil {
   166  		t.Fatal("loadStore is nil in xdsClient")
   167  	}
   168  	const dropCount = rpcCount * dropNumerator / dropDenominator
   169  	wantStatsData0 := []*load.Data{{
   170  		Cluster:    testClusterName,
   171  		Service:    testServiceName,
   172  		TotalDrops: dropCount,
   173  		Drops:      map[string]uint64{dropReason: dropCount},
   174  		LocalityStats: map[string]load.LocalityData{
   175  			assertString(xdsinternal.LocalityID{}.ToString): {RequestStats: load.RequestData{Succeeded: rpcCount - dropCount}},
   176  		},
   177  	}}
   178  
   179  	gotStatsData0 := loadStore.Stats([]string{testClusterName})
   180  	if diff := cmp.Diff(gotStatsData0, wantStatsData0, cmpOpts); diff != "" {
   181  		t.Fatalf("got unexpected reports, diff (-got, +want): %v", diff)
   182  	}
   183  
   184  	// Send an update with new drop configs.
   185  	const (
   186  		dropReason2      = "test-dropping-category-2"
   187  		dropNumerator2   = 1
   188  		dropDenominator2 = 4
   189  	)
   190  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   191  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   192  		BalancerConfig: &LBConfig{
   193  			Cluster:             testClusterName,
   194  			EDSServiceName:      testServiceName,
   195  			LoadReportingServer: testLRSServerConfig,
   196  			DropCategories: []DropConfig{{
   197  				Category:           dropReason2,
   198  				RequestsPerMillion: million * dropNumerator2 / dropDenominator2,
   199  			}},
   200  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   201  				Name: roundrobin.Name,
   202  			},
   203  		},
   204  	}); err != nil {
   205  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   206  	}
   207  
   208  	if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error {
   209  		for i := 0; i < rpcCount; i++ {
   210  			gotSCSt, err := p.Pick(balancer.PickInfo{})
   211  			// Even RPCs are dropped.
   212  			if i%4 == 0 {
   213  				if err == nil || !strings.Contains(err.Error(), "dropped") {
   214  					return fmt.Errorf("pick.Pick, got %v, %v, want error RPC dropped", gotSCSt, err)
   215  				}
   216  				continue
   217  			}
   218  			if err != nil || gotSCSt.SubConn != sc1 {
   219  				return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
   220  			}
   221  			if gotSCSt.Done != nil {
   222  				gotSCSt.Done(balancer.DoneInfo{})
   223  			}
   224  		}
   225  		return nil
   226  	}); err != nil {
   227  		t.Fatal(err.Error())
   228  	}
   229  
   230  	const dropCount2 = rpcCount * dropNumerator2 / dropDenominator2
   231  	wantStatsData1 := []*load.Data{{
   232  		Cluster:    testClusterName,
   233  		Service:    testServiceName,
   234  		TotalDrops: dropCount2,
   235  		Drops:      map[string]uint64{dropReason2: dropCount2},
   236  		LocalityStats: map[string]load.LocalityData{
   237  			assertString(xdsinternal.LocalityID{}.ToString): {RequestStats: load.RequestData{Succeeded: rpcCount - dropCount2}},
   238  		},
   239  	}}
   240  
   241  	gotStatsData1 := loadStore.Stats([]string{testClusterName})
   242  	if diff := cmp.Diff(gotStatsData1, wantStatsData1, cmpOpts); diff != "" {
   243  		t.Fatalf("got unexpected reports, diff (-got, +want): %v", diff)
   244  	}
   245  }
   246  
   247  // TestDropCircuitBreaking verifies that the balancer correctly drops the picks
   248  // due to circuit breaking, and that the drops are reported.
   249  func (s) TestDropCircuitBreaking(t *testing.T) {
   250  	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
   251  	xdsC := fakeclient.NewClient()
   252  
   253  	builder := balancer.Get(Name)
   254  	cc := testutils.NewBalancerClientConn(t)
   255  	b := builder.Build(cc, balancer.BuildOptions{})
   256  	defer b.Close()
   257  
   258  	var maxRequest uint32 = 50
   259  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   260  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   261  		BalancerConfig: &LBConfig{
   262  			Cluster:               testClusterName,
   263  			EDSServiceName:        testServiceName,
   264  			LoadReportingServer:   testLRSServerConfig,
   265  			MaxConcurrentRequests: &maxRequest,
   266  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   267  				Name: roundrobin.Name,
   268  			},
   269  		},
   270  	}); err != nil {
   271  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   272  	}
   273  
   274  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   275  	defer cancel()
   276  
   277  	got, err := xdsC.WaitForReportLoad(ctx)
   278  	if err != nil {
   279  		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
   280  	}
   281  	if got.Server != testLRSServerConfig {
   282  		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig)
   283  	}
   284  
   285  	sc1 := <-cc.NewSubConnCh
   286  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   287  	// This should get the connecting picker.
   288  	if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil {
   289  		t.Fatal(err.Error())
   290  	}
   291  
   292  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
   293  	// Test pick with one backend.
   294  	const rpcCount = 100
   295  	if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error {
   296  		dones := []func(){}
   297  		for i := 0; i < rpcCount; i++ {
   298  			gotSCSt, err := p.Pick(balancer.PickInfo{})
   299  			if i < 50 && err != nil {
   300  				return fmt.Errorf("The first 50%% picks should be non-drops, got error %v", err)
   301  			} else if i > 50 && err == nil {
   302  				return fmt.Errorf("The second 50%% picks should be drops, got error <nil>")
   303  			}
   304  			dones = append(dones, func() {
   305  				if gotSCSt.Done != nil {
   306  					gotSCSt.Done(balancer.DoneInfo{})
   307  				}
   308  			})
   309  		}
   310  		for _, done := range dones {
   311  			done()
   312  		}
   313  
   314  		dones = []func(){}
   315  		// Pick without drops.
   316  		for i := 0; i < 50; i++ {
   317  			gotSCSt, err := p.Pick(balancer.PickInfo{})
   318  			if err != nil {
   319  				t.Errorf("The third 50%% picks should be non-drops, got error %v", err)
   320  			}
   321  			dones = append(dones, func() {
   322  				if gotSCSt.Done != nil {
   323  					gotSCSt.Done(balancer.DoneInfo{})
   324  				}
   325  			})
   326  		}
   327  		for _, done := range dones {
   328  			done()
   329  		}
   330  
   331  		return nil
   332  	}); err != nil {
   333  		t.Fatal(err.Error())
   334  	}
   335  
   336  	// Dump load data from the store and compare with expected counts.
   337  	loadStore := xdsC.LoadStore()
   338  	if loadStore == nil {
   339  		t.Fatal("loadStore is nil in xdsClient")
   340  	}
   341  
   342  	wantStatsData0 := []*load.Data{{
   343  		Cluster:    testClusterName,
   344  		Service:    testServiceName,
   345  		TotalDrops: uint64(maxRequest),
   346  		LocalityStats: map[string]load.LocalityData{
   347  			assertString(xdsinternal.LocalityID{}.ToString): {RequestStats: load.RequestData{Succeeded: uint64(rpcCount - maxRequest + 50)}},
   348  		},
   349  	}}
   350  
   351  	gotStatsData0 := loadStore.Stats([]string{testClusterName})
   352  	if diff := cmp.Diff(gotStatsData0, wantStatsData0, cmpOpts); diff != "" {
   353  		t.Fatalf("got unexpected drop reports, diff (-got, +want): %v", diff)
   354  	}
   355  }
   356  
   357  // TestPickerUpdateAfterClose covers the case where a child policy sends a
   358  // picker update after the cluster_impl policy is closed. Because picker updates
   359  // are handled in the run() goroutine, which exits before Close() returns, we
   360  // expect the above picker update to be dropped.
   361  func (s) TestPickerUpdateAfterClose(t *testing.T) {
   362  	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
   363  	xdsC := fakeclient.NewClient()
   364  
   365  	builder := balancer.Get(Name)
   366  	cc := testutils.NewBalancerClientConn(t)
   367  	b := builder.Build(cc, balancer.BuildOptions{})
   368  
   369  	// Create a stub balancer which waits for the cluster_impl policy to be
   370  	// closed before sending a picker update (upon receipt of a subConn state
   371  	// change).
   372  	closeCh := make(chan struct{})
   373  	const childPolicyName = "stubBalancer-TestPickerUpdateAfterClose"
   374  	stub.Register(childPolicyName, stub.BalancerFuncs{
   375  		UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
   376  			// Create a subConn which will be used later on to test the race
   377  			// between StateListener() and Close().
   378  			sc, err := bd.ClientConn.NewSubConn(ccs.ResolverState.Addresses, balancer.NewSubConnOptions{
   379  				StateListener: func(balancer.SubConnState) {
   380  					go func() {
   381  						// Wait for Close() to be called on the parent policy before
   382  						// sending the picker update.
   383  						<-closeCh
   384  						bd.ClientConn.UpdateState(balancer.State{
   385  							Picker: base.NewErrPicker(errors.New("dummy error picker")),
   386  						})
   387  					}()
   388  				},
   389  			})
   390  			if err != nil {
   391  				return err
   392  			}
   393  			sc.Connect()
   394  			return nil
   395  		},
   396  	})
   397  
   398  	var maxRequest uint32 = 50
   399  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   400  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   401  		BalancerConfig: &LBConfig{
   402  			Cluster:               testClusterName,
   403  			EDSServiceName:        testServiceName,
   404  			MaxConcurrentRequests: &maxRequest,
   405  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   406  				Name: childPolicyName,
   407  			},
   408  		},
   409  	}); err != nil {
   410  		b.Close()
   411  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   412  	}
   413  
   414  	// Send a subConn state change to trigger a picker update. The stub balancer
   415  	// that we use as the child policy will not send a picker update until the
   416  	// parent policy is closed.
   417  	sc1 := <-cc.NewSubConnCh
   418  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   419  	b.Close()
   420  	close(closeCh)
   421  
   422  	select {
   423  	case <-cc.NewPickerCh:
   424  		t.Fatalf("unexpected picker update after balancer is closed")
   425  	case <-time.After(defaultShortTestTimeout):
   426  	}
   427  }
   428  
   429  // TestClusterNameInAddressAttributes covers the case that cluster name is
   430  // attached to the subconn address attributes.
   431  func (s) TestClusterNameInAddressAttributes(t *testing.T) {
   432  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   433  	defer cancel()
   434  
   435  	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
   436  	xdsC := fakeclient.NewClient()
   437  
   438  	builder := balancer.Get(Name)
   439  	cc := testutils.NewBalancerClientConn(t)
   440  	b := builder.Build(cc, balancer.BuildOptions{})
   441  	defer b.Close()
   442  
   443  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   444  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   445  		BalancerConfig: &LBConfig{
   446  			Cluster:        testClusterName,
   447  			EDSServiceName: testServiceName,
   448  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   449  				Name: roundrobin.Name,
   450  			},
   451  		},
   452  	}); err != nil {
   453  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   454  	}
   455  
   456  	sc1 := <-cc.NewSubConnCh
   457  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   458  	// This should get the connecting picker.
   459  	if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil {
   460  		t.Fatal(err.Error())
   461  	}
   462  
   463  	addrs1 := <-cc.NewSubConnAddrsCh
   464  	if got, want := addrs1[0].Addr, testBackendAddrs[0].Addr; got != want {
   465  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   466  	}
   467  	cn, ok := internal.GetXDSHandshakeClusterName(addrs1[0].Attributes)
   468  	if !ok || cn != testClusterName {
   469  		t.Fatalf("sc is created with addr with cluster name %v, %v, want cluster name %v", cn, ok, testClusterName)
   470  	}
   471  
   472  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
   473  	// Test pick with one backend.
   474  	if err := cc.WaitForRoundRobinPicker(ctx, sc1); err != nil {
   475  		t.Fatal(err.Error())
   476  	}
   477  
   478  	const testClusterName2 = "test-cluster-2"
   479  	var addr2 = resolver.Address{Addr: "2.2.2.2"}
   480  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   481  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: []resolver.Address{addr2}}, xdsC),
   482  		BalancerConfig: &LBConfig{
   483  			Cluster:        testClusterName2,
   484  			EDSServiceName: testServiceName,
   485  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   486  				Name: roundrobin.Name,
   487  			},
   488  		},
   489  	}); err != nil {
   490  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   491  	}
   492  
   493  	addrs2 := <-cc.NewSubConnAddrsCh
   494  	if got, want := addrs2[0].Addr, addr2.Addr; got != want {
   495  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   496  	}
   497  	// New addresses should have the new cluster name.
   498  	cn2, ok := internal.GetXDSHandshakeClusterName(addrs2[0].Attributes)
   499  	if !ok || cn2 != testClusterName2 {
   500  		t.Fatalf("sc is created with addr with cluster name %v, %v, want cluster name %v", cn2, ok, testClusterName2)
   501  	}
   502  }
   503  
   504  // TestReResolution verifies that when a SubConn turns transient failure,
   505  // re-resolution is triggered.
   506  func (s) TestReResolution(t *testing.T) {
   507  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   508  	defer cancel()
   509  
   510  	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
   511  	xdsC := fakeclient.NewClient()
   512  
   513  	builder := balancer.Get(Name)
   514  	cc := testutils.NewBalancerClientConn(t)
   515  	b := builder.Build(cc, balancer.BuildOptions{})
   516  	defer b.Close()
   517  
   518  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   519  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   520  		BalancerConfig: &LBConfig{
   521  			Cluster:        testClusterName,
   522  			EDSServiceName: testServiceName,
   523  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   524  				Name: roundrobin.Name,
   525  			},
   526  		},
   527  	}); err != nil {
   528  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   529  	}
   530  
   531  	sc1 := <-cc.NewSubConnCh
   532  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   533  	// This should get the connecting picker.
   534  	if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil {
   535  		t.Fatal(err.Error())
   536  	}
   537  
   538  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   539  	// This should get the transient failure picker.
   540  	if err := cc.WaitForErrPicker(ctx); err != nil {
   541  		t.Fatal(err.Error())
   542  	}
   543  
   544  	// The transient failure should trigger a re-resolution.
   545  	select {
   546  	case <-cc.ResolveNowCh:
   547  	case <-time.After(defaultTestTimeout):
   548  		t.Fatalf("timeout waiting for ResolveNow()")
   549  	}
   550  
   551  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
   552  	// Test pick with one backend.
   553  	if err := cc.WaitForRoundRobinPicker(ctx, sc1); err != nil {
   554  		t.Fatal(err.Error())
   555  	}
   556  
   557  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   558  	// This should get the transient failure picker.
   559  	if err := cc.WaitForErrPicker(ctx); err != nil {
   560  		t.Fatal(err.Error())
   561  	}
   562  
   563  	// The transient failure should trigger a re-resolution.
   564  	select {
   565  	case <-cc.ResolveNowCh:
   566  	case <-time.After(defaultTestTimeout):
   567  		t.Fatalf("timeout waiting for ResolveNow()")
   568  	}
   569  }
   570  
   571  func (s) TestLoadReporting(t *testing.T) {
   572  	var testLocality = xdsinternal.LocalityID{
   573  		Region:  "test-region",
   574  		Zone:    "test-zone",
   575  		SubZone: "test-sub-zone",
   576  	}
   577  
   578  	xdsC := fakeclient.NewClient()
   579  
   580  	builder := balancer.Get(Name)
   581  	cc := testutils.NewBalancerClientConn(t)
   582  	b := builder.Build(cc, balancer.BuildOptions{})
   583  	defer b.Close()
   584  
   585  	addrs := make([]resolver.Address, len(testBackendAddrs))
   586  	for i, a := range testBackendAddrs {
   587  		addrs[i] = xdsinternal.SetLocalityID(a, testLocality)
   588  	}
   589  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   590  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC),
   591  		BalancerConfig: &LBConfig{
   592  			Cluster:             testClusterName,
   593  			EDSServiceName:      testServiceName,
   594  			LoadReportingServer: testLRSServerConfig,
   595  			// Locality:                testLocality,
   596  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   597  				Name: roundrobin.Name,
   598  			},
   599  		},
   600  	}); err != nil {
   601  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   602  	}
   603  
   604  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   605  	defer cancel()
   606  
   607  	got, err := xdsC.WaitForReportLoad(ctx)
   608  	if err != nil {
   609  		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
   610  	}
   611  	if got.Server != testLRSServerConfig {
   612  		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig)
   613  	}
   614  
   615  	sc1 := <-cc.NewSubConnCh
   616  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   617  	// This should get the connecting picker.
   618  	if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil {
   619  		t.Fatal(err.Error())
   620  	}
   621  
   622  	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
   623  	// Test pick with one backend.
   624  	const successCount = 5
   625  	const errorCount = 5
   626  	if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error {
   627  		for i := 0; i < successCount; i++ {
   628  			gotSCSt, err := p.Pick(balancer.PickInfo{})
   629  			if gotSCSt.SubConn != sc1 {
   630  				return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
   631  			}
   632  			gotSCSt.Done(balancer.DoneInfo{})
   633  		}
   634  		for i := 0; i < errorCount; i++ {
   635  			gotSCSt, err := p.Pick(balancer.PickInfo{})
   636  			if gotSCSt.SubConn != sc1 {
   637  				return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
   638  			}
   639  			gotSCSt.Done(balancer.DoneInfo{Err: fmt.Errorf("error")})
   640  		}
   641  		return nil
   642  	}); err != nil {
   643  		t.Fatal(err.Error())
   644  	}
   645  
   646  	// Dump load data from the store and compare with expected counts.
   647  	loadStore := xdsC.LoadStore()
   648  	if loadStore == nil {
   649  		t.Fatal("loadStore is nil in xdsClient")
   650  	}
   651  	sds := loadStore.Stats([]string{testClusterName})
   652  	if len(sds) == 0 {
   653  		t.Fatalf("loads for cluster %v not found in store", testClusterName)
   654  	}
   655  	sd := sds[0]
   656  	if sd.Cluster != testClusterName || sd.Service != testServiceName {
   657  		t.Fatalf("got unexpected load for %q, %q, want %q, %q", sd.Cluster, sd.Service, testClusterName, testServiceName)
   658  	}
   659  	testLocalityJSON, _ := testLocality.ToString()
   660  	localityData, ok := sd.LocalityStats[testLocalityJSON]
   661  	if !ok {
   662  		t.Fatalf("loads for %v not found in store", testLocality)
   663  	}
   664  	reqStats := localityData.RequestStats
   665  	if reqStats.Succeeded != successCount {
   666  		t.Errorf("got succeeded %v, want %v", reqStats.Succeeded, successCount)
   667  	}
   668  	if reqStats.Errored != errorCount {
   669  		t.Errorf("got errord %v, want %v", reqStats.Errored, errorCount)
   670  	}
   671  	if reqStats.InProgress != 0 {
   672  		t.Errorf("got inProgress %v, want %v", reqStats.InProgress, 0)
   673  	}
   674  
   675  	b.Close()
   676  	if err := xdsC.WaitForCancelReportLoad(ctx); err != nil {
   677  		t.Fatalf("unexpected error waiting form load report to be canceled: %v", err)
   678  	}
   679  }
   680  
   681  // TestUpdateLRSServer covers the cases
   682  // - the init config specifies "" as the LRS server
   683  // - config modifies LRS server to a different string
   684  // - config sets LRS server to nil to stop load reporting
   685  func (s) TestUpdateLRSServer(t *testing.T) {
   686  	var testLocality = xdsinternal.LocalityID{
   687  		Region:  "test-region",
   688  		Zone:    "test-zone",
   689  		SubZone: "test-sub-zone",
   690  	}
   691  
   692  	xdsC := fakeclient.NewClient()
   693  
   694  	builder := balancer.Get(Name)
   695  	cc := testutils.NewBalancerClientConn(t)
   696  	b := builder.Build(cc, balancer.BuildOptions{})
   697  	defer b.Close()
   698  
   699  	addrs := make([]resolver.Address, len(testBackendAddrs))
   700  	for i, a := range testBackendAddrs {
   701  		addrs[i] = xdsinternal.SetLocalityID(a, testLocality)
   702  	}
   703  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   704  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC),
   705  		BalancerConfig: &LBConfig{
   706  			Cluster:             testClusterName,
   707  			EDSServiceName:      testServiceName,
   708  			LoadReportingServer: testLRSServerConfig,
   709  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   710  				Name: roundrobin.Name,
   711  			},
   712  		},
   713  	}); err != nil {
   714  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   715  	}
   716  
   717  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   718  	defer cancel()
   719  
   720  	got, err := xdsC.WaitForReportLoad(ctx)
   721  	if err != nil {
   722  		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
   723  	}
   724  	if got.Server != testLRSServerConfig {
   725  		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig)
   726  	}
   727  
   728  	testLRSServerConfig2 := &bootstrap.ServerConfig{
   729  		ServerURI: "trafficdirector-another.googleapis.com:443",
   730  		Creds: bootstrap.ChannelCreds{
   731  			Type: "google_default",
   732  		},
   733  	}
   734  	// Update LRS server to a different name.
   735  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   736  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC),
   737  		BalancerConfig: &LBConfig{
   738  			Cluster:             testClusterName,
   739  			EDSServiceName:      testServiceName,
   740  			LoadReportingServer: testLRSServerConfig2,
   741  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   742  				Name: roundrobin.Name,
   743  			},
   744  		},
   745  	}); err != nil {
   746  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   747  	}
   748  	if err := xdsC.WaitForCancelReportLoad(ctx); err != nil {
   749  		t.Fatalf("unexpected error waiting form load report to be canceled: %v", err)
   750  	}
   751  	got2, err2 := xdsC.WaitForReportLoad(ctx)
   752  	if err2 != nil {
   753  		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err2)
   754  	}
   755  	if got2.Server != testLRSServerConfig2 {
   756  		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got2.Server, testLRSServerConfig2)
   757  	}
   758  
   759  	// Update LRS server to nil, to disable LRS.
   760  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   761  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC),
   762  		BalancerConfig: &LBConfig{
   763  			Cluster:        testClusterName,
   764  			EDSServiceName: testServiceName,
   765  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   766  				Name: roundrobin.Name,
   767  			},
   768  		},
   769  	}); err != nil {
   770  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   771  	}
   772  	if err := xdsC.WaitForCancelReportLoad(ctx); err != nil {
   773  		t.Fatalf("unexpected error waiting form load report to be canceled: %v", err)
   774  	}
   775  
   776  	shortCtx, shortCancel := context.WithTimeout(context.Background(), defaultShortTestTimeout)
   777  	defer shortCancel()
   778  	if s, err := xdsC.WaitForReportLoad(shortCtx); err != context.DeadlineExceeded {
   779  		t.Fatalf("unexpected load report to server: %q", s)
   780  	}
   781  }
   782  
   783  func assertString(f func() (string, error)) string {
   784  	s, err := f()
   785  	if err != nil {
   786  		panic(err.Error())
   787  	}
   788  	return s
   789  }