github.com/polarismesh/polaris@v1.17.8/namespace/namespace.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 namespace
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"time"
    24  
    25  	"github.com/golang/protobuf/jsonpb"
    26  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    27  	apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage"
    28  	"go.uber.org/zap"
    29  
    30  	api "github.com/polarismesh/polaris/common/api/v1"
    31  	"github.com/polarismesh/polaris/common/model"
    32  	commonstore "github.com/polarismesh/polaris/common/store"
    33  	commontime "github.com/polarismesh/polaris/common/time"
    34  	"github.com/polarismesh/polaris/common/utils"
    35  )
    36  
    37  var _ NamespaceOperateServer = (*Server)(nil)
    38  
    39  func (s *Server) allowAutoCreate() bool {
    40  	return s.cfg.AutoCreate
    41  }
    42  
    43  // CreateNamespaces 批量创建命名空间
    44  func (s *Server) CreateNamespaces(ctx context.Context, req []*apimodel.Namespace) *apiservice.BatchWriteResponse {
    45  	if checkError := checkBatchNamespace(req); checkError != nil {
    46  		return checkError
    47  	}
    48  
    49  	responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess)
    50  	for _, namespace := range req {
    51  		response := s.CreateNamespace(ctx, namespace)
    52  		api.Collect(responses, response)
    53  	}
    54  
    55  	return responses
    56  }
    57  
    58  // CreateNamespaceIfAbsent 创建命名空间,如果不存在
    59  func (s *Server) CreateNamespaceIfAbsent(ctx context.Context,
    60  	req *apimodel.Namespace) (string, *apiservice.Response) {
    61  	if resp := checkCreateNamespace(req); resp != nil {
    62  		return "", resp
    63  	}
    64  	name := req.GetName().GetValue()
    65  	val, err := s.loadNamespace(name)
    66  	if err != nil {
    67  		return name, nil
    68  	}
    69  	if val == "" && !s.allowAutoCreate() {
    70  		return "", api.NewResponse(apimodel.Code_NotFoundNamespace)
    71  	}
    72  	ret, err, _ := s.createNamespaceSingle.Do(name, func() (interface{}, error) {
    73  		return s.CreateNamespace(ctx, req), nil
    74  	})
    75  	if err != nil {
    76  		return "", api.NewResponseWithMsg(apimodel.Code_ExecuteException, err.Error())
    77  	}
    78  	var (
    79  		resp = ret.(*apiservice.Response)
    80  		code = resp.GetCode().GetValue()
    81  	)
    82  	if code == uint32(apimodel.Code_ExecuteSuccess) || code == uint32(apimodel.Code_ExistedResource) {
    83  		return name, nil
    84  	}
    85  	return "", resp
    86  }
    87  
    88  // CreateNamespace 创建单个命名空间
    89  func (s *Server) CreateNamespace(ctx context.Context, req *apimodel.Namespace) *apiservice.Response {
    90  	requestID, _ := ctx.Value(utils.StringContext("request-id")).(string)
    91  
    92  	// 参数检查
    93  	if checkError := checkCreateNamespace(req); checkError != nil {
    94  		return checkError
    95  	}
    96  
    97  	namespaceName := req.GetName().GetValue()
    98  
    99  	// 检查是否存在
   100  	namespace, err := s.storage.GetNamespace(namespaceName)
   101  	if err != nil {
   102  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   103  		return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req)
   104  	}
   105  	if namespace != nil {
   106  		return api.NewNamespaceResponse(apimodel.Code_ExistedResource, req)
   107  	}
   108  
   109  	//
   110  	data := s.createNamespaceModel(req)
   111  
   112  	// 存储层操作
   113  	if err := s.storage.AddNamespace(data); err != nil {
   114  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   115  		return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req)
   116  	}
   117  
   118  	msg := fmt.Sprintf("create namespace: name=%s", namespaceName)
   119  	log.Info(msg, utils.ZapRequestID(requestID))
   120  
   121  	out := &apimodel.Namespace{
   122  		Name:  req.GetName(),
   123  		Token: utils.NewStringValue(data.Token),
   124  	}
   125  
   126  	_ = s.afterNamespaceResource(ctx, req, data, false)
   127  
   128  	return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, out)
   129  }
   130  
   131  /**
   132   * @brief 创建存储层命名空间模型
   133   */
   134  func (s *Server) createNamespaceModel(req *apimodel.Namespace) *model.Namespace {
   135  	namespace := &model.Namespace{
   136  		Name:    req.GetName().GetValue(),
   137  		Comment: req.GetComment().GetValue(),
   138  		Owner:   req.GetOwners().GetValue(),
   139  		Token:   utils.NewUUID(),
   140  	}
   141  
   142  	return namespace
   143  }
   144  
   145  // DeleteNamespaces 批量删除命名空间
   146  func (s *Server) DeleteNamespaces(ctx context.Context, req []*apimodel.Namespace) *apiservice.BatchWriteResponse {
   147  	if checkError := checkBatchNamespace(req); checkError != nil {
   148  		return checkError
   149  	}
   150  
   151  	responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess)
   152  	for _, namespace := range req {
   153  		response := s.DeleteNamespace(ctx, namespace)
   154  		api.Collect(responses, response)
   155  	}
   156  
   157  	return responses
   158  }
   159  
   160  // DeleteNamespace 删除单个命名空间
   161  func (s *Server) DeleteNamespace(ctx context.Context, req *apimodel.Namespace) *apiservice.Response {
   162  	requestID, _ := ctx.Value(utils.StringContext("request-id")).(string)
   163  
   164  	// 参数检查
   165  	if checkError := checkReviseNamespace(ctx, req); checkError != nil {
   166  		return checkError
   167  	}
   168  
   169  	tx, err := s.storage.CreateTransaction()
   170  	if err != nil {
   171  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   172  		return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req)
   173  	}
   174  	defer func() { _ = tx.Commit() }()
   175  
   176  	// 检查是否存在
   177  	namespace, err := tx.LockNamespace(req.GetName().GetValue())
   178  	if err != nil {
   179  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   180  		return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req)
   181  	}
   182  	if namespace == nil {
   183  		return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, req)
   184  	}
   185  
   186  	// // 鉴权
   187  	// if ok := s.authority.VerifyNamespace(namespace.Token, parseNamespaceToken(ctx, req)); !ok {
   188  	// 	return api.NewNamespaceResponse(api.Unauthorized, req)
   189  	// }
   190  
   191  	// 判断属于该命名空间的服务是否都已经被删除
   192  	total, err := s.getServicesCountWithNamespace(namespace.Name)
   193  	if err != nil {
   194  		log.Error("get services count with namespace err",
   195  			utils.ZapRequestID(requestID),
   196  			zap.String("err", err.Error()))
   197  		return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req)
   198  	}
   199  	if total != 0 {
   200  		log.Error("the removed namespace has remain services", utils.ZapRequestID(requestID))
   201  		return api.NewNamespaceResponse(apimodel.Code_NamespaceExistedServices, req)
   202  	}
   203  
   204  	// 判断属于该命名空间的服务是否都已经被删除
   205  	total, err = s.getConfigGroupCountWithNamespace(namespace.Name)
   206  	if err != nil {
   207  		log.Error("get config group count with namespace err",
   208  			utils.ZapRequestID(requestID),
   209  			zap.String("err", err.Error()))
   210  		return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req)
   211  	}
   212  	if total != 0 {
   213  		log.Error("the removed namespace has remain config-group", utils.ZapRequestID(requestID))
   214  		return api.NewNamespaceResponse(apimodel.Code_NamespaceExistedConfigGroups, req)
   215  	}
   216  
   217  	// 存储层操作
   218  	if err := tx.DeleteNamespace(namespace.Name); err != nil {
   219  		log.Error(err.Error(), utils.ZapRequestID(requestID))
   220  		return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req)
   221  	}
   222  
   223  	s.caches.Service().CleanNamespace(namespace.Name)
   224  
   225  	msg := fmt.Sprintf("delete namespace: name=%s", namespace.Name)
   226  	log.Info(msg, utils.ZapRequestID(requestID))
   227  	s.RecordHistory(namespaceRecordEntry(ctx, req, model.ODelete))
   228  
   229  	_ = s.afterNamespaceResource(ctx, req, &model.Namespace{Name: req.GetName().GetValue()}, true)
   230  
   231  	return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, req)
   232  }
   233  
   234  // UpdateNamespaces 批量修改命名空间
   235  func (s *Server) UpdateNamespaces(ctx context.Context, req []*apimodel.Namespace) *apiservice.BatchWriteResponse {
   236  	if checkError := checkBatchNamespace(req); checkError != nil {
   237  		return checkError
   238  	}
   239  
   240  	responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess)
   241  	for _, namespace := range req {
   242  		response := s.UpdateNamespace(ctx, namespace)
   243  		api.Collect(responses, response)
   244  	}
   245  
   246  	return responses
   247  }
   248  
   249  // UpdateNamespace 修改单个命名空间
   250  func (s *Server) UpdateNamespace(ctx context.Context, req *apimodel.Namespace) *apiservice.Response {
   251  	// 参数检查
   252  	if resp := checkReviseNamespace(ctx, req); resp != nil {
   253  		return resp
   254  	}
   255  
   256  	// 权限校验
   257  	namespace, resp := s.checkNamespaceAuthority(ctx, req)
   258  	if resp != nil {
   259  		return resp
   260  	}
   261  
   262  	rid := utils.ParseRequestID(ctx)
   263  	// 修改
   264  	s.updateNamespaceAttribute(req, namespace)
   265  
   266  	// 存储层操作
   267  	if err := s.storage.UpdateNamespace(namespace); err != nil {
   268  		log.Error(err.Error(), utils.ZapRequestID(rid))
   269  		return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req)
   270  	}
   271  
   272  	msg := fmt.Sprintf("update namespace: name=%s", namespace.Name)
   273  	log.Info(msg, utils.ZapRequestID(rid))
   274  	s.RecordHistory(namespaceRecordEntry(ctx, req, model.OUpdate))
   275  
   276  	if err := s.afterNamespaceResource(ctx, req, namespace, false); err != nil {
   277  		return api.NewNamespaceResponse(apimodel.Code_ExecuteException, req)
   278  	}
   279  
   280  	return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, req)
   281  }
   282  
   283  /**
   284   * @brief 修改命名空间属性
   285   */
   286  func (s *Server) updateNamespaceAttribute(req *apimodel.Namespace, namespace *model.Namespace) {
   287  	if req.GetComment() != nil {
   288  		namespace.Comment = req.GetComment().GetValue()
   289  	}
   290  
   291  	if req.GetOwners() != nil {
   292  		namespace.Owner = req.GetOwners().GetValue()
   293  	}
   294  }
   295  
   296  // UpdateNamespaceToken 更新命名空间token
   297  func (s *Server) UpdateNamespaceToken(ctx context.Context, req *apimodel.Namespace) *apiservice.Response {
   298  	if resp := checkReviseNamespace(ctx, req); resp != nil {
   299  		return resp
   300  	}
   301  	namespace, resp := s.checkNamespaceAuthority(ctx, req)
   302  	if resp != nil {
   303  		return resp
   304  	}
   305  
   306  	rid := utils.ParseRequestID(ctx)
   307  	// 生成token
   308  	token := utils.NewUUID()
   309  
   310  	// 存储层操作
   311  	if err := s.storage.UpdateNamespaceToken(namespace.Name, token); err != nil {
   312  		log.Error(err.Error(), utils.ZapRequestID(rid))
   313  		return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req)
   314  	}
   315  
   316  	msg := fmt.Sprintf("update namespace token: name=%s", namespace.Name)
   317  	log.Info(msg, utils.ZapRequestID(rid))
   318  	s.RecordHistory(namespaceRecordEntry(ctx, req, model.OUpdateToken))
   319  
   320  	out := &apimodel.Namespace{
   321  		Name:  req.GetName(),
   322  		Token: utils.NewStringValue(token),
   323  	}
   324  
   325  	return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, out)
   326  }
   327  
   328  // GetNamespaces 查询命名空间
   329  func (s *Server) GetNamespaces(ctx context.Context, query map[string][]string) *apiservice.BatchQueryResponse {
   330  	filter, offset, limit, checkError := checkGetNamespace(query)
   331  	if checkError != nil {
   332  		return checkError
   333  	}
   334  
   335  	namespaces, amount, err := s.storage.GetNamespaces(filter, offset, limit)
   336  	if err != nil {
   337  		return api.NewBatchQueryResponse(commonstore.StoreCode2APICode(err))
   338  	}
   339  
   340  	out := api.NewBatchQueryResponse(apimodel.Code_ExecuteSuccess)
   341  	out.Amount = utils.NewUInt32Value(amount)
   342  	out.Size = utils.NewUInt32Value(uint32(len(namespaces)))
   343  	var totalServiceCount, totalInstanceCount, totalHealthInstanceCount uint32
   344  	for _, namespace := range namespaces {
   345  		nsCntInfo := s.caches.Service().GetNamespaceCntInfo(namespace.Name)
   346  		api.AddNamespace(out, &apimodel.Namespace{
   347  			Id:                       utils.NewStringValue(namespace.Name),
   348  			Name:                     utils.NewStringValue(namespace.Name),
   349  			Comment:                  utils.NewStringValue(namespace.Comment),
   350  			Owners:                   utils.NewStringValue(namespace.Owner),
   351  			Ctime:                    utils.NewStringValue(commontime.Time2String(namespace.CreateTime)),
   352  			Mtime:                    utils.NewStringValue(commontime.Time2String(namespace.ModifyTime)),
   353  			TotalServiceCount:        utils.NewUInt32Value(nsCntInfo.ServiceCount),
   354  			TotalInstanceCount:       utils.NewUInt32Value(nsCntInfo.InstanceCnt.TotalInstanceCount),
   355  			TotalHealthInstanceCount: utils.NewUInt32Value(nsCntInfo.InstanceCnt.HealthyInstanceCount),
   356  		})
   357  		totalServiceCount += nsCntInfo.ServiceCount
   358  		totalInstanceCount += nsCntInfo.InstanceCnt.TotalInstanceCount
   359  		totalHealthInstanceCount += nsCntInfo.InstanceCnt.HealthyInstanceCount
   360  	}
   361  	api.AddNamespaceSummary(out, &apimodel.Summary{
   362  		TotalServiceCount:        totalServiceCount,
   363  		TotalInstanceCount:       totalInstanceCount,
   364  		TotalHealthInstanceCount: totalHealthInstanceCount,
   365  	})
   366  	return out
   367  }
   368  
   369  // GetNamespaceToken 获取命名空间的token
   370  func (s *Server) GetNamespaceToken(ctx context.Context, req *apimodel.Namespace) *apiservice.Response {
   371  	if resp := checkReviseNamespace(ctx, req); resp != nil {
   372  		return resp
   373  	}
   374  
   375  	namespace, resp := s.checkNamespaceAuthority(ctx, req)
   376  	if resp != nil {
   377  		return resp
   378  	}
   379  
   380  	// s.RecordHistory(namespaceRecordEntry(ctx, req, model.OGetToken))
   381  	// 构造返回数据
   382  	out := &apimodel.Namespace{
   383  		Name:  req.GetName(),
   384  		Token: utils.NewStringValue(namespace.Token),
   385  	}
   386  	return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, out)
   387  }
   388  
   389  // 根据命名空间查询服务总数
   390  func (s *Server) getServicesCountWithNamespace(namespace string) (uint32, error) {
   391  	filter := map[string]string{"namespace": namespace}
   392  	total, _, err := s.storage.GetServices(filter, nil, nil, 0, 1)
   393  	if err != nil {
   394  		return 0, err
   395  	}
   396  	return total, nil
   397  }
   398  
   399  // 根据命名空间查询配置分组总数
   400  func (s *Server) getConfigGroupCountWithNamespace(namespace string) (uint32, error) {
   401  	total, err := s.storage.CountConfigGroups(namespace)
   402  	if err != nil {
   403  		return 0, err
   404  	}
   405  	return uint32(total), nil
   406  }
   407  
   408  // loadNamespace
   409  func (s *Server) loadNamespace(name string) (string, error) {
   410  	if val := s.caches.Namespace().GetNamespace(name); val != nil {
   411  		return name, nil
   412  	}
   413  	val, err := s.storage.GetNamespace(name)
   414  	if err != nil {
   415  		return "", err
   416  	}
   417  	if val == nil {
   418  		return "", nil
   419  	}
   420  	return val.Name, nil
   421  }
   422  
   423  // 检查namespace的权限,并且返回namespace
   424  func (s *Server) checkNamespaceAuthority(
   425  	ctx context.Context, req *apimodel.Namespace) (*model.Namespace, *apiservice.Response) {
   426  	rid := utils.ParseRequestID(ctx)
   427  	namespaceName := req.GetName().GetValue()
   428  	// namespaceToken := parseNamespaceToken(ctx, req)
   429  
   430  	// 检查是否存在
   431  	namespace, err := s.storage.GetNamespace(namespaceName)
   432  	if err != nil {
   433  		log.Error(err.Error(), utils.ZapRequestID(rid))
   434  		return nil, api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req)
   435  	}
   436  	if namespace == nil {
   437  		return nil, api.NewNamespaceResponse(apimodel.Code_NotFoundResource, req)
   438  	}
   439  
   440  	// 鉴权
   441  	// if ok := s.authority.VerifyNamespace(namespace.Token, namespaceToken); !ok {
   442  	// 	return nil, api.NewNamespaceResponse(api.Unauthorized, req)
   443  	// }
   444  
   445  	return namespace, nil
   446  }
   447  
   448  // 检查批量请求
   449  func checkBatchNamespace(req []*apimodel.Namespace) *apiservice.BatchWriteResponse {
   450  	if len(req) == 0 {
   451  		return api.NewBatchWriteResponse(apimodel.Code_EmptyRequest)
   452  	}
   453  
   454  	if len(req) > utils.MaxBatchSize {
   455  		return api.NewBatchWriteResponse(apimodel.Code_BatchSizeOverLimit)
   456  	}
   457  
   458  	return nil
   459  }
   460  
   461  // 检查创建命名空间请求参数
   462  func checkCreateNamespace(req *apimodel.Namespace) *apiservice.Response {
   463  	if req == nil {
   464  		return api.NewNamespaceResponse(apimodel.Code_EmptyRequest, req)
   465  	}
   466  
   467  	if err := utils.CheckResourceName(req.GetName()); err != nil {
   468  		return api.NewNamespaceResponse(apimodel.Code_InvalidNamespaceName, req)
   469  	}
   470  
   471  	return nil
   472  }
   473  
   474  // 检查删除/修改命名空间请求参数
   475  func checkReviseNamespace(ctx context.Context, req *apimodel.Namespace) *apiservice.Response {
   476  	if req == nil {
   477  		return api.NewNamespaceResponse(apimodel.Code_EmptyRequest, req)
   478  	}
   479  
   480  	if err := utils.CheckResourceName(req.GetName()); err != nil {
   481  		return api.NewNamespaceResponse(apimodel.Code_InvalidNamespaceName, req)
   482  	}
   483  	return nil
   484  }
   485  
   486  // 检查查询命名空间请求参数
   487  func checkGetNamespace(query map[string][]string) (map[string][]string, int, int, *apiservice.BatchQueryResponse) {
   488  	filter := make(map[string][]string)
   489  
   490  	if value := query["name"]; len(value) > 0 {
   491  		filter["name"] = value
   492  	}
   493  
   494  	if value := query["owner"]; len(value) > 0 {
   495  		filter["owner"] = value
   496  	}
   497  
   498  	offset, err := utils.CheckQueryOffset(query["offset"])
   499  	if err != nil {
   500  		return nil, 0, 0, api.NewBatchQueryResponse(apimodel.Code_InvalidParameter)
   501  	}
   502  
   503  	limit, err := utils.CheckQueryLimit(query["limit"])
   504  	if err != nil {
   505  		return nil, 0, 0, api.NewBatchQueryResponse(apimodel.Code_InvalidParameter)
   506  	}
   507  
   508  	return filter, offset, limit, nil
   509  }
   510  
   511  // 返回命名空间请求的token
   512  // 默认先从req中获取,不存在,则使用header的
   513  func parseNamespaceToken(ctx context.Context, req *apimodel.Namespace) string {
   514  	if reqToken := req.GetToken().GetValue(); reqToken != "" {
   515  		return reqToken
   516  	}
   517  
   518  	if headerToken := utils.ParseToken(ctx); headerToken != "" {
   519  		return headerToken
   520  	}
   521  
   522  	return ""
   523  }
   524  
   525  // 生成命名空间的记录entry
   526  func namespaceRecordEntry(ctx context.Context, req *apimodel.Namespace, opt model.OperationType) *model.RecordEntry {
   527  	marshaler := jsonpb.Marshaler{}
   528  	datail, _ := marshaler.MarshalToString(req)
   529  	return &model.RecordEntry{
   530  		ResourceType:  model.RNamespace,
   531  		ResourceName:  req.GetName().GetValue(),
   532  		Namespace:     req.GetName().GetValue(),
   533  		OperationType: opt,
   534  		Operator:      utils.ParseOperator(ctx),
   535  		Detail:        datail,
   536  		HappenTime:    time.Now(),
   537  	}
   538  }