github.com/polarismesh/polaris@v1.17.8/service/faultdetect_config.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 checkBatchFaultDetectRules(req []*apifault.FaultDetectRule) *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  // CreateFaultDetectRules Create a FaultDetect rule
    52  func (s *Server) CreateFaultDetectRules(
    53  	ctx context.Context, request []*apifault.FaultDetectRule) *apiservice.BatchWriteResponse {
    54  	if checkErr := checkBatchFaultDetectRules(request); checkErr != nil {
    55  		return checkErr
    56  	}
    57  
    58  	responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess)
    59  	for _, cbRule := range request {
    60  		response := s.createFaultDetectRule(ctx, cbRule)
    61  		api.Collect(responses, response)
    62  	}
    63  	return api.FormatBatchWriteResponse(responses)
    64  }
    65  
    66  // DeleteFaultDetectRules Delete current Fault Detect rules
    67  func (s *Server) DeleteFaultDetectRules(
    68  	ctx context.Context, request []*apifault.FaultDetectRule) *apiservice.BatchWriteResponse {
    69  	if checkErr := checkBatchFaultDetectRules(request); checkErr != nil {
    70  		return checkErr
    71  	}
    72  
    73  	responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess)
    74  	for _, cbRule := range request {
    75  		response := s.deleteFaultDetectRule(ctx, cbRule)
    76  		api.Collect(responses, response)
    77  	}
    78  	return api.FormatBatchWriteResponse(responses)
    79  }
    80  
    81  // UpdateFaultDetectRules Modify the FaultDetect rule
    82  func (s *Server) UpdateFaultDetectRules(
    83  	ctx context.Context, request []*apifault.FaultDetectRule) *apiservice.BatchWriteResponse {
    84  	if checkErr := checkBatchFaultDetectRules(request); checkErr != nil {
    85  		return checkErr
    86  	}
    87  
    88  	responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess)
    89  	for _, cbRule := range request {
    90  		response := s.updateFaultDetectRule(ctx, cbRule)
    91  		api.Collect(responses, response)
    92  	}
    93  	return api.FormatBatchWriteResponse(responses)
    94  }
    95  
    96  func checkFaultDetectRuleParams(
    97  	req *apifault.FaultDetectRule, idRequired bool, nameRequired bool) *apiservice.Response {
    98  	if req == nil {
    99  		return api.NewResponse(apimodel.Code_EmptyRequest)
   100  	}
   101  	if resp := checkFaultDetectRuleParamsDbLen(req); nil != resp {
   102  		return resp
   103  	}
   104  	if nameRequired && len(req.GetName()) == 0 {
   105  		return api.NewResponse(apimodel.Code_InvalidCircuitBreakerName)
   106  	}
   107  	if idRequired && len(req.GetId()) == 0 {
   108  		return api.NewResponse(apimodel.Code_InvalidCircuitBreakerID)
   109  	}
   110  	return nil
   111  }
   112  
   113  func checkFaultDetectRuleParamsDbLen(req *apifault.FaultDetectRule) *apiservice.Response {
   114  	if err := utils.CheckDbRawStrFieldLen(req.GetTargetService().GetService(), MaxDbServiceNameLength); err != nil {
   115  		return api.NewResponse(apimodel.Code_InvalidServiceName)
   116  	}
   117  	if err := utils.CheckDbRawStrFieldLen(
   118  		req.GetTargetService().GetNamespace(), MaxDbServiceNamespaceLength); err != nil {
   119  		return api.NewResponse(apimodel.Code_InvalidNamespaceName)
   120  	}
   121  	if err := utils.CheckDbRawStrFieldLen(req.GetName(), MaxRuleName); err != nil {
   122  		return api.NewResponse(apimodel.Code_InvalidRateLimitName)
   123  	}
   124  	if err := utils.CheckDbRawStrFieldLen(req.GetNamespace(), MaxDbServiceNamespaceLength); err != nil {
   125  		return api.NewResponse(apimodel.Code_InvalidNamespaceName)
   126  	}
   127  	if err := utils.CheckDbRawStrFieldLen(req.GetDescription(), MaxCommentLength); err != nil {
   128  		return api.NewResponse(apimodel.Code_InvalidServiceComment)
   129  	}
   130  	return nil
   131  }
   132  
   133  func faultDetectRuleRecordEntry(ctx context.Context, req *apifault.FaultDetectRule, md *model.FaultDetectRule,
   134  	opt model.OperationType) *model.RecordEntry {
   135  	marshaler := jsonpb.Marshaler{}
   136  	detail, _ := marshaler.MarshalToString(req)
   137  	entry := &model.RecordEntry{
   138  		ResourceType:  model.RFaultDetectRule,
   139  		ResourceName:  fmt.Sprintf("%s(%s)", md.Name, md.ID),
   140  		Namespace:     req.GetNamespace(),
   141  		OperationType: opt,
   142  		Operator:      utils.ParseOperator(ctx),
   143  		Detail:        detail,
   144  		HappenTime:    time.Now(),
   145  	}
   146  	return entry
   147  }
   148  
   149  // createFaultDetectRule Create a FaultDetect rule
   150  func (s *Server) createFaultDetectRule(ctx context.Context, request *apifault.FaultDetectRule) *apiservice.Response {
   151  	requestID := utils.ParseRequestID(ctx)
   152  	if resp := checkFaultDetectRuleParams(request, false, true); resp != nil {
   153  		return resp
   154  	}
   155  	data, err := api2FaultDetectRule(request)
   156  	if err != nil {
   157  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   158  		return api.NewResponse(apimodel.Code_ParseException)
   159  	}
   160  	exists, err := s.storage.HasFaultDetectRuleByName(data.Name, data.Namespace)
   161  	if err != nil {
   162  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   163  		return storeError2Response(err)
   164  	}
   165  	if exists {
   166  		return api.NewResponse(apimodel.Code_FaultDetectRuleExisted)
   167  	}
   168  	data.ID = utils.NewUUID()
   169  
   170  	// 存储层操作
   171  	if err := s.storage.CreateFaultDetectRule(data); err != nil {
   172  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   173  		return storeError2Response(err)
   174  	}
   175  
   176  	msg := fmt.Sprintf("create fault detect rule: id=%v, name=%v, namespace=%v",
   177  		data.ID, request.GetName(), request.GetNamespace())
   178  	log.Info(msg, utils.ZapRequestID(requestID))
   179  
   180  	s.RecordHistory(ctx, faultDetectRuleRecordEntry(ctx, request, data, model.OCreate))
   181  
   182  	request.Id = data.ID
   183  	return api.NewAnyDataResponse(apimodel.Code_ExecuteSuccess, request)
   184  }
   185  
   186  // updateFaultDetectRule Update a FaultDetect rule
   187  func (s *Server) updateFaultDetectRule(ctx context.Context, request *apifault.FaultDetectRule) *apiservice.Response {
   188  	requestID := utils.ParseRequestID(ctx)
   189  	if resp := checkFaultDetectRuleParams(request, true, true); resp != nil {
   190  		return resp
   191  	}
   192  	resp := s.checkFaultDetectRuleExists(request.GetId(), requestID)
   193  	if resp != nil {
   194  		return resp
   195  	}
   196  	fdRuleId := &apifault.FaultDetectRule{Id: request.GetId()}
   197  	fdRule, err := api2FaultDetectRule(request)
   198  	if err != nil {
   199  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   200  		return api.NewAnyDataResponse(apimodel.Code_ParseException, fdRuleId)
   201  	}
   202  	fdRule.ID = request.GetId()
   203  	exists, err := s.storage.HasFaultDetectRuleByNameExcludeId(fdRule.Name, fdRule.Namespace, fdRule.ID)
   204  	if err != nil {
   205  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   206  		return storeError2Response(err)
   207  	}
   208  	if exists {
   209  		return api.NewAnyDataResponse(apimodel.Code_FaultDetectRuleExisted, fdRuleId)
   210  	}
   211  	if err := s.storage.UpdateFaultDetectRule(fdRule); err != nil {
   212  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   213  		return storeError2AnyResponse(err, fdRuleId)
   214  	}
   215  
   216  	msg := fmt.Sprintf("update fault detect rule: id=%v, name=%v, namespace=%v",
   217  		request.GetId(), request.GetName(), request.GetNamespace())
   218  	log.Info(msg, utils.ZapRequestID(requestID))
   219  
   220  	s.RecordHistory(ctx, faultDetectRuleRecordEntry(ctx, request, fdRule, model.OUpdate))
   221  	return api.NewAnyDataResponse(apimodel.Code_ExecuteSuccess, fdRuleId)
   222  }
   223  
   224  // deleteFaultDetectRule Delete a FaultDetect rule
   225  func (s *Server) deleteFaultDetectRule(ctx context.Context, request *apifault.FaultDetectRule) *apiservice.Response {
   226  	requestID := utils.ParseRequestID(ctx)
   227  	if resp := checkFaultDetectRuleParams(request, true, false); resp != nil {
   228  		return resp
   229  	}
   230  	resp := s.checkFaultDetectRuleExists(request.GetId(), requestID)
   231  	if resp != nil {
   232  		if resp.GetCode().GetValue() == uint32(apimodel.Code_NotFoundResource) {
   233  			resp.Code = &wrappers.UInt32Value{Value: uint32(apimodel.Code_ExecuteSuccess)}
   234  		}
   235  		return resp
   236  	}
   237  	cbRuleId := &apifault.FaultDetectRule{Id: request.GetId()}
   238  	err := s.storage.DeleteFaultDetectRule(request.GetId())
   239  	if err != nil {
   240  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   241  		return api.NewAnyDataResponse(apimodel.Code_ParseException, cbRuleId)
   242  	}
   243  	msg := fmt.Sprintf("delete fault detect rule: id=%v, name=%v, namespace=%v",
   244  		request.GetId(), request.GetName(), request.GetNamespace())
   245  	log.Info(msg, utils.ZapRequestID(requestID))
   246  
   247  	cbRule := &model.FaultDetectRule{ID: request.GetId(), Name: request.GetName(), Namespace: request.GetNamespace()}
   248  	s.RecordHistory(ctx, faultDetectRuleRecordEntry(ctx, request, cbRule, model.ODelete))
   249  	return api.NewAnyDataResponse(apimodel.Code_ExecuteSuccess, cbRuleId)
   250  }
   251  
   252  func (s *Server) checkFaultDetectRuleExists(id, requestID string) *apiservice.Response {
   253  	exists, err := s.storage.HasFaultDetectRule(id)
   254  	if err != nil {
   255  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   256  		return api.NewResponse(commonstore.StoreCode2APICode(err))
   257  	}
   258  	if !exists {
   259  		return api.NewResponse(apimodel.Code_NotFoundResource)
   260  	}
   261  	return nil
   262  }
   263  
   264  var (
   265  	// FaultDetectRuleFilters filter fault detect rule query parameters
   266  	FaultDetectRuleFilters = map[string]bool{
   267  		"brief":            true,
   268  		"offset":           true,
   269  		"limit":            true,
   270  		"id":               true,
   271  		"name":             true,
   272  		"namespace":        true,
   273  		"service":          true,
   274  		"serviceNamespace": true,
   275  		"dstService":       true,
   276  		"dstNamespace":     true,
   277  		"dstMethod":        true,
   278  		"description":      true,
   279  	}
   280  )
   281  
   282  func (s *Server) GetFaultDetectRules(ctx context.Context, query map[string]string) *apiservice.BatchQueryResponse {
   283  	for key := range query {
   284  		if _, ok := FaultDetectRuleFilters[key]; !ok {
   285  			log.Errorf("params %s is not allowed in querying fault detect rule", key)
   286  			return api.NewBatchQueryResponse(apimodel.Code_InvalidParameter)
   287  		}
   288  	}
   289  	offset, limit, err := utils.ParseOffsetAndLimit(query)
   290  	if err != nil {
   291  		return api.NewBatchQueryResponse(apimodel.Code_InvalidParameter)
   292  	}
   293  	total, cbRules, err := s.storage.GetFaultDetectRules(query, offset, limit)
   294  	if err != nil {
   295  		log.Errorf("get fault detect rules store err: %s", err.Error())
   296  		return api.NewBatchQueryResponse(commonstore.StoreCode2APICode(err))
   297  	}
   298  	out := api.NewBatchQueryResponse(apimodel.Code_ExecuteSuccess)
   299  	out.Amount = utils.NewUInt32Value(total)
   300  	out.Size = utils.NewUInt32Value(uint32(len(cbRules)))
   301  	for _, cbRule := range cbRules {
   302  		cbRuleProto, err := faultDetectRule2api(cbRule)
   303  		if nil != err {
   304  			log.Errorf("marshal circuitbreaker rule fail: %v", err)
   305  			continue
   306  		}
   307  		if nil == cbRuleProto {
   308  			continue
   309  		}
   310  		err = api.AddAnyDataIntoBatchQuery(out, cbRuleProto)
   311  		if nil != err {
   312  			log.Errorf("add circuitbreaker rule as any data fail: %v", err)
   313  			continue
   314  		}
   315  	}
   316  	return out
   317  }
   318  
   319  func marshalFaultDetectRule(req *apifault.FaultDetectRule) (string, error) {
   320  	r := &apifault.FaultDetectRule{
   321  		TargetService: req.TargetService,
   322  		Interval:      req.Interval,
   323  		Timeout:       req.Timeout,
   324  		Port:          req.Port,
   325  		Protocol:      req.Protocol,
   326  		HttpConfig:    req.HttpConfig,
   327  		TcpConfig:     req.TcpConfig,
   328  		UdpConfig:     req.UdpConfig,
   329  	}
   330  	rule, err := json.Marshal(r)
   331  	if err != nil {
   332  		return "", err
   333  	}
   334  	return string(rule), nil
   335  }
   336  
   337  // api2FaultDetectRule 把API参数转化为内部数据结构
   338  func api2FaultDetectRule(req *apifault.FaultDetectRule) (*model.FaultDetectRule, error) {
   339  	rule, err := marshalFaultDetectRule(req)
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  
   344  	out := &model.FaultDetectRule{
   345  		Name:         req.GetName(),
   346  		Namespace:    req.GetNamespace(),
   347  		Description:  req.GetDescription(),
   348  		DstService:   req.GetTargetService().GetService(),
   349  		DstNamespace: req.GetTargetService().GetNamespace(),
   350  		DstMethod:    req.GetTargetService().GetMethod().GetValue().GetValue(),
   351  		Rule:         rule,
   352  		Revision:     utils.NewUUID(),
   353  	}
   354  	if out.Namespace == "" {
   355  		out.Namespace = DefaultNamespace
   356  	}
   357  	return out, nil
   358  }
   359  
   360  func faultDetectRule2api(fdRule *model.FaultDetectRule) (*apifault.FaultDetectRule, error) {
   361  	if fdRule == nil {
   362  		return nil, nil
   363  	}
   364  	fdRule.Proto = &apifault.FaultDetectRule{}
   365  	if len(fdRule.Rule) > 0 {
   366  		if err := json.Unmarshal([]byte(fdRule.Rule), fdRule.Proto); err != nil {
   367  			return nil, err
   368  		}
   369  	} else {
   370  		// brief search, to display the services in list result
   371  		fdRule.Proto.TargetService = &apifault.FaultDetectRule_DestinationService{
   372  			Service:   fdRule.DstService,
   373  			Namespace: fdRule.DstNamespace,
   374  			Method:    &apimodel.MatchString{Value: &wrappers.StringValue{Value: fdRule.DstMethod}},
   375  		}
   376  	}
   377  	fdRule.Proto.Id = fdRule.ID
   378  	fdRule.Proto.Name = fdRule.Name
   379  	fdRule.Proto.Namespace = fdRule.Namespace
   380  	fdRule.Proto.Description = fdRule.Description
   381  	fdRule.Proto.Revision = fdRule.Revision
   382  	fdRule.Proto.Ctime = commontime.Time2String(fdRule.CreateTime)
   383  	fdRule.Proto.Mtime = commontime.Time2String(fdRule.ModifyTime)
   384  	return fdRule.Proto, nil
   385  }
   386  
   387  // faultDetectRule2ClientAPI 把内部数据结构转化为客户端API参数
   388  func faultDetectRule2ClientAPI(req *model.ServiceWithFaultDetectRules) (*apifault.FaultDetector, error) {
   389  	if req == nil {
   390  		return nil, nil
   391  	}
   392  
   393  	out := &apifault.FaultDetector{}
   394  	out.Revision = req.Revision
   395  	out.Rules = make([]*apifault.FaultDetectRule, 0, req.CountFaultDetectRules())
   396  	var iterateErr error
   397  	req.IterateFaultDetectRules(func(rule *model.FaultDetectRule) {
   398  		cbRule, err := faultDetectRule2api(rule)
   399  		if err != nil {
   400  			iterateErr = err
   401  			return
   402  		}
   403  		out.Rules = append(out.Rules, cbRule)
   404  	})
   405  	if nil != iterateErr {
   406  		return nil, iterateErr
   407  	}
   408  	return out, nil
   409  }