github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/xdsclient/client_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 xdsclient
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	"github.com/google/go-cmp/cmp/cmpopts"
    29  	"github.com/hxx258456/ccgo/grpc/internal/grpclog"
    30  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/load"
    31  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/pubsub"
    32  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource"
    33  	"google.golang.org/protobuf/types/known/anypb"
    34  
    35  	grpc "github.com/hxx258456/ccgo/grpc"
    36  	"github.com/hxx258456/ccgo/grpc/credentials/insecure"
    37  	"github.com/hxx258456/ccgo/grpc/internal/grpcsync"
    38  	"github.com/hxx258456/ccgo/grpc/internal/grpctest"
    39  	"github.com/hxx258456/ccgo/grpc/internal/testutils"
    40  	xdstestutils "github.com/hxx258456/ccgo/grpc/xds/internal/testutils"
    41  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/bootstrap"
    42  	"google.golang.org/protobuf/testing/protocmp"
    43  )
    44  
    45  type s struct {
    46  	grpctest.Tester
    47  }
    48  
    49  func Test(t *testing.T) {
    50  	grpctest.RunSubTests(t, s{})
    51  }
    52  
    53  const (
    54  	testXDSServer          = "xds-server"
    55  	testXDSServerAuthority = "xds-server-authority"
    56  
    57  	testAuthority  = "test-authority"
    58  	testAuthority2 = "test-authority-2"
    59  	testLDSName    = "test-lds"
    60  	testRDSName    = "test-rds"
    61  	testCDSName    = "test-cds"
    62  	testEDSName    = "test-eds"
    63  
    64  	defaultTestWatchExpiryTimeout = 500 * time.Millisecond
    65  	defaultTestTimeout            = 5 * time.Second
    66  	defaultTestShortTimeout       = 10 * time.Millisecond // For events expected to *not* happen.
    67  )
    68  
    69  func newStringP(s string) *string {
    70  	return &s
    71  }
    72  
    73  func clientOpts() *bootstrap.Config {
    74  	return &bootstrap.Config{
    75  		XDSServer: &bootstrap.ServerConfig{
    76  			ServerURI: testXDSServer,
    77  			Creds:     grpc.WithTransportCredentials(insecure.NewCredentials()),
    78  			NodeProto: xdstestutils.EmptyNodeProtoV2,
    79  		},
    80  		Authorities: map[string]*bootstrap.Authority{
    81  			testAuthority: {
    82  				XDSServer: &bootstrap.ServerConfig{
    83  					ServerURI: testXDSServerAuthority,
    84  					Creds:     grpc.WithTransportCredentials(insecure.NewCredentials()),
    85  					NodeProto: xdstestutils.EmptyNodeProtoV2,
    86  				},
    87  			},
    88  		},
    89  	}
    90  }
    91  
    92  type testController struct {
    93  	// config is the config this controller is created with.
    94  	config *bootstrap.ServerConfig
    95  
    96  	done          *grpcsync.Event
    97  	addWatches    map[xdsresource.ResourceType]*testutils.Channel
    98  	removeWatches map[xdsresource.ResourceType]*testutils.Channel
    99  }
   100  
   101  func overrideNewController(t *testing.T) *testutils.Channel {
   102  	origNewController := newController
   103  	ch := testutils.NewChannel()
   104  	newController = func(config *bootstrap.ServerConfig, pubsub *pubsub.Pubsub, validator xdsresource.UpdateValidatorFunc, logger *grpclog.PrefixLogger) (controllerInterface, error) {
   105  		ret := newTestController(config)
   106  		ch.Send(ret)
   107  		return ret, nil
   108  	}
   109  	t.Cleanup(func() { newController = origNewController })
   110  	return ch
   111  }
   112  
   113  func newTestController(config *bootstrap.ServerConfig) *testController {
   114  	addWatches := map[xdsresource.ResourceType]*testutils.Channel{
   115  		xdsresource.ListenerResource:    testutils.NewChannel(),
   116  		xdsresource.RouteConfigResource: testutils.NewChannel(),
   117  		xdsresource.ClusterResource:     testutils.NewChannel(),
   118  		xdsresource.EndpointsResource:   testutils.NewChannel(),
   119  	}
   120  	removeWatches := map[xdsresource.ResourceType]*testutils.Channel{
   121  		xdsresource.ListenerResource:    testutils.NewChannel(),
   122  		xdsresource.RouteConfigResource: testutils.NewChannel(),
   123  		xdsresource.ClusterResource:     testutils.NewChannel(),
   124  		xdsresource.EndpointsResource:   testutils.NewChannel(),
   125  	}
   126  	return &testController{
   127  		config:        config,
   128  		done:          grpcsync.NewEvent(),
   129  		addWatches:    addWatches,
   130  		removeWatches: removeWatches,
   131  	}
   132  }
   133  
   134  func (c *testController) AddWatch(resourceType xdsresource.ResourceType, resourceName string) {
   135  	c.addWatches[resourceType].Send(resourceName)
   136  }
   137  
   138  func (c *testController) RemoveWatch(resourceType xdsresource.ResourceType, resourceName string) {
   139  	c.removeWatches[resourceType].Send(resourceName)
   140  }
   141  
   142  func (c *testController) ReportLoad(server string) (*load.Store, func()) {
   143  	panic("ReportLoad is not implemented")
   144  }
   145  
   146  func (c *testController) Close() {
   147  	c.done.Fire()
   148  }
   149  
   150  // TestWatchCallAnotherWatch covers the case where watch() is called inline by a
   151  // callback. It makes sure it doesn't cause a deadlock.
   152  func (s) TestWatchCallAnotherWatch(t *testing.T) {
   153  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   154  	defer cancel()
   155  	// Start a watch for some resource, so that the controller and update
   156  	// handlers are built for this authority. The test needs these to make an
   157  	// inline watch in a callback.
   158  	client, ctrlCh := testClientSetup(t, false)
   159  	newWatch(t, client, xdsresource.ClusterResource, "doesnot-matter")
   160  	controller, updateHandler := getControllerAndPubsub(ctx, t, client, ctrlCh, xdsresource.ClusterResource, "doesnot-matter")
   161  
   162  	clusterUpdateCh := testutils.NewChannel()
   163  	firstTime := true
   164  	client.WatchCluster(testCDSName, func(update xdsresource.ClusterUpdate, err error) {
   165  		clusterUpdateCh.Send(xdsresource.ClusterUpdateErrTuple{Update: update, Err: err})
   166  		// Calls another watch inline, to ensure there's deadlock.
   167  		client.WatchCluster("another-random-name", func(xdsresource.ClusterUpdate, error) {})
   168  
   169  		if _, err := controller.addWatches[xdsresource.ClusterResource].Receive(ctx); firstTime && err != nil {
   170  			t.Fatalf("want new watch to start, got error %v", err)
   171  		}
   172  		firstTime = false
   173  	})
   174  	if _, err := controller.addWatches[xdsresource.ClusterResource].Receive(ctx); err != nil {
   175  		t.Fatalf("want new watch to start, got error %v", err)
   176  	}
   177  
   178  	wantUpdate := xdsresource.ClusterUpdate{ClusterName: testEDSName}
   179  	updateHandler.NewClusters(map[string]xdsresource.ClusterUpdateErrTuple{testCDSName: {Update: wantUpdate}}, xdsresource.UpdateMetadata{})
   180  	if err := verifyClusterUpdate(ctx, clusterUpdateCh, wantUpdate, nil); err != nil {
   181  		t.Fatal(err)
   182  	}
   183  
   184  	// The second update needs to be different in the underlying resource proto
   185  	// for the watch callback to be invoked.
   186  	wantUpdate2 := xdsresource.ClusterUpdate{ClusterName: testEDSName + "2", Raw: &anypb.Any{}}
   187  	updateHandler.NewClusters(map[string]xdsresource.ClusterUpdateErrTuple{testCDSName: {Update: wantUpdate2}}, xdsresource.UpdateMetadata{})
   188  	if err := verifyClusterUpdate(ctx, clusterUpdateCh, wantUpdate2, nil); err != nil {
   189  		t.Fatal(err)
   190  	}
   191  }
   192  
   193  func verifyListenerUpdate(ctx context.Context, updateCh *testutils.Channel, wantUpdate xdsresource.ListenerUpdate, wantErr error) error {
   194  	u, err := updateCh.Receive(ctx)
   195  	if err != nil {
   196  		return fmt.Errorf("timeout when waiting for listener update: %v", err)
   197  	}
   198  	gotUpdate := u.(xdsresource.ListenerUpdateErrTuple)
   199  	if wantErr != nil {
   200  		if gotUpdate.Err != wantErr {
   201  			return fmt.Errorf("unexpected error: %v, want %v", gotUpdate.Err, wantErr)
   202  		}
   203  		return nil
   204  	}
   205  	if gotUpdate.Err != nil || !cmp.Equal(gotUpdate.Update, wantUpdate, protocmp.Transform()) {
   206  		return fmt.Errorf("unexpected endpointsUpdate: (%v, %v), want: (%v, nil)", gotUpdate.Update, gotUpdate.Err, wantUpdate)
   207  	}
   208  	return nil
   209  }
   210  
   211  func verifyRouteConfigUpdate(ctx context.Context, updateCh *testutils.Channel, wantUpdate xdsresource.RouteConfigUpdate, wantErr error) error {
   212  	u, err := updateCh.Receive(ctx)
   213  	if err != nil {
   214  		return fmt.Errorf("timeout when waiting for route configuration update: %v", err)
   215  	}
   216  	gotUpdate := u.(xdsresource.RouteConfigUpdateErrTuple)
   217  	if wantErr != nil {
   218  		if gotUpdate.Err != wantErr {
   219  			return fmt.Errorf("unexpected error: %v, want %v", gotUpdate.Err, wantErr)
   220  		}
   221  		return nil
   222  	}
   223  	if gotUpdate.Err != nil || !cmp.Equal(gotUpdate.Update, wantUpdate, protocmp.Transform()) {
   224  		return fmt.Errorf("unexpected route config update: (%v, %v), want: (%v, nil)", gotUpdate.Update, gotUpdate.Err, wantUpdate)
   225  	}
   226  	return nil
   227  }
   228  
   229  func verifyClusterUpdate(ctx context.Context, updateCh *testutils.Channel, wantUpdate xdsresource.ClusterUpdate, wantErr error) error {
   230  	u, err := updateCh.Receive(ctx)
   231  	if err != nil {
   232  		return fmt.Errorf("timeout when waiting for cluster update: %v", err)
   233  	}
   234  	gotUpdate := u.(xdsresource.ClusterUpdateErrTuple)
   235  	if wantErr != nil {
   236  		if gotUpdate.Err != wantErr {
   237  			return fmt.Errorf("unexpected error: %v, want %v", gotUpdate.Err, wantErr)
   238  		}
   239  		return nil
   240  	}
   241  	if !cmp.Equal(gotUpdate.Update, wantUpdate, protocmp.Transform()) {
   242  		return fmt.Errorf("unexpected clusterUpdate: (%v, %v), want: (%v, nil)", gotUpdate.Update, gotUpdate.Err, wantUpdate)
   243  	}
   244  	return nil
   245  }
   246  
   247  func verifyEndpointsUpdate(ctx context.Context, updateCh *testutils.Channel, wantUpdate xdsresource.EndpointsUpdate, wantErr error) error {
   248  	u, err := updateCh.Receive(ctx)
   249  	if err != nil {
   250  		return fmt.Errorf("timeout when waiting for endpoints update: %v", err)
   251  	}
   252  	gotUpdate := u.(xdsresource.EndpointsUpdateErrTuple)
   253  	if wantErr != nil {
   254  		if gotUpdate.Err != wantErr {
   255  			return fmt.Errorf("unexpected error: %v, want %v", gotUpdate.Err, wantErr)
   256  		}
   257  		return nil
   258  	}
   259  	if gotUpdate.Err != nil || !cmp.Equal(gotUpdate.Update, wantUpdate, cmpopts.EquateEmpty(), protocmp.Transform()) {
   260  		return fmt.Errorf("unexpected endpointsUpdate: (%v, %v), want: (%v, nil)", gotUpdate.Update, gotUpdate.Err, wantUpdate)
   261  	}
   262  	return nil
   263  }
   264  
   265  // Test that multiple New() returns the same Client. And only when the last
   266  // client is closed, the underlying client is closed.
   267  func (s) TestClientNewSingleton(t *testing.T) {
   268  	oldBootstrapNewConfig := bootstrapNewConfig
   269  	bootstrapNewConfig = func() (*bootstrap.Config, error) {
   270  		return &bootstrap.Config{
   271  			XDSServer: &bootstrap.ServerConfig{
   272  				ServerURI: testXDSServer,
   273  				Creds:     grpc.WithInsecure(),
   274  				NodeProto: xdstestutils.EmptyNodeProtoV2,
   275  			},
   276  		}, nil
   277  	}
   278  	defer func() { bootstrapNewConfig = oldBootstrapNewConfig }()
   279  
   280  	ctrlCh := overrideNewController(t)
   281  
   282  	// The first New(). Should create a Client and a new APIClient.
   283  	client, err := newRefCounted()
   284  	if err != nil {
   285  		t.Fatalf("failed to create client: %v", err)
   286  	}
   287  
   288  	// Call a watch to create the controller.
   289  	client.WatchCluster("doesnot-matter", func(update xdsresource.ClusterUpdate, err error) {})
   290  
   291  	clientImpl := client.clientImpl
   292  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   293  	defer cancel()
   294  	c, err := ctrlCh.Receive(ctx)
   295  	if err != nil {
   296  		t.Fatalf("timeout when waiting for API client to be created: %v", err)
   297  	}
   298  	apiClient := c.(*testController)
   299  
   300  	// Call New() again. They should all return the same client implementation,
   301  	// and should not create new API client.
   302  	const count = 9
   303  	for i := 0; i < count; i++ {
   304  		tc, terr := newRefCounted()
   305  		if terr != nil {
   306  			client.Close()
   307  			t.Fatalf("%d-th call to New() failed with error: %v", i, terr)
   308  		}
   309  		if tc.clientImpl != clientImpl {
   310  			client.Close()
   311  			tc.Close()
   312  			t.Fatalf("%d-th call to New() got a different client %p, want %p", i, tc.clientImpl, clientImpl)
   313  		}
   314  
   315  		sctx, scancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   316  		defer scancel()
   317  		_, err := ctrlCh.Receive(sctx)
   318  		if err == nil {
   319  			client.Close()
   320  			t.Fatalf("%d-th call to New() created a new API client", i)
   321  		}
   322  	}
   323  
   324  	// Call Close(). Nothing should be actually closed until the last ref calls
   325  	// Close().
   326  	for i := 0; i < count; i++ {
   327  		client.Close()
   328  		if clientImpl.done.HasFired() {
   329  			t.Fatalf("%d-th call to Close(), unexpected done in the client implemenation", i)
   330  		}
   331  		if apiClient.done.HasFired() {
   332  			t.Fatalf("%d-th call to Close(), unexpected done in the API client", i)
   333  		}
   334  	}
   335  
   336  	// Call the last Close(). The underlying implementation and API Client
   337  	// should all be closed.
   338  	client.Close()
   339  	if !clientImpl.done.HasFired() {
   340  		t.Fatalf("want client implementation to be closed, got not done")
   341  	}
   342  	if !apiClient.done.HasFired() {
   343  		t.Fatalf("want API client to be closed, got not done")
   344  	}
   345  
   346  	// Call New() again after the previous Client is actually closed. Should
   347  	// create a Client and a new APIClient.
   348  	client2, err2 := newRefCounted()
   349  	if err2 != nil {
   350  		t.Fatalf("failed to create client: %v", err)
   351  	}
   352  	defer client2.Close()
   353  
   354  	// Call a watch to create the controller.
   355  	client2.WatchCluster("abc", func(update xdsresource.ClusterUpdate, err error) {})
   356  
   357  	c2, err := ctrlCh.Receive(ctx)
   358  	if err != nil {
   359  		t.Fatalf("timeout when waiting for API client to be created: %v", err)
   360  	}
   361  	apiClient2 := c2.(*testController)
   362  
   363  	// The client wrapper with ref count should be the same.
   364  	if client2 != client {
   365  		t.Fatalf("New() after Close() should return the same client wrapper, got different %p, %p", client2, client)
   366  	}
   367  	if client2.clientImpl == clientImpl {
   368  		t.Fatalf("New() after Close() should return different client implementation, got the same %p", client2.clientImpl)
   369  	}
   370  	if apiClient2 == apiClient {
   371  		t.Fatalf("New() after Close() should return different API client, got the same %p", apiClient2)
   372  	}
   373  }