github.com/polarismesh/polaris@v1.17.8/namespace/namespace.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 namespace 19 20 import ( 21 "context" 22 "fmt" 23 "time" 24 25 "github.com/golang/protobuf/jsonpb" 26 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 27 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 28 "go.uber.org/zap" 29 30 api "github.com/polarismesh/polaris/common/api/v1" 31 "github.com/polarismesh/polaris/common/model" 32 commonstore "github.com/polarismesh/polaris/common/store" 33 commontime "github.com/polarismesh/polaris/common/time" 34 "github.com/polarismesh/polaris/common/utils" 35 ) 36 37 var _ NamespaceOperateServer = (*Server)(nil) 38 39 func (s *Server) allowAutoCreate() bool { 40 return s.cfg.AutoCreate 41 } 42 43 // CreateNamespaces 批量创建命名空间 44 func (s *Server) CreateNamespaces(ctx context.Context, req []*apimodel.Namespace) *apiservice.BatchWriteResponse { 45 if checkError := checkBatchNamespace(req); checkError != nil { 46 return checkError 47 } 48 49 responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess) 50 for _, namespace := range req { 51 response := s.CreateNamespace(ctx, namespace) 52 api.Collect(responses, response) 53 } 54 55 return responses 56 } 57 58 // CreateNamespaceIfAbsent 创建命名空间,如果不存在 59 func (s *Server) CreateNamespaceIfAbsent(ctx context.Context, 60 req *apimodel.Namespace) (string, *apiservice.Response) { 61 if resp := checkCreateNamespace(req); resp != nil { 62 return "", resp 63 } 64 name := req.GetName().GetValue() 65 val, err := s.loadNamespace(name) 66 if err != nil { 67 return name, nil 68 } 69 if val == "" && !s.allowAutoCreate() { 70 return "", api.NewResponse(apimodel.Code_NotFoundNamespace) 71 } 72 ret, err, _ := s.createNamespaceSingle.Do(name, func() (interface{}, error) { 73 return s.CreateNamespace(ctx, req), nil 74 }) 75 if err != nil { 76 return "", api.NewResponseWithMsg(apimodel.Code_ExecuteException, err.Error()) 77 } 78 var ( 79 resp = ret.(*apiservice.Response) 80 code = resp.GetCode().GetValue() 81 ) 82 if code == uint32(apimodel.Code_ExecuteSuccess) || code == uint32(apimodel.Code_ExistedResource) { 83 return name, nil 84 } 85 return "", resp 86 } 87 88 // CreateNamespace 创建单个命名空间 89 func (s *Server) CreateNamespace(ctx context.Context, req *apimodel.Namespace) *apiservice.Response { 90 requestID, _ := ctx.Value(utils.StringContext("request-id")).(string) 91 92 // 参数检查 93 if checkError := checkCreateNamespace(req); checkError != nil { 94 return checkError 95 } 96 97 namespaceName := req.GetName().GetValue() 98 99 // 检查是否存在 100 namespace, err := s.storage.GetNamespace(namespaceName) 101 if err != nil { 102 log.Error(err.Error(), utils.ZapRequestID(requestID)) 103 return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req) 104 } 105 if namespace != nil { 106 return api.NewNamespaceResponse(apimodel.Code_ExistedResource, req) 107 } 108 109 // 110 data := s.createNamespaceModel(req) 111 112 // 存储层操作 113 if err := s.storage.AddNamespace(data); err != nil { 114 log.Error(err.Error(), utils.ZapRequestID(requestID)) 115 return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req) 116 } 117 118 msg := fmt.Sprintf("create namespace: name=%s", namespaceName) 119 log.Info(msg, utils.ZapRequestID(requestID)) 120 121 out := &apimodel.Namespace{ 122 Name: req.GetName(), 123 Token: utils.NewStringValue(data.Token), 124 } 125 126 _ = s.afterNamespaceResource(ctx, req, data, false) 127 128 return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, out) 129 } 130 131 /** 132 * @brief 创建存储层命名空间模型 133 */ 134 func (s *Server) createNamespaceModel(req *apimodel.Namespace) *model.Namespace { 135 namespace := &model.Namespace{ 136 Name: req.GetName().GetValue(), 137 Comment: req.GetComment().GetValue(), 138 Owner: req.GetOwners().GetValue(), 139 Token: utils.NewUUID(), 140 } 141 142 return namespace 143 } 144 145 // DeleteNamespaces 批量删除命名空间 146 func (s *Server) DeleteNamespaces(ctx context.Context, req []*apimodel.Namespace) *apiservice.BatchWriteResponse { 147 if checkError := checkBatchNamespace(req); checkError != nil { 148 return checkError 149 } 150 151 responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess) 152 for _, namespace := range req { 153 response := s.DeleteNamespace(ctx, namespace) 154 api.Collect(responses, response) 155 } 156 157 return responses 158 } 159 160 // DeleteNamespace 删除单个命名空间 161 func (s *Server) DeleteNamespace(ctx context.Context, req *apimodel.Namespace) *apiservice.Response { 162 requestID, _ := ctx.Value(utils.StringContext("request-id")).(string) 163 164 // 参数检查 165 if checkError := checkReviseNamespace(ctx, req); checkError != nil { 166 return checkError 167 } 168 169 tx, err := s.storage.CreateTransaction() 170 if err != nil { 171 log.Error(err.Error(), utils.ZapRequestID(requestID)) 172 return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req) 173 } 174 defer func() { _ = tx.Commit() }() 175 176 // 检查是否存在 177 namespace, err := tx.LockNamespace(req.GetName().GetValue()) 178 if err != nil { 179 log.Error(err.Error(), utils.ZapRequestID(requestID)) 180 return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req) 181 } 182 if namespace == nil { 183 return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, req) 184 } 185 186 // // 鉴权 187 // if ok := s.authority.VerifyNamespace(namespace.Token, parseNamespaceToken(ctx, req)); !ok { 188 // return api.NewNamespaceResponse(api.Unauthorized, req) 189 // } 190 191 // 判断属于该命名空间的服务是否都已经被删除 192 total, err := s.getServicesCountWithNamespace(namespace.Name) 193 if err != nil { 194 log.Error("get services count with namespace err", 195 utils.ZapRequestID(requestID), 196 zap.String("err", err.Error())) 197 return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req) 198 } 199 if total != 0 { 200 log.Error("the removed namespace has remain services", utils.ZapRequestID(requestID)) 201 return api.NewNamespaceResponse(apimodel.Code_NamespaceExistedServices, req) 202 } 203 204 // 判断属于该命名空间的服务是否都已经被删除 205 total, err = s.getConfigGroupCountWithNamespace(namespace.Name) 206 if err != nil { 207 log.Error("get config group count with namespace err", 208 utils.ZapRequestID(requestID), 209 zap.String("err", err.Error())) 210 return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req) 211 } 212 if total != 0 { 213 log.Error("the removed namespace has remain config-group", utils.ZapRequestID(requestID)) 214 return api.NewNamespaceResponse(apimodel.Code_NamespaceExistedConfigGroups, req) 215 } 216 217 // 存储层操作 218 if err := tx.DeleteNamespace(namespace.Name); err != nil { 219 log.Error(err.Error(), utils.ZapRequestID(requestID)) 220 return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req) 221 } 222 223 s.caches.Service().CleanNamespace(namespace.Name) 224 225 msg := fmt.Sprintf("delete namespace: name=%s", namespace.Name) 226 log.Info(msg, utils.ZapRequestID(requestID)) 227 s.RecordHistory(namespaceRecordEntry(ctx, req, model.ODelete)) 228 229 _ = s.afterNamespaceResource(ctx, req, &model.Namespace{Name: req.GetName().GetValue()}, true) 230 231 return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, req) 232 } 233 234 // UpdateNamespaces 批量修改命名空间 235 func (s *Server) UpdateNamespaces(ctx context.Context, req []*apimodel.Namespace) *apiservice.BatchWriteResponse { 236 if checkError := checkBatchNamespace(req); checkError != nil { 237 return checkError 238 } 239 240 responses := api.NewBatchWriteResponse(apimodel.Code_ExecuteSuccess) 241 for _, namespace := range req { 242 response := s.UpdateNamespace(ctx, namespace) 243 api.Collect(responses, response) 244 } 245 246 return responses 247 } 248 249 // UpdateNamespace 修改单个命名空间 250 func (s *Server) UpdateNamespace(ctx context.Context, req *apimodel.Namespace) *apiservice.Response { 251 // 参数检查 252 if resp := checkReviseNamespace(ctx, req); resp != nil { 253 return resp 254 } 255 256 // 权限校验 257 namespace, resp := s.checkNamespaceAuthority(ctx, req) 258 if resp != nil { 259 return resp 260 } 261 262 rid := utils.ParseRequestID(ctx) 263 // 修改 264 s.updateNamespaceAttribute(req, namespace) 265 266 // 存储层操作 267 if err := s.storage.UpdateNamespace(namespace); err != nil { 268 log.Error(err.Error(), utils.ZapRequestID(rid)) 269 return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req) 270 } 271 272 msg := fmt.Sprintf("update namespace: name=%s", namespace.Name) 273 log.Info(msg, utils.ZapRequestID(rid)) 274 s.RecordHistory(namespaceRecordEntry(ctx, req, model.OUpdate)) 275 276 if err := s.afterNamespaceResource(ctx, req, namespace, false); err != nil { 277 return api.NewNamespaceResponse(apimodel.Code_ExecuteException, req) 278 } 279 280 return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, req) 281 } 282 283 /** 284 * @brief 修改命名空间属性 285 */ 286 func (s *Server) updateNamespaceAttribute(req *apimodel.Namespace, namespace *model.Namespace) { 287 if req.GetComment() != nil { 288 namespace.Comment = req.GetComment().GetValue() 289 } 290 291 if req.GetOwners() != nil { 292 namespace.Owner = req.GetOwners().GetValue() 293 } 294 } 295 296 // UpdateNamespaceToken 更新命名空间token 297 func (s *Server) UpdateNamespaceToken(ctx context.Context, req *apimodel.Namespace) *apiservice.Response { 298 if resp := checkReviseNamespace(ctx, req); resp != nil { 299 return resp 300 } 301 namespace, resp := s.checkNamespaceAuthority(ctx, req) 302 if resp != nil { 303 return resp 304 } 305 306 rid := utils.ParseRequestID(ctx) 307 // 生成token 308 token := utils.NewUUID() 309 310 // 存储层操作 311 if err := s.storage.UpdateNamespaceToken(namespace.Name, token); err != nil { 312 log.Error(err.Error(), utils.ZapRequestID(rid)) 313 return api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req) 314 } 315 316 msg := fmt.Sprintf("update namespace token: name=%s", namespace.Name) 317 log.Info(msg, utils.ZapRequestID(rid)) 318 s.RecordHistory(namespaceRecordEntry(ctx, req, model.OUpdateToken)) 319 320 out := &apimodel.Namespace{ 321 Name: req.GetName(), 322 Token: utils.NewStringValue(token), 323 } 324 325 return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, out) 326 } 327 328 // GetNamespaces 查询命名空间 329 func (s *Server) GetNamespaces(ctx context.Context, query map[string][]string) *apiservice.BatchQueryResponse { 330 filter, offset, limit, checkError := checkGetNamespace(query) 331 if checkError != nil { 332 return checkError 333 } 334 335 namespaces, amount, err := s.storage.GetNamespaces(filter, offset, limit) 336 if err != nil { 337 return api.NewBatchQueryResponse(commonstore.StoreCode2APICode(err)) 338 } 339 340 out := api.NewBatchQueryResponse(apimodel.Code_ExecuteSuccess) 341 out.Amount = utils.NewUInt32Value(amount) 342 out.Size = utils.NewUInt32Value(uint32(len(namespaces))) 343 var totalServiceCount, totalInstanceCount, totalHealthInstanceCount uint32 344 for _, namespace := range namespaces { 345 nsCntInfo := s.caches.Service().GetNamespaceCntInfo(namespace.Name) 346 api.AddNamespace(out, &apimodel.Namespace{ 347 Id: utils.NewStringValue(namespace.Name), 348 Name: utils.NewStringValue(namespace.Name), 349 Comment: utils.NewStringValue(namespace.Comment), 350 Owners: utils.NewStringValue(namespace.Owner), 351 Ctime: utils.NewStringValue(commontime.Time2String(namespace.CreateTime)), 352 Mtime: utils.NewStringValue(commontime.Time2String(namespace.ModifyTime)), 353 TotalServiceCount: utils.NewUInt32Value(nsCntInfo.ServiceCount), 354 TotalInstanceCount: utils.NewUInt32Value(nsCntInfo.InstanceCnt.TotalInstanceCount), 355 TotalHealthInstanceCount: utils.NewUInt32Value(nsCntInfo.InstanceCnt.HealthyInstanceCount), 356 }) 357 totalServiceCount += nsCntInfo.ServiceCount 358 totalInstanceCount += nsCntInfo.InstanceCnt.TotalInstanceCount 359 totalHealthInstanceCount += nsCntInfo.InstanceCnt.HealthyInstanceCount 360 } 361 api.AddNamespaceSummary(out, &apimodel.Summary{ 362 TotalServiceCount: totalServiceCount, 363 TotalInstanceCount: totalInstanceCount, 364 TotalHealthInstanceCount: totalHealthInstanceCount, 365 }) 366 return out 367 } 368 369 // GetNamespaceToken 获取命名空间的token 370 func (s *Server) GetNamespaceToken(ctx context.Context, req *apimodel.Namespace) *apiservice.Response { 371 if resp := checkReviseNamespace(ctx, req); resp != nil { 372 return resp 373 } 374 375 namespace, resp := s.checkNamespaceAuthority(ctx, req) 376 if resp != nil { 377 return resp 378 } 379 380 // s.RecordHistory(namespaceRecordEntry(ctx, req, model.OGetToken)) 381 // 构造返回数据 382 out := &apimodel.Namespace{ 383 Name: req.GetName(), 384 Token: utils.NewStringValue(namespace.Token), 385 } 386 return api.NewNamespaceResponse(apimodel.Code_ExecuteSuccess, out) 387 } 388 389 // 根据命名空间查询服务总数 390 func (s *Server) getServicesCountWithNamespace(namespace string) (uint32, error) { 391 filter := map[string]string{"namespace": namespace} 392 total, _, err := s.storage.GetServices(filter, nil, nil, 0, 1) 393 if err != nil { 394 return 0, err 395 } 396 return total, nil 397 } 398 399 // 根据命名空间查询配置分组总数 400 func (s *Server) getConfigGroupCountWithNamespace(namespace string) (uint32, error) { 401 total, err := s.storage.CountConfigGroups(namespace) 402 if err != nil { 403 return 0, err 404 } 405 return uint32(total), nil 406 } 407 408 // loadNamespace 409 func (s *Server) loadNamespace(name string) (string, error) { 410 if val := s.caches.Namespace().GetNamespace(name); val != nil { 411 return name, nil 412 } 413 val, err := s.storage.GetNamespace(name) 414 if err != nil { 415 return "", err 416 } 417 if val == nil { 418 return "", nil 419 } 420 return val.Name, nil 421 } 422 423 // 检查namespace的权限,并且返回namespace 424 func (s *Server) checkNamespaceAuthority( 425 ctx context.Context, req *apimodel.Namespace) (*model.Namespace, *apiservice.Response) { 426 rid := utils.ParseRequestID(ctx) 427 namespaceName := req.GetName().GetValue() 428 // namespaceToken := parseNamespaceToken(ctx, req) 429 430 // 检查是否存在 431 namespace, err := s.storage.GetNamespace(namespaceName) 432 if err != nil { 433 log.Error(err.Error(), utils.ZapRequestID(rid)) 434 return nil, api.NewNamespaceResponse(commonstore.StoreCode2APICode(err), req) 435 } 436 if namespace == nil { 437 return nil, api.NewNamespaceResponse(apimodel.Code_NotFoundResource, req) 438 } 439 440 // 鉴权 441 // if ok := s.authority.VerifyNamespace(namespace.Token, namespaceToken); !ok { 442 // return nil, api.NewNamespaceResponse(api.Unauthorized, req) 443 // } 444 445 return namespace, nil 446 } 447 448 // 检查批量请求 449 func checkBatchNamespace(req []*apimodel.Namespace) *apiservice.BatchWriteResponse { 450 if len(req) == 0 { 451 return api.NewBatchWriteResponse(apimodel.Code_EmptyRequest) 452 } 453 454 if len(req) > utils.MaxBatchSize { 455 return api.NewBatchWriteResponse(apimodel.Code_BatchSizeOverLimit) 456 } 457 458 return nil 459 } 460 461 // 检查创建命名空间请求参数 462 func checkCreateNamespace(req *apimodel.Namespace) *apiservice.Response { 463 if req == nil { 464 return api.NewNamespaceResponse(apimodel.Code_EmptyRequest, req) 465 } 466 467 if err := utils.CheckResourceName(req.GetName()); err != nil { 468 return api.NewNamespaceResponse(apimodel.Code_InvalidNamespaceName, req) 469 } 470 471 return nil 472 } 473 474 // 检查删除/修改命名空间请求参数 475 func checkReviseNamespace(ctx context.Context, req *apimodel.Namespace) *apiservice.Response { 476 if req == nil { 477 return api.NewNamespaceResponse(apimodel.Code_EmptyRequest, req) 478 } 479 480 if err := utils.CheckResourceName(req.GetName()); err != nil { 481 return api.NewNamespaceResponse(apimodel.Code_InvalidNamespaceName, req) 482 } 483 return nil 484 } 485 486 // 检查查询命名空间请求参数 487 func checkGetNamespace(query map[string][]string) (map[string][]string, int, int, *apiservice.BatchQueryResponse) { 488 filter := make(map[string][]string) 489 490 if value := query["name"]; len(value) > 0 { 491 filter["name"] = value 492 } 493 494 if value := query["owner"]; len(value) > 0 { 495 filter["owner"] = value 496 } 497 498 offset, err := utils.CheckQueryOffset(query["offset"]) 499 if err != nil { 500 return nil, 0, 0, api.NewBatchQueryResponse(apimodel.Code_InvalidParameter) 501 } 502 503 limit, err := utils.CheckQueryLimit(query["limit"]) 504 if err != nil { 505 return nil, 0, 0, api.NewBatchQueryResponse(apimodel.Code_InvalidParameter) 506 } 507 508 return filter, offset, limit, nil 509 } 510 511 // 返回命名空间请求的token 512 // 默认先从req中获取,不存在,则使用header的 513 func parseNamespaceToken(ctx context.Context, req *apimodel.Namespace) string { 514 if reqToken := req.GetToken().GetValue(); reqToken != "" { 515 return reqToken 516 } 517 518 if headerToken := utils.ParseToken(ctx); headerToken != "" { 519 return headerToken 520 } 521 522 return "" 523 } 524 525 // 生成命名空间的记录entry 526 func namespaceRecordEntry(ctx context.Context, req *apimodel.Namespace, opt model.OperationType) *model.RecordEntry { 527 marshaler := jsonpb.Marshaler{} 528 datail, _ := marshaler.MarshalToString(req) 529 return &model.RecordEntry{ 530 ResourceType: model.RNamespace, 531 ResourceName: req.GetName().GetValue(), 532 Namespace: req.GetName().GetValue(), 533 OperationType: opt, 534 Operator: utils.ParseOperator(ctx), 535 Detail: datail, 536 HappenTime: time.Now(), 537 } 538 }