github.com/polarismesh/polaris@v1.17.8/service/circuitbreaker_rule.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
    19  
    20  import (
    21  	"context"
    22  	"encoding/json"
    23  	"fmt"
    24  	"time"
    25  
    26  	"github.com/golang/protobuf/jsonpb"
    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  
    32  	api "github.com/polarismesh/polaris/common/api/v1"
    33  	"github.com/polarismesh/polaris/common/model"
    34  	commonstore "github.com/polarismesh/polaris/common/store"
    35  	commontime "github.com/polarismesh/polaris/common/time"
    36  	"github.com/polarismesh/polaris/common/utils"
    37  )
    38  
    39  func checkBatchCircuitBreakerRules(req []*apifault.CircuitBreakerRule) *apiservice.BatchWriteResponse {
    40  	if len(req) == 0 {
    41  		return api.NewBatchWriteResponse(apimodel.Code_EmptyRequest)
    42  	}
    43  
    44  	if len(req) > MaxBatchSize {
    45  		return api.NewBatchWriteResponse(apimodel.Code_BatchSizeOverLimit)
    46  	}
    47  
    48  	return nil
    49  }
    50  
    51  // CreateCircuitBreakerRules Create a CircuitBreaker rule
    52  func (s *Server) CreateCircuitBreakerRules(
    53  	ctx context.Context, request []*apifault.CircuitBreakerRule) *apiservice.BatchWriteResponse {
    54  	if checkErr := checkBatchCircuitBreakerRules(request); checkErr != nil {
    55  		return checkErr
    56  	}
    57  
    58  	responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess)
    59  	for _, cbRule := range request {
    60  		response := s.createCircuitBreakerRule(ctx, cbRule)
    61  		api.Collect(responses, response)
    62  	}
    63  	return api.FormatBatchWriteResponse(responses)
    64  }
    65  
    66  // CreateCircuitBreakerRule Create a CircuitBreaker rule
    67  func (s *Server) createCircuitBreakerRule(
    68  	ctx context.Context, request *apifault.CircuitBreakerRule) *apiservice.Response {
    69  	requestID := utils.ParseRequestID(ctx)
    70  	if resp := checkCircuitBreakerRuleParams(request, false, true); resp != nil {
    71  		return resp
    72  	}
    73  
    74  	// 构造底层数据结构
    75  	data, err := api2CircuitBreakerRule(request)
    76  	if err != nil {
    77  		log.Error(err.Error(), utils.ZapRequestID(requestID))
    78  		return api.NewResponse(apimodel.Code_ParseCircuitBreakerException)
    79  	}
    80  	exists, err := s.storage.HasCircuitBreakerRuleByName(data.Name, data.Namespace)
    81  	if err != nil {
    82  		log.Error(err.Error(), utils.ZapRequestID(requestID))
    83  		return storeError2Response(err)
    84  	}
    85  	if exists {
    86  		return api.NewResponse(apimodel.Code_ServiceExistedCircuitBreakers)
    87  	}
    88  	data.ID = utils.NewUUID()
    89  
    90  	// 存储层操作
    91  	if err := s.storage.CreateCircuitBreakerRule(data); err != nil {
    92  		log.Error(err.Error(), utils.ZapRequestID(requestID))
    93  		return storeError2Response(err)
    94  	}
    95  
    96  	msg := fmt.Sprintf("create circuitBreaker rule: id=%v, name=%v, namespace=%v",
    97  		data.ID, request.GetName(), request.GetNamespace())
    98  	log.Info(msg, utils.ZapRequestID(requestID))
    99  
   100  	s.RecordHistory(ctx, circuitBreakerRuleRecordEntry(ctx, request, data, model.OCreate))
   101  
   102  	request.Id = data.ID
   103  	return api.NewAnyDataResponse(apimodel.Code_ExecuteSuccess, request)
   104  }
   105  
   106  func checkCircuitBreakerRuleParams(
   107  	req *apifault.CircuitBreakerRule, idRequired bool, nameRequired bool) *apiservice.Response {
   108  	if req == nil {
   109  		return api.NewResponse(apimodel.Code_EmptyRequest)
   110  	}
   111  	if resp := checkCircuitBreakerRuleParamsDbLen(req); nil != resp {
   112  		return resp
   113  	}
   114  	if nameRequired && len(req.GetName()) == 0 {
   115  		return api.NewResponse(apimodel.Code_InvalidCircuitBreakerName)
   116  	}
   117  	if idRequired && len(req.GetId()) == 0 {
   118  		return api.NewResponse(apimodel.Code_InvalidCircuitBreakerID)
   119  	}
   120  	return nil
   121  }
   122  
   123  func checkCircuitBreakerRuleParamsDbLen(req *apifault.CircuitBreakerRule) *apiservice.Response {
   124  	if err := utils.CheckDbRawStrFieldLen(
   125  		req.RuleMatcher.GetSource().GetService(), MaxDbServiceNameLength); err != nil {
   126  		return api.NewResponse(apimodel.Code_InvalidServiceName)
   127  	}
   128  	if err := utils.CheckDbRawStrFieldLen(
   129  		req.RuleMatcher.GetSource().GetNamespace(), MaxDbServiceNamespaceLength); err != nil {
   130  		return api.NewResponse(apimodel.Code_InvalidNamespaceName)
   131  	}
   132  	if err := utils.CheckDbRawStrFieldLen(req.GetName(), MaxRuleName); err != nil {
   133  		return api.NewResponse(apimodel.Code_InvalidCircuitBreakerName)
   134  	}
   135  	if err := utils.CheckDbRawStrFieldLen(req.GetNamespace(), MaxDbServiceNamespaceLength); err != nil {
   136  		return api.NewResponse(apimodel.Code_InvalidNamespaceName)
   137  	}
   138  	if err := utils.CheckDbRawStrFieldLen(req.GetDescription(), MaxCommentLength); err != nil {
   139  		return api.NewResponse(apimodel.Code_InvalidServiceComment)
   140  	}
   141  	return nil
   142  }
   143  
   144  func circuitBreakerRuleRecordEntry(ctx context.Context, req *apifault.CircuitBreakerRule, md *model.CircuitBreakerRule,
   145  	opt model.OperationType) *model.RecordEntry {
   146  	marshaler := jsonpb.Marshaler{}
   147  	detail, _ := marshaler.MarshalToString(req)
   148  	entry := &model.RecordEntry{
   149  		ResourceType:  model.RCircuitBreakerRule,
   150  		ResourceName:  fmt.Sprintf("%s(%s)", md.Name, md.ID),
   151  		Namespace:     req.GetNamespace(),
   152  		OperationType: opt,
   153  		Operator:      utils.ParseOperator(ctx),
   154  		Detail:        detail,
   155  		HappenTime:    time.Now(),
   156  	}
   157  	return entry
   158  }
   159  
   160  var (
   161  	// CircuitBreakerRuleFilters filter circuitbreaker rule query parameters
   162  	CircuitBreakerRuleFilters = map[string]bool{
   163  		"brief":            true,
   164  		"offset":           true,
   165  		"limit":            true,
   166  		"id":               true,
   167  		"name":             true,
   168  		"namespace":        true,
   169  		"enable":           true,
   170  		"level":            true,
   171  		"service":          true,
   172  		"serviceNamespace": true,
   173  		"srcService":       true,
   174  		"srcNamespace":     true,
   175  		"dstService":       true,
   176  		"dstNamespace":     true,
   177  		"dstMethod":        true,
   178  		"description":      true,
   179  	}
   180  )
   181  
   182  // DeleteCircuitBreakerRules Delete current CircuitBreaker rules
   183  func (s *Server) DeleteCircuitBreakerRules(
   184  	ctx context.Context, request []*apifault.CircuitBreakerRule) *apiservice.BatchWriteResponse {
   185  	if err := checkBatchCircuitBreakerRules(request); err != nil {
   186  		return err
   187  	}
   188  
   189  	responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess)
   190  	for _, entry := range request {
   191  		resp := s.deleteCircuitBreakerRule(ctx, entry)
   192  		api.Collect(responses, resp)
   193  	}
   194  	return api.FormatBatchWriteResponse(responses)
   195  }
   196  
   197  // deleteCircuitBreakerRule delete current CircuitBreaker rule
   198  func (s *Server) deleteCircuitBreakerRule(
   199  	ctx context.Context, request *apifault.CircuitBreakerRule) *apiservice.Response {
   200  	requestID := utils.ParseRequestID(ctx)
   201  	if resp := checkCircuitBreakerRuleParams(request, true, false); resp != nil {
   202  		return resp
   203  	}
   204  	resp := s.checkCircuitBreakerRuleExists(request.GetId(), requestID)
   205  	if resp != nil {
   206  		if resp.GetCode().GetValue() == uint32(apimodel.Code_NotFoundCircuitBreaker) {
   207  			resp.Code = &wrappers.UInt32Value{Value: uint32(apimodel.Code_ExecuteSuccess)}
   208  		}
   209  		return resp
   210  	}
   211  	cbRuleId := &apifault.CircuitBreakerRule{Id: request.GetId()}
   212  	err := s.storage.DeleteCircuitBreakerRule(request.GetId())
   213  	if err != nil {
   214  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   215  		return api.NewAnyDataResponse(apimodel.Code_ParseCircuitBreakerException, cbRuleId)
   216  	}
   217  	msg := fmt.Sprintf("delete circuitbreaker rule: id=%v, name=%v, namespace=%v",
   218  		request.GetId(), request.GetName(), request.GetNamespace())
   219  	log.Info(msg, utils.ZapRequestID(requestID))
   220  
   221  	cbRule := &model.CircuitBreakerRule{
   222  		ID: request.GetId(), Name: request.GetName(), Namespace: request.GetNamespace()}
   223  	s.RecordHistory(ctx, circuitBreakerRuleRecordEntry(ctx, request, cbRule, model.ODelete))
   224  	return api.NewAnyDataResponse(apimodel.Code_ExecuteSuccess, cbRuleId)
   225  }
   226  
   227  // EnableCircuitBreakerRules Enable the CircuitBreaker rule
   228  func (s *Server) EnableCircuitBreakerRules(
   229  	ctx context.Context, request []*apifault.CircuitBreakerRule) *apiservice.BatchWriteResponse {
   230  	if err := checkBatchCircuitBreakerRules(request); err != nil {
   231  		return err
   232  	}
   233  
   234  	responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess)
   235  	for _, entry := range request {
   236  		resp := s.enableCircuitBreakerRule(ctx, entry)
   237  		api.Collect(responses, resp)
   238  	}
   239  	return api.FormatBatchWriteResponse(responses)
   240  }
   241  
   242  func (s *Server) enableCircuitBreakerRule(
   243  	ctx context.Context, request *apifault.CircuitBreakerRule) *apiservice.Response {
   244  	requestID := utils.ParseRequestID(ctx)
   245  	if resp := checkCircuitBreakerRuleParams(request, true, false); resp != nil {
   246  		return resp
   247  	}
   248  	resp := s.checkCircuitBreakerRuleExists(request.GetId(), requestID)
   249  	if resp != nil {
   250  		return resp
   251  	}
   252  	cbRuleId := &apifault.CircuitBreakerRule{Id: request.GetId()}
   253  	cbRule := &model.CircuitBreakerRule{
   254  		ID:        request.GetId(),
   255  		Namespace: request.GetNamespace(),
   256  		Name:      request.GetName(),
   257  		Enable:    request.GetEnable(),
   258  		Revision:  utils.NewUUID(),
   259  	}
   260  	if err := s.storage.EnableCircuitBreakerRule(cbRule); err != nil {
   261  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   262  		return storeError2AnyResponse(err, cbRuleId)
   263  	}
   264  
   265  	msg := fmt.Sprintf("enable circuitbreaker rule: id=%v, name=%v, namespace=%v",
   266  		request.GetId(), request.GetName(), request.GetNamespace())
   267  	log.Info(msg, utils.ZapRequestID(requestID))
   268  
   269  	s.RecordHistory(ctx, circuitBreakerRuleRecordEntry(ctx, request, cbRule, model.OUpdate))
   270  	return api.NewAnyDataResponse(apimodel.Code_ExecuteSuccess, cbRuleId)
   271  }
   272  
   273  // UpdateCircuitBreakerRules Modify the CircuitBreaker rule
   274  func (s *Server) UpdateCircuitBreakerRules(
   275  	ctx context.Context, request []*apifault.CircuitBreakerRule) *apiservice.BatchWriteResponse {
   276  	if err := checkBatchCircuitBreakerRules(request); err != nil {
   277  		return err
   278  	}
   279  
   280  	responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess)
   281  	for _, entry := range request {
   282  		response := s.updateCircuitBreakerRule(ctx, entry)
   283  		api.Collect(responses, response)
   284  	}
   285  	return api.FormatBatchWriteResponse(responses)
   286  }
   287  
   288  func (s *Server) updateCircuitBreakerRule(
   289  	ctx context.Context, request *apifault.CircuitBreakerRule) *apiservice.Response {
   290  	requestID := utils.ParseRequestID(ctx)
   291  	if resp := checkCircuitBreakerRuleParams(request, true, true); resp != nil {
   292  		return resp
   293  	}
   294  	resp := s.checkCircuitBreakerRuleExists(request.GetId(), requestID)
   295  	if resp != nil {
   296  		return resp
   297  	}
   298  	cbRuleId := &apifault.CircuitBreakerRule{Id: request.GetId()}
   299  	cbRule, err := api2CircuitBreakerRule(request)
   300  	if err != nil {
   301  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   302  		return api.NewAnyDataResponse(apimodel.Code_ParseCircuitBreakerException, cbRuleId)
   303  	}
   304  	cbRule.ID = request.GetId()
   305  	exists, err := s.storage.HasCircuitBreakerRuleByNameExcludeId(cbRule.Name, cbRule.Namespace, cbRule.ID)
   306  	if err != nil {
   307  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   308  		return storeError2Response(err)
   309  	}
   310  	if exists {
   311  		return api.NewResponse(apimodel.Code_ServiceExistedCircuitBreakers)
   312  	}
   313  	if err := s.storage.UpdateCircuitBreakerRule(cbRule); err != nil {
   314  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   315  		return storeError2AnyResponse(err, cbRuleId)
   316  	}
   317  
   318  	msg := fmt.Sprintf("update circuitbreaker rule: id=%v, name=%v, namespace=%v",
   319  		request.GetId(), request.GetName(), request.GetNamespace())
   320  	log.Info(msg, utils.ZapRequestID(requestID))
   321  
   322  	s.RecordHistory(ctx, circuitBreakerRuleRecordEntry(ctx, request, cbRule, model.OUpdate))
   323  	return api.NewAnyDataResponse(apimodel.Code_ExecuteSuccess, cbRuleId)
   324  }
   325  
   326  func (s *Server) checkCircuitBreakerRuleExists(id, requestID string) *apiservice.Response {
   327  	exists, err := s.storage.HasCircuitBreakerRule(id)
   328  	if err != nil {
   329  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   330  		return api.NewResponse(commonstore.StoreCode2APICode(err))
   331  	}
   332  	if !exists {
   333  		return api.NewResponse(apimodel.Code_NotFoundCircuitBreaker)
   334  	}
   335  	return nil
   336  }
   337  
   338  // GetCircuitBreakerRules Query CircuitBreaker rules
   339  func (s *Server) GetCircuitBreakerRules(ctx context.Context, query map[string]string) *apiservice.BatchQueryResponse {
   340  	offset, limit, err := utils.ParseOffsetAndLimit(query)
   341  	if err != nil {
   342  		return api.NewBatchQueryResponse(apimodel.Code_InvalidParameter)
   343  	}
   344  	searchFilter := make(map[string]string, len(query))
   345  	for key, value := range query {
   346  		if _, ok := CircuitBreakerRuleFilters[key]; !ok {
   347  			log.Errorf("params %s is not allowed in querying circuitbreaker rule", key)
   348  			return api.NewBatchQueryResponse(apimodel.Code_InvalidParameter)
   349  		}
   350  		if value == "" {
   351  			continue
   352  		}
   353  		searchFilter[key] = value
   354  	}
   355  	total, cbRules, err := s.storage.GetCircuitBreakerRules(searchFilter, offset, limit)
   356  	if err != nil {
   357  		log.Errorf("get circuitbreaker rules store err: %s", err.Error())
   358  		return api.NewBatchQueryResponse(commonstore.StoreCode2APICode(err))
   359  	}
   360  	out := api.NewBatchQueryResponse(apimodel.Code_ExecuteSuccess)
   361  	out.Amount = utils.NewUInt32Value(total)
   362  	out.Size = utils.NewUInt32Value(uint32(len(cbRules)))
   363  	for _, cbRule := range cbRules {
   364  		cbRuleProto, err := circuitBreakerRule2api(cbRule)
   365  		if nil != err {
   366  			log.Errorf("marshal circuitbreaker rule fail: %v", err)
   367  			continue
   368  		}
   369  		if nil == cbRuleProto {
   370  			continue
   371  		}
   372  		err = api.AddAnyDataIntoBatchQuery(out, cbRuleProto)
   373  		if nil != err {
   374  			log.Errorf("add circuitbreaker rule as any data fail: %v", err)
   375  			continue
   376  		}
   377  	}
   378  	return out
   379  }
   380  
   381  func marshalCircuitBreakerRuleV2(req *apifault.CircuitBreakerRule) (string, error) {
   382  	r := &apifault.CircuitBreakerRule{
   383  		RuleMatcher:        req.RuleMatcher,
   384  		ErrorConditions:    req.ErrorConditions,
   385  		TriggerCondition:   req.TriggerCondition,
   386  		MaxEjectionPercent: req.MaxEjectionPercent,
   387  		RecoverCondition:   req.RecoverCondition,
   388  		FaultDetectConfig:  req.FaultDetectConfig,
   389  		FallbackConfig:     req.FallbackConfig,
   390  	}
   391  	rule, err := json.Marshal(r)
   392  	if err != nil {
   393  		return "", err
   394  	}
   395  	return string(rule), nil
   396  }
   397  
   398  // api2CircuitBreakerRule 把API参数转化为内部数据结构
   399  func api2CircuitBreakerRule(req *apifault.CircuitBreakerRule) (*model.CircuitBreakerRule, error) {
   400  	rule, err := marshalCircuitBreakerRuleV2(req)
   401  	if err != nil {
   402  		return nil, err
   403  	}
   404  
   405  	out := &model.CircuitBreakerRule{
   406  		Name:         req.GetName(),
   407  		Namespace:    req.GetNamespace(),
   408  		Description:  req.GetDescription(),
   409  		Level:        int(req.GetLevel()),
   410  		SrcService:   req.GetRuleMatcher().GetSource().GetService(),
   411  		SrcNamespace: req.GetRuleMatcher().GetSource().GetNamespace(),
   412  		DstService:   req.GetRuleMatcher().GetDestination().GetService(),
   413  		DstNamespace: req.GetRuleMatcher().GetDestination().GetNamespace(),
   414  		DstMethod:    req.GetRuleMatcher().GetDestination().GetMethod().GetValue().GetValue(),
   415  		Enable:       req.GetEnable(),
   416  		Rule:         rule,
   417  		Revision:     utils.NewUUID(),
   418  	}
   419  	if out.Namespace == "" {
   420  		out.Namespace = DefaultNamespace
   421  	}
   422  	return out, nil
   423  }
   424  
   425  func circuitBreakerRule2api(cbRule *model.CircuitBreakerRule) (*apifault.CircuitBreakerRule, error) {
   426  	if cbRule == nil {
   427  		return nil, nil
   428  	}
   429  	cbRule.Proto = &apifault.CircuitBreakerRule{}
   430  	if len(cbRule.Rule) > 0 {
   431  		if err := json.Unmarshal([]byte(cbRule.Rule), cbRule.Proto); err != nil {
   432  			return nil, err
   433  		}
   434  	} else {
   435  		// brief search, to display the services in list result
   436  		cbRule.Proto.RuleMatcher = &apifault.RuleMatcher{
   437  			Source: &apifault.RuleMatcher_SourceService{
   438  				Service:   cbRule.SrcService,
   439  				Namespace: cbRule.SrcNamespace,
   440  			},
   441  			Destination: &apifault.RuleMatcher_DestinationService{
   442  				Service:   cbRule.DstService,
   443  				Namespace: cbRule.DstNamespace,
   444  				Method:    &apimodel.MatchString{Value: &wrappers.StringValue{Value: cbRule.DstMethod}},
   445  			},
   446  		}
   447  	}
   448  	cbRule.Proto.Id = cbRule.ID
   449  	cbRule.Proto.Name = cbRule.Name
   450  	cbRule.Proto.Namespace = cbRule.Namespace
   451  	cbRule.Proto.Description = cbRule.Description
   452  	cbRule.Proto.Level = apifault.Level(cbRule.Level)
   453  	cbRule.Proto.Enable = cbRule.Enable
   454  	cbRule.Proto.Revision = cbRule.Revision
   455  	cbRule.Proto.Ctime = commontime.Time2String(cbRule.CreateTime)
   456  	cbRule.Proto.Mtime = commontime.Time2String(cbRule.ModifyTime)
   457  	cbRule.Proto.Enable = cbRule.Enable
   458  	if cbRule.EnableTime.Year() > 2000 {
   459  		cbRule.Proto.Etime = commontime.Time2String(cbRule.EnableTime)
   460  	} else {
   461  		cbRule.Proto.Etime = ""
   462  	}
   463  	return cbRule.Proto, nil
   464  }
   465  
   466  // circuitBreaker2ClientAPI 把内部数据结构转化为客户端API参数
   467  func circuitBreaker2ClientAPI(
   468  	req *model.ServiceWithCircuitBreakerRules, service string, namespace string) (*apifault.CircuitBreaker, error) {
   469  	if req == nil {
   470  		return nil, nil
   471  	}
   472  
   473  	out := &apifault.CircuitBreaker{}
   474  	out.Revision = &wrappers.StringValue{Value: req.Revision}
   475  	out.Rules = make([]*apifault.CircuitBreakerRule, 0, req.CountCircuitBreakerRules())
   476  	var iterateErr error
   477  	req.IterateCircuitBreakerRules(func(rule *model.CircuitBreakerRule) {
   478  		cbRule, err := circuitBreakerRule2api(rule)
   479  		if err != nil {
   480  			iterateErr = err
   481  			return
   482  		}
   483  		out.Rules = append(out.Rules, cbRule)
   484  	})
   485  	if nil != iterateErr {
   486  		return nil, iterateErr
   487  	}
   488  
   489  	out.Service = utils.NewStringValue(service)
   490  	out.ServiceNamespace = utils.NewStringValue(namespace)
   491  
   492  	return out, nil
   493  }