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 }