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 }