github.com/polarismesh/polaris@v1.17.8/config/config_file.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  	"strings"
    23  	"time"
    24  
    25  	"github.com/gogo/protobuf/jsonpb"
    26  	apiconfig "github.com/polarismesh/specification/source/go/api/v1/config_manage"
    27  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    28  	"go.uber.org/zap"
    29  	"google.golang.org/protobuf/types/known/wrapperspb"
    30  
    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  	"github.com/polarismesh/polaris/store"
    36  )
    37  
    38  // CreateConfigFile 创建配置文件
    39  func (s *Server) CreateConfigFile(ctx context.Context, req *apiconfig.ConfigFile) *apiconfig.ConfigResponse {
    40  	if rsp := s.prepareCreateConfigFile(ctx, req); rsp.Code.Value != api.ExecuteSuccess {
    41  		return rsp
    42  	}
    43  
    44  	tx, err := s.storage.StartTx()
    45  	if err != nil {
    46  		log.Error("[Config][File] create config file begin tx.", utils.RequestID(ctx), zap.Error(err))
    47  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
    48  	}
    49  	defer func() {
    50  		_ = tx.Rollback()
    51  	}()
    52  
    53  	resp := s.handleCreateConfigFile(ctx, tx, req)
    54  	if resp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) {
    55  		return resp
    56  	}
    57  	if err := tx.Commit(); err != nil {
    58  		log.Error("[Config][File] create config file commit tx.", utils.RequestID(ctx), zap.Error(err))
    59  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
    60  	}
    61  	resp.ConfigFile = req
    62  	return resp
    63  }
    64  
    65  func (s *Server) handleCreateConfigFile(ctx context.Context, tx store.Tx,
    66  	req *apiconfig.ConfigFile) *apiconfig.ConfigResponse {
    67  
    68  	data, err := s.storage.GetConfigFileTx(tx, req.GetNamespace().GetValue(), req.GetGroup().GetValue(),
    69  		req.GetName().GetValue())
    70  	if err != nil {
    71  		log.Error("[Config][File] create config file when get save data.", utils.RequestID(ctx),
    72  			utils.ZapNamespace(req.GetNamespace().GetValue()), utils.ZapGroup(req.GetGroup().GetValue()),
    73  			utils.ZapFileName(req.GetName().GetValue()), zap.Error(err))
    74  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
    75  	}
    76  	if data != nil {
    77  		return api.NewConfigResponse(apimodel.Code_ExistedResource)
    78  	}
    79  
    80  	savaData := model.ToConfigFileStore(req)
    81  	if errResp := s.chains.BeforeCreateFile(ctx, savaData); errResp != nil {
    82  		return errResp
    83  	}
    84  	// 创建配置文件
    85  	if err := s.storage.CreateConfigFileTx(tx, savaData); err != nil {
    86  		log.Error("[Config][File] create config file error.", utils.RequestID(ctx),
    87  			utils.ZapNamespace(req.GetNamespace().GetValue()), utils.ZapGroup(req.GetGroup().GetValue()),
    88  			utils.ZapFileName(req.GetName().GetValue()), zap.Error(err))
    89  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
    90  	}
    91  	s.RecordHistory(ctx, configFileRecordEntry(ctx, req, model.OCreate))
    92  	return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
    93  }
    94  
    95  // UpdateConfigFile 更新配置文件
    96  func (s *Server) UpdateConfigFile(ctx context.Context, req *apiconfig.ConfigFile) *apiconfig.ConfigResponse {
    97  	if checkRsp := s.checkConfigFileParams(req); checkRsp != nil {
    98  		return checkRsp
    99  	}
   100  	tx, err := s.storage.StartTx()
   101  	if err != nil {
   102  		log.Error("[Config][File] update config file begin tx.", utils.RequestID(ctx), zap.Error(err))
   103  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   104  	}
   105  	defer func() {
   106  		_ = tx.Rollback()
   107  	}()
   108  
   109  	resp := s.handleUpdateConfigFile(ctx, tx, req)
   110  	if resp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) {
   111  		return resp
   112  	}
   113  	if err := tx.Commit(); err != nil {
   114  		log.Error("[Config][File] update config file commit tx.", utils.RequestID(ctx), zap.Error(err))
   115  		return api.NewConfigResponseWithInfo(commonstore.StoreCode2APICode(err), err.Error())
   116  	}
   117  	s.RecordHistory(ctx, configFileRecordEntry(ctx, req, model.OUpdate))
   118  	return resp
   119  }
   120  
   121  func (s *Server) handleUpdateConfigFile(ctx context.Context, tx store.Tx,
   122  	req *apiconfig.ConfigFile) *apiconfig.ConfigResponse {
   123  
   124  	namespace := req.Namespace.GetValue()
   125  	group := req.Group.GetValue()
   126  	name := req.Name.GetValue()
   127  
   128  	saveData, err := s.storage.GetConfigFileTx(tx, req.GetNamespace().GetValue(), req.GetGroup().GetValue(),
   129  		req.GetName().GetValue())
   130  	if err != nil {
   131  		log.Error("[Config][File] update config file when get save data.", utils.RequestID(ctx), zap.Error(err))
   132  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   133  	}
   134  	if saveData == nil {
   135  		return api.NewConfigResponse(apimodel.Code_NotFoundResource)
   136  	}
   137  	updateData, needUpdate := s.updateConfigFileAttribute(saveData, model.ToConfigFileStore(req))
   138  	if !needUpdate {
   139  		return api.NewConfigResponse(apimodel.Code_NoNeedUpdate)
   140  	}
   141  
   142  	if errResp := s.chains.BeforeUpdateFile(ctx, updateData); errResp != nil {
   143  		return errResp
   144  	}
   145  
   146  	if err := s.storage.UpdateConfigFileTx(tx, updateData); err != nil {
   147  		log.Error("[Config][File] update config file error.", utils.RequestID(ctx),
   148  			utils.ZapNamespace(namespace), utils.ZapGroup(group), utils.ZapFileName(name), zap.Error(err))
   149  		return api.NewConfigFileResponse(commonstore.StoreCode2APICode(err), req)
   150  	}
   151  	return api.NewConfigFileResponse(apimodel.Code_ExecuteSuccess, model.ToConfigFileAPI(updateData))
   152  }
   153  
   154  func (s *Server) updateConfigFileAttribute(saveData, updateData *model.ConfigFile) (*model.ConfigFile, bool) {
   155  	needUpdate := false
   156  	oldMetadata := saveData.Metadata
   157  	oldEncrtptAlgo := saveData.EncryptAlgo
   158  	if saveData.Comment != updateData.Comment {
   159  		needUpdate = true
   160  		saveData.Comment = updateData.Comment
   161  	}
   162  	if saveData.Comment != updateData.Content {
   163  		needUpdate = true
   164  		saveData.Content = updateData.Content
   165  	}
   166  	if saveData.Format != updateData.Format {
   167  		needUpdate = true
   168  		saveData.Format = updateData.Format
   169  	}
   170  	if utils.IsNotEqualMap(updateData.Metadata, saveData.Metadata) {
   171  		needUpdate = true
   172  		saveData.Metadata = updateData.Metadata
   173  	}
   174  	if saveData.Encrypt != updateData.Encrypt {
   175  		needUpdate = true
   176  		saveData.Encrypt = updateData.Encrypt
   177  	}
   178  	if saveData.EncryptAlgo != updateData.EncryptAlgo {
   179  		needUpdate = true
   180  		saveData.EncryptAlgo = updateData.EncryptAlgo
   181  	}
   182  	// 填充加密所需要的 Metadata Key 数据
   183  	if saveData.Encrypt && saveData.EncryptAlgo == oldEncrtptAlgo {
   184  		if len(saveData.Metadata) == 0 {
   185  			saveData.Metadata = map[string]string{}
   186  		}
   187  		saveData.Metadata[utils.ConfigFileTagKeyDataKey] = oldMetadata[utils.ConfigFileTagKeyDataKey]
   188  		saveData.Metadata[utils.ConfigFileTagKeyEncryptAlgo] = oldMetadata[utils.ConfigFileTagKeyEncryptAlgo]
   189  	}
   190  
   191  	return saveData, needUpdate
   192  }
   193  
   194  func (s *Server) prepareCreateConfigFile(ctx context.Context,
   195  	configFile *apiconfig.ConfigFile) *apiconfig.ConfigResponse {
   196  
   197  	configFile.CreateBy = utils.NewStringValue(utils.ParseUserName(ctx))
   198  	configFile.ModifyBy = utils.NewStringValue(utils.ParseUserName(ctx))
   199  
   200  	// 如果配置文件组不存在则自动创建
   201  	createGroupRsp := s.createConfigFileGroupIfAbsent(ctx, &apiconfig.ConfigFileGroup{
   202  		Namespace: configFile.Namespace,
   203  		Name:      configFile.Group,
   204  		CreateBy:  configFile.CreateBy,
   205  		Comment:   utils.NewStringValue("auto created"),
   206  	})
   207  
   208  	if createGroupRsp.Code.GetValue() != uint32(apimodel.Code_ExecuteSuccess) {
   209  		return api.NewConfigFileResponse(apimodel.Code(createGroupRsp.Code.GetValue()), configFile)
   210  	}
   211  	return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
   212  }
   213  
   214  // BatchDeleteConfigFile 批量删除配置文件
   215  func (s *Server) BatchDeleteConfigFile(ctx context.Context, req []*apiconfig.ConfigFile) *apiconfig.ConfigResponse {
   216  	if len(req) == 0 {
   217  		api.NewConfigFileResponse(apimodel.Code_ExecuteSuccess, nil)
   218  	}
   219  	for _, configFile := range req {
   220  		rsp := s.DeleteConfigFile(ctx, configFile)
   221  		if rsp.Code.GetValue() != uint32(apimodel.Code_ExecuteSuccess) {
   222  			return rsp
   223  		}
   224  	}
   225  	return api.NewConfigFileResponse(apimodel.Code_ExecuteSuccess, nil)
   226  }
   227  
   228  // DeleteConfigFile 删除配置文件,删除配置文件同时会通知客户端 Not_Found
   229  func (s *Server) DeleteConfigFile(ctx context.Context, req *apiconfig.ConfigFile) *apiconfig.ConfigResponse {
   230  	if errResp := checkReadFileParameter(req); errResp != nil {
   231  		return errResp
   232  	}
   233  
   234  	namespace := req.GetNamespace().GetValue()
   235  	group := req.GetGroup().GetValue()
   236  	fileName := req.GetName().GetValue()
   237  
   238  	tx, err := s.storage.StartTx()
   239  	if err != nil {
   240  		log.Error("[Config][File] delete config file begin tx.", utils.RequestID(ctx), zap.Error(err))
   241  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   242  	}
   243  	defer func() { _ = tx.Rollback() }()
   244  
   245  	file, err := s.storage.LockConfigFile(tx, &model.ConfigFileKey{
   246  		Namespace: namespace,
   247  		Group:     group,
   248  		Name:      fileName,
   249  	})
   250  	if err != nil {
   251  		log.Error("[Config][File] delete config file when lock.", utils.RequestID(ctx),
   252  			utils.ZapNamespace(namespace), utils.ZapGroup(group), utils.ZapFileName(fileName), zap.Error(err))
   253  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   254  	}
   255  	if file == nil {
   256  		return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
   257  	}
   258  	// 1. 删除配置文件发布内容
   259  	if errResp := s.cleanConfigFileReleases(ctx, tx, file); errResp != nil {
   260  		return errResp
   261  	}
   262  	// 2. 删除配置文件
   263  	if err = s.storage.DeleteConfigFileTx(tx, namespace, group, fileName); err != nil {
   264  		log.Error("[Config][File] delete config file error.", utils.RequestID(ctx),
   265  			utils.ZapNamespace(namespace), utils.ZapGroup(group), utils.ZapFileName(fileName), zap.Error(err))
   266  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   267  	}
   268  	if err := tx.Commit(); err != nil {
   269  		log.Error("[Config][File] delete config file when commit tx.", utils.RequestID(ctx),
   270  			utils.ZapNamespace(namespace), utils.ZapGroup(group), utils.ZapFileName(fileName), zap.Error(err))
   271  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   272  	}
   273  	s.RecordHistory(ctx, configFileRecordEntry(ctx, &apiconfig.ConfigFile{
   274  		Namespace: utils.NewStringValue(namespace),
   275  		Group:     utils.NewStringValue(group),
   276  		Name:      utils.NewStringValue(fileName),
   277  	}, model.ODelete))
   278  	return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
   279  }
   280  
   281  // GetConfigFileRichInfo 获取单个配置文件基础信息,包含发布状态等信息
   282  func (s *Server) GetConfigFileRichInfo(ctx context.Context, req *apiconfig.ConfigFile) *apiconfig.ConfigResponse {
   283  	if errResp := checkReadFileParameter(req); errResp != nil {
   284  		return errResp
   285  	}
   286  
   287  	namespace := req.GetNamespace().GetValue()
   288  	group := req.GetGroup().GetValue()
   289  	fileName := req.GetName().GetValue()
   290  
   291  	file, err := s.storage.GetConfigFile(namespace, group, fileName)
   292  	if err != nil {
   293  		log.Error("[Config][File] get config file error.", utils.RequestID(ctx),
   294  			utils.ZapNamespace(namespace), utils.ZapGroup(group), utils.ZapFileName(fileName), zap.Error(err))
   295  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   296  	}
   297  	if file == nil {
   298  		return api.NewConfigResponse(apimodel.Code_NotFoundResource)
   299  	}
   300  
   301  	// 填充发布信息、标签信息等
   302  	richFile, err := s.chains.AfterGetFile(ctx, file)
   303  	if err != nil {
   304  		return api.NewConfigResponseWithInfo(apimodel.Code_ExecuteException, err.Error())
   305  	}
   306  	ret := model.ToConfigFileAPI(richFile)
   307  	return api.NewConfigFileResponse(apimodel.Code_ExecuteSuccess, ret)
   308  }
   309  
   310  // SearchConfigFile 查询配置文件
   311  func (s *Server) SearchConfigFile(ctx context.Context, filter map[string]string) *apiconfig.ConfigBatchQueryResponse {
   312  	offset, limit, err := utils.ParseOffsetAndLimit(filter)
   313  	if err != nil {
   314  		out := api.NewConfigBatchQueryResponse(apimodel.Code_BadRequest)
   315  		out.Info = utils.NewStringValue(err.Error())
   316  		return out
   317  	}
   318  	searchFilters := map[string]string{}
   319  	for k, v := range filter {
   320  		// 无效查询参数自动忽略
   321  		if v == "" {
   322  			continue
   323  		}
   324  		if _, ok := availableSearch["config_file"][k]; ok {
   325  			searchFilters[k] = v
   326  		}
   327  	}
   328  	count, files, err := s.storage.QueryConfigFiles(searchFilters, offset, limit)
   329  	if err != nil {
   330  		log.Error("[Config][File] search config files.", utils.RequestID(ctx), zap.Error(err))
   331  		out := api.NewConfigBatchQueryResponse(commonstore.StoreCode2APICode(err))
   332  		return out
   333  	}
   334  
   335  	if len(files) == 0 {
   336  		out := api.NewConfigBatchQueryResponse(apimodel.Code_ExecuteSuccess)
   337  		out.Total = utils.NewUInt32Value(count)
   338  		return out
   339  	}
   340  
   341  	ret := make([]*apiconfig.ConfigFile, 0, len(files))
   342  	for _, file := range files {
   343  		file, err := s.chains.AfterGetFile(ctx, file)
   344  		if err != nil {
   345  			log.Error("[Config][File] search files run chain after-get file.", utils.RequestID(ctx),
   346  				zap.Error(err))
   347  			return api.NewConfigBatchQueryResponse(apimodel.Code_ExecuteException)
   348  		}
   349  		ret = append(ret, model.ToConfigFileAPI(file))
   350  	}
   351  	out := api.NewConfigBatchQueryResponse(apimodel.Code_ExecuteSuccess)
   352  	out.Total = utils.NewUInt32Value(count)
   353  	out.ConfigFiles = ret
   354  	return out
   355  }
   356  
   357  // ExportConfigFile 导出配置文件
   358  func (s *Server) ExportConfigFile(ctx context.Context,
   359  	configFileExport *apiconfig.ConfigFileExportRequest) *apiconfig.ConfigExportResponse {
   360  	namespace := configFileExport.Namespace.GetValue()
   361  	var groups []string
   362  	for _, group := range configFileExport.Groups {
   363  		groups = append(groups, group.GetValue())
   364  	}
   365  	var names []string
   366  	for _, name := range configFileExport.Names {
   367  		names = append(names, name.GetValue())
   368  	}
   369  	// 检查参数
   370  	if err := CheckResourceName(configFileExport.Namespace); err != nil {
   371  		return api.NewConfigFileExportResponse(apimodel.Code_InvalidNamespaceName, nil)
   372  	}
   373  	var (
   374  		isExportGroup bool
   375  		configFiles   []*model.ConfigFile
   376  	)
   377  	if len(groups) >= 1 && len(names) == 0 {
   378  		// 导出配置组
   379  		isExportGroup = true
   380  		for _, group := range groups {
   381  			files, err := s.getGroupAllConfigFiles(namespace, group)
   382  			if err != nil {
   383  				log.Error("[Config][File] get config file by group error.", utils.RequestID(ctx),
   384  					utils.ZapNamespace(namespace), utils.ZapGroup(group), zap.Error(err))
   385  				return api.NewConfigFileExportResponse(commonstore.StoreCode2APICode(err), nil)
   386  			}
   387  			configFiles = append(configFiles, files...)
   388  		}
   389  	} else if len(groups) == 1 && len(names) > 0 {
   390  		// 导出配置文件
   391  		for _, name := range names {
   392  			file, err := s.storage.GetConfigFile(namespace, groups[0], name)
   393  			if err != nil {
   394  				log.Error("[Config][File] get config file error.", utils.RequestID(ctx),
   395  					utils.ZapNamespace(namespace), utils.ZapGroup(groups[0]), utils.ZapFileName(name),
   396  					zap.Error(err))
   397  				return api.NewConfigFileExportResponse(commonstore.StoreCode2APICode(err), nil)
   398  			}
   399  			configFiles = append(configFiles, file)
   400  		}
   401  	} else {
   402  		log.Error("[Config][File] export config file error.", utils.RequestID(ctx),
   403  			utils.ZapNamespace(namespace), zap.String("groups", strings.Join(groups, ",")),
   404  			zap.String("names", strings.Join(names, ",")))
   405  		return api.NewConfigFileExportResponse(apimodel.Code_InvalidParameter, nil)
   406  	}
   407  	if len(configFiles) == 0 {
   408  		return api.NewConfigFileExportResponse(apimodel.Code_NotFoundResourceConfigFile, nil)
   409  	}
   410  	// 查询配置文件的标签
   411  	fileID2Tags := make(map[uint64][]*model.ConfigFileTag)
   412  	for _, file := range configFiles {
   413  		filterTags := make([]*model.ConfigFileTag, 0, len(file.Metadata))
   414  		for tagKey, tagVal := range file.Metadata {
   415  			filterTags = append(filterTags, &model.ConfigFileTag{
   416  				Key:   tagKey,
   417  				Value: tagVal,
   418  			})
   419  		}
   420  		fileID2Tags[file.Id] = filterTags
   421  	}
   422  	// 生成ZIP文件
   423  	buf, err := CompressConfigFiles(configFiles, fileID2Tags, isExportGroup)
   424  	if err != nil {
   425  		log.Error("[Config][Servie]export config files compress to zip error.", zap.Error(err))
   426  	}
   427  	return api.NewConfigFileExportResponse(apimodel.Code_ExecuteSuccess, buf.Bytes())
   428  }
   429  
   430  // ImportConfigFile 导入配置文件
   431  func (s *Server) ImportConfigFile(ctx context.Context,
   432  	configFiles []*apiconfig.ConfigFile, conflictHandling string) *apiconfig.ConfigImportResponse {
   433  	// 预创建命名空间和分组
   434  	for _, configFile := range configFiles {
   435  		if checkRsp := s.checkConfigFileParams(configFile); checkRsp != nil {
   436  			return api.NewConfigFileImportResponse(apimodel.Code(checkRsp.Code.GetValue()), nil, nil, nil)
   437  		}
   438  		if rsp := s.prepareCreateConfigFile(ctx, configFile); rsp.Code.Value != api.ExecuteSuccess {
   439  			return api.NewConfigFileImportResponse(apimodel.Code(rsp.Code.GetValue()), nil, nil, nil)
   440  		}
   441  	}
   442  
   443  	// 开启事务
   444  	tx, err := s.storage.StartTx()
   445  	if err != nil {
   446  		return api.NewConfigFileImportResponse(commonstore.StoreCode2APICode(err), nil, nil, nil)
   447  	}
   448  	defer func() { _ = tx.Rollback() }()
   449  
   450  	// 记录创建,跳过,覆盖的配置文件
   451  	var (
   452  		createConfigFiles    []*apiconfig.ConfigFile
   453  		skipConfigFiles      []*apiconfig.ConfigFile
   454  		overwriteConfigFiles []*apiconfig.ConfigFile
   455  	)
   456  	for _, configFile := range configFiles {
   457  		namespace := configFile.Namespace.GetValue()
   458  		group := configFile.Group.GetValue()
   459  		name := configFile.Name.GetValue()
   460  
   461  		managedFile, err := s.storage.GetConfigFileTx(tx, namespace, group, name)
   462  		if err != nil {
   463  			log.Error("[Config][File] get config file error.", utils.RequestID(ctx),
   464  				utils.ZapNamespace(namespace), utils.ZapGroup(group), utils.ZapFileName(name), zap.Error(err))
   465  			return api.NewConfigFileImportResponse(commonstore.StoreCode2APICode(err), nil, nil, nil)
   466  		}
   467  		// 如果配置文件存在
   468  		if managedFile != nil {
   469  			if conflictHandling == utils.ConfigFileImportConflictSkip {
   470  				skipConfigFiles = append(skipConfigFiles, configFile)
   471  				continue
   472  			} else if conflictHandling == utils.ConfigFileImportConflictOverwrite {
   473  				resp := s.handleUpdateConfigFile(ctx, tx, configFile)
   474  				if resp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) {
   475  					log.Error("[Config][File] update config file error.", utils.RequestID(ctx),
   476  						utils.ZapNamespace(namespace), utils.ZapGroup(group), utils.ZapFileName(name), zap.Error(err))
   477  					return api.NewConfigFileImportResponse(commonstore.StoreCode2APICode(err), nil, nil, nil)
   478  				}
   479  				overwriteConfigFiles = append(overwriteConfigFiles, configFile)
   480  				s.RecordHistory(ctx, configFileRecordEntry(ctx, configFile, model.OUpdate))
   481  			}
   482  		} else {
   483  			// 配置文件不存在则创建
   484  			resp := s.handleCreateConfigFile(ctx, tx, configFile)
   485  			if resp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) {
   486  				log.Error("[Config][File] create config file error.", utils.RequestID(ctx),
   487  					utils.ZapNamespace(namespace), utils.ZapGroup(group), utils.ZapFileName(name), zap.Error(err))
   488  				return api.NewConfigFileImportResponse(commonstore.StoreCode2APICode(err), nil, nil, nil)
   489  			}
   490  			createConfigFiles = append(createConfigFiles, configFile)
   491  			s.RecordHistory(ctx, configFileRecordEntry(ctx, configFile, model.OCreate))
   492  		}
   493  	}
   494  
   495  	if err := tx.Commit(); err != nil {
   496  		log.Error("[Config][File] commit import config file tx error.", utils.RequestID(ctx), zap.Error(err))
   497  		return api.NewConfigFileImportResponse(commonstore.StoreCode2APICode(err), nil, nil, nil)
   498  	}
   499  
   500  	return api.NewConfigFileImportResponse(apimodel.Code_ExecuteSuccess,
   501  		createConfigFiles, skipConfigFiles, overwriteConfigFiles)
   502  }
   503  
   504  func (s *Server) getGroupAllConfigFiles(namespace, group string) ([]*model.ConfigFile, error) {
   505  	var configFiles []*model.ConfigFile
   506  	offset := uint32(0)
   507  	limit := uint32(100)
   508  	for {
   509  		_, files, err := s.storage.QueryConfigFiles(map[string]string{
   510  			"namespace": namespace,
   511  			"group":     group,
   512  		}, offset, limit)
   513  		if err != nil {
   514  			return nil, err
   515  		}
   516  		if len(files) == 0 {
   517  			break
   518  		}
   519  		configFiles = append(configFiles, files...)
   520  		offset += uint32(len(files))
   521  	}
   522  	return configFiles, nil
   523  }
   524  
   525  func (s *Server) checkConfigFileParams(configFile *apiconfig.ConfigFile) *apiconfig.ConfigResponse {
   526  	if configFile == nil {
   527  		return api.NewConfigFileResponse(apimodel.Code_InvalidParameter, configFile)
   528  	}
   529  	if err := CheckFileName(configFile.Name); err != nil {
   530  		return api.NewConfigFileResponse(apimodel.Code_InvalidConfigFileName, configFile)
   531  	}
   532  	if err := CheckResourceName(configFile.Namespace); err != nil {
   533  		return api.NewConfigFileResponse(apimodel.Code_InvalidNamespaceName, configFile)
   534  	}
   535  	if err := CheckContentLength(configFile.Content.GetValue(), int(s.cfg.ContentMaxLength)); err != nil {
   536  		return api.NewConfigResponseWithInfo(apimodel.Code_InvalidConfigFileContentLength, err.Error())
   537  	}
   538  	if len(configFile.Tags) > 0 {
   539  		for _, tag := range configFile.Tags {
   540  			if tag.Key.GetValue() == "" || tag.Value.GetValue() == "" {
   541  				return api.NewConfigFileResponse(apimodel.Code_InvalidConfigFileTags, configFile)
   542  			}
   543  		}
   544  	}
   545  	return nil
   546  }
   547  
   548  // GetAllConfigEncryptAlgorithms 获取配置加密算法
   549  func (s *Server) GetAllConfigEncryptAlgorithms(ctx context.Context) *apiconfig.ConfigEncryptAlgorithmResponse {
   550  	if s.cryptoManager == nil {
   551  		return api.NewConfigEncryptAlgorithmResponse(apimodel.Code_ExecuteSuccess, nil)
   552  	}
   553  	var algorithms []*wrapperspb.StringValue
   554  	for _, name := range s.cryptoManager.GetCryptoAlgoNames() {
   555  		algorithms = append(algorithms, utils.NewStringValue(name))
   556  	}
   557  	return api.NewConfigEncryptAlgorithmResponse(apimodel.Code_ExecuteSuccess, algorithms)
   558  }
   559  
   560  // configFileRecordEntry 生成服务的记录entry
   561  func configFileRecordEntry(ctx context.Context, req *apiconfig.ConfigFile,
   562  	operationType model.OperationType) *model.RecordEntry {
   563  
   564  	marshaler := jsonpb.Marshaler{}
   565  	detail, _ := marshaler.MarshalToString(req)
   566  
   567  	entry := &model.RecordEntry{
   568  		ResourceType:  model.RConfigFile,
   569  		ResourceName:  req.GetName().GetValue(),
   570  		Namespace:     req.GetNamespace().GetValue(),
   571  		OperationType: operationType,
   572  		Operator:      utils.ParseOperator(ctx),
   573  		Detail:        detail,
   574  		HappenTime:    time.Now(),
   575  	}
   576  
   577  	return entry
   578  }
   579  
   580  func checkReadFileParameter(req *apiconfig.ConfigFile) *apiconfig.ConfigResponse {
   581  	if req.GetNamespace().GetValue() == "" {
   582  		return api.NewConfigResponse(apimodel.Code_InvalidNamespaceName)
   583  	}
   584  	if req.GetGroup().GetValue() == "" {
   585  		return api.NewConfigResponse(apimodel.Code_InvalidConfigFileGroupName)
   586  	}
   587  	if req.GetName().GetValue() == "" {
   588  		return api.NewConfigResponse(apimodel.Code_InvalidConfigFileName)
   589  	}
   590  	return nil
   591  }