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  }