1
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
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
89 tryAgainErr codes.Code
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
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