github.com/polarismesh/polaris@v1.17.8/service/routing_config_v1tov2.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  
    23  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    24  	apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage"
    25  	apitraffic "github.com/polarismesh/specification/source/go/api/v1/traffic_manage"
    26  	"go.uber.org/zap"
    27  
    28  	apiv1 "github.com/polarismesh/polaris/common/api/v1"
    29  	"github.com/polarismesh/polaris/common/model"
    30  	commonstore "github.com/polarismesh/polaris/common/store"
    31  	"github.com/polarismesh/polaris/common/utils"
    32  )
    33  
    34  // createRoutingConfigV1toV2 Compatible with V1 version of the creation routing rules, convert V1 to V2 for storage
    35  func (s *Server) createRoutingConfigV1toV2(ctx context.Context, req *apitraffic.Routing) *apiservice.Response {
    36  	if resp := checkRoutingConfig(req); resp != nil {
    37  		return resp
    38  	}
    39  
    40  	serviceName := req.GetService().GetValue()
    41  	namespaceName := req.GetNamespace().GetValue()
    42  	svc, errResp := s.loadService(namespaceName, serviceName)
    43  	if errResp != nil {
    44  		log.Error("[Service][Routing] get read lock for service", zap.String("service", serviceName),
    45  			zap.String("namespace", namespaceName), utils.RequestID(ctx), zap.Any("err", errResp))
    46  		return apiv1.NewRoutingResponse(apimodel.Code(errResp.GetCode().GetValue()), req)
    47  	}
    48  	if svc == nil {
    49  		return apiv1.NewRoutingResponse(apimodel.Code_NotFoundService, req)
    50  	}
    51  	if svc.IsAlias() {
    52  		return apiv1.NewRoutingResponse(apimodel.Code_NotAllowAliasCreateRouting, req)
    53  	}
    54  
    55  	inDatas, outDatas, resp := batchBuildV2Routings(req)
    56  	if resp != nil {
    57  		return resp
    58  	}
    59  
    60  	resp = s.saveRoutingV1toV2(ctx, svc.ID, inDatas, outDatas)
    61  	if resp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) {
    62  		return resp
    63  	}
    64  
    65  	return apiv1.NewRoutingResponse(apimodel.Code_ExecuteSuccess, req)
    66  }
    67  
    68  // updateRoutingConfigV1toV2 Compatible with V1 version update routing rules, convert the data of V1 to V2 for storage
    69  // Once the V1 rule is converted to V2 rules, the original V1 rules will be removed from storage
    70  func (s *Server) updateRoutingConfigV1toV2(ctx context.Context, req *apitraffic.Routing) *apiservice.Response {
    71  	svc, resp := s.routingConfigCommonCheck(ctx, req)
    72  	if resp != nil {
    73  		return resp
    74  	}
    75  
    76  	serviceTx, err := s.storage.CreateTransaction()
    77  	if err != nil {
    78  		log.Error(err.Error(), utils.RequestID(ctx))
    79  		return apiv1.NewRoutingResponse(commonstore.StoreCode2APICode(err), req)
    80  	}
    81  	// Release the lock for the service
    82  	defer func() {
    83  		_ = serviceTx.Commit()
    84  	}()
    85  
    86  	// Need to prohibit the concurrent modification of the V1 rules
    87  	if _, err = serviceTx.LockService(svc.Name, svc.Namespace); err != nil {
    88  		log.Error("[Service][Routing] get service x-lock", zap.String("service", svc.Name),
    89  			zap.String("namespace", svc.Namespace), utils.RequestID(ctx), zap.Error(err))
    90  		return apiv1.NewRoutingResponse(commonstore.StoreCode2APICode(err), req)
    91  	}
    92  
    93  	conf, err := s.storage.GetRoutingConfigWithService(svc.Name, svc.Namespace)
    94  	if err != nil {
    95  		log.Error(err.Error(), utils.RequestID(ctx))
    96  		return apiv1.NewRoutingResponse(commonstore.StoreCode2APICode(err), req)
    97  	}
    98  	if conf == nil {
    99  		return apiv1.NewRoutingResponse(apimodel.Code_NotFoundRouting, req)
   100  	}
   101  
   102  	inDatas, outDatas, resp := batchBuildV2Routings(req)
   103  	if resp != nil {
   104  		return resp
   105  	}
   106  
   107  	if resp := s.saveRoutingV1toV2(ctx, svc.ID, inDatas, outDatas); resp.GetCode().GetValue() != uint32(
   108  		apimodel.Code_ExecuteSuccess) {
   109  		return resp
   110  	}
   111  
   112  	return apiv1.NewRoutingResponse(apimodel.Code_ExecuteSuccess, req)
   113  }
   114  
   115  // saveRoutingV1toV2 Convert the V1 rules of the target to V2 rule
   116  func (s *Server) saveRoutingV1toV2(ctx context.Context, svcId string,
   117  	inRules, outRules []*apitraffic.RouteRule) *apiservice.Response {
   118  	tx, err := s.storage.StartTx()
   119  	if err != nil {
   120  		log.Error("[Service][Routing] create routing v2 from v1 open tx",
   121  			utils.RequestID(ctx), zap.Error(err))
   122  		return apiv1.NewResponse(commonstore.StoreCode2APICode(err))
   123  	}
   124  	defer func() {
   125  		_ = tx.Rollback()
   126  	}()
   127  
   128  	// Need to delete the routing rules of V1 first
   129  	if err := s.storage.DeleteRoutingConfigTx(tx, svcId); err != nil {
   130  		log.Error("[Service][Routing] clean routing v1 from store",
   131  			utils.RequestID(ctx), zap.Error(err))
   132  		return apiv1.NewResponse(commonstore.StoreCode2APICode(err))
   133  	}
   134  
   135  	saveOperation := func(routings []*apitraffic.RouteRule) *apiservice.Response {
   136  		priorityMax := 0
   137  		for i := range routings {
   138  			item := routings[i]
   139  			if item.Id == "" {
   140  				item.Id = utils.NewRoutingV2UUID()
   141  			}
   142  			item.Revision = utils.NewV2Revision()
   143  			data := &model.RouterConfig{}
   144  			if err := data.ParseRouteRuleFromAPI(item); err != nil {
   145  				return apiv1.NewResponse(apimodel.Code_ExecuteException)
   146  			}
   147  
   148  			data.Valid = true
   149  			data.Enable = true
   150  			if priorityMax > 10 {
   151  				priorityMax = 10
   152  			}
   153  
   154  			data.Priority = uint32(priorityMax)
   155  			priorityMax++
   156  
   157  			if err := s.storage.CreateRoutingConfigV2Tx(tx, data); err != nil {
   158  				log.Error("[Routing][V2] create routing v2 from v1 into store",
   159  					utils.RequestID(ctx), zap.Error(err))
   160  				return apiv1.NewResponse(commonstore.StoreCode2APICode(err))
   161  			}
   162  			s.RecordHistory(ctx, routingV2RecordEntry(ctx, item, data, model.OCreate))
   163  		}
   164  
   165  		return nil
   166  	}
   167  
   168  	if resp := saveOperation(inRules); resp != nil {
   169  		return resp
   170  	}
   171  	if resp := saveOperation(outRules); resp != nil {
   172  		return resp
   173  	}
   174  
   175  	if err := tx.Commit(); err != nil {
   176  		log.Error("[Service][Routing] create routing v2 from v1 commit",
   177  			utils.RequestID(ctx), zap.Error(err))
   178  		return apiv1.NewResponse(apimodel.Code_ExecuteException)
   179  	}
   180  
   181  	return apiv1.NewResponse(apimodel.Code_ExecuteSuccess)
   182  }
   183  
   184  func batchBuildV2Routings(
   185  	req *apitraffic.Routing) ([]*apitraffic.RouteRule, []*apitraffic.RouteRule, *apiservice.Response) {
   186  	inBounds := req.GetInbounds()
   187  	outBounds := req.GetOutbounds()
   188  	inRoutings := make([]*apitraffic.RouteRule, 0, len(inBounds))
   189  	outRoutings := make([]*apitraffic.RouteRule, 0, len(outBounds))
   190  	for i := range inBounds {
   191  		routing, err := model.BuildV2RoutingFromV1Route(req, inBounds[i])
   192  		if err != nil {
   193  			return nil, nil, apiv1.NewResponse(apimodel.Code_ExecuteException)
   194  		}
   195  		routing.Name = req.GetNamespace().GetValue() + "." + req.GetService().GetValue()
   196  		inRoutings = append(inRoutings, routing)
   197  	}
   198  
   199  	for i := range outBounds {
   200  		routing, err := model.BuildV2RoutingFromV1Route(req, outBounds[i])
   201  		if err != nil {
   202  			return nil, nil, apiv1.NewResponse(apimodel.Code_ExecuteException)
   203  		}
   204  		outRoutings = append(outRoutings, routing)
   205  	}
   206  
   207  	return inRoutings, outRoutings, nil
   208  }