github.com/polarismesh/polaris@v1.17.8/service/routing_config_v1.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/gogo/protobuf/jsonpb" 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 31 api "github.com/polarismesh/polaris/common/api/v1" 32 "github.com/polarismesh/polaris/common/model" 33 commonstore "github.com/polarismesh/polaris/common/store" 34 commontime "github.com/polarismesh/polaris/common/time" 35 "github.com/polarismesh/polaris/common/utils" 36 ) 37 38 var ( 39 // RoutingConfigFilterAttrs router config filter attrs 40 RoutingConfigFilterAttrs = map[string]bool{ 41 "service": true, 42 "namespace": true, 43 "offset": true, 44 "limit": true, 45 } 46 ) 47 48 // CreateRoutingConfigs Create a routing configuration 49 func (s *Server) CreateRoutingConfigs(ctx context.Context, req []*apitraffic.Routing) *apiservice.BatchWriteResponse { 50 if err := checkBatchRoutingConfig(req); err != nil { 51 return err 52 } 53 54 resp := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess) 55 for _, entry := range req { 56 api.Collect(resp, s.createRoutingConfigV1toV2(ctx, entry)) 57 } 58 59 return api.FormatBatchWriteResponse(resp) 60 } 61 62 // CreateRoutingConfig Create a routing configuration, Creating route configuration requires locking 63 // services to prevent the service from being deleted 64 // Deprecated: This method is ready to abandon 65 func (s *Server) CreateRoutingConfig(ctx context.Context, req *apitraffic.Routing) *apiservice.Response { 66 rid := utils.ParseRequestID(ctx) 67 pid := utils.ParsePlatformID(ctx) 68 if resp := checkRoutingConfig(req); resp != nil { 69 return resp 70 } 71 72 serviceName := req.GetService().GetValue() 73 namespaceName := req.GetNamespace().GetValue() 74 service, errResp := s.loadService(namespaceName, serviceName) 75 if errResp != nil { 76 log.Error(errResp.GetInfo().GetValue(), utils.ZapRequestID(rid), utils.ZapPlatformID(pid)) 77 return api.NewRoutingResponse(apimodel.Code(errResp.GetCode().GetValue()), req) 78 } 79 if service == nil { 80 return api.NewRoutingResponse(apimodel.Code_NotFoundService, req) 81 } 82 if service.IsAlias() { 83 return api.NewRoutingResponse(apimodel.Code_NotAllowAliasCreateRouting, req) 84 } 85 86 routingConfig, err := s.storage.GetRoutingConfigWithService(service.Name, service.Namespace) 87 if err != nil { 88 log.Error(err.Error(), utils.ZapRequestID(rid), utils.ZapPlatformID(pid)) 89 return api.NewRoutingResponse(commonstore.StoreCode2APICode(err), req) 90 } 91 if routingConfig != nil { 92 return api.NewRoutingResponse(apimodel.Code_ExistedResource, req) 93 } 94 95 conf, err := api2RoutingConfig(service.ID, req) 96 if err != nil { 97 log.Error(err.Error(), utils.ZapRequestID(rid), utils.ZapPlatformID(pid)) 98 return api.NewRoutingResponse(apimodel.Code_ExecuteException, req) 99 } 100 if err := s.storage.CreateRoutingConfig(conf); err != nil { 101 log.Error(err.Error(), utils.ZapRequestID(rid), utils.ZapPlatformID(pid)) 102 return wrapperRoutingStoreResponse(req, err) 103 } 104 105 s.RecordHistory(ctx, routingRecordEntry(ctx, req, service, conf, model.OCreate)) 106 return api.NewRoutingResponse(apimodel.Code_ExecuteSuccess, req) 107 } 108 109 // DeleteRoutingConfigs Batch delete routing configuration 110 func (s *Server) DeleteRoutingConfigs(ctx context.Context, req []*apitraffic.Routing) *apiservice.BatchWriteResponse { 111 if err := checkBatchRoutingConfig(req); err != nil { 112 return err 113 } 114 115 out := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess) 116 for _, entry := range req { 117 resp := s.DeleteRoutingConfig(ctx, entry) 118 api.Collect(out, resp) 119 } 120 121 return api.FormatBatchWriteResponse(out) 122 } 123 124 // DeleteRoutingConfig Delete a routing configuration 125 // Deprecated: This method is ready to abandon 126 func (s *Server) DeleteRoutingConfig(ctx context.Context, req *apitraffic.Routing) *apiservice.Response { 127 rid := utils.ParseRequestID(ctx) 128 pid := utils.ParsePlatformID(ctx) 129 service, resp := s.routingConfigCommonCheck(ctx, req) 130 if resp != nil { 131 return resp 132 } 133 134 if err := s.storage.DeleteRoutingConfig(service.ID); err != nil { 135 log.Error(err.Error(), utils.ZapRequestID(rid), utils.ZapPlatformID(pid)) 136 return wrapperRoutingStoreResponse(req, err) 137 } 138 139 s.RecordHistory(ctx, routingRecordEntry(ctx, req, service, nil, model.ODelete)) 140 return api.NewResponse(apimodel.Code_ExecuteSuccess) 141 } 142 143 // UpdateRoutingConfigs Batch update routing configuration 144 func (s *Server) UpdateRoutingConfigs(ctx context.Context, req []*apitraffic.Routing) *apiservice.BatchWriteResponse { 145 if err := checkBatchRoutingConfig(req); err != nil { 146 return err 147 } 148 149 out := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess) 150 for _, entry := range req { 151 resp := s.updateRoutingConfigV1toV2(ctx, entry) 152 api.Collect(out, resp) 153 } 154 155 return api.FormatBatchWriteResponse(out) 156 } 157 158 // UpdateRoutingConfig Update a routing configuration 159 // Deprecated: 该方法准备舍弃 160 func (s *Server) UpdateRoutingConfig(ctx context.Context, req *apitraffic.Routing) *apiservice.Response { 161 rid := utils.ParseRequestID(ctx) 162 pid := utils.ParsePlatformID(ctx) 163 service, resp := s.routingConfigCommonCheck(ctx, req) 164 if resp != nil { 165 return resp 166 } 167 168 conf, err := s.storage.GetRoutingConfigWithService(service.Name, service.Namespace) 169 if err != nil { 170 log.Error(err.Error(), utils.ZapRequestID(rid), utils.ZapPlatformID(pid)) 171 return api.NewRoutingResponse(commonstore.StoreCode2APICode(err), req) 172 } 173 if conf == nil { 174 return api.NewRoutingResponse(apimodel.Code_NotFoundRouting, req) 175 } 176 177 reqModel, err := api2RoutingConfig(service.ID, req) 178 if err != nil { 179 log.Error(err.Error(), utils.ZapRequestID(rid), utils.ZapPlatformID(pid)) 180 return api.NewRoutingResponse(apimodel.Code_ParseRoutingException, req) 181 } 182 183 if err := s.storage.UpdateRoutingConfig(reqModel); err != nil { 184 log.Error(err.Error(), utils.ZapRequestID(rid), utils.ZapPlatformID(pid)) 185 return wrapperRoutingStoreResponse(req, err) 186 } 187 188 s.RecordHistory(ctx, routingRecordEntry(ctx, req, service, reqModel, model.OUpdate)) 189 return api.NewRoutingResponse(apimodel.Code_ExecuteSuccess, req) 190 } 191 192 // GetRoutingConfigs Get the routing configuration in batches, and provide the interface of 193 // the query routing configuration to the OSS 194 // Deprecated: This method is ready to abandon 195 func (s *Server) GetRoutingConfigs(ctx context.Context, query map[string]string) *apiservice.BatchQueryResponse { 196 rid := utils.ParseRequestID(ctx) 197 pid := utils.ParsePlatformID(ctx) 198 199 offset, limit, err := utils.ParseOffsetAndLimit(query) 200 if err != nil { 201 return api.NewBatchQueryResponse(apimodel.Code_InvalidParameter) 202 } 203 204 filter := make(map[string]string) 205 for key, value := range query { 206 if _, ok := RoutingConfigFilterAttrs[key]; !ok { 207 log.Errorf("[Server][RoutingConfig][Query] attribute(%s) is not allowed", key) 208 return api.NewBatchQueryResponse(apimodel.Code_InvalidParameter) 209 } 210 filter[key] = value 211 } 212 // service -- > name This special treatment 213 if service, ok := filter["service"]; ok { 214 filter["name"] = service 215 delete(filter, "service") 216 } 217 218 // Can be filtered according to name and namespace 219 total, routings, err := s.storage.GetRoutingConfigs(filter, offset, limit) 220 if err != nil { 221 log.Error(err.Error(), utils.ZapRequestID(rid), utils.ZapPlatformID(pid)) 222 return api.NewBatchQueryResponse(commonstore.StoreCode2APICode(err)) 223 } 224 225 resp := api.NewBatchQueryResponse(apimodel.Code_ExecuteSuccess) 226 resp.Amount = utils.NewUInt32Value(total) 227 resp.Size = utils.NewUInt32Value(uint32(len(routings))) 228 resp.Routings = make([]*apitraffic.Routing, 0, len(routings)) 229 for _, entry := range routings { 230 routing, err := routingConfig2API(entry.Config, entry.ServiceName, entry.NamespaceName) 231 if err != nil { 232 log.Error(err.Error(), utils.ZapRequestID(rid), utils.ZapPlatformID(pid)) 233 return api.NewBatchQueryResponse(apimodel.Code_ParseRoutingException) 234 } 235 resp.Routings = append(resp.Routings, routing) 236 } 237 238 return resp 239 } 240 241 // routingConfigCommonCheck Public examination of routing configuration operation 242 func (s *Server) routingConfigCommonCheck( 243 ctx context.Context, req *apitraffic.Routing) (*model.Service, *apiservice.Response) { 244 if resp := checkRoutingConfig(req); resp != nil { 245 return nil, resp 246 } 247 248 rid := utils.ParseRequestID(ctx) 249 pid := utils.ParsePlatformID(ctx) 250 serviceName := req.GetService().GetValue() 251 namespaceName := req.GetNamespace().GetValue() 252 253 service, err := s.storage.GetService(serviceName, namespaceName) 254 if err != nil { 255 log.Error(err.Error(), utils.ZapRequestID(rid), utils.ZapPlatformID(pid)) 256 return nil, api.NewRoutingResponse(commonstore.StoreCode2APICode(err), req) 257 } 258 if service == nil { 259 return nil, api.NewRoutingResponse(apimodel.Code_NotFoundService, req) 260 } 261 262 return service, nil 263 } 264 265 // checkRoutingConfig Check the validity of the basic parameter of the routing configuration 266 func checkRoutingConfig(req *apitraffic.Routing) *apiservice.Response { 267 if req == nil { 268 return api.NewRoutingResponse(apimodel.Code_EmptyRequest, req) 269 } 270 if err := checkResourceName(req.GetService()); err != nil { 271 return api.NewRoutingResponse(apimodel.Code_InvalidServiceName, req) 272 } 273 274 if err := checkResourceName(req.GetNamespace()); err != nil { 275 return api.NewRoutingResponse(apimodel.Code_InvalidNamespaceName, req) 276 } 277 278 if err := utils.CheckDbStrFieldLen(req.GetService(), MaxDbServiceNameLength); err != nil { 279 return api.NewRoutingResponse(apimodel.Code_InvalidServiceName, req) 280 } 281 if err := utils.CheckDbStrFieldLen(req.GetNamespace(), MaxDbServiceNamespaceLength); err != nil { 282 return api.NewRoutingResponse(apimodel.Code_InvalidNamespaceName, req) 283 } 284 if err := utils.CheckDbStrFieldLen(req.GetServiceToken(), MaxDbServiceToken); err != nil { 285 return api.NewRoutingResponse(apimodel.Code_InvalidServiceToken, req) 286 } 287 288 return nil 289 } 290 291 // parseServiceRoutingToken Get token from RoutingConfig request parameters 292 func parseServiceRoutingToken(ctx context.Context, req *apitraffic.Routing) string { 293 if reqToken := req.GetServiceToken().GetValue(); reqToken != "" { 294 return reqToken 295 } 296 297 return utils.ParseToken(ctx) 298 } 299 300 // api2RoutingConfig Convert the API parameter to internal data structure 301 func api2RoutingConfig(serviceID string, req *apitraffic.Routing) (*model.RoutingConfig, error) { 302 inBounds, outBounds, err := marshalRoutingConfig(req.GetInbounds(), req.GetOutbounds()) 303 if err != nil { 304 return nil, err 305 } 306 307 out := &model.RoutingConfig{ 308 ID: serviceID, 309 InBounds: string(inBounds), 310 OutBounds: string(outBounds), 311 Revision: utils.NewUUID(), 312 } 313 314 return out, nil 315 } 316 317 // routingConfig2API Convert the internal data structure to API parameter to pass out 318 func routingConfig2API(req *model.RoutingConfig, service string, namespace string) (*apitraffic.Routing, error) { 319 if req == nil { 320 return nil, nil 321 } 322 323 out := &apitraffic.Routing{ 324 Service: utils.NewStringValue(service), 325 Namespace: utils.NewStringValue(namespace), 326 Revision: utils.NewStringValue(req.Revision), 327 Ctime: utils.NewStringValue(commontime.Time2String(req.CreateTime)), 328 Mtime: utils.NewStringValue(commontime.Time2String(req.ModifyTime)), 329 } 330 331 if req.InBounds != "" { 332 var inBounds []*apitraffic.Route 333 if err := json.Unmarshal([]byte(req.InBounds), &inBounds); err != nil { 334 return nil, err 335 } 336 out.Inbounds = inBounds 337 } 338 if req.OutBounds != "" { 339 var outBounds []*apitraffic.Route 340 if err := json.Unmarshal([]byte(req.OutBounds), &outBounds); err != nil { 341 return nil, err 342 } 343 out.Outbounds = outBounds 344 } 345 346 return out, nil 347 } 348 349 // marshalRoutingConfig Formulate Inbounds and OUTBOUNDS 350 func marshalRoutingConfig(in []*apitraffic.Route, out []*apitraffic.Route) ([]byte, []byte, error) { 351 inBounds, err := json.Marshal(in) 352 if err != nil { 353 return nil, nil, err 354 } 355 356 outBounds, err := json.Marshal(out) 357 if err != nil { 358 return nil, nil, err 359 } 360 361 return inBounds, outBounds, nil 362 } 363 364 // checkBatchRoutingConfig Check batch request 365 func checkBatchRoutingConfig(req []*apitraffic.Routing) *apiservice.BatchWriteResponse { 366 if len(req) == 0 { 367 return api.NewBatchWriteResponse(apimodel.Code_EmptyRequest) 368 } 369 370 if len(req) > MaxBatchSize { 371 return api.NewBatchWriteResponse(apimodel.Code_BatchSizeOverLimit) 372 } 373 374 return nil 375 } 376 377 // routingRecordEntry Construction of RoutingConfig's record Entry 378 func routingRecordEntry(ctx context.Context, req *apitraffic.Routing, svc *model.Service, md *model.RoutingConfig, 379 opt model.OperationType) *model.RecordEntry { 380 381 marshaler := jsonpb.Marshaler{} 382 detail, _ := marshaler.MarshalToString(req) 383 384 entry := &model.RecordEntry{ 385 ResourceType: model.RRouting, 386 ResourceName: fmt.Sprintf("%s(%s)", svc.Name, svc.ID), 387 Namespace: svc.Namespace, 388 OperationType: opt, 389 Operator: utils.ParseOperator(ctx), 390 Detail: detail, 391 HappenTime: time.Now(), 392 } 393 394 return entry 395 } 396 397 // routingV2RecordEntry Construction of RoutingConfig's record Entry 398 func routingV2RecordEntry(ctx context.Context, req *apitraffic.RouteRule, md *model.RouterConfig, 399 opt model.OperationType) *model.RecordEntry { 400 401 marshaler := jsonpb.Marshaler{} 402 detail, _ := marshaler.MarshalToString(req) 403 404 entry := &model.RecordEntry{ 405 ResourceType: model.RRouting, 406 ResourceName: fmt.Sprintf("%s(%s)", md.Name, md.ID), 407 Namespace: req.GetNamespace(), 408 OperationType: opt, 409 Operator: utils.ParseOperator(ctx), 410 Detail: detail, 411 HappenTime: time.Now(), 412 } 413 return entry 414 } 415 416 // wrapperRoutingStoreResponse Packing routing storage layer error 417 func wrapperRoutingStoreResponse(routing *apitraffic.Routing, err error) *apiservice.Response { 418 resp := storeError2Response(err) 419 if resp == nil { 420 return nil 421 } 422 resp.Routing = routing 423 return resp 424 }