github.com/polarismesh/polaris@v1.17.8/service/service_alias.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  	"fmt"
    23  
    24  	"github.com/golang/protobuf/ptypes/wrappers"
    25  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    26  	apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage"
    27  	"go.uber.org/zap"
    28  
    29  	api "github.com/polarismesh/polaris/common/api/v1"
    30  	"github.com/polarismesh/polaris/common/model"
    31  	commonstore "github.com/polarismesh/polaris/common/store"
    32  	commontime "github.com/polarismesh/polaris/common/time"
    33  	"github.com/polarismesh/polaris/common/utils"
    34  	"github.com/polarismesh/polaris/store"
    35  )
    36  
    37  var (
    38  	// AliasFilterAttributes filer attrs alias
    39  	AliasFilterAttributes = map[string]bool{
    40  		"alias":           true,
    41  		"alias_namespace": true,
    42  		"namespace":       true,
    43  		"service":         true,
    44  		"owner":           true,
    45  		"offset":          true,
    46  		"limit":           true,
    47  	}
    48  )
    49  
    50  // CreateServiceAlias 创建服务别名
    51  func (s *Server) CreateServiceAlias(ctx context.Context, req *apiservice.ServiceAlias) *apiservice.Response {
    52  	if resp := checkCreateServiceAliasReq(ctx, req); resp != nil {
    53  		return resp
    54  	}
    55  
    56  	rid := utils.ParseRequestID(ctx)
    57  	tx, err := s.storage.CreateTransaction()
    58  	if err != nil {
    59  		log.Error(err.Error(), utils.ZapRequestID(rid))
    60  		return api.NewServiceAliasResponse(commonstore.StoreCode2APICode(err), req)
    61  	}
    62  	defer func() { _ = tx.Commit() }()
    63  
    64  	service, response, done := s.checkPointServiceAlias(tx, req, rid)
    65  	if done {
    66  		return response
    67  	}
    68  
    69  	// 检查是否存在同名的alias
    70  	if req.GetAlias().GetValue() != "" {
    71  		oldAlias, getErr := s.storage.GetService(req.GetAlias().GetValue(),
    72  			req.GetAliasNamespace().GetValue())
    73  		if getErr != nil {
    74  			log.Error(getErr.Error(), utils.ZapRequestID(rid))
    75  			return api.NewServiceAliasResponse(commonstore.StoreCode2APICode(err), req)
    76  		}
    77  		if oldAlias != nil {
    78  			return api.NewServiceAliasResponse(apimodel.Code_ExistedResource, req)
    79  		}
    80  	}
    81  
    82  	// 构建别名的信息,这里包括了创建SID
    83  	input, resp := s.createServiceAliasModel(req, service.ID)
    84  	if resp != nil {
    85  		return resp
    86  	}
    87  	if err := s.storage.AddService(input); err != nil {
    88  		log.Error(err.Error(), utils.ZapRequestID(rid))
    89  		return api.NewServiceAliasResponse(commonstore.StoreCode2APICode(err), req)
    90  	}
    91  
    92  	log.Info(fmt.Sprintf("create service alias, service(%s, %s), alias(%s, %s)",
    93  		req.Service.Value, req.Namespace.Value, input.Name, input.Namespace), utils.ZapRequestID(rid))
    94  	out := &apiservice.ServiceAlias{
    95  		Service:        req.Service,
    96  		Namespace:      req.Namespace,
    97  		Alias:          req.Alias,
    98  		AliasNamespace: req.AliasNamespace,
    99  		ServiceToken:   &wrappers.StringValue{Value: input.Token},
   100  	}
   101  	if out.GetAlias().GetValue() == "" {
   102  		out.Alias = utils.NewStringValue(input.Name)
   103  	}
   104  	record := &apiservice.Service{Name: out.Alias, Namespace: out.AliasNamespace}
   105  	s.RecordHistory(ctx, serviceRecordEntry(ctx, record, input, model.OCreate))
   106  	return api.NewServiceAliasResponse(apimodel.Code_ExecuteSuccess, out)
   107  }
   108  
   109  func (s *Server) checkPointServiceAlias(
   110  	tx store.Transaction, req *apiservice.ServiceAlias, rid string) (*model.Service, *apiservice.Response, bool) {
   111  	// 检查指向服务是否存在以及是否为别名
   112  	service, err := tx.LockService(req.GetService().GetValue(), req.GetNamespace().GetValue())
   113  	if err != nil {
   114  		log.Error(err.Error(), utils.ZapRequestID(rid))
   115  		return nil, api.NewServiceAliasResponse(commonstore.StoreCode2APICode(err), req), true
   116  	}
   117  	if service == nil {
   118  		return nil, api.NewServiceAliasResponse(apimodel.Code_NotFoundService, req), true
   119  	}
   120  	// 检查该服务是否已经是一个别名服务,不允许再为别名创建别名
   121  	if service.IsAlias() {
   122  		return nil, api.NewServiceAliasResponse(apimodel.Code_NotAllowCreateAliasForAlias, req), true
   123  	}
   124  	return service, nil, false
   125  }
   126  
   127  // DeleteServiceAlias 删除服务别名
   128  //
   129  //	需要带上源服务name,namespace,token
   130  //	另外一种删除别名的方式,是直接调用删除服务的接口,也是可行的
   131  func (s *Server) DeleteServiceAlias(ctx context.Context, req *apiservice.ServiceAlias) *apiservice.Response {
   132  	if resp := checkDeleteServiceAliasReq(ctx, req); resp != nil {
   133  		return resp
   134  	}
   135  	rid := utils.ParseRequestID(ctx)
   136  	alias, err := s.storage.GetService(req.GetAlias().GetValue(),
   137  		req.GetAliasNamespace().GetValue())
   138  	if err != nil {
   139  		log.Error(err.Error(), utils.ZapRequestID(rid))
   140  		return api.NewServiceAliasResponse(commonstore.StoreCode2APICode(err), req)
   141  	}
   142  	if alias == nil {
   143  		return api.NewServiceAliasResponse(apimodel.Code_NotFoundServiceAlias, req)
   144  	}
   145  
   146  	// 直接删除alias
   147  	if err := s.storage.DeleteServiceAlias(req.GetAlias().GetValue(),
   148  		req.GetAliasNamespace().GetValue()); err != nil {
   149  		log.Error(err.Error(), utils.ZapRequestID(rid))
   150  		return api.NewServiceAliasResponse(commonstore.StoreCode2APICode(err), req)
   151  	}
   152  
   153  	return api.NewServiceAliasResponse(apimodel.Code_ExecuteSuccess, req)
   154  }
   155  
   156  func checkBatchAlias(req []*apiservice.ServiceAlias) *apiservice.BatchWriteResponse {
   157  	if len(req) == 0 {
   158  		return api.NewBatchWriteResponse(apimodel.Code_EmptyRequest)
   159  	}
   160  
   161  	if len(req) > MaxBatchSize {
   162  		return api.NewBatchWriteResponse(apimodel.Code_BatchSizeOverLimit)
   163  	}
   164  
   165  	return nil
   166  }
   167  
   168  // DeleteServiceAliases 删除服务别名列表
   169  func (s *Server) DeleteServiceAliases(
   170  	ctx context.Context, req []*apiservice.ServiceAlias) *apiservice.BatchWriteResponse {
   171  	if checkError := checkBatchAlias(req); checkError != nil {
   172  		return checkError
   173  	}
   174  
   175  	responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess)
   176  	for _, alias := range req {
   177  		response := s.DeleteServiceAlias(ctx, alias)
   178  		api.Collect(responses, response)
   179  	}
   180  
   181  	return api.FormatBatchWriteResponse(responses)
   182  }
   183  
   184  // UpdateServiceAlias 修改服务别名
   185  func (s *Server) UpdateServiceAlias(ctx context.Context, req *apiservice.ServiceAlias) *apiservice.Response {
   186  	rid := utils.ParseRequestID(ctx)
   187  
   188  	// 检查请求参数
   189  	if resp := checkReviseServiceAliasReq(ctx, req); resp != nil {
   190  		return resp
   191  	}
   192  
   193  	// 检查别名负责人
   194  	// if err := checkResourceOwners(req.GetOwners()); err != nil {
   195  	//	return api.NewServiceAliasResponse(api.InvalidServiceAliasOwners, req)
   196  	// }
   197  
   198  	// 检查服务别名是否存在
   199  	alias, err := s.storage.GetService(req.GetAlias().GetValue(), req.GetAliasNamespace().GetValue())
   200  	if err != nil {
   201  		log.Error(err.Error(), utils.ZapRequestID(rid))
   202  		return api.NewServiceAliasResponse(commonstore.StoreCode2APICode(err), req)
   203  	}
   204  	if alias == nil {
   205  		return api.NewServiceAliasResponse(apimodel.Code_NotFoundServiceAlias, req)
   206  	}
   207  
   208  	// 检查将要指向的服务是否存在
   209  	service, err := s.storage.GetService(req.GetService().GetValue(), req.GetNamespace().GetValue())
   210  	if err != nil {
   211  		log.Error(err.Error(), utils.ZapRequestID(rid))
   212  		return api.NewServiceAliasResponse(commonstore.StoreCode2APICode(err), req)
   213  	}
   214  	if service == nil {
   215  		return api.NewServiceAliasResponse(apimodel.Code_NotFoundService, req)
   216  	}
   217  	// 检查该服务是否已经是一个别名服务,不允许再为别名创建别名
   218  	if service.IsAlias() {
   219  		return api.NewServiceAliasResponse(apimodel.Code_NotAllowCreateAliasForAlias, req)
   220  	}
   221  
   222  	// 判断是否需要修改
   223  	resp, needUpdate, needUpdateOwner := s.updateServiceAliasAttribute(req, alias, service.ID)
   224  	if resp != nil {
   225  		return resp
   226  	}
   227  
   228  	if !needUpdate {
   229  		log.Info("update service alias data no change, no need update", utils.ZapRequestID(rid),
   230  			zap.String("service alias", req.String()))
   231  		return api.NewServiceAliasResponse(apimodel.Code_NoNeedUpdate, req)
   232  	}
   233  
   234  	// 执行存储层操作
   235  	if err := s.storage.UpdateServiceAlias(alias, needUpdateOwner); err != nil {
   236  		log.Error(err.Error(), utils.ZapRequestID(rid))
   237  		return wrapperServiceAliasResponse(req, err)
   238  	}
   239  
   240  	log.Info(fmt.Sprintf("update service alias, service(%s, %s), alias(%s)",
   241  		req.GetService().GetValue(), req.GetNamespace().GetValue(), req.GetAlias().GetValue()), utils.ZapRequestID(rid))
   242  
   243  	record := &apiservice.Service{Name: req.Alias, Namespace: req.Namespace}
   244  	s.RecordHistory(ctx, serviceRecordEntry(ctx, record, alias, model.OUpdate))
   245  
   246  	return api.NewServiceAliasResponse(apimodel.Code_ExecuteSuccess, req)
   247  }
   248  
   249  // GetServiceAliases 查找服务别名
   250  func (s *Server) GetServiceAliases(ctx context.Context, query map[string]string) *apiservice.BatchQueryResponse {
   251  	// 先处理offset和limit
   252  	offset, limit, err := utils.ParseOffsetAndLimit(query)
   253  	if err != nil {
   254  		return api.NewBatchQueryResponse(apimodel.Code_InvalidParameter)
   255  	}
   256  
   257  	// 处理剩余的参数
   258  	filter := make(map[string]string)
   259  	for key, value := range query {
   260  		if _, ok := AliasFilterAttributes[key]; !ok {
   261  			log.Errorf("[Server][Alias][Query] attribute(%s) is not allowed", key)
   262  			return api.NewBatchQueryResponse(apimodel.Code_InvalidParameter)
   263  		}
   264  		filter[key] = value
   265  	}
   266  
   267  	total, aliases, err := s.storage.GetServiceAliases(filter, offset, limit)
   268  	if err != nil {
   269  		log.Errorf("[Server][Alias] get aliases err: %s", err.Error())
   270  		return api.NewBatchQueryResponse(commonstore.StoreCode2APICode(err))
   271  	}
   272  
   273  	resp := api.NewBatchQueryResponse(apimodel.Code_ExecuteSuccess)
   274  	resp.Amount = utils.NewUInt32Value(total)
   275  	resp.Size = utils.NewUInt32Value(uint32(len(aliases)))
   276  	resp.Aliases = make([]*apiservice.ServiceAlias, 0, len(aliases))
   277  	for _, entry := range aliases {
   278  		item := &apiservice.ServiceAlias{
   279  			Id:             utils.NewStringValue(entry.ID),
   280  			Service:        utils.NewStringValue(entry.Service),
   281  			Namespace:      utils.NewStringValue(entry.Namespace),
   282  			Alias:          utils.NewStringValue(entry.Alias),
   283  			AliasNamespace: utils.NewStringValue(entry.AliasNamespace),
   284  			Owners:         utils.NewStringValue(entry.Owner),
   285  			Comment:        utils.NewStringValue(entry.Comment),
   286  			Ctime:          utils.NewStringValue(commontime.Time2String(entry.CreateTime)),
   287  			Mtime:          utils.NewStringValue(commontime.Time2String(entry.ModifyTime)),
   288  		}
   289  		resp.Aliases = append(resp.Aliases, item)
   290  	}
   291  
   292  	return resp
   293  }
   294  
   295  // checkCreateServiceAliasReq 检查别名请求
   296  func checkCreateServiceAliasReq(ctx context.Context, req *apiservice.ServiceAlias) *apiservice.Response {
   297  	response, done := preCheckAlias(req)
   298  	if done {
   299  		return response
   300  	}
   301  	// 检查字段长度是否大于DB中对应字段长
   302  	err, notOk := CheckDbServiceAliasFieldLen(req)
   303  	if notOk {
   304  		return err
   305  	}
   306  	return nil
   307  }
   308  
   309  func preCheckAlias(req *apiservice.ServiceAlias) (*apiservice.Response, bool) {
   310  	if req == nil {
   311  		return api.NewServiceAliasResponse(apimodel.Code_EmptyRequest, req), true
   312  	}
   313  
   314  	if err := checkResourceName(req.GetService()); err != nil {
   315  		return api.NewServiceAliasResponse(apimodel.Code_InvalidServiceName, req), true
   316  	}
   317  
   318  	if err := checkResourceName(req.GetNamespace()); err != nil {
   319  		return api.NewServiceAliasResponse(apimodel.Code_InvalidNamespaceName, req), true
   320  	}
   321  
   322  	if err := checkResourceName(req.GetAliasNamespace()); err != nil {
   323  		return api.NewServiceAliasResponse(apimodel.Code_InvalidNamespaceWithAlias, req), true
   324  	}
   325  
   326  	// 默认类型,需要检查alias是否为空
   327  	if req.GetType() == apiservice.AliasType_DEFAULT {
   328  		if err := checkResourceName(req.GetAlias()); err != nil {
   329  			return api.NewServiceAliasResponse(apimodel.Code_InvalidServiceAlias, req), true
   330  		}
   331  	}
   332  	return nil, false
   333  }
   334  
   335  // checkReviseServiceAliasReq 检查删除、修改别名请求
   336  func checkReviseServiceAliasReq(ctx context.Context, req *apiservice.ServiceAlias) *apiservice.Response {
   337  	resp := checkDeleteServiceAliasReq(ctx, req)
   338  	if resp != nil {
   339  		return resp
   340  	}
   341  	// 检查服务名
   342  	if err := checkResourceName(req.GetService()); err != nil {
   343  		return api.NewServiceAliasResponse(apimodel.Code_InvalidServiceName, req)
   344  	}
   345  
   346  	// 检查命名空间
   347  	if err := checkResourceName(req.GetNamespace()); err != nil {
   348  		return api.NewServiceAliasResponse(apimodel.Code_InvalidNamespaceName, req)
   349  	}
   350  	return nil
   351  }
   352  
   353  // checkDeleteServiceAliasReq 检查删除、修改别名请求
   354  func checkDeleteServiceAliasReq(ctx context.Context, req *apiservice.ServiceAlias) *apiservice.Response {
   355  	if req == nil {
   356  		return api.NewServiceAliasResponse(apimodel.Code_EmptyRequest, req)
   357  	}
   358  
   359  	// 检查服务别名
   360  	if err := checkResourceName(req.GetAlias()); err != nil {
   361  		return api.NewServiceAliasResponse(apimodel.Code_InvalidServiceAlias, req)
   362  	}
   363  
   364  	// 检查服务别名命名空间
   365  	if err := checkResourceName(req.GetAliasNamespace()); err != nil {
   366  		return api.NewServiceAliasResponse(apimodel.Code_InvalidNamespaceWithAlias, req)
   367  	}
   368  
   369  	// 检查字段长度是否大于DB中对应字段长
   370  	err, notOk := CheckDbServiceAliasFieldLen(req)
   371  	if notOk {
   372  		return err
   373  	}
   374  
   375  	return nil
   376  }
   377  
   378  // updateServiceAliasAttribute 修改服务别名属性
   379  func (s *Server) updateServiceAliasAttribute(req *apiservice.ServiceAlias, alias *model.Service, serviceID string) (
   380  	*apiservice.Response, bool, bool) {
   381  	var (
   382  		needUpdate      bool
   383  		needUpdateOwner bool
   384  	)
   385  
   386  	// 获取当前指向服务
   387  	service, err := s.storage.GetServiceByID(alias.Reference)
   388  	if err != nil {
   389  		return api.NewServiceAliasResponse(commonstore.StoreCode2APICode(err), req), needUpdate, needUpdateOwner
   390  	}
   391  
   392  	if service.ID != serviceID {
   393  		alias.Reference = serviceID
   394  		needUpdate = true
   395  	}
   396  
   397  	if len(req.GetOwners().GetValue()) > 0 && req.GetOwners().GetValue() != alias.Owner {
   398  		alias.Owner = req.GetOwners().GetValue()
   399  		needUpdate = true
   400  		needUpdateOwner = true
   401  	}
   402  
   403  	if req.GetComment() != nil && req.GetComment().GetValue() != alias.Comment {
   404  		alias.Comment = req.GetComment().GetValue()
   405  		needUpdate = true
   406  	}
   407  
   408  	if needUpdate {
   409  		alias.Revision = utils.NewUUID()
   410  	}
   411  
   412  	return nil, needUpdate, needUpdateOwner
   413  }
   414  
   415  // createServiceAliasModel 构建存储结构
   416  func (s *Server) createServiceAliasModel(req *apiservice.ServiceAlias, svcId string) (
   417  	*model.Service, *apiservice.Response) {
   418  	out := &model.Service{
   419  		ID:        utils.NewUUID(),
   420  		Name:      req.GetAlias().GetValue(),
   421  		Namespace: req.GetAliasNamespace().GetValue(),
   422  		Reference: svcId,
   423  		Token:     utils.NewUUID(),
   424  		Owner:     req.GetOwners().GetValue(),
   425  		Comment:   req.GetComment().GetValue(),
   426  		Revision:  utils.NewUUID(),
   427  	}
   428  
   429  	// sid类型,则创建SID
   430  	if req.GetType() == apiservice.AliasType_CL5SID {
   431  		layoutID, ok := Namespace2SidLayoutID[req.GetAliasNamespace().GetValue()]
   432  		if !ok {
   433  			log.Errorf("[Server][Alias] namespace(%s) not allow to create sid alias",
   434  				req.GetNamespace().GetValue())
   435  			return nil, api.NewServiceAliasResponse(apimodel.Code_InvalidNamespaceWithAlias, req)
   436  		}
   437  		sid, err := s.storage.GenNextL5Sid(layoutID)
   438  		if err != nil {
   439  			log.Errorf("[Server] gen next l5 sid err: %s", err.Error())
   440  			return nil, api.NewServiceAliasResponse(commonstore.StoreCode2APICode(err), req)
   441  		}
   442  		out.Name = sid
   443  	}
   444  
   445  	return out, nil
   446  }
   447  
   448  // wrapperServiceAliasResponse wrapper service alias error
   449  func wrapperServiceAliasResponse(alias *apiservice.ServiceAlias, err error) *apiservice.Response {
   450  	resp := storeError2Response(err)
   451  	if resp == nil {
   452  		return nil
   453  	}
   454  
   455  	resp.Alias = alias
   456  	return resp
   457  }
   458  
   459  // CheckDbServiceAliasFieldLen 检查DB中service表对应的入参字段合法性
   460  func CheckDbServiceAliasFieldLen(req *apiservice.ServiceAlias) (*apiservice.Response, bool) {
   461  	if err := utils.CheckDbStrFieldLen(req.GetService(), MaxDbServiceNameLength); err != nil {
   462  		return api.NewServiceAliasResponse(apimodel.Code_InvalidServiceName, req), true
   463  	}
   464  	if err := utils.CheckDbStrFieldLen(req.GetNamespace(), MaxDbServiceNamespaceLength); err != nil {
   465  		return api.NewServiceAliasResponse(apimodel.Code_InvalidNamespaceName, req), true
   466  	}
   467  	if err := utils.CheckDbStrFieldLen(req.GetAlias(), MaxDbServiceNameLength); err != nil {
   468  		return api.NewServiceAliasResponse(apimodel.Code_InvalidServiceAlias, req), true
   469  	}
   470  	if err := utils.CheckDbStrFieldLen(req.GetAliasNamespace(), MaxDbServiceNamespaceLength); err != nil {
   471  		return api.NewServiceAliasResponse(apimodel.Code_InvalidNamespaceWithAlias, req), true
   472  	}
   473  	if err := utils.CheckDbStrFieldLen(req.GetComment(), MaxDbServiceCommentLength); err != nil {
   474  		return api.NewServiceAliasResponse(apimodel.Code_InvalidServiceAliasComment, req), true
   475  	}
   476  	if err := utils.CheckDbStrFieldLen(req.GetOwners(), MaxDbServiceOwnerLength); err != nil {
   477  		return api.NewServiceAliasResponse(apimodel.Code_InvalidServiceAliasOwners, req), true
   478  	}
   479  	return nil, false
   480  }