diff --git a/api/swagger/docs.go b/api/swagger/docs.go index 23696710..2ef5030a 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -2114,6 +2114,50 @@ const docTemplate = `{ } } }, + "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get resource quota by cloudAccount", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "CloudAccounts" + ], + "summary": "Get resource quota by cloudAccount", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "cloudAccountId", + "name": "cloudAccountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.GetCloudAccountResourceQuotaResponse" + } + } + } + } + }, "/organizations/{organizationId}/dashboard/charts": { "get": { "security": [ @@ -4533,9 +4577,8 @@ const docTemplate = `{ "required": [ "cloudAccountId", "name", - "stackTemplateId", - "tksInfraNode", - "tksUserNode" + "nodes", + "stackTemplateId" ], "properties": { "cloudAccountId": { @@ -4547,38 +4590,10 @@ const docTemplate = `{ "name": { "type": "string" }, - "stackTemplateId": { - "type": "string" - }, - "tksCpNode": { - "type": "integer" - }, - "tksCpNodeMax": { - "type": "integer" - }, - "tksCpNodeType": { - "type": "string" - }, - "tksInfraNode": { - "type": "integer", - "maximum": 3, - "minimum": 1 - }, - "tksInfraNodeMax": { - "type": "integer" - }, - "tksInfraNodeType": { - "type": "string" - }, - "tksUserNode": { - "type": "integer", - "maximum": 100, - "minimum": 0 + "nodes": { + "$ref": "#/definitions/domain.StackNodesIO" }, - "tksUserNodeMax": { - "type": "integer" - }, - "tksUserNodeType": { + "stackTemplateId": { "type": "string" } } @@ -4620,6 +4635,13 @@ const docTemplate = `{ "template": { "type": "string" }, + "templateType": { + "type": "string", + "enum": [ + "STANDARD", + "MSA" + ] + }, "version": { "type": "string" } @@ -4966,6 +4988,17 @@ const docTemplate = `{ } } }, + "domain.GetCloudAccountResourceQuotaResponse": { + "type": "object", + "properties": { + "available": { + "type": "boolean" + }, + "resourceQuota": { + "$ref": "#/definitions/domain.ResourceQuota" + } + } + }, "domain.GetCloudAccountResponse": { "type": "object", "properties": { @@ -5426,6 +5459,34 @@ const docTemplate = `{ } } }, + "domain.ResourceQuota": { + "type": "object", + "properties": { + "quotas": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.ResourceQuotaAttr" + } + } + } + }, + "domain.ResourceQuotaAttr": { + "type": "object", + "properties": { + "quota": { + "type": "integer" + }, + "required": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "usage": { + "type": "integer" + } + } + }, "domain.Role": { "type": "object", "properties": { @@ -5574,6 +5635,34 @@ const docTemplate = `{ } } }, + "domain.StackNodeIO": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "hostNames": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "domain.StackNodesIO": { + "type": "object", + "properties": { + "tksCpNode": { + "$ref": "#/definitions/domain.StackNodeIO" + }, + "tksInfraNode": { + "$ref": "#/definitions/domain.StackNodeIO" + }, + "tksUserNode": { + "$ref": "#/definitions/domain.StackNodeIO" + } + } + }, "domain.StackResponse": { "type": "object", "properties": { @@ -5686,6 +5775,9 @@ const docTemplate = `{ "template": { "type": "string" }, + "templateType": { + "type": "string" + }, "updatedAt": { "type": "string" }, @@ -5739,6 +5831,9 @@ const docTemplate = `{ "template": { "type": "string" }, + "templateType": { + "type": "string" + }, "updatedAt": { "type": "string" }, diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 29a14b8a..761169d4 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -2107,6 +2107,50 @@ } } }, + "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get resource quota by cloudAccount", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "CloudAccounts" + ], + "summary": "Get resource quota by cloudAccount", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "cloudAccountId", + "name": "cloudAccountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.GetCloudAccountResourceQuotaResponse" + } + } + } + } + }, "/organizations/{organizationId}/dashboard/charts": { "get": { "security": [ @@ -4526,9 +4570,8 @@ "required": [ "cloudAccountId", "name", - "stackTemplateId", - "tksInfraNode", - "tksUserNode" + "nodes", + "stackTemplateId" ], "properties": { "cloudAccountId": { @@ -4540,38 +4583,10 @@ "name": { "type": "string" }, - "stackTemplateId": { - "type": "string" - }, - "tksCpNode": { - "type": "integer" - }, - "tksCpNodeMax": { - "type": "integer" - }, - "tksCpNodeType": { - "type": "string" - }, - "tksInfraNode": { - "type": "integer", - "maximum": 3, - "minimum": 1 - }, - "tksInfraNodeMax": { - "type": "integer" - }, - "tksInfraNodeType": { - "type": "string" - }, - "tksUserNode": { - "type": "integer", - "maximum": 100, - "minimum": 0 + "nodes": { + "$ref": "#/definitions/domain.StackNodesIO" }, - "tksUserNodeMax": { - "type": "integer" - }, - "tksUserNodeType": { + "stackTemplateId": { "type": "string" } } @@ -4613,6 +4628,13 @@ "template": { "type": "string" }, + "templateType": { + "type": "string", + "enum": [ + "STANDARD", + "MSA" + ] + }, "version": { "type": "string" } @@ -4959,6 +4981,17 @@ } } }, + "domain.GetCloudAccountResourceQuotaResponse": { + "type": "object", + "properties": { + "available": { + "type": "boolean" + }, + "resourceQuota": { + "$ref": "#/definitions/domain.ResourceQuota" + } + } + }, "domain.GetCloudAccountResponse": { "type": "object", "properties": { @@ -5419,6 +5452,34 @@ } } }, + "domain.ResourceQuota": { + "type": "object", + "properties": { + "quotas": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.ResourceQuotaAttr" + } + } + } + }, + "domain.ResourceQuotaAttr": { + "type": "object", + "properties": { + "quota": { + "type": "integer" + }, + "required": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "usage": { + "type": "integer" + } + } + }, "domain.Role": { "type": "object", "properties": { @@ -5567,6 +5628,34 @@ } } }, + "domain.StackNodeIO": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "hostNames": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "domain.StackNodesIO": { + "type": "object", + "properties": { + "tksCpNode": { + "$ref": "#/definitions/domain.StackNodeIO" + }, + "tksInfraNode": { + "$ref": "#/definitions/domain.StackNodeIO" + }, + "tksUserNode": { + "$ref": "#/definitions/domain.StackNodeIO" + } + } + }, "domain.StackResponse": { "type": "object", "properties": { @@ -5679,6 +5768,9 @@ "template": { "type": "string" }, + "templateType": { + "type": "string" + }, "updatedAt": { "type": "string" }, @@ -5732,6 +5824,9 @@ "template": { "type": "string" }, + "templateType": { + "type": "string" + }, "updatedAt": { "type": "string" }, diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index c1ddc1b3..f9dc2a40 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -677,36 +677,15 @@ definitions: type: string name: type: string + nodes: + $ref: '#/definitions/domain.StackNodesIO' stackTemplateId: type: string - tksCpNode: - type: integer - tksCpNodeMax: - type: integer - tksCpNodeType: - type: string - tksInfraNode: - maximum: 3 - minimum: 1 - type: integer - tksInfraNodeMax: - type: integer - tksInfraNodeType: - type: string - tksUserNode: - maximum: 100 - minimum: 0 - type: integer - tksUserNodeMax: - type: integer - tksUserNodeType: - type: string required: - cloudAccountId - name + - nodes - stackTemplateId - - tksInfraNode - - tksUserNode type: object domain.CreateStackResponse: properties: @@ -729,6 +708,11 @@ definitions: type: string template: type: string + templateType: + enum: + - STANDARD + - MSA + type: string version: type: string required: @@ -965,6 +949,13 @@ definitions: $ref: '#/definitions/domain.ApplicationResponse' type: array type: object + domain.GetCloudAccountResourceQuotaResponse: + properties: + available: + type: boolean + resourceQuota: + $ref: '#/definitions/domain.ResourceQuota' + type: object domain.GetCloudAccountResponse: properties: cloudAccount: @@ -1263,6 +1254,24 @@ definitions: value: type: integer type: object + domain.ResourceQuota: + properties: + quotas: + items: + $ref: '#/definitions/domain.ResourceQuotaAttr' + type: array + type: object + domain.ResourceQuotaAttr: + properties: + quota: + type: integer + required: + type: integer + type: + type: string + usage: + type: integer + type: object domain.Role: properties: createdAt: @@ -1361,6 +1370,24 @@ definitions: - tksInfraNode - tksUserNode type: object + domain.StackNodeIO: + properties: + count: + type: integer + hostNames: + items: + type: string + type: array + type: object + domain.StackNodesIO: + properties: + tksCpNode: + $ref: '#/definitions/domain.StackNodeIO' + tksInfraNode: + $ref: '#/definitions/domain.StackNodeIO' + tksUserNode: + $ref: '#/definitions/domain.StackNodeIO' + type: object domain.StackResponse: properties: cloudAccount: @@ -1435,6 +1462,8 @@ definitions: type: array template: type: string + templateType: + type: string updatedAt: type: string updator: @@ -1470,6 +1499,8 @@ definitions: type: array template: type: string + templateType: + type: string updatedAt: type: string updator: @@ -3096,6 +3127,34 @@ paths: summary: Delete Force CloudAccount tags: - CloudAccounts + /organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota: + get: + consumes: + - application/json + description: Get resource quota by cloudAccount + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: cloudAccountId + in: path + name: cloudAccountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/domain.GetCloudAccountResourceQuotaResponse' + security: + - JWT: [] + summary: Get resource quota by cloudAccount + tags: + - CloudAccounts /organizations/{organizationId}/cloud-accounts/aws-account-id/{awsAccountId}/existence: get: consumes: diff --git a/internal/delivery/http/cloud-account.go b/internal/delivery/http/cloud-account.go index 574d9744..02809f30 100644 --- a/internal/delivery/http/cloud-account.go +++ b/internal/delivery/http/cloud-account.go @@ -373,3 +373,43 @@ func (h *CloudAccountHandler) CheckAwsAccountId(w http.ResponseWriter, r *http.R ResponseJSON(w, r, http.StatusOK, out) } + +// GetResourceQuota godoc +// @Tags CloudAccounts +// @Summary Get resource quota by cloudAccount +// @Description Get resource quota by cloudAccount +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param cloudAccountId path string true "cloudAccountId" +// @Success 200 {object} domain.GetCloudAccountResourceQuotaResponse +// @Router /organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota [GET] +// @Security JWT +func (h *CloudAccountHandler) GetResourceQuota(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["cloudAccountId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid cloudAccountId"), "C_INVALID_CLOUD_ACCOUNT_ID", "")) + return + } + + cloudAccountId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_CLOUD_ACCOUNT_ID", "")) + return + } + + available, resourceQuota, err := h.usecase.GetResourceQuota(r.Context(), cloudAccountId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetCloudAccountResourceQuotaResponse + if err := serializer.Map(resourceQuota, &out.ResourceQuota); err != nil { + log.InfoWithContext(r.Context(), err) + } + out.Available = available + + ResponseJSON(w, r, http.StatusOK, out) +} diff --git a/internal/delivery/http/stack.go b/internal/delivery/http/stack.go index 6ca05aa0..0b1f6e0e 100644 --- a/internal/delivery/http/stack.go +++ b/internal/delivery/http/stack.go @@ -53,9 +53,6 @@ func (h *StackHandler) CreateStack(w http.ResponseWriter, r *http.Request) { if err = serializer.Map(input, &dto); err != nil { log.InfoWithContext(r.Context(), err) } - if err = serializer.Map(input, &dto.Conf); err != nil { - log.InfoWithContext(r.Context(), err) - } dto.OrganizationId = organizationId stackId, err := h.usecase.Create(r.Context(), dto) diff --git a/internal/repository/alert.go b/internal/repository/alert.go index 93649ab9..f983c304 100644 --- a/internal/repository/alert.go +++ b/internal/repository/alert.go @@ -214,6 +214,9 @@ func (r *AlertRepository) CreateAlertAction(dto domain.AlertAction) (alertAction } func reflectAlert(alert Alert) (out domain.Alert) { + if err := serializer.Map(alert.Model, &out); err != nil { + log.Error(err) + } if err := serializer.Map(alert, &out); err != nil { log.Error(err) } diff --git a/internal/repository/app-group.go b/internal/repository/app-group.go index 2115e45c..e999ae99 100644 --- a/internal/repository/app-group.go +++ b/internal/repository/app-group.go @@ -212,6 +212,9 @@ func (r *AppGroupRepository) InitWorkflowDescription(clusterId domain.ClusterId) } func reflectAppGroup(appGroup AppGroup) (out domain.AppGroup) { + if err := serializer.Map(appGroup.Model, &out); err != nil { + log.Error(err) + } if err := serializer.Map(appGroup, &out); err != nil { log.Error(err) } diff --git a/internal/repository/cloud-account.go b/internal/repository/cloud-account.go index 4818390b..786e9f53 100644 --- a/internal/repository/cloud-account.go +++ b/internal/repository/cloud-account.go @@ -169,6 +169,9 @@ func (r *CloudAccountRepository) InitWorkflow(cloudAccountId uuid.UUID, workflow } func reflectCloudAccount(cloudAccount CloudAccount) (out domain.CloudAccount) { + if err := serializer.Map(cloudAccount.Model, &out); err != nil { + log.Error(err) + } if err := serializer.Map(cloudAccount, &out); err != nil { log.Error(err) } diff --git a/internal/repository/cluster.go b/internal/repository/cluster.go index 83f0faf3..a3daa11c 100644 --- a/internal/repository/cluster.go +++ b/internal/repository/cluster.go @@ -255,6 +255,9 @@ func (r *ClusterRepository) InitWorkflowDescription(clusterId domain.ClusterId) } func reflectCluster(cluster Cluster) (out domain.Cluster) { + if err := serializer.Map(cluster.Model, &out); err != nil { + log.Error(err) + } if err := serializer.Map(cluster, &out); err != nil { log.Error(err) } diff --git a/internal/repository/organization.go b/internal/repository/organization.go index fbf9b30d..75cc2e7a 100644 --- a/internal/repository/organization.go +++ b/internal/repository/organization.go @@ -166,6 +166,9 @@ func (r *OrganizationRepository) InitWorkflow(organizationId string, workflowId } func (r *OrganizationRepository) reflect(organization Organization) (out domain.Organization) { + if err := serializer.Map(organization.Model, &out); err != nil { + log.Error(err) + } if err := serializer.Map(organization, &out); err != nil { log.Error(err) } diff --git a/internal/repository/stack-template.go b/internal/repository/stack-template.go index 775e1813..8a373949 100644 --- a/internal/repository/stack-template.go +++ b/internal/repository/stack-template.go @@ -44,6 +44,7 @@ type StackTemplate struct { Name string `gorm:"index"` Description string `gorm:"index"` Template string + TemplateType string Version string CloudService string Platform string @@ -104,6 +105,7 @@ func (r *StackTemplateRepository) Create(dto domain.StackTemplate) (stackTemplat CloudService: dto.CloudService, Platform: dto.Platform, Template: dto.Template, + TemplateType: dto.TemplateType, CreatorId: &dto.CreatorId, UpdatorId: nil} res := r.db.Create(&stackTemplate) @@ -134,6 +136,9 @@ func (r *StackTemplateRepository) Delete(dto domain.StackTemplate) (err error) { } func reflectStackTemplate(stackTemplate StackTemplate) (out domain.StackTemplate) { + if err := serializer.Map(stackTemplate.Model, &out); err != nil { + log.Error(err) + } if err := serializer.Map(stackTemplate, &out); err != nil { log.Error(err) } diff --git a/internal/route/route.go b/internal/route/route.go index 429e8c1c..1859de7e 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -148,6 +148,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.UpdateCloudAccount))).Methods(http.MethodPut) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.DeleteCloudAccount))).Methods(http.MethodDelete) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/error", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.DeleteForceCloudAccount))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quotas", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.GetResourceQuota))).Methods(http.MethodGet) stackTemplateHandler := delivery.NewStackTemplateHandler(usecase.NewStackTemplateUsecase(repoFactory)) r.Handle(API_PREFIX+API_VERSION+"/stack-templates", authMiddleware.Handle(http.HandlerFunc(stackTemplateHandler.GetStackTemplates))).Methods(http.MethodGet) diff --git a/internal/usecase/cloud-account.go b/internal/usecase/cloud-account.go index 713f953d..3d3f1be4 100644 --- a/internal/usecase/cloud-account.go +++ b/internal/usecase/cloud-account.go @@ -5,6 +5,17 @@ import ( "fmt" "strings" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/credentials/stscreds" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/eks" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" + "github.com/aws/aws-sdk-go-v2/service/servicequotas" + "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/openinfradev/tks-api/internal/kubernetes" "github.com/openinfradev/tks-api/internal/middleware/auth/request" "github.com/google/uuid" @@ -24,6 +35,7 @@ type ICloudAccountUsecase interface { Get(ctx context.Context, cloudAccountId uuid.UUID) (domain.CloudAccount, error) GetByName(ctx context.Context, organizationId string, name string) (domain.CloudAccount, error) GetByAwsAccountId(ctx context.Context, awsAccountId string) (domain.CloudAccount, error) + GetResourceQuota(ctx context.Context, cloudAccountId uuid.UUID) (available bool, out domain.ResourceQuota, err error) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]domain.CloudAccount, error) Create(ctx context.Context, dto domain.CloudAccount) (cloudAccountId uuid.UUID, err error) Update(ctx context.Context, dto domain.CloudAccount) error @@ -228,6 +240,205 @@ func (u *CloudAccountUsecase) DeleteForce(ctx context.Context, cloudAccountId uu return nil } +func (u *CloudAccountUsecase) GetResourceQuota(ctx context.Context, cloudAccountId uuid.UUID) (available bool, out domain.ResourceQuota, err error) { + cloudAccount, err := u.repo.Get(cloudAccountId) + if err != nil { + return false, out, err + } + + awsAccessKeyId, awsSecretAccessKey, _ := kubernetes.GetAwsSecret() + if err != nil || awsAccessKeyId == "" || awsSecretAccessKey == "" { + log.ErrorWithContext(ctx, err) + return false, out, httpErrors.NewInternalServerError(fmt.Errorf("Invalid aws secret."), "", "") + } + + cfg, err := config.LoadDefaultConfig(ctx, + config.WithCredentialsProvider(credentials.StaticCredentialsProvider{ + Value: aws.Credentials{ + AccessKeyID: awsAccessKeyId, SecretAccessKey: awsSecretAccessKey, + }, + })) + if err != nil { + log.ErrorWithContext(ctx, err) + } + + stsSvc := sts.NewFromConfig(cfg) + + if !strings.Contains(cloudAccount.Name, domain.CLOUD_ACCOUNT_INCLUSTER) { + log.InfoWithContext(ctx, "Use assume role. awsAccountId : ", cloudAccount.AwsAccountId) + creds := stscreds.NewAssumeRoleProvider(stsSvc, "arn:aws:iam::"+cloudAccount.AwsAccountId+":role/controllers.cluster-api-provider-aws.sigs.k8s.io") + cfg.Credentials = aws.NewCredentialsCache(creds) + } + client := servicequotas.NewFromConfig(cfg) + + quotaMap := map[string]string{ + "L-69A177A2": "elasticloadbalancing", // NLB + "L-E9E9831D": "elasticloadbalancing", // Classic + "L-A4707A72": "vpc", // IGW + "L-1194D53C": "eks", // Cluster + "L-0263D0A3": "ec2", // Elastic IP + } + + // current usage + type CurrentUsage struct { + NLB int + CLB int + IGW int + Cluster int + EIP int + } + + out.Quotas = make([]domain.ResourceQuotaAttr, 0) + + // get current usage + currentUsage := CurrentUsage{} + { + c := elasticloadbalancingv2.NewFromConfig(cfg) + pageSize := int32(100) + res, err := c.DescribeLoadBalancers(ctx, &elasticloadbalancingv2.DescribeLoadBalancersInput{ + PageSize: &pageSize, + }, func(o *elasticloadbalancingv2.Options) { + o.Region = "ap-northeast-2" + }) + if err != nil { + return false, out, err + } + + for _, elb := range res.LoadBalancers { + switch elb.Type { + case "network": + currentUsage.NLB += 1 + } + } + } + + { + c := elasticloadbalancing.NewFromConfig(cfg) + pageSize := int32(100) + res, err := c.DescribeLoadBalancers(ctx, &elasticloadbalancing.DescribeLoadBalancersInput{ + PageSize: &pageSize, + }, func(o *elasticloadbalancing.Options) { + o.Region = "ap-northeast-2" + }) + if err != nil { + return false, out, err + } + currentUsage.CLB = len(res.LoadBalancerDescriptions) + } + + { + c := ec2.NewFromConfig(cfg) + res, err := c.DescribeInternetGateways(ctx, &ec2.DescribeInternetGatewaysInput{}, func(o *ec2.Options) { + o.Region = "ap-northeast-2" + }) + if err != nil { + return false, out, err + } + currentUsage.IGW = len(res.InternetGateways) + } + + { + c := eks.NewFromConfig(cfg) + res, err := c.ListClusters(ctx, &eks.ListClustersInput{}, func(o *eks.Options) { + o.Region = "ap-northeast-2" + }) + if err != nil { + return false, out, err + } + currentUsage.Cluster = len(res.Clusters) + } + + { + c := ec2.NewFromConfig(cfg) + res, err := c.DescribeAddresses(ctx, &ec2.DescribeAddressesInput{}, func(o *ec2.Options) { + o.Region = "ap-northeast-2" + }) + if err != nil { + log.ErrorWithContext(ctx, err) + return false, out, err + } + currentUsage.EIP = len(res.Addresses) + } + + for key, val := range quotaMap { + res, err := getServiceQuota(client, key, val) + if err != nil { + return false, out, err + } + log.DebugfWithContext(ctx, "%s %s %v", *res.Quota.QuotaName, *res.Quota.QuotaCode, *res.Quota.Value) + + quotaValue := int(*res.Quota.Value) + + // stack 1개 생성하는데 필요한 quota + // Classic 1 + // Network 5 + // IGW 1 + // EIP 3 + // Cluster 1 + switch key { + case "L-69A177A2": // NLB + log.InfofWithContext(ctx, "NLB : usage %d, quota %d", currentUsage.NLB, quotaValue) + out.Quotas = append(out.Quotas, domain.ResourceQuotaAttr{ + Type: "NLB", + Usage: currentUsage.NLB, + Quota: quotaValue, + Required: 5, + }) + if quotaValue < currentUsage.NLB+5 { + available = false + } + case "L-E9E9831D": // Classic + log.InfofWithContext(ctx, "CLB : usage %d, quota %d", currentUsage.CLB, quotaValue) + out.Quotas = append(out.Quotas, domain.ResourceQuotaAttr{ + Type: "CLB", + Usage: currentUsage.CLB, + Quota: quotaValue, + Required: 1, + }) + if quotaValue < currentUsage.CLB+1 { + available = false + } + case "L-A4707A72": // IGW + log.InfofWithContext(ctx, "IGW : usage %d, quota %d", currentUsage.IGW, quotaValue) + out.Quotas = append(out.Quotas, domain.ResourceQuotaAttr{ + Type: "IGW", + Usage: currentUsage.IGW, + Quota: quotaValue, + Required: 1, + }) + if quotaValue < currentUsage.IGW+1 { + available = false + } + case "L-1194D53C": // Cluster + log.InfofWithContext(ctx, "Cluster : usage %d, quota %d", currentUsage.Cluster, quotaValue) + out.Quotas = append(out.Quotas, domain.ResourceQuotaAttr{ + Type: "EKS", + Usage: currentUsage.Cluster, + Quota: quotaValue, + Required: 1, + }) + if quotaValue < currentUsage.Cluster+1 { + available = false + } + case "L-0263D0A3": // Elastic IP + log.InfofWithContext(ctx, "Elastic IP : usage %d, quota %d", currentUsage.EIP, quotaValue) + out.Quotas = append(out.Quotas, domain.ResourceQuotaAttr{ + Type: "EIP", + Usage: currentUsage.EIP, + Quota: quotaValue, + Required: 3, + }) + if quotaValue < currentUsage.EIP+3 { + available = false + } + } + + } + + //return fmt.Errorf("Always return err") + return true, out, nil +} + func (u *CloudAccountUsecase) getClusterCnt(cloudAccountId uuid.UUID) (cnt int) { cnt = 0 diff --git a/internal/usecase/cluster.go b/internal/usecase/cluster.go index 127772ef..417c6375 100644 --- a/internal/usecase/cluster.go +++ b/internal/usecase/cluster.go @@ -152,20 +152,6 @@ func (u *ClusterUsecase) Create(ctx context.Context, dto domain.Cluster) (cluste return "", httpErrors.NewBadRequestError(errors.Wrap(err, "Invalid stackTemplateId"), "", "") } - /*************************** - * Pre-process cluster conf * - ***************************/ - /* - clConf, err := u.constructClusterConf(&domain.ClusterConf{ - Region: dto.Conf.Region, - NumOfAz: dto.Conf.NumOfAz, - SshKeyName: "", - MachineType: dto.Conf.MachineType, - MachineReplicas: dto.Conf.MachineReplicas, - }, - ) - */ - userId := user.GetUserId() dto.CreatorId = &userId clusterId, err = u.repo.Create(dto) diff --git a/internal/usecase/stack.go b/internal/usecase/stack.go index 213be377..869c86b1 100644 --- a/internal/usecase/stack.go +++ b/internal/usecase/stack.go @@ -6,16 +6,7 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/credentials" - "github.com/aws/aws-sdk-go-v2/credentials/stscreds" - "github.com/aws/aws-sdk-go-v2/service/ec2" - "github.com/aws/aws-sdk-go-v2/service/eks" - "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing" - "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" "github.com/aws/aws-sdk-go-v2/service/servicequotas" - "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/internal/kubernetes" "github.com/openinfradev/tks-api/internal/middleware/auth/request" @@ -80,7 +71,7 @@ func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId do return "", httpErrors.NewInternalServerError(errors.Wrap(err, "Invalid stackTemplateId"), "S_INVALID_STACK_TEMPLATE", "") } - cloudAccount, err := u.cloudAccountRepo.Get(dto.CloudAccountId) + _, err = u.cloudAccountRepo.Get(dto.CloudAccountId) if err != nil { return "", httpErrors.NewInternalServerError(errors.Wrap(err, "Invalid cloudAccountId"), "S_INVALID_CLOUD_ACCOUNT", "") } @@ -95,26 +86,23 @@ func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId do } log.DebugWithContext(ctx, "isPrimary ", isPrimary) + // Make stack nodes + stackConf := domain.StackConfResponse{ + TksCpNode: dto.NodesIO.TksCpNode.Count, + TksInfraNode: dto.NodesIO.TksInfraNode.Count, + TksUserNode: dto.NodesIO.TksUserNode.Count, + } + workflow := "" - if strings.Contains(stackTemplate.Template, "aws-reference") || strings.Contains(stackTemplate.Template, "eks-reference") { + if stackTemplate.CloudService == "AWS" { workflow = "tks-stack-create-aws" - } else if strings.Contains(stackTemplate.Template, "aws-msa-reference") || strings.Contains(stackTemplate.Template, "eks-msa-reference") { - workflow = "tks-stack-create-aws-msa" + } else if stackTemplate.CloudService == "BYOH" { + workflow = "tks-stack-create-byoh" } else { log.ErrorWithContext(ctx, "Invalid template : ", stackTemplate.Template) return "", httpErrors.NewInternalServerError(fmt.Errorf("Invalid stackTemplate. %s", stackTemplate.Template), "", "") } - var stackConf domain.StackConfResponse - if err = serializer.Map(dto.Conf, &stackConf); err != nil { - log.InfoWithContext(ctx, err) - } - - // Check service quota - if err = u.checkAwsResourceQuota(ctx, cloudAccount); err != nil { - return "", err - } - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ Parameters: []string{ fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")), @@ -161,168 +149,6 @@ func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId do return dto.ID, nil } -func (u *StackUsecase) checkAwsResourceQuota(ctx context.Context, cloudAccount domain.CloudAccount) (err error) { - awsAccessKeyId, awsSecretAccessKey, _ := kubernetes.GetAwsSecret() - if err != nil || awsAccessKeyId == "" || awsSecretAccessKey == "" { - log.ErrorWithContext(ctx, err) - return httpErrors.NewInternalServerError(fmt.Errorf("Invalid aws secret."), "", "") - } - - cfg, err := config.LoadDefaultConfig(ctx, - config.WithCredentialsProvider(credentials.StaticCredentialsProvider{ - Value: aws.Credentials{ - AccessKeyID: awsAccessKeyId, SecretAccessKey: awsSecretAccessKey, - }, - })) - if err != nil { - log.ErrorWithContext(ctx, err) - } - - stsSvc := sts.NewFromConfig(cfg) - - if !strings.Contains(cloudAccount.Name, domain.CLOUD_ACCOUNT_INCLUSTER) { - log.InfoWithContext(ctx, "Use assume role. awsAccountId : ", cloudAccount.AwsAccountId) - creds := stscreds.NewAssumeRoleProvider(stsSvc, "arn:aws:iam::"+cloudAccount.AwsAccountId+":role/controllers.cluster-api-provider-aws.sigs.k8s.io") - cfg.Credentials = aws.NewCredentialsCache(creds) - } - client := servicequotas.NewFromConfig(cfg) - - quotaMap := map[string]string{ - "L-69A177A2": "elasticloadbalancing", // NLB - "L-E9E9831D": "elasticloadbalancing", // Classic - "L-A4707A72": "vpc", // IGW - "L-1194D53C": "eks", // Cluster - "L-0263D0A3": "ec2", // Elastic IP - } - - // current usage - type CurrentUsage struct { - NLB int - CLB int - IGW int - Cluster int - EIP int - } - - // get current usage - currentUsage := CurrentUsage{} - { - c := elasticloadbalancingv2.NewFromConfig(cfg) - pageSize := int32(100) - res, err := c.DescribeLoadBalancers(ctx, &elasticloadbalancingv2.DescribeLoadBalancersInput{ - PageSize: &pageSize, - }, func(o *elasticloadbalancingv2.Options) { - o.Region = "ap-northeast-2" - }) - if err != nil { - return err - } - - for _, elb := range res.LoadBalancers { - switch elb.Type { - case "network": - currentUsage.NLB += 1 - } - } - } - - { - c := elasticloadbalancing.NewFromConfig(cfg) - pageSize := int32(100) - res, err := c.DescribeLoadBalancers(ctx, &elasticloadbalancing.DescribeLoadBalancersInput{ - PageSize: &pageSize, - }, func(o *elasticloadbalancing.Options) { - o.Region = "ap-northeast-2" - }) - if err != nil { - return err - } - currentUsage.CLB = len(res.LoadBalancerDescriptions) - } - - { - c := ec2.NewFromConfig(cfg) - res, err := c.DescribeInternetGateways(ctx, &ec2.DescribeInternetGatewaysInput{}, func(o *ec2.Options) { - o.Region = "ap-northeast-2" - }) - if err != nil { - return err - } - currentUsage.IGW = len(res.InternetGateways) - } - - { - c := eks.NewFromConfig(cfg) - res, err := c.ListClusters(ctx, &eks.ListClustersInput{}, func(o *eks.Options) { - o.Region = "ap-northeast-2" - }) - if err != nil { - return err - } - currentUsage.Cluster = len(res.Clusters) - } - - { - c := ec2.NewFromConfig(cfg) - res, err := c.DescribeAddresses(ctx, &ec2.DescribeAddressesInput{}, func(o *ec2.Options) { - o.Region = "ap-northeast-2" - }) - if err != nil { - log.ErrorWithContext(ctx, err) - return err - } - currentUsage.EIP = len(res.Addresses) - } - - for key, val := range quotaMap { - res, err := getServiceQuota(client, key, val) - if err != nil { - return err - } - log.DebugfWithContext(ctx, "%s %s %v", *res.Quota.QuotaName, *res.Quota.QuotaCode, *res.Quota.Value) - - quotaValue := int(*res.Quota.Value) - - // stack 1개 생성하는데 필요한 quota - // Classic 1 - // Network 5 - // IGW 1 - // EIP 3 - // Cluster 1 - switch key { - case "L-69A177A2": // NLB - log.InfofWithContext(ctx, "NLB : usage %d, quota %d", currentUsage.NLB, quotaValue) - if quotaValue < currentUsage.NLB+5 { - return httpErrors.NewInternalServerError(fmt.Errorf("Not enough quota (NLB). current[%d], quota[%d]", currentUsage.NLB, quotaValue), "S_NOT_ENOUGH_QUOTA", "") - } - case "L-E9E9831D": // Classic - log.InfofWithContext(ctx, "CLB : usage %d, quota %d", currentUsage.CLB, quotaValue) - if quotaValue < currentUsage.CLB+1 { - return httpErrors.NewInternalServerError(fmt.Errorf("Not enough quota (Classic ELB). current[%d], quota[%d]", currentUsage.CLB, quotaValue), "S_NOT_ENOUGH_QUOTA", "") - } - case "L-A4707A72": // IGW - log.InfofWithContext(ctx, "IGW : usage %d, quota %d", currentUsage.IGW, quotaValue) - if quotaValue < currentUsage.IGW+1 { - return httpErrors.NewInternalServerError(fmt.Errorf("Not enough quota (Internet Gateway). current[%d], quota[%d]", currentUsage.IGW, quotaValue), "S_NOT_ENOUGH_QUOTA", "") - } - case "L-1194D53C": // Cluster - log.InfofWithContext(ctx, "Cluster : usage %d, quota %d", currentUsage.Cluster, quotaValue) - if quotaValue < currentUsage.Cluster+1 { - return httpErrors.NewInternalServerError(fmt.Errorf("Not enough quota (EKS cluster quota). current[%d], quota[%d]", currentUsage.Cluster, quotaValue), "S_NOT_ENOUGH_QUOTA", "") - } - case "L-0263D0A3": // Elastic IP - log.InfofWithContext(ctx, "Elastic IP : usage %d, quota %d", currentUsage.EIP, quotaValue) - if quotaValue < currentUsage.EIP+3 { - return httpErrors.NewInternalServerError(fmt.Errorf("Not enough quota (Elastic IP). current[%d], quota[%d]", currentUsage.EIP, quotaValue), "S_NOT_ENOUGH_QUOTA", "") - } - } - - } - - //return fmt.Errorf("Always return err") - return nil -} - func (u *StackUsecase) Get(ctx context.Context, stackId domain.StackId) (out domain.Stack, err error) { cluster, err := u.clusterRepo.Get(domain.ClusterId(stackId)) if err != nil { @@ -662,37 +488,48 @@ func (u *StackUsecase) GetStepStatus(ctx context.Context, stackId domain.StackId return } -func reflectClusterToStack(cluster domain.Cluster, appGroups []domain.AppGroup) domain.Stack { - status, statusDesc := getStackStatus(cluster, appGroups) - return domain.Stack{ - ID: domain.StackId(cluster.ID), - OrganizationId: cluster.OrganizationId, - Name: cluster.Name, - Description: cluster.Description, - Status: status, - StatusDesc: statusDesc, - CloudAccountId: cluster.CloudAccountId, - CloudAccount: cluster.CloudAccount, - StackTemplateId: cluster.StackTemplateId, - StackTemplate: cluster.StackTemplate, - CreatorId: cluster.CreatorId, - Creator: cluster.Creator, - UpdatorId: cluster.UpdatorId, - Updator: cluster.Updator, - CreatedAt: cluster.CreatedAt, - UpdatedAt: cluster.UpdatedAt, - Conf: domain.StackConf{ - TksCpNode: cluster.Conf.TksCpNode, - TksCpNodeMax: cluster.Conf.TksCpNodeMax, - TksCpNodeType: cluster.Conf.TksCpNodeType, - TksInfraNode: cluster.Conf.TksInfraNode, - TksInfraNodeMax: cluster.Conf.TksInfraNodeMax, - TksInfraNodeType: cluster.Conf.TksInfraNodeType, - TksUserNode: cluster.Conf.TksUserNode, - TksUserNodeMax: cluster.Conf.TksUserNodeMax, - TksUserNodeType: cluster.Conf.TksUserNodeType, - }, +func reflectClusterToStack(cluster domain.Cluster, appGroups []domain.AppGroup) (out domain.Stack) { + if err := serializer.Map(cluster, &out); err != nil { + log.Error(err) } + status, statusDesc := getStackStatus(cluster, appGroups) + + out.ID = domain.StackId(cluster.ID) + out.Status = status + out.StatusDesc = statusDesc + + /* + return domain.Stack{ + ID: domain.StackId(cluster.ID), + OrganizationId: cluster.OrganizationId, + Name: cluster.Name, + Description: cluster.Description, + Status: status, + StatusDesc: statusDesc, + CloudAccountId: cluster.CloudAccountId, + CloudAccount: cluster.CloudAccount, + StackTemplateId: cluster.StackTemplateId, + StackTemplate: cluster.StackTemplate, + CreatorId: cluster.CreatorId, + Creator: cluster.Creator, + UpdatorId: cluster.UpdatorId, + Updator: cluster.Updator, + CreatedAt: cluster.CreatedAt, + UpdatedAt: cluster.UpdatedAt, + Conf: domain.StackConf{ + TksCpNode: cluster.Conf.TksCpNode, + TksCpNodeMax: cluster.Conf.TksCpNodeMax, + TksCpNodeType: cluster.Conf.TksCpNodeType, + TksInfraNode: cluster.Conf.TksInfraNode, + TksInfraNodeMax: cluster.Conf.TksInfraNodeMax, + TksInfraNodeType: cluster.Conf.TksInfraNodeType, + TksUserNode: cluster.Conf.TksUserNode, + TksUserNodeMax: cluster.Conf.TksUserNodeMax, + TksUserNodeType: cluster.Conf.TksUserNodeType, + }, + } + */ + return } // [TODO] more pretty diff --git a/pkg/domain/cloud-account.go b/pkg/domain/cloud-account.go index 981f7589..65609b23 100644 --- a/pkg/domain/cloud-account.go +++ b/pkg/domain/cloud-account.go @@ -72,6 +72,17 @@ type CloudAccount struct { UpdatedAt time.Time } +type ResourceQuotaAttr struct { + Type string `json:"type"` + Usage int `json:"usage"` + Quota int `json:"quota"` + Required int `json:"required"` +} + +type ResourceQuota struct { + Quotas []ResourceQuotaAttr `json:"quotas"` +} + type CloudAccountResponse struct { ID string `json:"id"` OrganizationId string `json:"organizationId"` @@ -140,3 +151,8 @@ type CheckCloudAccountNameResponse struct { type CheckCloudAccountAwsAccountIdResponse struct { Existed bool `json:"existed"` } + +type GetCloudAccountResourceQuotaResponse struct { + Available bool `json:"available"` + ResourceQuota ResourceQuota `json:"resourceQuota"` +} diff --git a/pkg/domain/stack-template.go b/pkg/domain/stack-template.go index 0be6ae35..800e2ec3 100644 --- a/pkg/domain/stack-template.go +++ b/pkg/domain/stack-template.go @@ -6,6 +6,9 @@ import ( "github.com/google/uuid" ) +const STACK_TEMPLATE_TYPE_STANDARD = "STANDARD" +const STACK_TEMPLATE_TYPE_MSA = "MSA" + // 내부 type StackTemplate struct { ID uuid.UUID @@ -13,6 +16,7 @@ type StackTemplate struct { Name string Description string Template string + TemplateType string CloudService string Version string Platform string @@ -43,6 +47,7 @@ type StackTemplateResponse struct { Name string `json:"name"` Description string `json:"description"` Template string `json:"template"` + TemplateType string `json:"templateType"` CloudService string `json:"cloudService"` Version string `json:"version"` Platform string `json:"platform"` @@ -79,6 +84,7 @@ type CreateStackTemplateRequest struct { Version string `json:"version" validate:"required"` Platform string `json:"platform" validate:"required"` Template string `json:"template" validate:"required"` + TemplateType string `json:"templateType" validate:"oneof=STANDARD MSA"` } type CreateStackTemplateResponse struct { diff --git a/pkg/domain/stack.go b/pkg/domain/stack.go index 6c344652..29afdad6 100644 --- a/pkg/domain/stack.go +++ b/pkg/domain/stack.go @@ -81,7 +81,8 @@ type Stack = struct { StackTemplate StackTemplate Status StackStatus StatusDesc string - Conf StackConf + Nodes []StackNode + NodesIO StackNodesIO PrimaryCluster bool GrafanaUrl string CreatorId *uuid.UUID @@ -92,18 +93,6 @@ type Stack = struct { UpdatedAt time.Time } -type StackConf struct { - TksCpNode int - TksCpNodeMax int - TksCpNodeType string - TksInfraNode int - TksInfraNodeMax int - TksInfraNodeType string - TksUserNode int - TksUserNodeMax int - TksUserNodeType string -} - type StackStepStatus struct { Status string `json:"status"` Stage string `json:"stage"` @@ -111,20 +100,28 @@ type StackStepStatus struct { MaxStep int `json:"maxStep"` } +type StackNode struct { + StackNodeType string `json:"type" validate:"oneof=TKS_CP_NODE TKS_INFRA_NODE TKS_USER_NODE"` + HostName []string +} + +type StackNodeIO struct { + Count int `json:"count"` + HostName []string `json:"hostNames"` +} + +type StackNodesIO struct { + TksCpNode StackNodeIO `json:"tksCpNode"` + TksInfraNode StackNodeIO `json:"tksInfraNode"` + TksUserNode StackNodeIO `json:"tksUserNode"` +} + type CreateStackRequest struct { - Name string `json:"name" validate:"required,name,rfc1123"` - Description string `json:"description"` - StackTemplateId string `json:"stackTemplateId" validate:"required"` - CloudAccountId string `json:"cloudAccountId" validate:"required"` - TksCpNode int `json:"tksCpNode"` - TksCpNodeMax int `json:"tksCpNodeMax,omitempty"` - TksCpNodeType string `json:"tksCpNodeType,omitempty"` - TksInfraNode int `json:"tksInfraNode" validate:"required,min=1,max=3"` - TksInfraNodeMax int `json:"tksInfraNodeMax,omitempty"` - TksInfraNodeType string `json:"tksInfraNodeType,omitempty"` - TksUserNode int `json:"tksUserNode" validate:"required,min=0,max=100"` - TksUserNodeMax int `json:"tksUserNodeMax,omitempty"` - TksUserNodeType string `json:"tksUserNodeType,omitempty"` + Name string `json:"name" validate:"required,name,rfc1123"` + Description string `json:"description"` + StackTemplateId string `json:"stackTemplateId" validate:"required"` + CloudAccountId string `json:"cloudAccountId" validate:"required"` + NodesIO StackNodesIO `json:"nodes" validate:"required"` } type CreateStackResponse struct { diff --git a/scripts/init_postgres.sql b/scripts/init_postgres.sql index cf90c12b..40a2a3d0 100644 --- a/scripts/init_postgres.sql +++ b/scripts/init_postgres.sql @@ -15,15 +15,15 @@ insert into policies ( role_id, name, description, c, create_priviledge, u, upda insert into organizations ( id, name, description, created_at, updated_at ) values ( 'master', 'master', 'tks', now(), now() ); insert into users ( id, account_id, name, password, organization_id, role_id, created_at, updated_at ) values ( 'bf67de40-ce15-4dc0-b6c2-17f053ca504f', 'admin', 'admin', '$2a$10$Akf03nbLHk93sTtozm35XuINXkJeNX7A1T9o/Pxpg9R2B2PToBPOO', 'master', 'b2b689f0-ceeb-46c2-b280-0bc06896acd1', now(), now() ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, kube_version, kube_type, created_at, updated_at, services ) -values ( '49901092-be76-4d4f-94e9-b84525f560b5', 'master', 'AWS Standard (x86)', 'included LMA', 'v1', 'AWS', 'x86', 'aws-reference', 'v1.25', 'AWS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, kube_version, kube_type, created_at, updated_at, services ) -values ( '44d5e76b-63db-4dd0-a16e-11bd3f6054cf', 'master', 'AWS MSA Standard (x86)', 'included LMA, SERVICE MESH', 'v1', 'AWS', 'x86', 'aws-msa-reference', 'v1.25', 'AWS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}, {"name": "MSA", "type": "SERVICE_MESH", "applications": [{"name": "istio", "version": "v1.13.1", "description": "MSA 플랫폼"}, {"name": "jagger", "version": "v2.27.1", "description": "분산 서비스간 트랜잭션 추적을 위한 로깅 플랫폼"}, {"name": "kiali", "version": "v1.45.1", "description": "MSA 통합 모니터링포탈"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, kube_version, kube_type, created_at, updated_at, services ) -values ( 'fe1d97e0-7428-4be6-9c69-310a88b4ff46', 'master', 'AWS Standard (arm)', 'included LMA', 'v2', 'AWS', 'arm', 'aws-arm-reference', 'v1.25', 'EKS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, kube_version, kube_type, created_at, updated_at, services ) -values ( '3696cb38-4da0-4235-97eb-b6eb15962bd1', 'master', 'AWS Standard (arm)', 'included LMA, SERVICE_MESH', 'v2', 'AWS', 'arm', 'aws-arm-msa-reference', 'v1.25', 'EKS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}, {"name": "MSA", "type": "SERVICE_MESH", "applications": [{"name": "istio", "version": "v1.13.1", "description": "MSA 플랫폼"}, {"name": "jagger", "version": "v2.27.1", "description": "분산 서비스간 트랜잭션 추적을 위한 로깅 플랫폼"}, {"name": "kiali", "version": "v1.45.1", "description": "MSA 통합 모니터링포탈"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, kube_version, kube_type, created_at, updated_at, services ) -values ( 'c8a4658d-d5a6-4191-8a91-e26f6aee007f', 'master', 'EKS Standard (x86)', 'included LMA', 'v1', 'AWS', 'x86', 'eks-reference', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, kube_version, kube_type, created_at, updated_at, services ) -values ( '39f18a09-5b94-4772-bdba-e4c32ee002f7', 'master', 'EKS MSA Standard (x86)', 'included LMA, SERVICE MESH', 'v1', 'AWS', 'x86', 'eks-msa-reference', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]},{"name":"MSA","type":"SERVICE_MESH","applications":[{"name":"istio","version":"v1.17.2","description":"MSA 플랫폼"},{"name":"jagger","version":"1.35.0","description":"분산 서비스간 트랜잭션 추적을 위한 플랫폼"},{"name":"kiali","version":"v1.63.0","description":"MSA 구조 및 성능을 볼 수 있는 Dashboard"},{"name":"k8ssandra","version":"1.6.0","description":"분산 서비스간 호출 로그를 저장하는 스토리지"}]}]' ); +insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '49901092-be76-4d4f-94e9-b84525f560b5', 'master', 'AWS Standard (x86)', 'included LMA', 'v1', 'AWS', 'x86', 'aws-reference', 'STANDARD', 'v1.25', 'AWS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}]' ); +insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '44d5e76b-63db-4dd0-a16e-11bd3f6054cf', 'master', 'AWS MSA Standard (x86)', 'included LMA, SERVICE MESH', 'v1', 'AWS', 'x86', 'aws-msa-reference', 'MSA', 'v1.25', 'AWS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}, {"name": "MSA", "type": "SERVICE_MESH", "applications": [{"name": "istio", "version": "v1.13.1", "description": "MSA 플랫폼"}, {"name": "jagger", "version": "v2.27.1", "description": "분산 서비스간 트랜잭션 추적을 위한 로깅 플랫폼"}, {"name": "kiali", "version": "v1.45.1", "description": "MSA 통합 모니터링포탈"}]}]' ); +insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( 'fe1d97e0-7428-4be6-9c69-310a88b4ff46', 'master', 'AWS Standard (arm)', 'included LMA', 'v2', 'AWS', 'arm', 'aws-arm-reference', 'STANDARD', 'v1.25', 'EKS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}]' ); +insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '3696cb38-4da0-4235-97eb-b6eb15962bd1', 'master', 'AWS Standard (arm)', 'included LMA, SERVICE_MESH', 'v2', 'AWS', 'arm', 'aws-arm-msa-reference', 'MSA', 'v1.25', 'EKS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}, {"name": "MSA", "type": "SERVICE_MESH", "applications": [{"name": "istio", "version": "v1.13.1", "description": "MSA 플랫폼"}, {"name": "jagger", "version": "v2.27.1", "description": "분산 서비스간 트랜잭션 추적을 위한 로깅 플랫폼"}, {"name": "kiali", "version": "v1.45.1", "description": "MSA 통합 모니터링포탈"}]}]' ); +insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( 'c8a4658d-d5a6-4191-8a91-e26f6aee007f', 'master', 'EKS Standard (x86)', 'included LMA', 'v1', 'AWS', 'x86', 'eks-reference', 'STANDARD', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); +insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '39f18a09-5b94-4772-bdba-e4c32ee002f7', 'master', 'EKS MSA Standard (x86)', 'included LMA, SERVICE MESH', 'v1', 'AWS', 'x86', 'eks-msa-reference', 'MSA', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]},{"name":"MSA","type":"SERVICE_MESH","applications":[{"name":"istio","version":"v1.17.2","description":"MSA 플랫폼"},{"name":"jagger","version":"1.35.0","description":"분산 서비스간 트랜잭션 추적을 위한 플랫폼"},{"name":"kiali","version":"v1.63.0","description":"MSA 구조 및 성능을 볼 수 있는 Dashboard"},{"name":"k8ssandra","version":"1.6.0","description":"분산 서비스간 호출 로그를 저장하는 스토리지"}]}]' ); \ No newline at end of file