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 }