github.com/polarismesh/polaris@v1.17.8/service/circuitbreaker_rule_test.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package service_test 19 20 import ( 21 "fmt" 22 "strconv" 23 "sync" 24 "testing" 25 26 "github.com/golang/protobuf/ptypes" 27 "github.com/golang/protobuf/ptypes/wrappers" 28 apifault "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" 29 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 30 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 31 "github.com/stretchr/testify/assert" 32 33 "github.com/polarismesh/polaris/service" 34 ) 35 36 func buildUnnamedCircuitBreakerRule() *apifault.CircuitBreakerRule { 37 return &apifault.CircuitBreakerRule{ 38 Namespace: service.DefaultNamespace, 39 Enable: true, 40 Description: "comment me", 41 Level: apifault.Level_GROUP, 42 RuleMatcher: &apifault.RuleMatcher{ 43 Source: &apifault.RuleMatcher_SourceService{ 44 Service: "testSrcService", 45 Namespace: "test", 46 }, 47 Destination: &apifault.RuleMatcher_DestinationService{ 48 Service: "testDestService", 49 Namespace: "test", 50 Method: &apimodel.MatchString{Type: apimodel.MatchString_IN, Value: &wrappers.StringValue{Value: "/foo"}}, 51 }, 52 }, 53 } 54 } 55 56 func buildCircuitBreakerRule(index int) *apifault.CircuitBreakerRule { 57 return &apifault.CircuitBreakerRule{ 58 Name: fmt.Sprintf("test-circuitbreaker-rule-%d", index), 59 Namespace: service.DefaultNamespace, 60 Enable: true, 61 Description: "comment me", 62 Level: apifault.Level_GROUP, 63 RuleMatcher: &apifault.RuleMatcher{ 64 Source: &apifault.RuleMatcher_SourceService{ 65 Service: "testSrcService", 66 Namespace: "test", 67 }, 68 Destination: &apifault.RuleMatcher_DestinationService{ 69 Service: "testDestService", 70 Namespace: "test", 71 Method: &apimodel.MatchString{Type: apimodel.MatchString_IN, Value: &wrappers.StringValue{Value: "/foo"}}, 72 }, 73 }, 74 ErrorConditions: []*apifault.ErrorCondition{ 75 { 76 InputType: apifault.ErrorCondition_RET_CODE, 77 Condition: &apimodel.MatchString{Type: apimodel.MatchString_IN, Value: &wrappers.StringValue{Value: "400, 500"}}, 78 }, 79 { 80 InputType: apifault.ErrorCondition_DELAY, 81 Condition: &apimodel.MatchString{Type: apimodel.MatchString_EXACT, Value: &wrappers.StringValue{Value: "500"}}, 82 }, 83 }, 84 TriggerCondition: []*apifault.TriggerCondition{ 85 { 86 TriggerType: apifault.TriggerCondition_CONSECUTIVE_ERROR, 87 ErrorCount: 10, 88 }, 89 { 90 TriggerType: apifault.TriggerCondition_ERROR_RATE, 91 ErrorPercent: 50, 92 Interval: 30, 93 }, 94 }, 95 MaxEjectionPercent: 90, 96 RecoverCondition: &apifault.RecoverCondition{ 97 ConsecutiveSuccess: 3, 98 SleepWindow: 60, 99 }, 100 FaultDetectConfig: &apifault.FaultDetectConfig{Enable: true}, 101 FallbackConfig: &apifault.FallbackConfig{ 102 Enable: true, 103 Response: &apifault.FallbackResponse{ 104 Code: 500, 105 Headers: []*apifault.FallbackResponse_MessageHeader{ 106 { 107 Key: "x-redirect", 108 Value: "test.com", 109 }, 110 }, 111 Body: "<h>Too many request</h>", 112 }, 113 }, 114 } 115 } 116 117 const testCount = 5 118 119 func createCircuitBreakerRules(discoverSuit *DiscoverTestSuit, count int) ([]*apifault.CircuitBreakerRule, *apiservice.BatchWriteResponse) { 120 cbRules := make([]*apifault.CircuitBreakerRule, 0, count) 121 for i := 0; i < count; i++ { 122 cbRule := buildCircuitBreakerRule(i) 123 cbRules = append(cbRules, cbRule) 124 } 125 resp := discoverSuit.DiscoverServer().CreateCircuitBreakerRules(discoverSuit.DefaultCtx, cbRules) 126 return cbRules, resp 127 } 128 129 func queryCircuitBreakerRules(discoverSuit *DiscoverTestSuit, query map[string]string) *apiservice.BatchQueryResponse { 130 return discoverSuit.DiscoverServer().GetCircuitBreakerRules(discoverSuit.DefaultCtx, query) 131 } 132 133 func cleanCircuitBreakerRules(discoverSuit *DiscoverTestSuit, response *apiservice.BatchWriteResponse) { 134 cbRules := parseResponseToCircuitBreakerRules(response) 135 if len(cbRules) == 0 { 136 return 137 } 138 discoverSuit.DiscoverServer().DeleteCircuitBreakerRules(discoverSuit.DefaultCtx, cbRules) 139 } 140 141 func checkCircuitBreakerRuleResponse(t *testing.T, requests []*apifault.CircuitBreakerRule, response *apiservice.BatchWriteResponse) { 142 assertions := assert.New(t) 143 assertions.Equal(len(requests), len(response.Responses)) 144 for _, resp := range response.Responses { 145 assertions.Equal(uint32(apimodel.Code_ExecuteSuccess), resp.GetCode().GetValue()) 146 msg := &apifault.CircuitBreakerRule{} 147 err := ptypes.UnmarshalAny(resp.GetData(), msg) 148 assertions.Nil(err) 149 assertions.True(len(msg.GetId()) > 0) 150 } 151 } 152 153 func parseResponseToCircuitBreakerRules(response *apiservice.BatchWriteResponse) []*apifault.CircuitBreakerRule { 154 cbRules := make([]*apifault.CircuitBreakerRule, 0, len(response.GetResponses())) 155 for _, resp := range response.GetResponses() { 156 if resp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) { 157 continue 158 } 159 msg := &apifault.CircuitBreakerRule{} 160 _ = ptypes.UnmarshalAny(resp.GetData(), msg) 161 cbRules = append(cbRules, msg) 162 } 163 return cbRules 164 } 165 166 // TestCreateCircuitBreakerRule test create circuitbreaker rule 167 func TestCreateCircuitBreakerRule(t *testing.T) { 168 discoverSuit := &DiscoverTestSuit{} 169 if err := discoverSuit.Initialize(); err != nil { 170 t.Fatal(err) 171 } 172 defer discoverSuit.Destroy() 173 174 t.Run("正常创建熔断规则,返回成功", func(t *testing.T) { 175 cbRules, resp := createCircuitBreakerRules(discoverSuit, testCount) 176 defer cleanCircuitBreakerRules(discoverSuit, resp) 177 qResp := queryCircuitBreakerRules(discoverSuit, map[string]string{"level": strconv.Itoa(int(apifault.Level_GROUP))}) 178 assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), qResp.GetCode().GetValue()) 179 checkCircuitBreakerRuleResponse(t, cbRules, resp) 180 181 }) 182 183 t.Run("重复创建熔断规则,返回错误", func(t *testing.T) { 184 cbRules, firstResp := createCircuitBreakerRules(discoverSuit, 1) 185 defer cleanCircuitBreakerRules(discoverSuit, firstResp) 186 checkCircuitBreakerRuleResponse(t, cbRules, firstResp) 187 188 if resp := discoverSuit.DiscoverServer().CreateCircuitBreakerRules(discoverSuit.DefaultCtx, cbRules); !respSuccess(resp) { 189 t.Logf("pass: %s", resp.GetInfo().GetValue()) 190 } else { 191 t.Fatal("error, duplicate rule can not be passed") 192 } 193 }) 194 195 t.Run("创建熔断规则,删除,再创建,返回成功", func(t *testing.T) { 196 cbRules, firstResp := createCircuitBreakerRules(discoverSuit, 1) 197 cleanCircuitBreakerRules(discoverSuit, firstResp) 198 cbRules, resp := createCircuitBreakerRules(discoverSuit, 1) 199 defer cleanCircuitBreakerRules(discoverSuit, resp) 200 checkCircuitBreakerRuleResponse(t, cbRules, resp) 201 }) 202 203 t.Run("创建熔断规则时,没有传递规则名,返回错误", func(t *testing.T) { 204 cbRule := buildUnnamedCircuitBreakerRule() 205 if resp := discoverSuit.DiscoverServer().CreateCircuitBreakerRules(discoverSuit.DefaultCtx, []*apifault.CircuitBreakerRule{cbRule}); !respSuccess(resp) { 206 t.Logf("pass: %s", resp.GetInfo().GetValue()) 207 } else { 208 t.Fatal("error, unnamed rule can not be passed") 209 } 210 }) 211 212 t.Run("并发创建熔断规则,返回成功", func(t *testing.T) { 213 var wg sync.WaitGroup 214 for i := 0; i < 50; i++ { 215 wg.Add(1) 216 go func(index int) { 217 defer wg.Done() 218 cbRule := buildCircuitBreakerRule(index) 219 cbRules := []*apifault.CircuitBreakerRule{cbRule} 220 resp := discoverSuit.DiscoverServer().CreateCircuitBreakerRules(discoverSuit.DefaultCtx, cbRules) 221 cleanCircuitBreakerRules(discoverSuit, resp) 222 }(i) 223 } 224 wg.Wait() 225 }) 226 227 t.Run("创建多个熔断规则,并进行查询,返回查询结果", func(t *testing.T) { 228 cbRules, firstResp := createCircuitBreakerRules(discoverSuit, 5) 229 defer cleanCircuitBreakerRules(discoverSuit, firstResp) 230 checkCircuitBreakerRuleResponse(t, cbRules, firstResp) 231 batchResp := discoverSuit.DiscoverServer().GetCircuitBreakerRules( 232 discoverSuit.DefaultCtx, map[string]string{"name": "test-circuitbreaker-rule"}) 233 assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), batchResp.GetCode().GetValue()) 234 anyValues := batchResp.GetData() 235 assert.Equal(t, len(cbRules), len(anyValues)) 236 batchResp = discoverSuit.DiscoverServer().GetCircuitBreakerRules( 237 discoverSuit.DefaultCtx, map[string]string{"name": "test-circuitbreaker-rule", "srcNamespace": "test1"}) 238 assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), batchResp.GetCode().GetValue()) 239 anyValues = batchResp.GetData() 240 assert.Equal(t, 0, len(anyValues)) 241 batchResp = discoverSuit.DiscoverServer().GetCircuitBreakerRules( 242 discoverSuit.DefaultCtx, map[string]string{"name": "test-circuitbreaker-rule", "dstService": "test1"}) 243 assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), batchResp.GetCode().GetValue()) 244 anyValues = batchResp.GetData() 245 assert.Equal(t, 0, len(anyValues)) 246 }) 247 } 248 249 func TestEnableCircuitBreakerRule(t *testing.T) { 250 discoverSuit := &DiscoverTestSuit{} 251 if err := discoverSuit.Initialize(); err != nil { 252 t.Fatal(err) 253 } 254 defer discoverSuit.Destroy() 255 256 t.Run("正常创建熔断规则,返回成功", func(t *testing.T) { 257 cbRules, resp := createCircuitBreakerRules(discoverSuit, testCount) 258 defer cleanCircuitBreakerRules(discoverSuit, resp) 259 qResp := queryCircuitBreakerRules(discoverSuit, map[string]string{"level": strconv.Itoa(int(apifault.Level_GROUP))}) 260 assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), qResp.GetCode().GetValue()) 261 checkCircuitBreakerRuleResponse(t, cbRules, resp) 262 263 testRule := cbRules[0] 264 testRule.Enable = false 265 resp = discoverSuit.DiscoverServer().EnableCircuitBreakerRules(discoverSuit.DefaultCtx, []*apifault.CircuitBreakerRule{testRule}) 266 assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), resp.GetCode().GetValue()) 267 qResp = queryCircuitBreakerRules(discoverSuit, map[string]string{"id": testRule.Id}) 268 assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), qResp.GetCode().GetValue()) 269 270 for _, resp := range qResp.GetData() { 271 msg := &apifault.CircuitBreakerRule{} 272 err := ptypes.UnmarshalAny(resp, msg) 273 assert.Nil(t, err) 274 assert.False(t, msg.Enable) 275 } 276 }) 277 } 278 279 func TestUpdateCircuitBreakerRule(t *testing.T) { 280 discoverSuit := &DiscoverTestSuit{} 281 if err := discoverSuit.Initialize(); err != nil { 282 t.Fatal(err) 283 } 284 defer discoverSuit.Destroy() 285 286 t.Run("正常更新熔断规则,返回成功", func(t *testing.T) { 287 cbRules, resp := createCircuitBreakerRules(discoverSuit, testCount) 288 defer cleanCircuitBreakerRules(discoverSuit, resp) 289 qResp := queryCircuitBreakerRules(discoverSuit, map[string]string{"level": strconv.Itoa(int(apifault.Level_GROUP))}) 290 assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), qResp.GetCode().GetValue()) 291 checkCircuitBreakerRuleResponse(t, cbRules, resp) 292 293 mockDescr := "update circuitbreaker rule info" 294 testRule := cbRules[0] 295 testRule.Description = mockDescr 296 resp = discoverSuit.DiscoverServer().UpdateCircuitBreakerRules(discoverSuit.DefaultCtx, []*apifault.CircuitBreakerRule{testRule}) 297 assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), resp.GetCode().GetValue()) 298 qResp = queryCircuitBreakerRules(discoverSuit, map[string]string{"id": testRule.Id}) 299 assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), qResp.GetCode().GetValue()) 300 301 for _, resp := range qResp.GetData() { 302 msg := &apifault.CircuitBreakerRule{} 303 err := ptypes.UnmarshalAny(resp, msg) 304 assert.Nil(t, err) 305 assert.Equal(t, mockDescr, msg.Description) 306 } 307 }) 308 }