github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/test/xds_client_integration_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
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  	"net"
    28  	"testing"
    29  
    30  	grpc "github.com/hxx258456/ccgo/grpc"
    31  	"github.com/hxx258456/ccgo/grpc/codes"
    32  	"github.com/hxx258456/ccgo/grpc/credentials/insecure"
    33  	"github.com/hxx258456/ccgo/grpc/internal/stubserver"
    34  	"github.com/hxx258456/ccgo/grpc/internal/testutils"
    35  	"github.com/hxx258456/ccgo/grpc/status"
    36  	"github.com/hxx258456/ccgo/grpc/xds/internal/testutils/e2e"
    37  
    38  	wrapperspb "github.com/golang/protobuf/ptypes/wrappers"
    39  	v3routepb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/route/v3"
    40  	testpb "github.com/hxx258456/ccgo/grpc/test/grpc_testing"
    41  )
    42  
    43  // clientSetup performs a bunch of steps common to all xDS client tests here:
    44  // - spin up a gRPC server and register the test service on it
    45  // - create a local TCP listener and start serving on it
    46  //
    47  // Returns the following:
    48  // - the port the server is listening on
    49  // - cleanup function to be invoked by the tests when done
    50  func clientSetup(t *testing.T, tss testpb.TestServiceServer) (uint32, func()) {
    51  	// Initialize a gRPC server and register the stubServer on it.
    52  	server := grpc.NewServer()
    53  	testpb.RegisterTestServiceServer(server, tss)
    54  
    55  	// Create a local listener and pass it to Serve().
    56  	lis, err := testutils.LocalTCPListener()
    57  	if err != nil {
    58  		t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
    59  	}
    60  
    61  	go func() {
    62  		if err := server.Serve(lis); err != nil {
    63  			t.Errorf("Serve() failed: %v", err)
    64  		}
    65  	}()
    66  
    67  	return uint32(lis.Addr().(*net.TCPAddr).Port), func() {
    68  		server.Stop()
    69  	}
    70  }
    71  
    72  func (s) TestClientSideXDS(t *testing.T) {
    73  	managementServer, nodeID, _, resolver, cleanup1 := setupManagementServer(t)
    74  	defer cleanup1()
    75  
    76  	port, cleanup2 := clientSetup(t, &testService{})
    77  	defer cleanup2()
    78  
    79  	const serviceName = "my-service-client-side-xds"
    80  	resources := e2e.DefaultClientResources(e2e.ResourceParams{
    81  		DialTarget: serviceName,
    82  		NodeID:     nodeID,
    83  		Host:       "localhost",
    84  		Port:       port,
    85  		SecLevel:   e2e.SecurityLevelNone,
    86  	})
    87  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
    88  	defer cancel()
    89  	if err := managementServer.Update(ctx, resources); err != nil {
    90  		t.Fatal(err)
    91  	}
    92  
    93  	// Create a ClientConn and make a successful RPC.
    94  	cc, err := grpc.Dial(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(resolver))
    95  	if err != nil {
    96  		t.Fatalf("failed to dial local test server: %v", err)
    97  	}
    98  	defer cc.Close()
    99  
   100  	client := testpb.NewTestServiceClient(cc)
   101  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   102  		t.Fatalf("rpc EmptyCall() failed: %v", err)
   103  	}
   104  }
   105  
   106  func (s) TestClientSideRetry(t *testing.T) {
   107  	ctr := 0
   108  	errs := []codes.Code{codes.ResourceExhausted}
   109  	ss := &stubserver.StubServer{
   110  		EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
   111  			defer func() { ctr++ }()
   112  			if ctr < len(errs) {
   113  				return nil, status.Errorf(errs[ctr], "this should be retried")
   114  			}
   115  			return &testpb.Empty{}, nil
   116  		},
   117  	}
   118  
   119  	managementServer, nodeID, _, resolver, cleanup1 := setupManagementServer(t)
   120  	defer cleanup1()
   121  
   122  	port, cleanup2 := clientSetup(t, ss)
   123  	defer cleanup2()
   124  
   125  	const serviceName = "my-service-client-side-xds"
   126  	resources := e2e.DefaultClientResources(e2e.ResourceParams{
   127  		DialTarget: serviceName,
   128  		NodeID:     nodeID,
   129  		Host:       "localhost",
   130  		Port:       port,
   131  		SecLevel:   e2e.SecurityLevelNone,
   132  	})
   133  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   134  	if err := managementServer.Update(ctx, resources); err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	// Create a ClientConn and make a successful RPC.
   139  	cc, err := grpc.Dial(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(resolver))
   140  	if err != nil {
   141  		t.Fatalf("failed to dial local test server: %v", err)
   142  	}
   143  	defer cc.Close()
   144  
   145  	client := testpb.NewTestServiceClient(cc)
   146  	defer cancel()
   147  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); status.Code(err) != codes.ResourceExhausted {
   148  		t.Fatalf("rpc EmptyCall() = _, %v; want _, ResourceExhausted", err)
   149  	}
   150  
   151  	testCases := []struct {
   152  		name        string
   153  		vhPolicy    *v3routepb.RetryPolicy
   154  		routePolicy *v3routepb.RetryPolicy
   155  		errs        []codes.Code // the errors returned by the server for each RPC
   156  		tryAgainErr codes.Code   // the error that would be returned if we are still using the old retry policies.
   157  		errWant     codes.Code
   158  	}{{
   159  		name: "virtualHost only, fail",
   160  		vhPolicy: &v3routepb.RetryPolicy{
   161  			RetryOn:    "resource-exhausted,unavailable",
   162  			NumRetries: &wrapperspb.UInt32Value{Value: 1},
   163  		},
   164  		errs:        []codes.Code{codes.ResourceExhausted, codes.Unavailable},
   165  		routePolicy: nil,
   166  		tryAgainErr: codes.ResourceExhausted,
   167  		errWant:     codes.Unavailable,
   168  	}, {
   169  		name: "virtualHost only",
   170  		vhPolicy: &v3routepb.RetryPolicy{
   171  			RetryOn:    "resource-exhausted, unavailable",
   172  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   173  		},
   174  		errs:        []codes.Code{codes.ResourceExhausted, codes.Unavailable},
   175  		routePolicy: nil,
   176  		tryAgainErr: codes.Unavailable,
   177  		errWant:     codes.OK,
   178  	}, {
   179  		name: "virtualHost+route, fail",
   180  		vhPolicy: &v3routepb.RetryPolicy{
   181  			RetryOn:    "resource-exhausted,unavailable",
   182  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   183  		},
   184  		routePolicy: &v3routepb.RetryPolicy{
   185  			RetryOn:    "resource-exhausted",
   186  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   187  		},
   188  		errs:        []codes.Code{codes.ResourceExhausted, codes.Unavailable},
   189  		tryAgainErr: codes.OK,
   190  		errWant:     codes.Unavailable,
   191  	}, {
   192  		name: "virtualHost+route",
   193  		vhPolicy: &v3routepb.RetryPolicy{
   194  			RetryOn:    "resource-exhausted",
   195  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   196  		},
   197  		routePolicy: &v3routepb.RetryPolicy{
   198  			RetryOn:    "unavailable",
   199  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   200  		},
   201  		errs:        []codes.Code{codes.Unavailable},
   202  		tryAgainErr: codes.Unavailable,
   203  		errWant:     codes.OK,
   204  	}, {
   205  		name: "virtualHost+route, not enough attempts",
   206  		vhPolicy: &v3routepb.RetryPolicy{
   207  			RetryOn:    "unavailable",
   208  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   209  		},
   210  		routePolicy: &v3routepb.RetryPolicy{
   211  			RetryOn:    "unavailable",
   212  			NumRetries: &wrapperspb.UInt32Value{Value: 1},
   213  		},
   214  		errs:        []codes.Code{codes.Unavailable, codes.Unavailable},
   215  		tryAgainErr: codes.OK,
   216  		errWant:     codes.Unavailable,
   217  	}}
   218  
   219  	for _, tc := range testCases {
   220  		t.Run(tc.name, func(t *testing.T) {
   221  			errs = tc.errs
   222  
   223  			// Confirm tryAgainErr is correct before updating resources.
   224  			ctr = 0
   225  			_, err := client.EmptyCall(ctx, &testpb.Empty{})
   226  			if code := status.Code(err); code != tc.tryAgainErr {
   227  				t.Fatalf("with old retry policy: EmptyCall() = _, %v; want _, %v", err, tc.tryAgainErr)
   228  			}
   229  
   230  			resources.Routes[0].VirtualHosts[0].RetryPolicy = tc.vhPolicy
   231  			resources.Routes[0].VirtualHosts[0].Routes[0].GetRoute().RetryPolicy = tc.routePolicy
   232  			if err := managementServer.Update(ctx, resources); err != nil {
   233  				t.Fatal(err)
   234  			}
   235  
   236  			for {
   237  				ctr = 0
   238  				_, err := client.EmptyCall(ctx, &testpb.Empty{})
   239  				if code := status.Code(err); code == tc.tryAgainErr {
   240  					continue
   241  				} else if code != tc.errWant {
   242  					t.Fatalf("rpc EmptyCall() = _, %v; want _, %v", err, tc.errWant)
   243  				}
   244  				break
   245  			}
   246  		})
   247  	}
   248  }