github.com/polarismesh/polaris@v1.17.8/config/config_file_release.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  	"errors"
    23  	"fmt"
    24  	"strings"
    25  	"sync/atomic"
    26  	"time"
    27  
    28  	"github.com/gogo/protobuf/jsonpb"
    29  	apiconfig "github.com/polarismesh/specification/source/go/api/v1/config_manage"
    30  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    31  	"go.uber.org/zap"
    32  
    33  	cachetypes "github.com/polarismesh/polaris/cache/api"
    34  	api "github.com/polarismesh/polaris/common/api/v1"
    35  	"github.com/polarismesh/polaris/common/model"
    36  	commonstore "github.com/polarismesh/polaris/common/store"
    37  	commontime "github.com/polarismesh/polaris/common/time"
    38  	"github.com/polarismesh/polaris/common/utils"
    39  	"github.com/polarismesh/polaris/store"
    40  )
    41  
    42  // PublishConfigFile 发布配置文件
    43  func (s *Server) PublishConfigFile(ctx context.Context, req *apiconfig.ConfigFileRelease) *apiconfig.ConfigResponse {
    44  
    45  	if err := CheckFileName(req.GetFileName()); err != nil {
    46  		return api.NewConfigResponse(apimodel.Code_InvalidConfigFileName)
    47  	}
    48  	if err := CheckResourceName(req.GetNamespace()); err != nil {
    49  		return api.NewConfigResponse(apimodel.Code_InvalidNamespaceName)
    50  	}
    51  	if err := CheckResourceName(req.GetGroup()); err != nil {
    52  		return api.NewConfigResponse(apimodel.Code_InvalidConfigFileGroupName)
    53  	}
    54  	if !s.checkNamespaceExisted(req.GetNamespace().GetValue()) {
    55  		return api.NewConfigResponse(apimodel.Code_NotFoundNamespace)
    56  	}
    57  
    58  	tx, err := s.storage.StartTx()
    59  	if err != nil {
    60  		log.Error("[Config][Release] publish config file begin tx.", utils.RequestID(ctx), zap.Error(err))
    61  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
    62  	}
    63  	defer func() {
    64  		_ = tx.Rollback()
    65  	}()
    66  
    67  	data, resp := s.handlePublishConfigFile(ctx, tx, req)
    68  	if resp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) {
    69  		_ = tx.Rollback()
    70  		if data != nil {
    71  			s.recordReleaseFail(ctx, utils.ReleaseTypeNormal, data, errors.New(resp.GetInfo().GetValue()))
    72  		}
    73  		return resp
    74  	}
    75  
    76  	if err := tx.Commit(); err != nil {
    77  		s.recordReleaseFail(ctx, utils.ReleaseTypeNormal, data, err)
    78  		log.Error("[Config][Release] publish config file commit tx.", utils.RequestID(ctx), zap.Error(err))
    79  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
    80  	}
    81  	s.recordReleaseSuccess(ctx, utils.ReleaseTypeNormal, data)
    82  	resp.ConfigFileRelease = req
    83  	return resp
    84  }
    85  
    86  func (s *Server) nextSequence() int64 {
    87  	return atomic.AddInt64(&s.sequence, 1)
    88  }
    89  
    90  // PublishConfigFile 发布配置文件
    91  func (s *Server) handlePublishConfigFile(ctx context.Context, tx store.Tx,
    92  	req *apiconfig.ConfigFileRelease) (*model.ConfigFileRelease, *apiconfig.ConfigResponse) {
    93  	namespace := req.GetNamespace().GetValue()
    94  	group := req.GetGroup().GetValue()
    95  	fileName := req.GetFileName().GetValue()
    96  
    97  	// 获取待发布的 configFile 信息
    98  	toPublishFile, err := s.storage.GetConfigFileTx(tx, namespace, group, fileName)
    99  	if err != nil {
   100  		log.Error("[Config][Release] publish config file when get file.", utils.RequestID(ctx),
   101  			utils.ZapNamespace(namespace), utils.ZapGroup(group), utils.ZapFileName(fileName),
   102  			zap.Error(err))
   103  		s.recordReleaseFail(ctx, utils.ReleaseTypeNormal, model.ToConfigFileReleaseStore(req), err)
   104  		return nil, api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   105  	}
   106  	if toPublishFile == nil {
   107  		return nil, api.NewConfigResponse(apimodel.Code_NotFoundResource)
   108  	}
   109  	if releaseName := req.GetName().GetValue(); releaseName == "" {
   110  		// 这里要保证每一次发布都有唯一的 release_name 名称
   111  		req.Name = utils.NewStringValue(fmt.Sprintf("%s-%d-%d", fileName, time.Now().Unix(), s.nextSequence()))
   112  	}
   113  
   114  	fileRelease := &model.ConfigFileRelease{
   115  		SimpleConfigFileRelease: &model.SimpleConfigFileRelease{
   116  			ConfigFileReleaseKey: &model.ConfigFileReleaseKey{
   117  				Name:      req.GetName().GetValue(),
   118  				Namespace: namespace,
   119  				Group:     group,
   120  				FileName:  fileName,
   121  			},
   122  			Format:             toPublishFile.Format,
   123  			Metadata:           toPublishFile.Metadata,
   124  			Comment:            req.GetComment().GetValue(),
   125  			Md5:                CalMd5(toPublishFile.Content),
   126  			CreateBy:           utils.ParseUserName(ctx),
   127  			ModifyBy:           utils.ParseUserName(ctx),
   128  			ReleaseDescription: req.GetReleaseDescription().GetValue(),
   129  		},
   130  		Content: toPublishFile.Content,
   131  	}
   132  	saveRelease, err := s.storage.GetConfigFileReleaseTx(tx, fileRelease.ConfigFileReleaseKey)
   133  	if err != nil {
   134  		log.Error("[Config][Release] publish config file when get release.",
   135  			utils.RequestID(ctx), utils.ZapNamespace(namespace), utils.ZapGroup(group),
   136  			utils.ZapFileName(fileName), zap.Error(err))
   137  		return fileRelease, api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   138  	}
   139  	// 重新激活
   140  	if saveRelease != nil {
   141  		if err := s.storage.ActiveConfigFileReleaseTx(tx, fileRelease); err != nil {
   142  			log.Error("[Config][Release] re-active config file release error.",
   143  				utils.RequestID(ctx), utils.ZapNamespace(namespace), utils.ZapGroup(group),
   144  				utils.ZapFileName(fileName), zap.Error(err))
   145  			return fileRelease, api.NewConfigFileResponse(commonstore.StoreCode2APICode(err), nil)
   146  		}
   147  	} else {
   148  		if err := s.storage.CreateConfigFileReleaseTx(tx, fileRelease); err != nil {
   149  			log.Error("[Config][Release] publish config file when create release.",
   150  				utils.RequestID(ctx), utils.ZapNamespace(namespace), utils.ZapGroup(group),
   151  				utils.ZapFileName(fileName), zap.Error(err))
   152  			return fileRelease, api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   153  		}
   154  	}
   155  
   156  	s.RecordHistory(ctx, configFileReleaseRecordEntry(ctx, req, fileRelease, model.OCreate))
   157  	return fileRelease, api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
   158  }
   159  
   160  // GetConfigFileRelease 获取配置文件发布内容
   161  func (s *Server) GetConfigFileRelease(ctx context.Context, req *apiconfig.ConfigFileRelease) *apiconfig.ConfigResponse {
   162  	namespace := req.GetNamespace().GetValue()
   163  	group := req.GetGroup().GetValue()
   164  	fileName := req.GetFileName().GetValue()
   165  	releaseName := req.GetName().GetValue()
   166  
   167  	if errCode, errMsg := checkBaseReleaseParam(req, false); errCode != apimodel.Code_ExecuteSuccess {
   168  		return api.NewConfigResponseWithInfo(errCode, errMsg)
   169  	}
   170  
   171  	var (
   172  		ret *model.ConfigFileRelease
   173  		err error
   174  	)
   175  
   176  	// 如果没有指定专门的 releaseName,则直接查询 active 状态的配置发布, 兼容老的控制台查询逻辑
   177  	if releaseName != "" {
   178  		ret, err = s.storage.GetConfigFileRelease(&model.ConfigFileReleaseKey{
   179  			Namespace: namespace,
   180  			Group:     group,
   181  			FileName:  fileName,
   182  			Name:      releaseName,
   183  		})
   184  	} else {
   185  		ret, err = s.storage.GetConfigFileActiveRelease(&model.ConfigFileKey{
   186  			Namespace: namespace,
   187  			Group:     group,
   188  			Name:      fileName,
   189  		})
   190  	}
   191  
   192  	if err != nil {
   193  		log.Error("[Config][Release] get config file release.", utils.RequestID(ctx),
   194  			utils.ZapNamespace(namespace), utils.ZapGroup(group), utils.ZapFileName(fileName), zap.Error(err))
   195  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   196  	}
   197  	if ret == nil {
   198  		return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
   199  	}
   200  	ret, err = s.chains.AfterGetFileRelease(ctx, ret)
   201  	if err != nil {
   202  		log.Error("[Config][Release] get config file release run chain.", utils.RequestID(ctx),
   203  			utils.ZapNamespace(namespace), utils.ZapGroup(group), utils.ZapFileName(fileName), zap.Error(err))
   204  		out := api.NewConfigResponse(apimodel.Code_ExecuteException)
   205  		return out
   206  	}
   207  	release := model.ToConfiogFileReleaseApi(ret)
   208  	return api.NewConfigFileReleaseResponse(apimodel.Code_ExecuteSuccess, release)
   209  }
   210  
   211  // DeleteConfigFileRelease 删除某个配置文件的发布 release
   212  func (s *Server) DeleteConfigFileReleases(ctx context.Context,
   213  	reqs []*apiconfig.ConfigFileRelease) *apiconfig.ConfigBatchWriteResponse {
   214  
   215  	responses := api.NewConfigBatchWriteResponse(apimodel.Code_ExecuteSuccess)
   216  	chs := make([]chan *apiconfig.ConfigResponse, 0, len(reqs))
   217  	for i, instance := range reqs {
   218  		chs = append(chs, make(chan *apiconfig.ConfigResponse))
   219  		go func(index int, ins *apiconfig.ConfigFileRelease) {
   220  			chs[index] <- s.handleDeleteConfigFileRelease(ctx, ins)
   221  		}(i, instance)
   222  	}
   223  
   224  	for _, ch := range chs {
   225  		resp := <-ch
   226  		api.ConfigCollect(responses, resp)
   227  	}
   228  	return responses
   229  }
   230  
   231  func (s *Server) handleDeleteConfigFileRelease(ctx context.Context,
   232  	req *apiconfig.ConfigFileRelease) *apiconfig.ConfigResponse {
   233  
   234  	if errCode, errMsg := checkBaseReleaseParam(req, true); errCode != apimodel.Code_ExecuteSuccess {
   235  		return api.NewConfigResponseWithInfo(errCode, errMsg)
   236  	}
   237  	release := &model.ConfigFileRelease{
   238  		SimpleConfigFileRelease: &model.SimpleConfigFileRelease{
   239  			ConfigFileReleaseKey: &model.ConfigFileReleaseKey{
   240  				Name:      req.GetName().GetValue(),
   241  				Namespace: req.GetNamespace().GetValue(),
   242  				Group:     req.GetGroup().GetValue(),
   243  				FileName:  req.GetFileName().GetValue(),
   244  			},
   245  		},
   246  	}
   247  	var (
   248  		errRef     error
   249  		needRecord = true
   250  		recordData *model.ConfigFileRelease
   251  	)
   252  	defer func() {
   253  		if !needRecord {
   254  			return
   255  		}
   256  		if errRef != nil {
   257  			s.recordReleaseFail(ctx, utils.ReleaseTypeDelete, recordData, errRef)
   258  		} else {
   259  			s.recordReleaseSuccess(ctx, utils.ReleaseTypeDelete, recordData)
   260  		}
   261  	}()
   262  
   263  	tx, err := s.storage.StartTx()
   264  	if err != nil {
   265  		log.Error("[Config][File] delete config file release when begin tx.",
   266  			utils.RequestID(ctx), zap.Error(err))
   267  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   268  	}
   269  	defer func() {
   270  		_ = tx.Rollback()
   271  	}()
   272  	if _, err := s.storage.LockConfigFile(tx, release.ToFileKey()); err != nil {
   273  		errRef = err
   274  		log.Error("[Config][File] delete config file release when lock.",
   275  			utils.RequestID(ctx), zap.Error(err))
   276  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   277  	}
   278  
   279  	saveData, err := s.storage.GetConfigFileReleaseTx(tx, release.ConfigFileReleaseKey)
   280  	if err != nil {
   281  		errRef = err
   282  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   283  	}
   284  	recordData = saveData
   285  	if saveData == nil {
   286  		needRecord = false
   287  		return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
   288  	}
   289  	// 如果存在处于 active 状态的配置,重新在激活一下,触发版本的更新变动
   290  	if saveData.Active {
   291  		if err := s.storage.ActiveConfigFileReleaseTx(tx, saveData); err != nil {
   292  			errRef = err
   293  			log.Error("[Config][File] delete config file release when re-active.",
   294  				utils.RequestID(ctx), zap.Error(err))
   295  			return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   296  		}
   297  	}
   298  
   299  	if err := s.storage.DeleteConfigFileReleaseTx(tx, saveData.ConfigFileReleaseKey); err != nil {
   300  		log.Error("[Config][Release] delete config file release error.",
   301  			utils.RequestID(ctx), utils.ZapNamespace(req.GetNamespace().GetValue()),
   302  			utils.ZapGroup(req.GetGroup().GetValue()), utils.ZapFileName(req.GetFileName().GetValue()),
   303  			zap.Error(err))
   304  		errRef = err
   305  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   306  	}
   307  
   308  	if err := tx.Commit(); err != nil {
   309  		log.Error("[Config][Release] delete config file release when commit tx.",
   310  			utils.RequestID(ctx), utils.ZapNamespace(req.GetNamespace().GetValue()),
   311  			utils.ZapGroup(req.GetGroup().GetValue()), utils.ZapFileName(req.GetFileName().GetValue()),
   312  			zap.Error(err))
   313  		errRef = err
   314  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   315  	}
   316  
   317  	s.RecordHistory(ctx, configFileReleaseRecordEntry(ctx, req, release, model.ODelete))
   318  	return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
   319  }
   320  
   321  func (s *Server) GetConfigFileReleaseVersions(ctx context.Context,
   322  	filters map[string]string) *apiconfig.ConfigBatchQueryResponse {
   323  
   324  	searchFilters := map[string]string{}
   325  	for k, v := range filters {
   326  		if nk, ok := availableSearch["config_file_release"][k]; ok {
   327  			searchFilters[nk] = v
   328  		}
   329  	}
   330  
   331  	namespace := searchFilters["namespace"]
   332  	group := searchFilters["group"]
   333  	fileName := searchFilters["file_name"]
   334  	if namespace == "" {
   335  		return api.NewConfigBatchQueryResponseWithInfo(apimodel.Code_BadRequest, "invalid namespace")
   336  	}
   337  	if group == "" {
   338  		return api.NewConfigBatchQueryResponseWithInfo(apimodel.Code_BadRequest, "invalid config group")
   339  	}
   340  	if fileName == "" {
   341  		return api.NewConfigBatchQueryResponseWithInfo(apimodel.Code_BadRequest, "invalid config file name")
   342  	}
   343  	args := cachetypes.ConfigReleaseArgs{
   344  		BaseConfigArgs: cachetypes.BaseConfigArgs{
   345  			Namespace: filters["namespace"],
   346  			Group:     filters["group"],
   347  		},
   348  		FileName:   filters["file_name"],
   349  		OnlyActive: false,
   350  		NoPage:     true,
   351  	}
   352  	return s.handleDescribeConfigFileReleases(ctx, args)
   353  }
   354  
   355  func (s *Server) GetConfigFileReleases(ctx context.Context,
   356  	filter map[string]string) *apiconfig.ConfigBatchQueryResponse {
   357  
   358  	searchFilters := map[string]string{}
   359  	for k, v := range filter {
   360  		if nk, ok := availableSearch["config_file_release"][k]; ok {
   361  			searchFilters[nk] = v
   362  		}
   363  	}
   364  
   365  	offset, limit, err := utils.ParseOffsetAndLimit(filter)
   366  	if err != nil {
   367  		return api.NewConfigBatchQueryResponseWithInfo(apimodel.Code_BadRequest, err.Error())
   368  	}
   369  
   370  	args := cachetypes.ConfigReleaseArgs{
   371  		BaseConfigArgs: cachetypes.BaseConfigArgs{
   372  			Namespace:  searchFilters["namespace"],
   373  			Group:      searchFilters["group"],
   374  			Offset:     offset,
   375  			Limit:      limit,
   376  			OrderField: searchFilters["order_field"],
   377  			OrderType:  searchFilters["order_type"],
   378  		},
   379  		FileName:    searchFilters["file_name"],
   380  		ReleaseName: searchFilters["release_name"],
   381  		OnlyActive:  strings.Compare(searchFilters["only_active"], "true") == 0,
   382  	}
   383  	return s.handleDescribeConfigFileReleases(ctx, args)
   384  }
   385  
   386  func (s *Server) handleDescribeConfigFileReleases(ctx context.Context,
   387  	args cachetypes.ConfigReleaseArgs) *apiconfig.ConfigBatchQueryResponse {
   388  
   389  	total, simpleReleases, err := s.fileCache.QueryReleases(&args)
   390  	if err != nil {
   391  		return api.NewConfigBatchQueryResponseWithInfo(apimodel.Code_ExecuteException, err.Error())
   392  	}
   393  	ret := make([]*apiconfig.ConfigFileRelease, 0, len(simpleReleases))
   394  	for i := range simpleReleases {
   395  		item := simpleReleases[i]
   396  		ret = append(ret, &apiconfig.ConfigFileRelease{
   397  			Id:                 utils.NewUInt64Value(item.Id),
   398  			Name:               utils.NewStringValue(item.Name),
   399  			Namespace:          utils.NewStringValue(item.Namespace),
   400  			Group:              utils.NewStringValue(item.Group),
   401  			FileName:           utils.NewStringValue(item.FileName),
   402  			Format:             utils.NewStringValue(item.Format),
   403  			Version:            utils.NewUInt64Value(item.Version),
   404  			Active:             utils.NewBoolValue(item.Active),
   405  			CreateTime:         utils.NewStringValue(commontime.Time2String(item.CreateTime)),
   406  			ModifyTime:         utils.NewStringValue(commontime.Time2String(item.ModifyTime)),
   407  			CreateBy:           utils.NewStringValue(item.CreateBy),
   408  			ModifyBy:           utils.NewStringValue(item.ModifyBy),
   409  			ReleaseDescription: utils.NewStringValue(item.ReleaseDescription),
   410  			Tags:               model.FromTagMap(item.Metadata),
   411  		})
   412  	}
   413  
   414  	resp := api.NewConfigBatchQueryResponse(apimodel.Code_ExecuteSuccess)
   415  	resp.Total = utils.NewUInt32Value(total)
   416  	resp.ConfigFileReleases = ret
   417  	return resp
   418  }
   419  
   420  func (s *Server) RollbackConfigFileReleases(ctx context.Context,
   421  	reqs []*apiconfig.ConfigFileRelease) *apiconfig.ConfigBatchWriteResponse {
   422  
   423  	responses := api.NewConfigBatchWriteResponse(apimodel.Code_ExecuteSuccess)
   424  	chs := make([]chan *apiconfig.ConfigResponse, 0, len(reqs))
   425  	for i, instance := range reqs {
   426  		chs = append(chs, make(chan *apiconfig.ConfigResponse))
   427  		go func(index int, ins *apiconfig.ConfigFileRelease) {
   428  			chs[index] <- s.RollbackConfigFileRelease(ctx, ins)
   429  		}(i, instance)
   430  	}
   431  
   432  	for _, ch := range chs {
   433  		resp := <-ch
   434  		api.ConfigCollect(responses, resp)
   435  	}
   436  	return responses
   437  }
   438  
   439  // RollbackConfigFileRelease 回滚配置
   440  func (s *Server) RollbackConfigFileRelease(ctx context.Context,
   441  	req *apiconfig.ConfigFileRelease) *apiconfig.ConfigResponse {
   442  	if errCode, errMsg := checkBaseReleaseParam(req, true); errCode != apimodel.Code_ExecuteSuccess {
   443  		return api.NewConfigResponseWithInfo(errCode, errMsg)
   444  	}
   445  	data := &model.ConfigFileRelease{
   446  		SimpleConfigFileRelease: &model.SimpleConfigFileRelease{
   447  			ConfigFileReleaseKey: &model.ConfigFileReleaseKey{
   448  				Name:      req.GetName().GetValue(),
   449  				Namespace: req.GetNamespace().GetValue(),
   450  				Group:     req.GetGroup().GetValue(),
   451  				FileName:  req.GetFileName().GetValue(),
   452  			},
   453  		},
   454  	}
   455  
   456  	tx, err := s.storage.StartTx()
   457  	if err != nil {
   458  		log.Error("[Config][File] rollback config file releasw when begin tx.",
   459  			utils.RequestID(ctx), zap.Error(err))
   460  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   461  	}
   462  	defer func() {
   463  		_ = tx.Rollback()
   464  	}()
   465  
   466  	targetRelease, ret := s.handleRollbackConfigFileRelease(ctx, tx, data)
   467  	if targetRelease != nil {
   468  		data = targetRelease
   469  	}
   470  	if ret != nil {
   471  		_ = tx.Rollback()
   472  		s.recordReleaseFail(ctx, utils.ReleaseTypeRollback, data, err)
   473  		return ret
   474  	}
   475  
   476  	if err := tx.Commit(); err != nil {
   477  		log.Error("[Config][File] rollback config file releasw when commit tx.",
   478  			utils.RequestID(ctx), zap.Error(err))
   479  		s.recordReleaseFail(ctx, utils.ReleaseTypeRollback, data, err)
   480  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   481  	}
   482  
   483  	s.recordReleaseSuccess(ctx, utils.ReleaseTypeRollback, data)
   484  	s.RecordHistory(ctx, configFileReleaseRecordEntry(ctx, req, data, model.ORollback))
   485  	return api.NewConfigResponse(apimodel.Code_ExecuteSuccess)
   486  }
   487  
   488  // handleRollbackConfigFileRelease 回滚配置
   489  func (s *Server) handleRollbackConfigFileRelease(ctx context.Context, tx store.Tx,
   490  	data *model.ConfigFileRelease) (*model.ConfigFileRelease, *apiconfig.ConfigResponse) {
   491  
   492  	targetRelease, err := s.storage.GetConfigFileReleaseTx(tx, data.ConfigFileReleaseKey)
   493  	if err != nil {
   494  		log.Error("[Config][Release] rollback config file get target release", zap.Error(err))
   495  		return nil, api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   496  	}
   497  	if targetRelease == nil {
   498  		log.Error("[Config][Release] rollback config file to target release not found")
   499  		return nil, api.NewConfigResponse(apimodel.Code_NotFoundResource)
   500  	}
   501  
   502  	if err := s.storage.ActiveConfigFileReleaseTx(tx, data); err != nil {
   503  		log.Error("[Config][Release] rollback config file release error.",
   504  			utils.RequestID(ctx), zap.String("namespace", data.Namespace),
   505  			zap.String("group", data.Group), zap.String("fileName", data.FileName), zap.Error(err))
   506  		return targetRelease, api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   507  	}
   508  	return targetRelease, nil
   509  }
   510  
   511  func (s *Server) UpsertAndReleaseConfigFile(ctx context.Context,
   512  	req *apiconfig.ConfigFilePublishInfo) *apiconfig.ConfigResponse {
   513  
   514  	if err := utils.CheckResourceName(req.GetNamespace()); err != nil {
   515  		return api.NewConfigResponseWithInfo(apimodel.Code_BadRequest, "invalid config namespace")
   516  	}
   517  	if err := utils.CheckResourceName(req.GetGroup()); err != nil {
   518  		return api.NewConfigResponseWithInfo(apimodel.Code_BadRequest, "invalid config group")
   519  	}
   520  	if err := CheckFileName(req.GetFileName()); err != nil {
   521  		return api.NewConfigResponseWithInfo(apimodel.Code_BadRequest, "invalid config file_name")
   522  	}
   523  
   524  	upsertFileReq := &apiconfig.ConfigFile{
   525  		Name:        req.GetFileName(),
   526  		Namespace:   req.GetNamespace(),
   527  		Group:       req.GetGroup(),
   528  		Content:     req.GetContent(),
   529  		Format:      req.GetFormat(),
   530  		Comment:     req.GetComment(),
   531  		Tags:        req.GetTags(),
   532  		CreateBy:    utils.NewStringValue(utils.ParseUserName(ctx)),
   533  		ModifyBy:    utils.NewStringValue(utils.ParseUserName(ctx)),
   534  		ReleaseTime: utils.NewStringValue(req.GetReleaseDescription().GetValue()),
   535  	}
   536  	if rsp := s.prepareCreateConfigFile(ctx, upsertFileReq); rsp.Code.Value != api.ExecuteSuccess {
   537  		return rsp
   538  	}
   539  
   540  	tx, err := s.storage.StartTx()
   541  	if err != nil {
   542  		log.Error("[Config][File] upsert config file when begin tx.", utils.RequestID(ctx), zap.Error(err))
   543  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   544  	}
   545  
   546  	defer func() {
   547  		_ = tx.Rollback()
   548  	}()
   549  	upsertResp := s.handleCreateConfigFile(ctx, tx, upsertFileReq)
   550  	if upsertResp.GetCode().GetValue() == uint32(apimodel.Code_ExistedResource) {
   551  		upsertResp = s.handleUpdateConfigFile(ctx, tx, upsertFileReq)
   552  	}
   553  	if upsertResp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) {
   554  		return upsertResp
   555  	}
   556  
   557  	data, releaseResp := s.handlePublishConfigFile(ctx, tx, &apiconfig.ConfigFileRelease{
   558  		Name:               req.GetReleaseName(),
   559  		Namespace:          req.GetNamespace(),
   560  		Group:              req.GetGroup(),
   561  		FileName:           req.GetFileName(),
   562  		CreateBy:           utils.NewStringValue(utils.ParseUserName(ctx)),
   563  		ModifyBy:           utils.NewStringValue(utils.ParseUserName(ctx)),
   564  		ReleaseDescription: req.GetReleaseDescription(),
   565  	})
   566  	if releaseResp.GetCode().GetValue() != uint32(apimodel.Code_ExecuteSuccess) {
   567  		_ = tx.Rollback()
   568  		if data != nil {
   569  			s.recordReleaseFail(ctx, utils.ReleaseTypeNormal, data, errors.New(releaseResp.GetInfo().GetValue()))
   570  		}
   571  		return releaseResp
   572  	}
   573  
   574  	if err := tx.Commit(); err != nil {
   575  		log.Error("[Config][File] upsert config file when commit tx.", utils.RequestID(ctx), zap.Error(err))
   576  		s.recordReleaseFail(ctx, utils.ReleaseTypeNormal, data, err)
   577  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   578  	}
   579  	s.recordReleaseHistory(ctx, data, utils.ReleaseTypeNormal, utils.ReleaseStatusSuccess, "")
   580  	return releaseResp
   581  }
   582  
   583  func (s *Server) cleanConfigFileReleases(ctx context.Context, tx store.Tx,
   584  	file *model.ConfigFile) *apiconfig.ConfigResponse {
   585  
   586  	// 先重新 active 下当前正在发布的
   587  	saveData, err := s.storage.GetConfigFileActiveReleaseTx(tx, file.Key())
   588  	if err != nil {
   589  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   590  	}
   591  	if saveData != nil {
   592  		if err := s.storage.ActiveConfigFileReleaseTx(tx, saveData); err != nil {
   593  			return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   594  		}
   595  	}
   596  	if err := s.storage.CleanConfigFileReleasesTx(tx, file.Namespace, file.Group, file.Name); err != nil {
   597  		return api.NewConfigResponse(commonstore.StoreCode2APICode(err))
   598  	}
   599  	return nil
   600  }
   601  
   602  func (s *Server) recordReleaseSuccess(ctx context.Context, rType string, release *model.ConfigFileRelease) {
   603  	s.recordReleaseHistory(ctx, release, rType, utils.ReleaseStatusSuccess, "")
   604  }
   605  
   606  func (s *Server) recordReleaseFail(ctx context.Context, rType string, release *model.ConfigFileRelease, err error) {
   607  	s.recordReleaseHistory(ctx, release, rType, utils.ReleaseStatusFail, err.Error())
   608  }
   609  
   610  // configFileReleaseRecordEntry 生成服务的记录entry
   611  func configFileReleaseRecordEntry(ctx context.Context, req *apiconfig.ConfigFileRelease, md *model.ConfigFileRelease,
   612  	operationType model.OperationType) *model.RecordEntry {
   613  
   614  	marshaler := jsonpb.Marshaler{}
   615  	detail, _ := marshaler.MarshalToString(req)
   616  
   617  	entry := &model.RecordEntry{
   618  		ResourceType:  model.RConfigFileRelease,
   619  		ResourceName:  req.GetName().GetValue(),
   620  		Namespace:     req.GetNamespace().GetValue(),
   621  		OperationType: operationType,
   622  		Operator:      utils.ParseOperator(ctx),
   623  		Detail:        detail,
   624  		HappenTime:    time.Now(),
   625  	}
   626  
   627  	return entry
   628  }
   629  
   630  func checkBaseReleaseParam(req *apiconfig.ConfigFileRelease, checkRelease bool) (apimodel.Code, string) {
   631  	namespace := req.GetNamespace().GetValue()
   632  	group := req.GetGroup().GetValue()
   633  	fileName := req.GetFileName().GetValue()
   634  	releaseName := req.GetName().GetValue()
   635  	if namespace == "" {
   636  		return apimodel.Code_BadRequest, "invalid namespace"
   637  	}
   638  	if group == "" {
   639  		return apimodel.Code_BadRequest, "invalid config group"
   640  	}
   641  	if fileName == "" {
   642  		return apimodel.Code_BadRequest, "invalid config file name"
   643  	}
   644  	if checkRelease {
   645  		if releaseName == "" {
   646  			return apimodel.Code_BadRequest, "invalid config release name"
   647  		}
   648  	}
   649  	return apimodel.Code_ExecuteSuccess, ""
   650  }