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 }