github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/test/xds_server_serving_mode_test.go (about)

     1  //go:build !386
     2  // +build !386
     3  
     4  /*
     5   *
     6   * Copyright 2021 gRPC authors.
     7   *
     8   * Licensed under the Apache License, Version 2.0 (the "License");
     9   * you may not use this file except in compliance with the License.
    10   * You may obtain a copy of the License at
    11   *
    12   *     http://www.apache.org/licenses/LICENSE-2.0
    13   *
    14   * Unless required by applicable law or agreed to in writing, software
    15   * distributed under the License is distributed on an "AS IS" BASIS,
    16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    17   * See the License for the specific language governing permissions and
    18   * limitations under the License.
    19   *
    20   */
    21  
    22  // Package xds_test contains e2e tests for xDS use.
    23  package xds_test
    24  
    25  import (
    26  	"context"
    27  	"fmt"
    28  	"net"
    29  	"testing"
    30  	"time"
    31  
    32  	grpc "github.com/hxx258456/ccgo/grpc"
    33  	"github.com/hxx258456/ccgo/grpc/connectivity"
    34  	"github.com/hxx258456/ccgo/grpc/credentials/insecure"
    35  	xdscreds "github.com/hxx258456/ccgo/grpc/credentials/xds"
    36  	"github.com/hxx258456/ccgo/grpc/internal/testutils"
    37  	"github.com/hxx258456/ccgo/grpc/xds"
    38  	"github.com/hxx258456/ccgo/grpc/xds/internal/testutils/e2e"
    39  
    40  	v3listenerpb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/listener/v3"
    41  	testpb "github.com/hxx258456/ccgo/grpc/test/grpc_testing"
    42  )
    43  
    44  // TestServerSideXDS_RedundantUpdateSuppression tests the scenario where the
    45  // control plane sends the same resource update. It verifies that the mode
    46  // change callback is not invoked and client connections to the server are not
    47  // recycled.
    48  func (s) TestServerSideXDS_RedundantUpdateSuppression(t *testing.T) {
    49  	managementServer, nodeID, bootstrapContents, _, cleanup := setupManagementServer(t)
    50  	defer cleanup()
    51  
    52  	creds, err := xdscreds.NewServerCredentials(xdscreds.ServerOptions{FallbackCreds: insecure.NewCredentials()})
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  	lis, err := testutils.LocalTCPListener()
    57  	if err != nil {
    58  		t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
    59  	}
    60  	updateCh := make(chan connectivity.ServingMode, 1)
    61  
    62  	// Create a server option to get notified about serving mode changes.
    63  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
    64  	defer cancel()
    65  	modeChangeOpt := xds.ServingModeCallback(func(addr net.Addr, args xds.ServingModeChangeArgs) {
    66  		t.Logf("serving mode for listener %q changed to %q, err: %v", addr.String(), args.Mode, args.Err)
    67  		updateCh <- args.Mode
    68  	})
    69  
    70  	// Initialize an xDS-enabled gRPC server and register the stubServer on it.
    71  	server := xds.NewGRPCServer(grpc.Creds(creds), modeChangeOpt, xds.BootstrapContentsForTesting(bootstrapContents))
    72  	defer server.Stop()
    73  	testpb.RegisterTestServiceServer(server, &testService{})
    74  
    75  	// Setup the management server to respond with the listener resources.
    76  	host, port, err := hostPortFromListener(lis)
    77  	if err != nil {
    78  		t.Fatalf("failed to retrieve host and port of server: %v", err)
    79  	}
    80  	listener := e2e.DefaultServerListener(host, port, e2e.SecurityLevelNone)
    81  	resources := e2e.UpdateOptions{
    82  		NodeID:    nodeID,
    83  		Listeners: []*v3listenerpb.Listener{listener},
    84  	}
    85  	if err := managementServer.Update(ctx, resources); err != nil {
    86  		t.Fatal(err)
    87  	}
    88  
    89  	go func() {
    90  		if err := server.Serve(lis); err != nil {
    91  			t.Errorf("Serve() failed: %v", err)
    92  		}
    93  	}()
    94  
    95  	// Wait for the listener to move to "serving" mode.
    96  	select {
    97  	case <-ctx.Done():
    98  		t.Fatalf("timed out waiting for a mode change update: %v", err)
    99  	case mode := <-updateCh:
   100  		if mode != connectivity.ServingModeServing {
   101  			t.Fatalf("listener received new mode %v, want %v", mode, connectivity.ServingModeServing)
   102  		}
   103  	}
   104  
   105  	// Create a ClientConn and make a successful RPCs.
   106  	cc, err := grpc.Dial(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
   107  	if err != nil {
   108  		t.Fatalf("failed to dial local test server: %v", err)
   109  	}
   110  	defer cc.Close()
   111  	waitForSuccessfulRPC(ctx, t, cc)
   112  
   113  	// Start a goroutine to make sure that we do not see any connectivity state
   114  	// changes on the client connection. If redundant updates are not
   115  	// suppressed, server will recycle client connections.
   116  	errCh := make(chan error, 1)
   117  	go func() {
   118  		if cc.WaitForStateChange(ctx, connectivity.Ready) {
   119  			errCh <- fmt.Errorf("unexpected connectivity state change {%s --> %s} on the client connection", connectivity.Ready, cc.GetState())
   120  			return
   121  		}
   122  		errCh <- nil
   123  	}()
   124  
   125  	// Update the management server with the same listener resource. This will
   126  	// update the resource version though, and should result in a the management
   127  	// server sending the same resource to the xDS-enabled gRPC server.
   128  	if err := managementServer.Update(ctx, e2e.UpdateOptions{
   129  		NodeID:    nodeID,
   130  		Listeners: []*v3listenerpb.Listener{listener},
   131  	}); err != nil {
   132  		t.Fatal(err)
   133  	}
   134  
   135  	// Since redundant resource updates are suppressed, we should not see the
   136  	// mode change callback being invoked.
   137  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   138  	defer sCancel()
   139  	select {
   140  	case <-sCtx.Done():
   141  	case mode := <-updateCh:
   142  		t.Fatalf("unexpected mode change callback with new mode %v", mode)
   143  	}
   144  
   145  	// Make sure RPCs continue to succeed.
   146  	waitForSuccessfulRPC(ctx, t, cc)
   147  
   148  	// Cancel the context to ensure that the WaitForStateChange call exits early
   149  	// and returns false.
   150  	cancel()
   151  	if err := <-errCh; err != nil {
   152  		t.Fatal(err)
   153  	}
   154  }
   155  
   156  // TestServerSideXDS_ServingModeChanges tests the serving mode functionality in
   157  // xDS enabled gRPC servers. It verifies that appropriate mode changes happen in
   158  // the server, and also verifies behavior of clientConns under these modes.
   159  func (s) TestServerSideXDS_ServingModeChanges(t *testing.T) {
   160  	managementServer, nodeID, bootstrapContents, _, cleanup := setupManagementServer(t)
   161  	defer cleanup()
   162  
   163  	// Configure xDS credentials to be used on the server-side.
   164  	creds, err := xdscreds.NewServerCredentials(xdscreds.ServerOptions{
   165  		FallbackCreds: insecure.NewCredentials(),
   166  	})
   167  	if err != nil {
   168  		t.Fatal(err)
   169  	}
   170  
   171  	// Create two local listeners and pass it to Serve().
   172  	lis1, err := testutils.LocalTCPListener()
   173  	if err != nil {
   174  		t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
   175  	}
   176  	lis2, err := testutils.LocalTCPListener()
   177  	if err != nil {
   178  		t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
   179  	}
   180  
   181  	// Create a couple of channels on which mode updates will be pushed.
   182  	updateCh1 := make(chan connectivity.ServingMode, 1)
   183  	updateCh2 := make(chan connectivity.ServingMode, 1)
   184  
   185  	// Create a server option to get notified about serving mode changes, and
   186  	// push the updated mode on the channels created above.
   187  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   188  	defer cancel()
   189  	modeChangeOpt := xds.ServingModeCallback(func(addr net.Addr, args xds.ServingModeChangeArgs) {
   190  		t.Logf("serving mode for listener %q changed to %q, err: %v", addr.String(), args.Mode, args.Err)
   191  		switch addr.String() {
   192  		case lis1.Addr().String():
   193  			updateCh1 <- args.Mode
   194  		case lis2.Addr().String():
   195  			updateCh2 <- args.Mode
   196  		default:
   197  			t.Logf("serving mode callback invoked for unknown listener address: %q", addr.String())
   198  		}
   199  	})
   200  
   201  	// Initialize an xDS-enabled gRPC server and register the stubServer on it.
   202  	server := xds.NewGRPCServer(grpc.Creds(creds), modeChangeOpt, xds.BootstrapContentsForTesting(bootstrapContents))
   203  	defer server.Stop()
   204  	testpb.RegisterTestServiceServer(server, &testService{})
   205  
   206  	// Setup the management server to respond with server-side Listener
   207  	// resources for both listeners.
   208  	host1, port1, err := hostPortFromListener(lis1)
   209  	if err != nil {
   210  		t.Fatalf("failed to retrieve host and port of server: %v", err)
   211  	}
   212  	listener1 := e2e.DefaultServerListener(host1, port1, e2e.SecurityLevelNone)
   213  	host2, port2, err := hostPortFromListener(lis2)
   214  	if err != nil {
   215  		t.Fatalf("failed to retrieve host and port of server: %v", err)
   216  	}
   217  	listener2 := e2e.DefaultServerListener(host2, port2, e2e.SecurityLevelNone)
   218  	resources := e2e.UpdateOptions{
   219  		NodeID:    nodeID,
   220  		Listeners: []*v3listenerpb.Listener{listener1, listener2},
   221  	}
   222  	if err := managementServer.Update(ctx, resources); err != nil {
   223  		t.Fatal(err)
   224  	}
   225  
   226  	go func() {
   227  		if err := server.Serve(lis1); err != nil {
   228  			t.Errorf("Serve() failed: %v", err)
   229  		}
   230  	}()
   231  	go func() {
   232  		if err := server.Serve(lis2); err != nil {
   233  			t.Errorf("Serve() failed: %v", err)
   234  		}
   235  	}()
   236  
   237  	// Wait for both listeners to move to "serving" mode.
   238  	select {
   239  	case <-ctx.Done():
   240  		t.Fatalf("timed out waiting for a mode change update: %v", err)
   241  	case mode := <-updateCh1:
   242  		if mode != connectivity.ServingModeServing {
   243  			t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeServing)
   244  		}
   245  	}
   246  	select {
   247  	case <-ctx.Done():
   248  		t.Fatalf("timed out waiting for a mode change update: %v", err)
   249  	case mode := <-updateCh2:
   250  		if mode != connectivity.ServingModeServing {
   251  			t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeServing)
   252  		}
   253  	}
   254  
   255  	// Create a ClientConn to the first listener and make a successful RPCs.
   256  	cc1, err := grpc.Dial(lis1.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
   257  	if err != nil {
   258  		t.Fatalf("failed to dial local test server: %v", err)
   259  	}
   260  	defer cc1.Close()
   261  	waitForSuccessfulRPC(ctx, t, cc1)
   262  
   263  	// Create a ClientConn to the second listener and make a successful RPCs.
   264  	cc2, err := grpc.Dial(lis2.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
   265  	if err != nil {
   266  		t.Fatalf("failed to dial local test server: %v", err)
   267  	}
   268  	defer cc2.Close()
   269  	waitForSuccessfulRPC(ctx, t, cc2)
   270  
   271  	// Update the management server to remove the second listener resource. This
   272  	// should push only the second listener into "not-serving" mode.
   273  	if err := managementServer.Update(ctx, e2e.UpdateOptions{
   274  		NodeID:    nodeID,
   275  		Listeners: []*v3listenerpb.Listener{listener1},
   276  	}); err != nil {
   277  		t.Error(err)
   278  	}
   279  
   280  	// Wait for lis2 to move to "not-serving" mode.
   281  	select {
   282  	case <-ctx.Done():
   283  		t.Fatalf("timed out waiting for a mode change update: %v", err)
   284  	case mode := <-updateCh2:
   285  		if mode != connectivity.ServingModeNotServing {
   286  			t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeNotServing)
   287  		}
   288  	}
   289  
   290  	// Make sure RPCs succeed on cc1 and fail on cc2.
   291  	waitForSuccessfulRPC(ctx, t, cc1)
   292  	waitForFailedRPC(ctx, t, cc2)
   293  
   294  	// Update the management server to remove the first listener resource as
   295  	// well. This should push the first listener into "not-serving" mode. Second
   296  	// listener is already in "not-serving" mode.
   297  	if err := managementServer.Update(ctx, e2e.UpdateOptions{
   298  		NodeID:    nodeID,
   299  		Listeners: []*v3listenerpb.Listener{},
   300  	}); err != nil {
   301  		t.Error(err)
   302  	}
   303  
   304  	// Wait for lis1 to move to "not-serving" mode. lis2 was already removed
   305  	// from the xdsclient's resource cache. So, lis2's callback will not be
   306  	// invoked this time around.
   307  	select {
   308  	case <-ctx.Done():
   309  		t.Fatalf("timed out waiting for a mode change update: %v", err)
   310  	case mode := <-updateCh1:
   311  		if mode != connectivity.ServingModeNotServing {
   312  			t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeNotServing)
   313  		}
   314  	}
   315  
   316  	// Make sure RPCs fail on both.
   317  	waitForFailedRPC(ctx, t, cc1)
   318  	waitForFailedRPC(ctx, t, cc2)
   319  
   320  	// Make sure new connection attempts to "not-serving" servers fail. We use a
   321  	// short timeout since we expect this to fail.
   322  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   323  	defer sCancel()
   324  	if _, err := grpc.DialContext(sCtx, lis1.Addr().String(), grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials())); err == nil {
   325  		t.Fatal("successfully created clientConn to a server in \"not-serving\" state")
   326  	}
   327  
   328  	// Update the management server with both listener resources.
   329  	if err := managementServer.Update(ctx, e2e.UpdateOptions{
   330  		NodeID:    nodeID,
   331  		Listeners: []*v3listenerpb.Listener{listener1, listener2},
   332  	}); err != nil {
   333  		t.Error(err)
   334  	}
   335  
   336  	// Wait for both listeners to move to "serving" mode.
   337  	select {
   338  	case <-ctx.Done():
   339  		t.Fatalf("timed out waiting for a mode change update: %v", err)
   340  	case mode := <-updateCh1:
   341  		if mode != connectivity.ServingModeServing {
   342  			t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeServing)
   343  		}
   344  	}
   345  	select {
   346  	case <-ctx.Done():
   347  		t.Fatalf("timed out waiting for a mode change update: %v", err)
   348  	case mode := <-updateCh2:
   349  		if mode != connectivity.ServingModeServing {
   350  			t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeServing)
   351  		}
   352  	}
   353  
   354  	// The clientConns created earlier should be able to make RPCs now.
   355  	waitForSuccessfulRPC(ctx, t, cc1)
   356  	waitForSuccessfulRPC(ctx, t, cc2)
   357  }
   358  
   359  func waitForSuccessfulRPC(ctx context.Context, t *testing.T, cc *grpc.ClientConn) {
   360  	t.Helper()
   361  
   362  	c := testpb.NewTestServiceClient(cc)
   363  	if _, err := c.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   364  		t.Fatalf("rpc EmptyCall() failed: %v", err)
   365  	}
   366  }
   367  
   368  func waitForFailedRPC(ctx context.Context, t *testing.T, cc *grpc.ClientConn) {
   369  	t.Helper()
   370  
   371  	// Attempt one RPC before waiting for the ticker to expire.
   372  	c := testpb.NewTestServiceClient(cc)
   373  	if _, err := c.EmptyCall(ctx, &testpb.Empty{}); err != nil {
   374  		return
   375  	}
   376  
   377  	ticker := time.NewTimer(1 * time.Second)
   378  	defer ticker.Stop()
   379  	for {
   380  		select {
   381  		case <-ctx.Done():
   382  			t.Fatalf("failure when waiting for RPCs to fail: %v", ctx.Err())
   383  		case <-ticker.C:
   384  			if _, err := c.EmptyCall(ctx, &testpb.Empty{}); err != nil {
   385  				return
   386  			}
   387  		}
   388  	}
   389  }