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  }