...

Source file src/google.golang.org/grpc/test/xds/xds_client_retry_test.go

Documentation: google.golang.org/grpc/test/xds

     1  /*
     2   *
     3   * Copyright 2022 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 xds_test
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"testing"
    25  
    26  	"google.golang.org/grpc"
    27  	"google.golang.org/grpc/codes"
    28  	"google.golang.org/grpc/credentials/insecure"
    29  	"google.golang.org/grpc/internal/stubserver"
    30  	"google.golang.org/grpc/internal/testutils"
    31  	"google.golang.org/grpc/internal/testutils/xds/e2e"
    32  	"google.golang.org/grpc/status"
    33  	"google.golang.org/protobuf/types/known/wrapperspb"
    34  
    35  	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
    36  	testgrpc "google.golang.org/grpc/interop/grpc_testing"
    37  	testpb "google.golang.org/grpc/interop/grpc_testing"
    38  )
    39  
    40  func (s) TestClientSideRetry(t *testing.T) {
    41  	ctr := 0
    42  	errs := []codes.Code{codes.ResourceExhausted}
    43  
    44  	managementServer, nodeID, _, resolver, cleanup1 := e2e.SetupManagementServer(t, e2e.ManagementServerOptions{})
    45  	defer cleanup1()
    46  
    47  	server := stubserver.StartTestService(t, &stubserver.StubServer{
    48  		EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
    49  			defer func() { ctr++ }()
    50  			if ctr < len(errs) {
    51  				return nil, status.Errorf(errs[ctr], "this should be retried")
    52  			}
    53  			return &testpb.Empty{}, nil
    54  		},
    55  	})
    56  	defer server.Stop()
    57  
    58  	const serviceName = "my-service-client-side-xds"
    59  	resources := e2e.DefaultClientResources(e2e.ResourceParams{
    60  		DialTarget: serviceName,
    61  		NodeID:     nodeID,
    62  		Host:       "localhost",
    63  		Port:       testutils.ParsePort(t, server.Address),
    64  		SecLevel:   e2e.SecurityLevelNone,
    65  	})
    66  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
    67  	if err := managementServer.Update(ctx, resources); err != nil {
    68  		t.Fatal(err)
    69  	}
    70  
    71  	// Create a ClientConn and make a successful RPC.
    72  	cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(resolver))
    73  	if err != nil {
    74  		t.Fatalf("failed to dial local test server: %v", err)
    75  	}
    76  	defer cc.Close()
    77  
    78  	client := testgrpc.NewTestServiceClient(cc)
    79  	defer cancel()
    80  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); status.Code(err) != codes.ResourceExhausted {
    81  		t.Fatalf("rpc EmptyCall() = _, %v; want _, ResourceExhausted", err)
    82  	}
    83  
    84  	testCases := []struct {
    85  		name        string
    86  		vhPolicy    *v3routepb.RetryPolicy
    87  		routePolicy *v3routepb.RetryPolicy
    88  		errs        []codes.Code // the errors returned by the server for each RPC
    89  		tryAgainErr codes.Code   // the error that would be returned if we are still using the old retry policies.
    90  		errWant     codes.Code
    91  	}{{
    92  		name: "virtualHost only, fail",
    93  		vhPolicy: &v3routepb.RetryPolicy{
    94  			RetryOn:    "resource-exhausted,unavailable",
    95  			NumRetries: &wrapperspb.UInt32Value{Value: 1},
    96  		},
    97  		errs:        []codes.Code{codes.ResourceExhausted, codes.Unavailable},
    98  		routePolicy: nil,
    99  		tryAgainErr: codes.ResourceExhausted,
   100  		errWant:     codes.Unavailable,
   101  	}, {
   102  		name: "virtualHost only",
   103  		vhPolicy: &v3routepb.RetryPolicy{
   104  			RetryOn:    "resource-exhausted, unavailable",
   105  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   106  		},
   107  		errs:        []codes.Code{codes.ResourceExhausted, codes.Unavailable},
   108  		routePolicy: nil,
   109  		tryAgainErr: codes.Unavailable,
   110  		errWant:     codes.OK,
   111  	}, {
   112  		name: "virtualHost+route, fail",
   113  		vhPolicy: &v3routepb.RetryPolicy{
   114  			RetryOn:    "resource-exhausted,unavailable",
   115  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   116  		},
   117  		routePolicy: &v3routepb.RetryPolicy{
   118  			RetryOn:    "resource-exhausted",
   119  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   120  		},
   121  		errs:        []codes.Code{codes.ResourceExhausted, codes.Unavailable},
   122  		tryAgainErr: codes.OK,
   123  		errWant:     codes.Unavailable,
   124  	}, {
   125  		name: "virtualHost+route",
   126  		vhPolicy: &v3routepb.RetryPolicy{
   127  			RetryOn:    "resource-exhausted",
   128  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   129  		},
   130  		routePolicy: &v3routepb.RetryPolicy{
   131  			RetryOn:    "unavailable",
   132  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   133  		},
   134  		errs:        []codes.Code{codes.Unavailable},
   135  		tryAgainErr: codes.Unavailable,
   136  		errWant:     codes.OK,
   137  	}, {
   138  		name: "virtualHost+route, not enough attempts",
   139  		vhPolicy: &v3routepb.RetryPolicy{
   140  			RetryOn:    "unavailable",
   141  			NumRetries: &wrapperspb.UInt32Value{Value: 2},
   142  		},
   143  		routePolicy: &v3routepb.RetryPolicy{
   144  			RetryOn:    "unavailable",
   145  			NumRetries: &wrapperspb.UInt32Value{Value: 1},
   146  		},
   147  		errs:        []codes.Code{codes.Unavailable, codes.Unavailable},
   148  		tryAgainErr: codes.OK,
   149  		errWant:     codes.Unavailable,
   150  	}}
   151  
   152  	for _, tc := range testCases {
   153  		t.Run(tc.name, func(t *testing.T) {
   154  			errs = tc.errs
   155  
   156  			// Confirm tryAgainErr is correct before updating resources.
   157  			ctr = 0
   158  			_, err := client.EmptyCall(ctx, &testpb.Empty{})
   159  			if code := status.Code(err); code != tc.tryAgainErr {
   160  				t.Fatalf("with old retry policy: EmptyCall() = _, %v; want _, %v", err, tc.tryAgainErr)
   161  			}
   162  
   163  			resources.Routes[0].VirtualHosts[0].RetryPolicy = tc.vhPolicy
   164  			resources.Routes[0].VirtualHosts[0].Routes[0].GetRoute().RetryPolicy = tc.routePolicy
   165  			if err := managementServer.Update(ctx, resources); err != nil {
   166  				t.Fatal(err)
   167  			}
   168  
   169  			for {
   170  				ctr = 0
   171  				_, err := client.EmptyCall(ctx, &testpb.Empty{})
   172  				if code := status.Code(err); code == tc.tryAgainErr {
   173  					continue
   174  				} else if code != tc.errWant {
   175  					t.Fatalf("rpc EmptyCall() = _, %v; want _, %v", err, tc.errWant)
   176  				}
   177  				break
   178  			}
   179  		})
   180  	}
   181  }
   182  

View as plain text