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  }