github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/clusterresolver/clusterresolver_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 clusterresolver
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	"github.com/hxx258456/ccgo/grpc/balancer"
    29  	"github.com/hxx258456/ccgo/grpc/connectivity"
    30  	"github.com/hxx258456/ccgo/grpc/internal/grpctest"
    31  	"github.com/hxx258456/ccgo/grpc/internal/testutils"
    32  	"github.com/hxx258456/ccgo/grpc/resolver"
    33  	"github.com/hxx258456/ccgo/grpc/xds/internal"
    34  	"github.com/hxx258456/ccgo/grpc/xds/internal/testutils/fakeclient"
    35  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient"
    36  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource"
    37  
    38  	_ "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/controller/version/v2" // V2 client registration.
    39  )
    40  
    41  const (
    42  	defaultTestTimeout      = 1 * time.Second
    43  	defaultTestShortTimeout = 10 * time.Millisecond
    44  	testEDSServcie          = "test-eds-service-name"
    45  	testClusterName         = "test-cluster-name"
    46  )
    47  
    48  var (
    49  	// A non-empty endpoints update which is expected to be accepted by the EDS
    50  	// LB policy.
    51  	defaultEndpointsUpdate = xdsresource.EndpointsUpdate{
    52  		Localities: []xdsresource.Locality{
    53  			{
    54  				Endpoints: []xdsresource.Endpoint{{Address: "endpoint1"}},
    55  				ID:        internal.LocalityID{Zone: "zone"},
    56  				Priority:  1,
    57  				Weight:    100,
    58  			},
    59  		},
    60  	}
    61  )
    62  
    63  func init() {
    64  	balancer.Register(bb{})
    65  }
    66  
    67  type s struct {
    68  	grpctest.Tester
    69  
    70  	cleanup func()
    71  }
    72  
    73  func (ss s) Teardown(t *testing.T) {
    74  	xdsclient.ClearAllCountersForTesting()
    75  	ss.Tester.Teardown(t)
    76  	if ss.cleanup != nil {
    77  		ss.cleanup()
    78  	}
    79  }
    80  
    81  func Test(t *testing.T) {
    82  	grpctest.RunSubTests(t, s{})
    83  }
    84  
    85  const testBalancerNameFooBar = "foo.bar"
    86  
    87  func newNoopTestClientConn() *noopTestClientConn {
    88  	return &noopTestClientConn{}
    89  }
    90  
    91  // noopTestClientConn is used in EDS balancer config update tests that only
    92  // cover the config update handling, but not SubConn/load-balancing.
    93  type noopTestClientConn struct {
    94  	balancer.ClientConn
    95  }
    96  
    97  func (t *noopTestClientConn) NewSubConn([]resolver.Address, balancer.NewSubConnOptions) (balancer.SubConn, error) {
    98  	return nil, nil
    99  }
   100  
   101  func (noopTestClientConn) Target() string { return testEDSServcie }
   102  
   103  type scStateChange struct {
   104  	sc    balancer.SubConn
   105  	state balancer.SubConnState
   106  }
   107  
   108  type fakeChildBalancer struct {
   109  	cc              balancer.ClientConn
   110  	subConnState    *testutils.Channel
   111  	clientConnState *testutils.Channel
   112  	resolverError   *testutils.Channel
   113  }
   114  
   115  func (f *fakeChildBalancer) UpdateClientConnState(state balancer.ClientConnState) error {
   116  	f.clientConnState.Send(state)
   117  	return nil
   118  }
   119  
   120  func (f *fakeChildBalancer) ResolverError(err error) {
   121  	f.resolverError.Send(err)
   122  }
   123  
   124  func (f *fakeChildBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
   125  	f.subConnState.Send(&scStateChange{sc: sc, state: state})
   126  }
   127  
   128  func (f *fakeChildBalancer) Close() {}
   129  
   130  func (f *fakeChildBalancer) ExitIdle() {}
   131  
   132  func (f *fakeChildBalancer) waitForClientConnStateChange(ctx context.Context) error {
   133  	_, err := f.clientConnState.Receive(ctx)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	return nil
   138  }
   139  
   140  func (f *fakeChildBalancer) waitForResolverError(ctx context.Context) error {
   141  	_, err := f.resolverError.Receive(ctx)
   142  	if err != nil {
   143  		return err
   144  	}
   145  	return nil
   146  }
   147  
   148  func (f *fakeChildBalancer) waitForSubConnStateChange(ctx context.Context, wantState *scStateChange) error {
   149  	val, err := f.subConnState.Receive(ctx)
   150  	if err != nil {
   151  		return err
   152  	}
   153  	gotState := val.(*scStateChange)
   154  	if !cmp.Equal(gotState, wantState, cmp.AllowUnexported(scStateChange{})) {
   155  		return fmt.Errorf("got subconnStateChange %v, want %v", gotState, wantState)
   156  	}
   157  	return nil
   158  }
   159  
   160  func newFakeChildBalancer(cc balancer.ClientConn) balancer.Balancer {
   161  	return &fakeChildBalancer{
   162  		cc:              cc,
   163  		subConnState:    testutils.NewChannelWithSize(10),
   164  		clientConnState: testutils.NewChannelWithSize(10),
   165  		resolverError:   testutils.NewChannelWithSize(10),
   166  	}
   167  }
   168  
   169  type fakeSubConn struct{}
   170  
   171  func (*fakeSubConn) UpdateAddresses([]resolver.Address) { panic("implement me") }
   172  func (*fakeSubConn) Connect()                           { panic("implement me") }
   173  
   174  // waitForNewChildLB makes sure that a new child LB is created by the top-level
   175  // clusterResolverBalancer.
   176  func waitForNewChildLB(ctx context.Context, ch *testutils.Channel) (*fakeChildBalancer, error) {
   177  	val, err := ch.Receive(ctx)
   178  	if err != nil {
   179  		return nil, fmt.Errorf("error when waiting for a new edsLB: %v", err)
   180  	}
   181  	return val.(*fakeChildBalancer), nil
   182  }
   183  
   184  // setup overrides the functions which are used to create the xdsClient and the
   185  // edsLB, creates fake version of them and makes them available on the provided
   186  // channels. The returned cancel function should be called by the test for
   187  // cleanup.
   188  func setup(childLBCh *testutils.Channel) (*fakeclient.Client, func()) {
   189  	xdsC := fakeclient.NewClientWithName(testBalancerNameFooBar)
   190  
   191  	origNewChildBalancer := newChildBalancer
   192  	newChildBalancer = func(_ balancer.Builder, cc balancer.ClientConn, _ balancer.BuildOptions) balancer.Balancer {
   193  		childLB := newFakeChildBalancer(cc)
   194  		defer func() { childLBCh.Send(childLB) }()
   195  		return childLB
   196  	}
   197  	return xdsC, func() {
   198  		newChildBalancer = origNewChildBalancer
   199  		xdsC.Close()
   200  	}
   201  }
   202  
   203  // TestSubConnStateChange verifies if the top-level clusterResolverBalancer passes on
   204  // the subConnState to appropriate child balancer.
   205  func (s) TestSubConnStateChange(t *testing.T) {
   206  	edsLBCh := testutils.NewChannel()
   207  	xdsC, cleanup := setup(edsLBCh)
   208  	defer cleanup()
   209  
   210  	builder := balancer.Get(Name)
   211  	edsB := builder.Build(newNoopTestClientConn(), balancer.BuildOptions{})
   212  	if edsB == nil {
   213  		t.Fatalf("builder.Build(%s) failed and returned nil", Name)
   214  	}
   215  	defer edsB.Close()
   216  
   217  	if err := edsB.UpdateClientConnState(balancer.ClientConnState{
   218  		ResolverState:  xdsclient.SetClient(resolver.State{}, xdsC),
   219  		BalancerConfig: newLBConfigWithOneEDS(testEDSServcie),
   220  	}); err != nil {
   221  		t.Fatalf("edsB.UpdateClientConnState() failed: %v", err)
   222  	}
   223  
   224  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   225  	defer cancel()
   226  	if _, err := xdsC.WaitForWatchEDS(ctx); err != nil {
   227  		t.Fatalf("xdsClient.WatchEndpoints failed with error: %v", err)
   228  	}
   229  	xdsC.InvokeWatchEDSCallback("", defaultEndpointsUpdate, nil)
   230  	edsLB, err := waitForNewChildLB(ctx, edsLBCh)
   231  	if err != nil {
   232  		t.Fatal(err)
   233  	}
   234  
   235  	fsc := &fakeSubConn{}
   236  	state := balancer.SubConnState{ConnectivityState: connectivity.Ready}
   237  	edsB.UpdateSubConnState(fsc, state)
   238  	if err := edsLB.waitForSubConnStateChange(ctx, &scStateChange{sc: fsc, state: state}); err != nil {
   239  		t.Fatal(err)
   240  	}
   241  }
   242  
   243  // TestErrorFromXDSClientUpdate verifies that an error from xdsClient update is
   244  // handled correctly.
   245  //
   246  // If it's resource-not-found, watch will NOT be canceled, the EDS impl will
   247  // receive an empty EDS update, and new RPCs will fail.
   248  //
   249  // If it's connection error, nothing will happen. This will need to change to
   250  // handle fallback.
   251  func (s) TestErrorFromXDSClientUpdate(t *testing.T) {
   252  	edsLBCh := testutils.NewChannel()
   253  	xdsC, cleanup := setup(edsLBCh)
   254  	defer cleanup()
   255  
   256  	builder := balancer.Get(Name)
   257  	edsB := builder.Build(newNoopTestClientConn(), balancer.BuildOptions{})
   258  	if edsB == nil {
   259  		t.Fatalf("builder.Build(%s) failed and returned nil", Name)
   260  	}
   261  	defer edsB.Close()
   262  
   263  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   264  	defer cancel()
   265  	if err := edsB.UpdateClientConnState(balancer.ClientConnState{
   266  		ResolverState:  xdsclient.SetClient(resolver.State{}, xdsC),
   267  		BalancerConfig: newLBConfigWithOneEDS(testEDSServcie),
   268  	}); err != nil {
   269  		t.Fatal(err)
   270  	}
   271  	if _, err := xdsC.WaitForWatchEDS(ctx); err != nil {
   272  		t.Fatalf("xdsClient.WatchEndpoints failed with error: %v", err)
   273  	}
   274  	xdsC.InvokeWatchEDSCallback("", xdsresource.EndpointsUpdate{}, nil)
   275  	edsLB, err := waitForNewChildLB(ctx, edsLBCh)
   276  	if err != nil {
   277  		t.Fatal(err)
   278  	}
   279  	if err := edsLB.waitForClientConnStateChange(ctx); err != nil {
   280  		t.Fatalf("EDS impl got unexpected update: %v", err)
   281  	}
   282  
   283  	connectionErr := xdsresource.NewErrorf(xdsresource.ErrorTypeConnection, "connection error")
   284  	xdsC.InvokeWatchEDSCallback("", xdsresource.EndpointsUpdate{}, connectionErr)
   285  
   286  	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   287  	defer sCancel()
   288  	if _, err := xdsC.WaitForCancelEDSWatch(sCtx); err != context.DeadlineExceeded {
   289  		t.Fatal("watch was canceled, want not canceled (timeout error)")
   290  	}
   291  
   292  	sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
   293  	defer sCancel()
   294  	if err := edsLB.waitForClientConnStateChange(sCtx); err != context.DeadlineExceeded {
   295  		t.Fatal(err)
   296  	}
   297  	if err := edsLB.waitForResolverError(ctx); err != nil {
   298  		t.Fatalf("want resolver error, got %v", err)
   299  	}
   300  
   301  	resourceErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "clusterResolverBalancer resource not found error")
   302  	xdsC.InvokeWatchEDSCallback("", xdsresource.EndpointsUpdate{}, resourceErr)
   303  	// Even if error is resource not found, watch shouldn't be canceled, because
   304  	// this is an EDS resource removed (and xds client actually never sends this
   305  	// error, but we still handles it).
   306  	sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
   307  	defer sCancel()
   308  	if _, err := xdsC.WaitForCancelEDSWatch(sCtx); err != context.DeadlineExceeded {
   309  		t.Fatal("watch was canceled, want not canceled (timeout error)")
   310  	}
   311  	if err := edsLB.waitForClientConnStateChange(sCtx); err != context.DeadlineExceeded {
   312  		t.Fatal(err)
   313  	}
   314  	if err := edsLB.waitForResolverError(ctx); err != nil {
   315  		t.Fatalf("want resolver error, got %v", err)
   316  	}
   317  
   318  	// An update with the same service name should not trigger a new watch.
   319  	if err := edsB.UpdateClientConnState(balancer.ClientConnState{
   320  		ResolverState:  xdsclient.SetClient(resolver.State{}, xdsC),
   321  		BalancerConfig: newLBConfigWithOneEDS(testEDSServcie),
   322  	}); err != nil {
   323  		t.Fatal(err)
   324  	}
   325  	sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
   326  	defer sCancel()
   327  	if _, err := xdsC.WaitForWatchEDS(sCtx); err != context.DeadlineExceeded {
   328  		t.Fatal("got unexpected new EDS watch")
   329  	}
   330  }
   331  
   332  // TestErrorFromResolver verifies that resolver errors are handled correctly.
   333  //
   334  // If it's resource-not-found, watch will be canceled, the EDS impl will receive
   335  // an empty EDS update, and new RPCs will fail.
   336  //
   337  // If it's connection error, nothing will happen. This will need to change to
   338  // handle fallback.
   339  func (s) TestErrorFromResolver(t *testing.T) {
   340  	edsLBCh := testutils.NewChannel()
   341  	xdsC, cleanup := setup(edsLBCh)
   342  	defer cleanup()
   343  
   344  	builder := balancer.Get(Name)
   345  	edsB := builder.Build(newNoopTestClientConn(), balancer.BuildOptions{})
   346  	if edsB == nil {
   347  		t.Fatalf("builder.Build(%s) failed and returned nil", Name)
   348  	}
   349  	defer edsB.Close()
   350  
   351  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   352  	defer cancel()
   353  	if err := edsB.UpdateClientConnState(balancer.ClientConnState{
   354  		ResolverState:  xdsclient.SetClient(resolver.State{}, xdsC),
   355  		BalancerConfig: newLBConfigWithOneEDS(testEDSServcie),
   356  	}); err != nil {
   357  		t.Fatal(err)
   358  	}
   359  
   360  	if _, err := xdsC.WaitForWatchEDS(ctx); err != nil {
   361  		t.Fatalf("xdsClient.WatchEndpoints failed with error: %v", err)
   362  	}
   363  	xdsC.InvokeWatchEDSCallback("", xdsresource.EndpointsUpdate{}, nil)
   364  	edsLB, err := waitForNewChildLB(ctx, edsLBCh)
   365  	if err != nil {
   366  		t.Fatal(err)
   367  	}
   368  	if err := edsLB.waitForClientConnStateChange(ctx); err != nil {
   369  		t.Fatalf("EDS impl got unexpected update: %v", err)
   370  	}
   371  
   372  	connectionErr := xdsresource.NewErrorf(xdsresource.ErrorTypeConnection, "connection error")
   373  	edsB.ResolverError(connectionErr)
   374  
   375  	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   376  	defer sCancel()
   377  	if _, err := xdsC.WaitForCancelEDSWatch(sCtx); err != context.DeadlineExceeded {
   378  		t.Fatal("watch was canceled, want not canceled (timeout error)")
   379  	}
   380  
   381  	sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
   382  	defer sCancel()
   383  	if err := edsLB.waitForClientConnStateChange(sCtx); err != context.DeadlineExceeded {
   384  		t.Fatal("eds impl got EDS resp, want timeout error")
   385  	}
   386  	if err := edsLB.waitForResolverError(ctx); err != nil {
   387  		t.Fatalf("want resolver error, got %v", err)
   388  	}
   389  
   390  	resourceErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "clusterResolverBalancer resource not found error")
   391  	edsB.ResolverError(resourceErr)
   392  	if _, err := xdsC.WaitForCancelEDSWatch(ctx); err != nil {
   393  		t.Fatalf("want watch to be canceled, waitForCancel failed: %v", err)
   394  	}
   395  	if err := edsLB.waitForClientConnStateChange(sCtx); err != context.DeadlineExceeded {
   396  		t.Fatal(err)
   397  	}
   398  	if err := edsLB.waitForResolverError(ctx); err != nil {
   399  		t.Fatalf("want resolver error, got %v", err)
   400  	}
   401  
   402  	// An update with the same service name should trigger a new watch, because
   403  	// the previous watch was canceled.
   404  	if err := edsB.UpdateClientConnState(balancer.ClientConnState{
   405  		ResolverState:  xdsclient.SetClient(resolver.State{}, xdsC),
   406  		BalancerConfig: newLBConfigWithOneEDS(testEDSServcie),
   407  	}); err != nil {
   408  		t.Fatal(err)
   409  	}
   410  	if _, err := xdsC.WaitForWatchEDS(ctx); err != nil {
   411  		t.Fatalf("xdsClient.WatchEndpoints failed with error: %v", err)
   412  	}
   413  }
   414  
   415  // Given a list of resource names, verifies that EDS requests for the same are
   416  // sent by the EDS balancer, through the fake xDS client.
   417  func verifyExpectedRequests(ctx context.Context, fc *fakeclient.Client, resourceNames ...string) error {
   418  	for _, name := range resourceNames {
   419  		if name == "" {
   420  			// ResourceName empty string indicates a cancel.
   421  			if _, err := fc.WaitForCancelEDSWatch(ctx); err != nil {
   422  				return fmt.Errorf("timed out when expecting resource %q", name)
   423  			}
   424  			continue
   425  		}
   426  
   427  		resName, err := fc.WaitForWatchEDS(ctx)
   428  		if err != nil {
   429  			return fmt.Errorf("timed out when expecting resource %q, %p", name, fc)
   430  		}
   431  		if resName != name {
   432  			return fmt.Errorf("got EDS request for resource %q, expected: %q", resName, name)
   433  		}
   434  	}
   435  	return nil
   436  }
   437  
   438  // TestClientWatchEDS verifies that the xdsClient inside the top-level EDS LB
   439  // policy registers an EDS watch for expected resource upon receiving an update
   440  // from gRPC.
   441  func (s) TestClientWatchEDS(t *testing.T) {
   442  	edsLBCh := testutils.NewChannel()
   443  	xdsC, cleanup := setup(edsLBCh)
   444  	defer cleanup()
   445  
   446  	builder := balancer.Get(Name)
   447  	edsB := builder.Build(newNoopTestClientConn(), balancer.BuildOptions{})
   448  	if edsB == nil {
   449  		t.Fatalf("builder.Build(%s) failed and returned nil", Name)
   450  	}
   451  	defer edsB.Close()
   452  
   453  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   454  	defer cancel()
   455  	// If eds service name is not set, should watch for cluster name.
   456  	if err := edsB.UpdateClientConnState(balancer.ClientConnState{
   457  		ResolverState:  xdsclient.SetClient(resolver.State{}, xdsC),
   458  		BalancerConfig: newLBConfigWithOneEDS("cluster-1"),
   459  	}); err != nil {
   460  		t.Fatal(err)
   461  	}
   462  	if err := verifyExpectedRequests(ctx, xdsC, "cluster-1"); err != nil {
   463  		t.Fatal(err)
   464  	}
   465  
   466  	// Update with an non-empty edsServiceName should trigger an EDS watch for
   467  	// the same.
   468  	if err := edsB.UpdateClientConnState(balancer.ClientConnState{
   469  		ResolverState:  xdsclient.SetClient(resolver.State{}, xdsC),
   470  		BalancerConfig: newLBConfigWithOneEDS("foobar-1"),
   471  	}); err != nil {
   472  		t.Fatal(err)
   473  	}
   474  	if err := verifyExpectedRequests(ctx, xdsC, "", "foobar-1"); err != nil {
   475  		t.Fatal(err)
   476  	}
   477  
   478  	// Also test the case where the edsServerName changes from one non-empty
   479  	// name to another, and make sure a new watch is registered. The previously
   480  	// registered watch will be cancelled, which will result in an EDS request
   481  	// with no resource names being sent to the server.
   482  	if err := edsB.UpdateClientConnState(balancer.ClientConnState{
   483  		ResolverState:  xdsclient.SetClient(resolver.State{}, xdsC),
   484  		BalancerConfig: newLBConfigWithOneEDS("foobar-2"),
   485  	}); err != nil {
   486  		t.Fatal(err)
   487  	}
   488  	if err := verifyExpectedRequests(ctx, xdsC, "", "foobar-2"); err != nil {
   489  		t.Fatal(err)
   490  	}
   491  }
   492  
   493  func newLBConfigWithOneEDS(edsServiceName string) *LBConfig {
   494  	return &LBConfig{
   495  		DiscoveryMechanisms: []DiscoveryMechanism{{
   496  			Cluster:        testClusterName,
   497  			Type:           DiscoveryMechanismTypeEDS,
   498  			EDSServiceName: edsServiceName,
   499  		}},
   500  	}
   501  }