istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/networking/core/route/retry/retry_test.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package retry_test 16 17 import ( 18 "reflect" 19 "testing" 20 "time" 21 22 envoyroute "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 23 previouspriorities "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/priority/previous_priorities/v3" 24 . "github.com/onsi/gomega" 25 "google.golang.org/protobuf/types/known/durationpb" 26 wrappers "google.golang.org/protobuf/types/known/wrapperspb" 27 28 networking "istio.io/api/networking/v1alpha3" 29 "istio.io/istio/pilot/pkg/networking/core/route/retry" 30 "istio.io/istio/pilot/pkg/util/protoconv" 31 ) 32 33 func TestRetry(t *testing.T) { 34 testCases := []struct { 35 name string 36 route *networking.HTTPRoute 37 assertFunc func(g *WithT, policy *envoyroute.RetryPolicy) 38 }{ 39 { 40 name: "TestNilRetryShouldReturnDefault", 41 // Create a route where no retry policy has been explicitly set. 42 route: &networking.HTTPRoute{}, 43 assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { 44 g.Expect(policy).To(Not(BeNil())) 45 g.Expect(policy).To(Equal(retry.DefaultPolicy())) 46 }, 47 }, 48 { 49 name: "TestZeroAttemptsShouldReturnNilPolicy", 50 // Create a route with a retry policy with zero attempts configured. 51 route: &networking.HTTPRoute{ 52 Retries: &networking.HTTPRetry{ 53 // Explicitly not retrying. 54 Attempts: 0, 55 }, 56 }, 57 assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { 58 g.Expect(policy).To(BeNil()) 59 }, 60 }, 61 { 62 name: "TestRetryWithAllFieldsSet", 63 // Create a route with a retry policy with all fields configured. 64 route: &networking.HTTPRoute{ 65 Retries: &networking.HTTPRetry{ 66 Attempts: 2, 67 RetryOn: "some,fake,conditions", 68 PerTryTimeout: durationpb.New(time.Second * 3), 69 }, 70 }, 71 assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { 72 g.Expect(policy).To(Not(BeNil())) 73 g.Expect(policy.RetryOn).To(Equal("some,fake,conditions")) 74 g.Expect(policy.PerTryTimeout).To(Equal(durationpb.New(time.Second * 3))) 75 g.Expect(policy.NumRetries.Value).To(Equal(uint32(2))) 76 g.Expect(policy.RetriableStatusCodes).To(Equal(make([]uint32, 0))) 77 g.Expect(policy.RetryPriority).To(BeNil()) 78 g.Expect(policy.HostSelectionRetryMaxAttempts).To(Equal(retry.DefaultPolicy().HostSelectionRetryMaxAttempts)) 79 g.Expect(policy.RetryHostPredicate).To(Equal(retry.DefaultPolicy().RetryHostPredicate)) 80 }, 81 }, 82 { 83 name: "TestRetryOnWithEmptyParts", 84 // Create a route with a retry policy with empty retry conditions configured. 85 route: &networking.HTTPRoute{ 86 Retries: &networking.HTTPRetry{ 87 // Explicitly not retrying. 88 Attempts: 2, 89 RetryOn: "some,fake,conditions,,,", 90 }, 91 }, 92 assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { 93 g.Expect(policy).To(Not(BeNil())) 94 g.Expect(policy.RetryOn).To(Equal("some,fake,conditions")) 95 g.Expect(policy.RetriableStatusCodes).To(Equal([]uint32{})) 96 }, 97 }, 98 { 99 name: "TestRetryOnWithRetriableStatusCodes", 100 // Create a route with a retry policy with retriable status code. 101 route: &networking.HTTPRoute{ 102 Retries: &networking.HTTPRetry{ 103 // Explicitly not retrying. 104 Attempts: 2, 105 RetryOn: "gateway-error,retriable-status-codes,503", 106 }, 107 }, 108 assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { 109 g.Expect(policy).To(Not(BeNil())) 110 g.Expect(policy.RetryOn).To(Equal("gateway-error,retriable-status-codes")) 111 g.Expect(policy.RetriableStatusCodes).To(Equal([]uint32{503})) 112 }, 113 }, 114 { 115 name: "TestRetryOnWithWhitespace", 116 // Create a route with a retry policy with retryOn having white spaces. 117 route: &networking.HTTPRoute{ 118 Retries: &networking.HTTPRetry{ 119 // Explicitly not retrying. 120 Attempts: 2, 121 RetryOn: " some, ,fake , conditions, ,", 122 }, 123 }, 124 assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { 125 g.Expect(policy).To(Not(BeNil())) 126 g.Expect(policy.RetryOn).To(Equal("some,fake,conditions")) 127 g.Expect(policy.RetriableStatusCodes).To(Equal([]uint32{})) 128 }, 129 }, 130 { 131 name: "TestRetryOnContainingStatusCodes", 132 // Create a route with a retry policy with status codes. 133 route: &networking.HTTPRoute{ 134 Retries: &networking.HTTPRetry{ 135 Attempts: 2, 136 RetryOn: "some,fake,5xx,404,conditions,503", 137 }, 138 }, 139 assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { 140 g.Expect(policy).To(Not(BeNil())) 141 g.Expect(policy.RetryOn).To(Equal("some,fake,5xx,conditions,retriable-status-codes")) 142 g.Expect(policy.RetriableStatusCodes).To(Equal([]uint32{404, 503})) 143 }, 144 }, 145 { 146 name: "TestRetryOnWithInvalidStatusCodesShouldAddToRetryOn", 147 // Create a route with a retry policy with invalid status codes. 148 route: &networking.HTTPRoute{ 149 Retries: &networking.HTTPRetry{ 150 Attempts: 2, 151 RetryOn: "some,fake,conditions,1000", 152 }, 153 }, 154 assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { 155 g.Expect(policy).To(Not(BeNil())) 156 g.Expect(policy.RetryOn).To(Equal("some,fake,conditions,1000")) 157 g.Expect(policy.RetriableStatusCodes).To(Equal([]uint32{})) 158 }, 159 }, 160 { 161 name: "TestMissingRetryOnShouldReturnDefaults", 162 // Create a route with a retry policy with two attempts configured. 163 route: &networking.HTTPRoute{ 164 Retries: &networking.HTTPRetry{ 165 Attempts: 2, 166 }, 167 }, 168 assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { 169 g.Expect(policy).To(Not(BeNil())) 170 g.Expect(policy.RetryOn).To(Equal(retry.DefaultPolicy().RetryOn)) 171 g.Expect(policy.RetriableStatusCodes).To(Equal(retry.DefaultPolicy().RetriableStatusCodes)) 172 }, 173 }, 174 { 175 name: "TestMissingPerTryTimeoutShouldReturnNil", 176 // Create a route with a retry policy without per try timeout. 177 route: &networking.HTTPRoute{ 178 Retries: &networking.HTTPRetry{ 179 Attempts: 2, 180 }, 181 }, 182 assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { 183 g.Expect(policy).To(Not(BeNil())) 184 g.Expect(policy.PerTryTimeout).To(BeNil()) 185 }, 186 }, 187 { 188 name: "TestRetryRemoteLocalities", 189 // Create a route with a retry policy with RetryRemoteLocalities enabled. 190 route: &networking.HTTPRoute{ 191 Retries: &networking.HTTPRetry{ 192 Attempts: 2, 193 RetryRemoteLocalities: &wrappers.BoolValue{ 194 Value: true, 195 }, 196 }, 197 }, 198 assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { 199 g.Expect(policy).To(Not(BeNil())) 200 g.Expect(policy.RetryOn).To(Equal(retry.DefaultPolicy().RetryOn)) 201 g.Expect(policy.RetriableStatusCodes).To(Equal(retry.DefaultPolicy().RetriableStatusCodes)) 202 203 previousPrioritiesConfig := &previouspriorities.PreviousPrioritiesConfig{ 204 UpdateFrequency: int32(2), 205 } 206 expected := &envoyroute.RetryPolicy_RetryPriority{ 207 Name: "envoy.retry_priorities.previous_priorities", 208 ConfigType: &envoyroute.RetryPolicy_RetryPriority_TypedConfig{ 209 TypedConfig: protoconv.MessageToAny(previousPrioritiesConfig), 210 }, 211 } 212 if !reflect.DeepEqual(policy.RetryPriority, expected) { 213 t.Fatalf("Expected %v, actual %v", expected, policy.RetryPriority) 214 } 215 }, 216 }, 217 } 218 for _, tc := range testCases { 219 t.Run(tc.name, func(t *testing.T) { 220 g := NewWithT(t) 221 policy := retry.ConvertPolicy(tc.route.Retries, false) 222 if tc.assertFunc != nil { 223 tc.assertFunc(g, policy) 224 } 225 }) 226 } 227 }