github.com/polarismesh/polaris@v1.17.8/service/routing_config_v2.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 "strconv" 23 24 "github.com/golang/protobuf/ptypes" 25 "github.com/golang/protobuf/ptypes/any" 26 "github.com/golang/protobuf/ptypes/wrappers" 27 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 28 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 29 apitraffic "github.com/polarismesh/specification/source/go/api/v1/traffic_manage" 30 "go.uber.org/zap" 31 32 cachetypes "github.com/polarismesh/polaris/cache/api" 33 apiv1 "github.com/polarismesh/polaris/common/api/v1" 34 "github.com/polarismesh/polaris/common/model" 35 commonstore "github.com/polarismesh/polaris/common/store" 36 "github.com/polarismesh/polaris/common/utils" 37 ) 38 39 var ( 40 // RoutingConfigV2FilterAttrs router config filter attrs 41 RoutingConfigV2FilterAttrs = map[string]bool{ 42 "id": true, 43 "name": true, 44 "service": true, 45 "namespace": true, 46 "source_service": true, 47 "destination_service": true, 48 "source_namespace": true, 49 "destination_namespace": true, 50 "enable": true, 51 "offset": true, 52 "limit": true, 53 "order_field": true, 54 "order_type": true, 55 } 56 ) 57 58 // CreateRoutingConfigsV2 Create a routing configuration 59 func (s *Server) CreateRoutingConfigsV2( 60 ctx context.Context, req []*apitraffic.RouteRule) *apiservice.BatchWriteResponse { 61 if err := checkBatchRoutingConfigV2(req); err != nil { 62 return err 63 } 64 65 resp := apiv1.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess) 66 for _, entry := range req { 67 apiv1.Collect(resp, s.createRoutingConfigV2(ctx, entry)) 68 } 69 70 return apiv1.FormatBatchWriteResponse(resp) 71 } 72 73 // createRoutingConfigV2 Create a routing configuration 74 func (s *Server) createRoutingConfigV2(ctx context.Context, req *apitraffic.RouteRule) *apiservice.Response { 75 if resp := checkRoutingConfigV2(req); resp != nil { 76 return resp 77 } 78 79 conf, err := Api2RoutingConfigV2(req) 80 if err != nil { 81 log.Error("[Routing][V2] parse routing config v2 from request for create", 82 utils.RequestID(ctx), zap.Error(err)) 83 return apiv1.NewResponse(apimodel.Code_ExecuteException) 84 } 85 86 if err := s.storage.CreateRoutingConfigV2(conf); err != nil { 87 log.Error("[Routing][V2] create routing config v2 store layer", 88 utils.RequestID(ctx), zap.Error(err)) 89 return apiv1.NewResponse(commonstore.StoreCode2APICode(err)) 90 } 91 92 s.RecordHistory(ctx, routingV2RecordEntry(ctx, req, conf, model.OCreate)) 93 94 req.Id = conf.ID 95 return apiv1.NewRouterResponse(apimodel.Code_ExecuteSuccess, req) 96 } 97 98 // DeleteRoutingConfigsV2 Batch delete routing configuration 99 func (s *Server) DeleteRoutingConfigsV2( 100 ctx context.Context, req []*apitraffic.RouteRule) *apiservice.BatchWriteResponse { 101 if err := checkBatchRoutingConfigV2(req); err != nil { 102 return err 103 } 104 105 out := apiv1.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess) 106 for _, entry := range req { 107 resp := s.deleteRoutingConfigV2(ctx, entry) 108 apiv1.Collect(out, resp) 109 } 110 111 return apiv1.FormatBatchWriteResponse(out) 112 } 113 114 // DeleteRoutingConfigV2 Delete a routing configuration 115 func (s *Server) deleteRoutingConfigV2(ctx context.Context, req *apitraffic.RouteRule) *apiservice.Response { 116 if resp := checkRoutingConfigIDV2(req); resp != nil { 117 return resp 118 } 119 120 // Determine whether the current routing rules are only converted from the memory transmission in the V1 version 121 if _, ok := s.Cache().RoutingConfig().IsConvertFromV1(req.Id); ok { 122 resp := s.transferV1toV2OnModify(ctx, req) 123 if resp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) { 124 return resp 125 } 126 } 127 128 if err := s.storage.DeleteRoutingConfigV2(req.Id); err != nil { 129 log.Error("[Routing][V2] delete routing config v2 store layer", 130 utils.RequestID(ctx), zap.Error(err)) 131 return apiv1.NewResponse(commonstore.StoreCode2APICode(err)) 132 } 133 134 s.RecordHistory(ctx, routingV2RecordEntry(ctx, req, &model.RouterConfig{ 135 ID: req.GetId(), 136 Name: req.GetName(), 137 }, model.ODelete)) 138 return apiv1.NewRouterResponse(apimodel.Code_ExecuteSuccess, req) 139 } 140 141 // UpdateRoutingConfigsV2 Batch update routing configuration 142 func (s *Server) UpdateRoutingConfigsV2( 143 ctx context.Context, req []*apitraffic.RouteRule) *apiservice.BatchWriteResponse { 144 if err := checkBatchRoutingConfigV2(req); err != nil { 145 return err 146 } 147 148 out := apiv1.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess) 149 for _, entry := range req { 150 resp := s.updateRoutingConfigV2(ctx, entry) 151 apiv1.Collect(out, resp) 152 } 153 154 return apiv1.FormatBatchWriteResponse(out) 155 } 156 157 // updateRoutingConfigV2 Update a single routing configuration 158 func (s *Server) updateRoutingConfigV2(ctx context.Context, req *apitraffic.RouteRule) *apiservice.Response { 159 // If V2 routing rules to be modified are from the V1 rule in the cache, need to do the following steps first 160 // step 1: Turn the V1 rule to the real V2 rule 161 // step 2: Find the corresponding route to the V2 rules to be modified in the V1 rules, set their rules ID 162 // step 3: Store persistence 163 if _, ok := s.Cache().RoutingConfig().IsConvertFromV1(req.Id); ok { 164 resp := s.transferV1toV2OnModify(ctx, req) 165 if resp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) { 166 return resp 167 } 168 } 169 170 if resp := checkUpdateRoutingConfigV2(req); resp != nil { 171 return resp 172 } 173 174 // Check whether the routing configuration exists 175 conf, err := s.storage.GetRoutingConfigV2WithID(req.Id) 176 if err != nil { 177 log.Error("[Routing][V2] get routing config v2 store layer", 178 utils.RequestID(ctx), zap.Error(err)) 179 return apiv1.NewResponse(commonstore.StoreCode2APICode(err)) 180 } 181 if conf == nil { 182 return apiv1.NewResponse(apimodel.Code_NotFoundRouting) 183 } 184 185 reqModel, err := Api2RoutingConfigV2(req) 186 reqModel.Revision = utils.NewV2Revision() 187 if err != nil { 188 log.Error("[Routing][V2] parse routing config v2 from request for update", 189 utils.RequestID(ctx), zap.Error(err)) 190 return apiv1.NewResponse(apimodel.Code_ExecuteException) 191 } 192 193 if err := s.storage.UpdateRoutingConfigV2(reqModel); err != nil { 194 log.Error("[Routing][V2] update routing config v2 store layer", 195 utils.RequestID(ctx), zap.Error(err)) 196 return apiv1.NewResponse(commonstore.StoreCode2APICode(err)) 197 } 198 199 s.RecordHistory(ctx, routingV2RecordEntry(ctx, req, reqModel, model.OUpdate)) 200 return apiv1.NewResponse(apimodel.Code_ExecuteSuccess) 201 } 202 203 // QueryRoutingConfigsV2 The interface of the query configuration to the OSS 204 func (s *Server) QueryRoutingConfigsV2(ctx context.Context, query map[string]string) *apiservice.BatchQueryResponse { 205 args, presp := parseRoutingArgs(query, ctx) 206 if presp != nil { 207 return apiv1.NewBatchQueryResponse(apimodel.Code(presp.GetCode().GetValue())) 208 } 209 210 total, ret, err := s.Cache().RoutingConfig().QueryRoutingConfigsV2(args) 211 if err != nil { 212 log.Error("[Routing][V2] query routing list from cache", utils.RequestID(ctx), zap.Error(err)) 213 return apiv1.NewBatchQueryResponse(apimodel.Code_ExecuteException) 214 } 215 216 routers, err := marshalRoutingV2toAnySlice(ret) 217 if err != nil { 218 log.Error("[Routing][V2] marshal routing list to anypb.Any list", 219 utils.RequestID(ctx), zap.Error(err)) 220 return apiv1.NewBatchQueryResponse(apimodel.Code_ExecuteException) 221 } 222 223 resp := apiv1.NewBatchQueryResponse(apimodel.Code_ExecuteSuccess) 224 resp.Amount = &wrappers.UInt32Value{Value: total} 225 resp.Size = &wrappers.UInt32Value{Value: uint32(len(ret))} 226 resp.Data = routers 227 return resp 228 } 229 230 // EnableRoutings batch enable routing rules 231 func (s *Server) EnableRoutings(ctx context.Context, req []*apitraffic.RouteRule) *apiservice.BatchWriteResponse { 232 out := apiv1.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess) 233 for _, entry := range req { 234 resp := s.enableRoutings(ctx, entry) 235 apiv1.Collect(out, resp) 236 } 237 238 return apiv1.FormatBatchWriteResponse(out) 239 } 240 241 func (s *Server) enableRoutings(ctx context.Context, req *apitraffic.RouteRule) *apiservice.Response { 242 if resp := checkRoutingConfigIDV2(req); resp != nil { 243 return resp 244 } 245 246 if _, ok := s.Cache().RoutingConfig().IsConvertFromV1(req.Id); ok { 247 resp := s.transferV1toV2OnModify(ctx, req) 248 if resp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) { 249 return resp 250 } 251 } 252 253 conf, err := s.storage.GetRoutingConfigV2WithID(req.Id) 254 if err != nil { 255 log.Error("[Routing][V2] get routing config v2 store layer", 256 utils.RequestID(ctx), zap.Error(err)) 257 return apiv1.NewResponse(commonstore.StoreCode2APICode(err)) 258 } 259 if conf == nil { 260 return apiv1.NewResponse(apimodel.Code_NotFoundRouting) 261 } 262 263 conf.Enable = req.GetEnable() 264 conf.Revision = utils.NewV2Revision() 265 266 if err := s.storage.EnableRouting(conf); err != nil { 267 log.Error("[Routing][V2] enable routing config v2 store layer", 268 utils.RequestID(ctx), zap.Error(err)) 269 return apiv1.NewResponse(commonstore.StoreCode2APICode(err)) 270 } 271 272 s.RecordHistory(ctx, routingV2RecordEntry(ctx, req, conf, model.OUpdate)) 273 return apiv1.NewResponse(apimodel.Code_ExecuteSuccess) 274 } 275 276 // transferV1toV2OnModify When enabled or prohibited for the V2 rules, the V1 rules need to be converted to V2 rules 277 // and execute persistent storage 278 func (s *Server) transferV1toV2OnModify(ctx context.Context, req *apitraffic.RouteRule) *apiservice.Response { 279 svcId, _ := s.Cache().RoutingConfig().IsConvertFromV1(req.Id) 280 v1conf, err := s.storage.GetRoutingConfigWithID(svcId) 281 if err != nil { 282 log.Error("[Routing][V2] get routing config v1 store layer", 283 utils.RequestID(ctx), zap.Error(err)) 284 return apiv1.NewResponse(commonstore.StoreCode2APICode(err)) 285 } 286 if v1conf != nil { 287 svc, err := s.loadServiceByID(svcId) 288 if svc == nil { 289 log.Error("[Routing][V2] convert routing config v1 to v2 find svc", 290 utils.RequestID(ctx), zap.Error(err)) 291 return apiv1.NewResponse(apimodel.Code_NotFoundService) 292 } 293 294 inV2, outV2, err := model.ConvertRoutingV1ToExtendV2(svc.Name, svc.Namespace, v1conf) 295 if err != nil { 296 log.Error("[Routing][V2] convert routing config v1 to v2", 297 utils.RequestID(ctx), zap.Error(err)) 298 return apiv1.NewResponse(apimodel.Code_ExecuteException) 299 } 300 301 formatApi := func(rules []*model.ExtendRouterConfig) ([]*apitraffic.RouteRule, *apiservice.Response) { 302 ret := make([]*apitraffic.RouteRule, 0, len(rules)) 303 for i := range rules { 304 item, err := rules[i].ToApi() 305 if err != nil { 306 log.Error("[Routing][V2] convert routing config v1 to v2, format v2 to api", 307 utils.RequestID(ctx), zap.Error(err)) 308 return nil, apiv1.NewResponse(apimodel.Code_ExecuteException) 309 } 310 ret = append(ret, item) 311 } 312 313 return ret, nil 314 } 315 316 inDatas, resp := formatApi(inV2) 317 if resp != nil { 318 return resp 319 } 320 outDatas, resp := formatApi(outV2) 321 if resp != nil { 322 return resp 323 } 324 325 if resp := s.saveRoutingV1toV2(ctx, svcId, inDatas, outDatas); resp.GetCode().GetValue() != apiv1.ExecuteSuccess { 326 return apiv1.NewResponse(apimodel.Code(resp.GetCode().GetValue())) 327 } 328 } 329 330 return apiv1.NewResponse(apimodel.Code_ExecuteSuccess) 331 } 332 333 // parseServiceArgs The query conditions of the analysis service 334 func parseRoutingArgs(query map[string]string, ctx context.Context) (*cachetypes.RoutingArgs, *apiservice.Response) { 335 offset, limit, err := utils.ParseOffsetAndLimit(query) 336 if err != nil { 337 return nil, apiv1.NewResponse(apimodel.Code_InvalidParameter) 338 } 339 340 filter := make(map[string]string) 341 for key, value := range query { 342 if _, ok := RoutingConfigV2FilterAttrs[key]; !ok { 343 log.Errorf("[Routing][V2][Query] attribute(%s) is not allowed", key) 344 return nil, apiv1.NewResponse(apimodel.Code_InvalidParameter) 345 } 346 filter[key] = value 347 } 348 349 res := &cachetypes.RoutingArgs{ 350 Filter: filter, 351 Name: filter["name"], 352 ID: filter["id"], 353 OrderField: filter["order_field"], 354 OrderType: filter["order_type"], 355 Offset: offset, 356 Limit: limit, 357 } 358 359 if _, ok := filter["service"]; ok { 360 res.Namespace = filter["namespace"] 361 res.Service = filter["service"] 362 } else { 363 res.SourceService = filter["source_service"] 364 res.SourceNamespace = filter["source_namespace"] 365 366 res.DestinationService = filter["destination_service"] 367 res.DestinationNamespace = filter["destination_namespace"] 368 } 369 370 if enableStr, ok := filter["enable"]; ok { 371 enable, err := strconv.ParseBool(enableStr) 372 if err == nil { 373 res.Enable = &enable 374 } else { 375 log.Error("[Service][Routing][Query] search with routing enable", zap.Error(err)) 376 } 377 } 378 log.Infof("[Service][Routing][Query] routing query args: %+v", res) 379 return res, nil 380 } 381 382 // checkBatchRoutingConfig Check batch request 383 func checkBatchRoutingConfigV2(req []*apitraffic.RouteRule) *apiservice.BatchWriteResponse { 384 if len(req) == 0 { 385 return apiv1.NewBatchWriteResponse(apimodel.Code_EmptyRequest) 386 } 387 388 if len(req) > MaxBatchSize { 389 return apiv1.NewBatchWriteResponse(apimodel.Code_BatchSizeOverLimit) 390 } 391 392 return nil 393 } 394 395 // checkRoutingConfig Check the validity of the basic parameter of the routing configuration 396 func checkRoutingConfigV2(req *apitraffic.RouteRule) *apiservice.Response { 397 if req == nil { 398 return apiv1.NewRouterResponse(apimodel.Code_EmptyRequest, req) 399 } 400 401 if err := checkRoutingNameAndNamespace(req); err != nil { 402 return err 403 } 404 405 if err := checkRoutingConfigPriorityV2(req); err != nil { 406 return err 407 } 408 409 if err := checkRoutingPolicyV2(req); err != nil { 410 return err 411 } 412 413 return nil 414 } 415 416 // checkUpdateRoutingConfigV2 Check the validity of the basic parameter of the routing configuration 417 func checkUpdateRoutingConfigV2(req *apitraffic.RouteRule) *apiservice.Response { 418 if resp := checkRoutingConfigIDV2(req); resp != nil { 419 return resp 420 } 421 422 if err := checkRoutingNameAndNamespace(req); err != nil { 423 return err 424 } 425 426 if err := checkRoutingConfigPriorityV2(req); err != nil { 427 return err 428 } 429 430 if err := checkRoutingPolicyV2(req); err != nil { 431 return err 432 } 433 434 return nil 435 } 436 437 func checkRoutingNameAndNamespace(req *apitraffic.RouteRule) *apiservice.Response { 438 if err := utils.CheckDbStrFieldLen(utils.NewStringValue(req.GetName()), MaxDbRoutingName); err != nil { 439 return apiv1.NewRouterResponse(apimodel.Code_InvalidRoutingName, req) 440 } 441 442 if err := utils.CheckDbStrFieldLen(utils.NewStringValue(req.GetNamespace()), 443 MaxDbServiceNamespaceLength); err != nil { 444 return apiv1.NewRouterResponse(apimodel.Code_InvalidNamespaceName, req) 445 } 446 447 return nil 448 } 449 450 func checkRoutingConfigIDV2(req *apitraffic.RouteRule) *apiservice.Response { 451 if req == nil { 452 return apiv1.NewRouterResponse(apimodel.Code_EmptyRequest, req) 453 } 454 455 if req.Id == "" { 456 return apiv1.NewResponse(apimodel.Code_InvalidRoutingID) 457 } 458 459 return nil 460 } 461 462 func checkRoutingConfigPriorityV2(req *apitraffic.RouteRule) *apiservice.Response { 463 if req == nil { 464 return apiv1.NewRouterResponse(apimodel.Code_EmptyRequest, req) 465 } 466 467 if req.Priority > 10 { 468 return apiv1.NewResponse(apimodel.Code_InvalidRoutingPriority) 469 } 470 471 return nil 472 } 473 474 func checkRoutingPolicyV2(req *apitraffic.RouteRule) *apiservice.Response { 475 if req == nil { 476 return apiv1.NewRouterResponse(apimodel.Code_EmptyRequest, req) 477 } 478 479 if req.GetRoutingPolicy() != apitraffic.RoutingPolicy_RulePolicy { 480 return apiv1.NewRouterResponse(apimodel.Code_InvalidRoutingPolicy, req) 481 } 482 483 // Automatically supplement @Type attribute according to Policy 484 if req.RoutingConfig.TypeUrl == "" { 485 if req.GetRoutingPolicy() == apitraffic.RoutingPolicy_RulePolicy { 486 req.RoutingConfig.TypeUrl = model.RuleRoutingTypeUrl 487 } 488 if req.GetRoutingPolicy() == apitraffic.RoutingPolicy_MetadataPolicy { 489 req.RoutingConfig.TypeUrl = model.MetaRoutingTypeUrl 490 } 491 } 492 493 return nil 494 } 495 496 // Api2RoutingConfigV2 Convert the API parameter to internal data structure 497 func Api2RoutingConfigV2(req *apitraffic.RouteRule) (*model.RouterConfig, error) { 498 out := &model.RouterConfig{ 499 Valid: true, 500 } 501 502 if req.Id == "" { 503 req.Id = utils.NewRoutingV2UUID() 504 } 505 if req.Revision == "" { 506 req.Revision = utils.NewV2Revision() 507 } 508 509 if err := out.ParseRouteRuleFromAPI(req); err != nil { 510 return nil, err 511 } 512 return out, nil 513 } 514 515 // marshalRoutingV2toAnySlice Converted to []*anypb.Any array 516 func marshalRoutingV2toAnySlice(routings []*model.ExtendRouterConfig) ([]*any.Any, error) { 517 ret := make([]*any.Any, 0, len(routings)) 518 519 for i := range routings { 520 entry, err := routings[i].ToApi() 521 if err != nil { 522 return nil, err 523 } 524 item, err := ptypes.MarshalAny(entry) 525 if err != nil { 526 return nil, err 527 } 528 529 ret = append(ret, item) 530 } 531 532 return ret, nil 533 }