github.com/polarismesh/polaris@v1.17.8/config/config_file_group.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 config
    19  
    20  import (
    21  	"context"
    22  	"time"
    23  
    24  	"github.com/gogo/protobuf/jsonpb"
    25  	apiconfig "github.com/polarismesh/specification/source/go/api/v1/config_manage"
    26  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    27  	"go.uber.org/zap"
    28  	"google.golang.org/protobuf/types/known/wrapperspb"
    29  
    30  	cachetypes "github.com/polarismesh/polaris/cache/api"
    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  	"github.com/polarismesh/polaris/common/utils"
    35  )
    36  
    37  // CreateConfigFileGroup 创建配置文件组
    38  func (s *Server) CreateConfigFileGroup(ctx context.Context, req *apiconfig.ConfigFileGroup) *apiconfig.ConfigResponse {
    39  	if checkError := checkConfigFileGroupParams(req); checkError != nil {
    40  		return checkError
    41  	}
    42  
    43  	namespace := req.Namespace.GetValue()
    44  	groupName := req.Name.GetValue()
    45  
    46  	// 如果 namespace 不存在则自动创建
    47  	if _, errResp := s.namespaceOperator.CreateNamespaceIfAbsent(ctx, &apimodel.Namespace{
    48  		Name: req.GetNamespace(),
    49  	}); errResp != nil {
    50  		log.Error("[Config][Group] create namespace failed.", utils.RequestID(ctx),
    51  			utils.ZapNamespace(namespace), utils.ZapGroup(groupName), zap.String("err", errResp.String()))
    52  		return api.NewConfigResponse(apimodel.Code(errResp.Code.GetValue()))
    53  	}
    54  
    55  	fileGroup, err := s.storage.GetConfigFileGroup(namespace, groupName)
    56  	if err != nil {
    57  		log.Error("[Config][Group] get config file group error.", utils.RequestID(ctx), zap.Error(err))
    58  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
    59  	}
    60  	if fileGroup != nil {
    61  		return api.NewConfigResponse(apimodel.Code_ExistedResource)
    62  	}
    63  
    64  	saveData := model.ToConfigGroupStore(req)
    65  	saveData.CreateBy = utils.ParseUserName(ctx)
    66  	saveData.ModifyBy = utils.ParseUserName(ctx)
    67  
    68  	ret, err := s.storage.CreateConfigFileGroup(saveData)
    69  	if err != nil {
    70  		log.Error("[Config][Group] create config file group error.", utils.RequestID(ctx),
    71  			utils.ZapNamespace(namespace), utils.ZapGroup(groupName), zap.Error(err))
    72  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
    73  	}
    74  
    75  	log.Info("[Config][Group] create config file group successful.", utils.RequestID(ctx),
    76  		utils.ZapNamespace(namespace), utils.ZapGroup(groupName))
    77  
    78  	// 这里设置在 config-group 的 id 信息
    79  	req.Id = utils.NewUInt64Value(ret.Id)
    80  	if err := s.afterConfigGroupResource(ctx, req); err != nil {
    81  		log.Error("[Config][Group] create config_file_group after resource",
    82  			utils.RequestID(ctx), zap.Error(err))
    83  		return api.NewConfigResponse(apimodel.Code_ExecuteException)
    84  	}
    85  
    86  	s.RecordHistory(ctx, configGroupRecordEntry(ctx, req, saveData, model.OCreate))
    87  	return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
    88  }
    89  
    90  // UpdateConfigFileGroup 更新配置文件组
    91  func (s *Server) UpdateConfigFileGroup(ctx context.Context, req *apiconfig.ConfigFileGroup) *apiconfig.ConfigResponse {
    92  	if resp := checkConfigFileGroupParams(req); resp != nil {
    93  		return resp
    94  	}
    95  
    96  	namespace := req.Namespace.GetValue()
    97  	groupName := req.Name.GetValue()
    98  
    99  	saveData, err := s.storage.GetConfigFileGroup(namespace, groupName)
   100  	if err != nil {
   101  		log.Error("[Config][Group] get config file group failed. ", utils.RequestID(ctx),
   102  			utils.ZapNamespace(namespace), utils.ZapGroup(groupName), zap.Error(err))
   103  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   104  	}
   105  	if saveData == nil {
   106  		return api.NewConfigResponse(apimodel.Code_NotFoundResource)
   107  	}
   108  
   109  	updateData := model.ToConfigGroupStore(req)
   110  	updateData.ModifyBy = utils.ParseOperator(ctx)
   111  	updateData, needUpdate := s.updateGroupAttribute(saveData, updateData)
   112  	if !needUpdate {
   113  		return api.NewConfigResponse(apimodel.Code_NoNeedUpdate)
   114  	}
   115  
   116  	if err := s.storage.UpdateConfigFileGroup(updateData); err != nil {
   117  		log.Error("[Config][Group] update config file group failed. ", utils.RequestID(ctx),
   118  			utils.ZapNamespace(namespace), utils.ZapGroup(groupName), zap.Error(err))
   119  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   120  	}
   121  
   122  	req.Id = utils.NewUInt64Value(saveData.Id)
   123  	if err := s.afterConfigGroupResource(ctx, req); err != nil {
   124  		log.Error("[Config][Group] update config_file_group after resource",
   125  			utils.RequestID(ctx), zap.Error(err))
   126  		return api.NewConfigResponse(apimodel.Code_ExecuteException)
   127  	}
   128  
   129  	s.RecordHistory(ctx, configGroupRecordEntry(ctx, req, updateData, model.OUpdate))
   130  	return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
   131  }
   132  
   133  func (s *Server) updateGroupAttribute(saveData, updateData *model.ConfigFileGroup) (*model.ConfigFileGroup, bool) {
   134  	needUpdate := false
   135  	if saveData.Comment != updateData.Comment {
   136  		needUpdate = true
   137  		saveData.Comment = updateData.Comment
   138  	}
   139  	if saveData.Business != updateData.Business {
   140  		needUpdate = true
   141  		saveData.Business = updateData.Business
   142  	}
   143  	if saveData.Department != updateData.Department {
   144  		needUpdate = true
   145  		saveData.Department = updateData.Department
   146  	}
   147  	if utils.IsNotEqualMap(updateData.Metadata, saveData.Metadata) {
   148  		needUpdate = true
   149  		saveData.Metadata = updateData.Metadata
   150  	}
   151  	return saveData, needUpdate
   152  }
   153  
   154  // createConfigFileGroupIfAbsent 如果不存在配置文件组,则自动创建
   155  func (s *Server) createConfigFileGroupIfAbsent(ctx context.Context,
   156  	configFileGroup *apiconfig.ConfigFileGroup) *apiconfig.ConfigResponse {
   157  	var (
   158  		namespace = configFileGroup.Namespace.GetValue()
   159  		name      = configFileGroup.Name.GetValue()
   160  	)
   161  
   162  	group, err := s.storage.GetConfigFileGroup(namespace, name)
   163  	if err != nil {
   164  		log.Error("[Config][Group] query config file group error.", utils.RequestID(ctx),
   165  			utils.ZapNamespace(namespace), utils.ZapGroup(name), zap.Error(err))
   166  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   167  	}
   168  	if group != nil {
   169  		return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
   170  	}
   171  	return s.CreateConfigFileGroup(ctx, configFileGroup)
   172  }
   173  
   174  // DeleteConfigFileGroup 删除配置文件组
   175  func (s *Server) DeleteConfigFileGroup(ctx context.Context, namespace, name string) *apiconfig.ConfigResponse {
   176  	if err := CheckResourceName(utils.NewStringValue(namespace)); err != nil {
   177  		return api.NewConfigResponse(apimodel.Code_InvalidNamespaceName)
   178  	}
   179  	if err := CheckResourceName(utils.NewStringValue(name)); err != nil {
   180  		return api.NewConfigResponse(apimodel.Code_InvalidConfigFileGroupName)
   181  	}
   182  
   183  	log.Info("[Config][Group] delete config file group. ", utils.RequestID(ctx),
   184  		utils.ZapNamespace(namespace), utils.ZapGroup(name))
   185  
   186  	configGroup, err := s.storage.GetConfigFileGroup(namespace, name)
   187  	if err != nil {
   188  		log.Error("[Config][Group] get config file group failed. ", utils.RequestID(ctx),
   189  			utils.ZapNamespace(namespace), utils.ZapGroup(name), zap.Error(err))
   190  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   191  	}
   192  	if configGroup == nil {
   193  		return api.NewConfigResponse(apimodel.Code_NotFoundResource)
   194  	}
   195  	if errResp := s.hasResourceInConfigGroup(ctx, namespace, name); errResp != nil {
   196  		return errResp
   197  	}
   198  
   199  	if err := s.storage.DeleteConfigFileGroup(namespace, name); err != nil {
   200  		log.Error("[Config][Group] delete config file group failed. ", utils.RequestID(ctx),
   201  			utils.ZapNamespace(namespace), utils.ZapGroup(name), zap.Error(err))
   202  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   203  	}
   204  
   205  	if err := s.afterConfigGroupResource(ctx, &apiconfig.ConfigFileGroup{
   206  		Id:        utils.NewUInt64Value(configGroup.Id),
   207  		Namespace: utils.NewStringValue(configGroup.Namespace),
   208  		Name:      utils.NewStringValue(configGroup.Name),
   209  	}); err != nil {
   210  		log.Error("[Config][Group] delete config_file_group after resource",
   211  			utils.RequestID(ctx), zap.Error(err))
   212  		return api.NewConfigResponse(apimodel.Code_ExecuteException)
   213  	}
   214  	s.RecordHistory(ctx, configGroupRecordEntry(ctx, &apiconfig.ConfigFileGroup{
   215  		Namespace: utils.NewStringValue(namespace),
   216  		Name:      utils.NewStringValue(name),
   217  	}, configGroup, model.ODelete))
   218  	return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
   219  }
   220  
   221  func (s *Server) hasResourceInConfigGroup(ctx context.Context, namespace, name string) *apiconfig.ConfigResponse {
   222  	total, err := s.storage.CountConfigFiles(namespace, name)
   223  	if err != nil {
   224  		log.Error("[Config][Group] get config file group failed. ", utils.RequestID(ctx),
   225  			utils.ZapNamespace(namespace), utils.ZapGroup(name), zap.Error(err))
   226  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   227  	}
   228  	if total != 0 {
   229  		return api.NewConfigResponse(apimodel.Code_ExistedResource)
   230  	}
   231  	total, err = s.storage.CountConfigReleases(namespace, name, true)
   232  	if err != nil {
   233  		log.Error("[Config][Group] get config file group failed. ", utils.RequestID(ctx),
   234  			utils.ZapNamespace(namespace), utils.ZapGroup(name), zap.Error(err))
   235  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   236  	}
   237  	if total != 0 {
   238  		return api.NewConfigResponse(apimodel.Code_ExistedResource)
   239  	}
   240  	return nil
   241  }
   242  
   243  // QueryConfigFileGroups 查询配置文件组
   244  func (s *Server) QueryConfigFileGroups(ctx context.Context,
   245  	filter map[string]string) *apiconfig.ConfigBatchQueryResponse {
   246  
   247  	offset, limit, err := utils.ParseOffsetAndLimit(filter)
   248  	if err != nil {
   249  		resp := api.NewConfigBatchQueryResponse(apimodel.Code_BadRequest)
   250  		resp.Info = utils.NewStringValue(err.Error())
   251  		return resp
   252  	}
   253  
   254  	searchFilters := map[string]string{}
   255  	for k, v := range filter {
   256  		if newK, ok := availableSearch["config_file_group"][k]; ok {
   257  			searchFilters[newK] = v
   258  		}
   259  	}
   260  
   261  	args := &cachetypes.ConfigGroupArgs{
   262  		Namespace:  searchFilters["namespace"],
   263  		Name:       searchFilters["name"],
   264  		Business:   searchFilters["business"],
   265  		Department: searchFilters["department"],
   266  		Offset:     offset,
   267  		Limit:      limit,
   268  		OrderField: searchFilters["order_field"],
   269  		OrderType:  searchFilters["order_type"],
   270  	}
   271  
   272  	total, ret, err := s.groupCache.Query(args)
   273  	if err != nil {
   274  		resp := api.NewConfigBatchQueryResponse(commonstore.StoreCode2APICode(err))
   275  		resp.Info = utils.NewStringValue(err.Error())
   276  		return resp
   277  	}
   278  	values := make([]*apiconfig.ConfigFileGroup, 0, len(ret))
   279  	for i := range ret {
   280  		item := model.ToConfigGroupAPI(ret[i])
   281  		fileCount, err := s.storage.CountConfigFiles(ret[i].Namespace, ret[i].Name)
   282  		if err != nil {
   283  			log.Error("[Config][Service] get config file count for group error.", utils.RequestID(ctx),
   284  				utils.ZapNamespace(ret[i].Namespace), utils.ZapGroup(ret[i].Name), zap.Error(err))
   285  		}
   286  		item.FileCount = wrapperspb.UInt64(fileCount)
   287  		values = append(values, item)
   288  	}
   289  
   290  	resp := api.NewConfigBatchQueryResponse(apimodel.Code_ExecuteSuccess)
   291  	resp.Total = utils.NewUInt32Value(total)
   292  	resp.ConfigFileGroups = values
   293  	return resp
   294  }
   295  
   296  func checkConfigFileGroupParams(configFileGroup *apiconfig.ConfigFileGroup) *apiconfig.ConfigResponse {
   297  	if configFileGroup == nil {
   298  		return api.NewConfigResponse(apimodel.Code_InvalidParameter)
   299  	}
   300  	if err := CheckResourceName(configFileGroup.Name); err != nil {
   301  		return api.NewConfigResponse(apimodel.Code_InvalidConfigFileGroupName)
   302  	}
   303  	if err := CheckResourceName(configFileGroup.Namespace); err != nil {
   304  		return api.NewConfigResponse(apimodel.Code_InvalidNamespaceName)
   305  	}
   306  	if len(configFileGroup.GetMetadata()) > utils.MaxMetadataLength {
   307  		return api.NewConfigResponse(apimodel.Code_InvalidMetadata)
   308  	}
   309  	return nil
   310  }
   311  
   312  // configGroupRecordEntry 生成服务的记录entry
   313  func configGroupRecordEntry(ctx context.Context, req *apiconfig.ConfigFileGroup, md *model.ConfigFileGroup,
   314  	operationType model.OperationType) *model.RecordEntry {
   315  
   316  	marshaler := jsonpb.Marshaler{}
   317  	detail, _ := marshaler.MarshalToString(req)
   318  
   319  	entry := &model.RecordEntry{
   320  		ResourceType:  model.RConfigGroup,
   321  		ResourceName:  req.GetName().GetValue(),
   322  		Namespace:     req.GetNamespace().GetValue(),
   323  		OperationType: operationType,
   324  		Operator:      utils.ParseOperator(ctx),
   325  		Detail:        detail,
   326  		HappenTime:    time.Now(),
   327  	}
   328  
   329  	return entry
   330  }