From e1f2e742ef2e31419f8a2a3b6829df3d896740dc Mon Sep 17 00:00:00 2001 From: "taekyu.kang" Date: Thu, 12 Oct 2023 15:10:24 +0900 Subject: [PATCH] feature. implementation byoh --- api/swagger/docs.go | 46 ++++++++++++++++++++++ api/swagger/swagger.json | 46 ++++++++++++++++++++++ api/swagger/swagger.yaml | 29 ++++++++++++++ internal/delivery/http/cluster.go | 31 ++++++++++++++- internal/delivery/http/stack.go | 19 +++++++++- internal/repository/cluster.go | 2 + internal/route/route.go | 4 +- internal/usecase/cluster.go | 59 +++++++++++++++++++++++++++-- internal/usecase/stack.go | 63 ++++++++++++++++++++++++++++++- pkg/api-client/api-client.go | 16 ++++---- pkg/domain/cluster.go | 8 ++++ pkg/domain/stack.go | 2 + pkg/httpErrors/errorCode.go | 5 ++- 13 files changed, 312 insertions(+), 18 deletions(-) diff --git a/api/swagger/docs.go b/api/swagger/docs.go index 654bb71b..5671a507 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -706,6 +706,40 @@ const docTemplate = `{ } } }, + "/clusters/{clusterId}/install": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Install cluster on tks cluster", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Clusters" + ], + "summary": "Install cluster on tks cluster", + "parameters": [ + { + "type": "string", + "description": "clusterId", + "name": "clusterId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/clusters/{clusterId}/site-values": { "get": { "security": [ @@ -4303,6 +4337,9 @@ const docTemplate = `{ "id": { "type": "string" }, + "isStack": { + "type": "boolean" + }, "name": { "type": "string" }, @@ -4423,6 +4460,9 @@ const docTemplate = `{ "id": { "type": "string" }, + "isStack": { + "type": "boolean" + }, "name": { "type": "string" }, @@ -4689,6 +4729,9 @@ const docTemplate = `{ "description": { "type": "string" }, + "isStack": { + "type": "boolean" + }, "name": { "type": "string" }, @@ -4779,6 +4822,9 @@ const docTemplate = `{ "BYOH" ] }, + "clusterId": { + "type": "string" + }, "description": { "type": "string" }, diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 38924b02..c1042949 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -699,6 +699,40 @@ } } }, + "/clusters/{clusterId}/install": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Install cluster on tks cluster", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Clusters" + ], + "summary": "Install cluster on tks cluster", + "parameters": [ + { + "type": "string", + "description": "clusterId", + "name": "clusterId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/clusters/{clusterId}/site-values": { "get": { "security": [ @@ -4296,6 +4330,9 @@ "id": { "type": "string" }, + "isStack": { + "type": "boolean" + }, "name": { "type": "string" }, @@ -4416,6 +4453,9 @@ "id": { "type": "string" }, + "isStack": { + "type": "boolean" + }, "name": { "type": "string" }, @@ -4682,6 +4722,9 @@ "description": { "type": "string" }, + "isStack": { + "type": "boolean" + }, "name": { "type": "string" }, @@ -4772,6 +4815,9 @@ "BYOH" ] }, + "clusterId": { + "type": "string" + }, "description": { "type": "string" }, diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index ea7862b9..83ff59a6 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -382,6 +382,8 @@ definitions: type: boolean id: type: string + isStack: + type: boolean name: type: string organizationId: @@ -461,6 +463,8 @@ definitions: type: string id: type: string + isStack: + type: boolean name: type: string organizationId: @@ -639,6 +643,8 @@ definitions: type: string description: type: string + isStack: + type: boolean name: type: string organizationId: @@ -701,6 +707,8 @@ definitions: - AWS - BYOH type: string + clusterId: + type: string description: type: string name: @@ -2369,6 +2377,27 @@ paths: summary: Get cluster tags: - Clusters + /clusters/{clusterId}/install: + post: + consumes: + - application/json + description: Install cluster on tks cluster + parameters: + - description: clusterId + in: path + name: clusterId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Install cluster on tks cluster + tags: + - Clusters /clusters/{clusterId}/site-values: get: consumes: diff --git a/internal/delivery/http/cluster.go b/internal/delivery/http/cluster.go index 2acd1efb..867e24b8 100644 --- a/internal/delivery/http/cluster.go +++ b/internal/delivery/http/cluster.go @@ -131,7 +131,7 @@ func (h *ClusterHandler) GetClusterSiteValues(w http.ResponseWriter, r *http.Req ResponseJSON(w, r, http.StatusOK, out) } -// GetCluster godoc +// CreateCluster godoc // @Tags Clusters // @Summary Create cluster // @Description Create cluster @@ -166,7 +166,7 @@ func (h *ClusterHandler) CreateCluster(w http.ResponseWriter, r *http.Request) { //txHandle := r.Context().Value("txHandle").(*gorm.DB) clusterId := domain.ClusterId("") if input.CloudService == domain.CloudService_BYOH { - clusterId, err = h.usecase.CreateByoh(r.Context(), dto) + clusterId, err = h.usecase.Bootstrap(r.Context(), dto) if err != nil { ErrorJSON(w, r, err) return @@ -186,6 +186,33 @@ func (h *ClusterHandler) CreateCluster(w http.ResponseWriter, r *http.Request) { ResponseJSON(w, r, http.StatusOK, out) } +// InstallCluster godoc +// @Tags Clusters +// @Summary Install cluster on tks cluster +// @Description Install cluster on tks cluster +// @Accept json +// @Produce json +// @Param clusterId path string true "clusterId" +// @Success 200 {object} nil +// @Router /clusters/{clusterId}/install [post] +// @Security JWT +func (h *ClusterHandler) InstallCluster(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + clusterId, ok := vars["clusterId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid clusterId"), "C_INVALID_CLUSTER_ID", "")) + return + } + + err := h.usecase.Install(r.Context(), domain.ClusterId(clusterId)) + if err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + // DeleteCluster godoc // @Tags Clusters // @Summary Delete cluster diff --git a/internal/delivery/http/stack.go b/internal/delivery/http/stack.go index 52ec4ec9..0b4cd0be 100644 --- a/internal/delivery/http/stack.go +++ b/internal/delivery/http/stack.go @@ -58,7 +58,6 @@ func (h *StackHandler) CreateStack(w http.ResponseWriter, r *http.Request) { log.InfoWithContext(r.Context(), err) } dto.OrganizationId = organizationId - stackId, err := h.usecase.Create(r.Context(), dto) if err != nil { ErrorJSON(w, r, err) @@ -72,6 +71,24 @@ func (h *StackHandler) CreateStack(w http.ResponseWriter, r *http.Request) { ResponseJSON(w, r, http.StatusOK, out) } +// SYSTEM-API +func (h *StackHandler) InstallStack(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + stackId, ok := vars["stackId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid stackId"), "S_INVALID_STACK_ID", "")) + return + } + + err := h.usecase.Install(r.Context(), domain.StackId(stackId)) + if err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + // GetStack godoc // @Tags Stacks // @Summary Get Stacks diff --git a/internal/repository/cluster.go b/internal/repository/cluster.go index 621b99d2..f10fdf0b 100644 --- a/internal/repository/cluster.go +++ b/internal/repository/cluster.go @@ -66,6 +66,7 @@ type Cluster struct { Favorites *[]ClusterFavorite ClusterType domain.ClusterType `gorm:"default:0"` ClusterEndpoint string + IsStack bool `gorm:"default:false"` TksCpNode int TksCpNodeMax int TksCpNodeType string @@ -224,6 +225,7 @@ func (r *ClusterRepository) Create(dto domain.Cluster) (clusterId domain.Cluster ClusterType: dto.ClusterType, CloudService: dto.CloudService, ClusterEndpoint: dto.ClusterEndpoint, + IsStack: dto.IsStack, TksCpNode: dto.Conf.TksCpNode, TksCpNodeMax: dto.Conf.TksCpNodeMax, TksCpNodeType: dto.Conf.TksCpNodeType, diff --git a/internal/route/route.go b/internal/route/route.go index 1613246d..38b196b0 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -113,8 +113,9 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/clusters", authMiddleware.Handle(http.HandlerFunc(clusterHandler.CreateCluster))).Methods(http.MethodPost) r.Handle(API_PREFIX+API_VERSION+"/clusters", authMiddleware.Handle(http.HandlerFunc(clusterHandler.GetClusters))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}", authMiddleware.Handle(http.HandlerFunc(clusterHandler.GetCluster))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/site-values", authMiddleware.Handle(http.HandlerFunc(clusterHandler.GetClusterSiteValues))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}", authMiddleware.Handle(http.HandlerFunc(clusterHandler.DeleteCluster))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/site-values", authMiddleware.Handle(http.HandlerFunc(clusterHandler.GetClusterSiteValues))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/install", authMiddleware.Handle(http.HandlerFunc(clusterHandler.InstallCluster))).Methods(http.MethodPost) appGroupHandler := delivery.NewAppGroupHandler(usecase.NewAppGroupUsecase(repoFactory, argoClient)) r.Handle(API_PREFIX+API_VERSION+"/app-groups", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.CreateAppGroup))).Methods(http.MethodPost) @@ -185,6 +186,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/favorite", authMiddleware.Handle(http.HandlerFunc(stackHandler.SetFavorite))).Methods(http.MethodPost) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/favorite", authMiddleware.Handle(http.HandlerFunc(stackHandler.DeleteFavorite))).Methods(http.MethodDelete) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/nodes", authMiddleware.Handle(http.HandlerFunc(stackHandler.GetNodes))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/install", authMiddleware.Handle(http.HandlerFunc(stackHandler.InstallStack))).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/alerttest", alertHandler.CreateAlert).Methods(http.MethodPost) // assets diff --git a/internal/usecase/cluster.go b/internal/usecase/cluster.go index 7bf77a6a..a175e9c6 100644 --- a/internal/usecase/cluster.go +++ b/internal/usecase/cluster.go @@ -26,7 +26,8 @@ type IClusterUsecase interface { Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]domain.Cluster, error) FetchByCloudAccountId(ctx context.Context, cloudAccountId uuid.UUID, pg *pagination.Pagination) (out []domain.Cluster, err error) Create(ctx context.Context, dto domain.Cluster) (clusterId domain.ClusterId, err error) - CreateByoh(ctx context.Context, dto domain.Cluster) (clusterId domain.ClusterId, err error) + Bootstrap(ctx context.Context, dto domain.Cluster) (clusterId domain.ClusterId, err error) + Install(ctx context.Context, clusterId domain.ClusterId) (err error) Get(ctx context.Context, clusterId domain.ClusterId) (out domain.Cluster, err error) GetClusterSiteValues(ctx context.Context, clusterId domain.ClusterId) (out domain.ClusterSiteValuesResponse, err error) Delete(ctx context.Context, clusterId domain.ClusterId) (err error) @@ -192,7 +193,7 @@ func (u *ClusterUsecase) Create(ctx context.Context, dto domain.Cluster) (cluste return clusterId, nil } -func (u *ClusterUsecase) CreateByoh(ctx context.Context, dto domain.Cluster) (clusterId domain.ClusterId, err error) { +func (u *ClusterUsecase) Bootstrap(ctx context.Context, dto domain.Cluster) (clusterId domain.ClusterId, err error) { user, ok := request.UserFrom(ctx) if !ok { return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") @@ -219,8 +220,7 @@ func (u *ClusterUsecase) CreateByoh(ctx context.Context, dto domain.Cluster) (cl return "", errors.Wrap(err, "Failed to create cluster") } - workflowId := "" - workflowId, err = u.argo.SumbitWorkflowFromWftpl( + workflowId, err := u.argo.SumbitWorkflowFromWftpl( "bootstrap-tks-usercluster", argowf.SubmitOptions{ Parameters: []string{ @@ -248,6 +248,57 @@ func (u *ClusterUsecase) CreateByoh(ctx context.Context, dto domain.Cluster) (cl return clusterId, nil } +func (u *ClusterUsecase) Install(ctx context.Context, clusterId domain.ClusterId) (err error) { + user, ok := request.UserFrom(ctx) + if !ok { + return httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") + } + + cluster, err := u.repo.Get(clusterId) + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("Invalid clusterId"), "C_INVALID_CLUSTER_ID", "") + } + if cluster.CloudService != domain.CloudService_BYOH { + return httpErrors.NewBadRequestError(fmt.Errorf("Invalid cloudService"), "C_INVALID_CLOUD_SERVICE", "") + } + + stackTemplate, err := u.stackTemplateRepo.Get(cluster.StackTemplateId) + if err != nil { + return httpErrors.NewBadRequestError(errors.Wrap(err, "Invalid stackTemplateId"), "", "") + } + if stackTemplate.CloudService != cluster.CloudService { + return httpErrors.NewBadRequestError(fmt.Errorf("Invalid cloudService for stackTemplate "), "", "") + } + + workflowId, err := u.argo.SumbitWorkflowFromWftpl( + "create-tks-usercluster", + argowf.SubmitOptions{ + Parameters: []string{ + fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")), + "contract_id=" + cluster.OrganizationId, + "cluster_id=" + cluster.ID.String(), + "site_name=" + cluster.ID.String(), + "template_name=" + stackTemplate.Template, + "git_account=" + viper.GetString("git-account"), + "creator=" + user.GetUserId().String(), + "cloud_account_id=NULL", + "base_repo_branch=" + viper.GetString("revision"), + //"manifest_repo_url=" + viper.GetString("git-base-url") + "/" + viper.GetString("git-account") + "/" + clusterId + "-manifests", + }, + }) + if err != nil { + log.ErrorWithContext(ctx, "failed to submit argo workflow template. err : ", err) + return err + } + log.InfoWithContext(ctx, "Successfully submited workflow: ", workflowId) + + if err := u.repo.InitWorkflow(cluster.ID, workflowId, domain.ClusterStatus_INSTALLING); err != nil { + return errors.Wrap(err, "Failed to initialize status") + } + + return nil +} + func (u *ClusterUsecase) Get(ctx context.Context, clusterId domain.ClusterId) (out domain.Cluster, err error) { cluster, err := u.repo.Get(clusterId) if err != nil { diff --git a/internal/usecase/stack.go b/internal/usecase/stack.go index 68532a20..4c6267b3 100644 --- a/internal/usecase/stack.go +++ b/internal/usecase/stack.go @@ -29,6 +29,7 @@ type IStackUsecase interface { GetByName(ctx context.Context, organizationId string, name string) (domain.Stack, error) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]domain.Stack, error) Create(ctx context.Context, dto domain.Stack) (stackId domain.StackId, err error) + Install(ctx context.Context, stackId domain.StackId) (err error) Update(ctx context.Context, dto domain.Stack) error Delete(ctx context.Context, dto domain.Stack) error GetKubeConfig(ctx context.Context, stackId domain.StackId) (kubeConfig string, err error) @@ -90,7 +91,10 @@ func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId do if dto.CloudService == domain.CloudService_BYOH { if dto.ClusterEndpoint == "" { - return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid userClusterDomain"), "C_INVALID_ADMINCLUSTER_URL", "") + return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid userClusterDomain"), "S_INVALID_ADMINCLUSTER_URL", "") + } + if dto.ClusterId == "" { + return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid clusterId"), "S_INVALID_CLUSTER_ID", "") } } else { if _, err = u.cloudAccountRepo.Get(dto.CloudAccountId); err != nil { @@ -104,7 +108,7 @@ func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId do log.InfoWithContext(ctx, err) } - workflow := "tks-stack-create-aws" + workflow := "tks-stack-create" workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ Parameters: []string{ fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")), @@ -152,6 +156,61 @@ func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId do return dto.ID, nil } +func (u *StackUsecase) Install(ctx context.Context, stackId domain.StackId) (err error) { + cluster, err := u.Get(ctx, stackId) + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("Invalid stackId"), "S_INVALID_STACK_ID", "") + } + + _, err = u.stackTemplateRepo.Get(cluster.StackTemplateId) + if err != nil { + return httpErrors.NewInternalServerError(errors.Wrap(err, "Invalid stackTemplateId"), "S_INVALID_STACK_TEMPLATE", "") + } + + clusters, err := u.clusterRepo.FetchByOrganizationId(cluster.OrganizationId, nil) + if err != nil { + return httpErrors.NewInternalServerError(errors.Wrap(err, "Failed to get clusters"), "S_FAILED_GET_CLUSTERS", "") + } + isPrimary := false + if len(clusters) == 0 { + isPrimary = true + } + log.DebugWithContext(ctx, "isPrimary ", isPrimary) + + if cluster.CloudService != domain.CloudService_BYOH { + return httpErrors.NewBadRequestError(fmt.Errorf("Invalid cloud service"), "S_INVALID_CLOUD_SERVICE", "") + } + + // Make stack nodes + var stackConf domain.StackConfResponse + if err = domain.Map(cluster.Conf, &stackConf); err != nil { + log.InfoWithContext(ctx, err) + } + + workflow := "tks-stack-create" + workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ + Parameters: []string{ + fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")), + "cluster_name=" + cluster.Name, + "description=" + cluster.Description, + "organization_id=" + cluster.OrganizationId, + "cloud_account_id=NULL", + "stack_template_id=" + cluster.StackTemplateId.String(), + "creator=" + (*cluster.CreatorId).String(), + "base_repo_branch=" + viper.GetString("revision"), + "infra_conf=" + strings.Replace(helper.ModelToJson(stackConf), "\"", "\\\"", -1), + "cloud_service=" + cluster.CloudService, + }, + }) + if err != nil { + log.ErrorWithContext(ctx, err) + return httpErrors.NewInternalServerError(err, "S_FAILED_TO_CALL_WORKFLOW", "") + } + log.DebugWithContext(ctx, "Submitted workflow: ", workflowId) + + 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 { diff --git a/pkg/api-client/api-client.go b/pkg/api-client/api-client.go index 1fd8d210..5b8aa675 100644 --- a/pkg/api-client/api-client.go +++ b/pkg/api-client/api-client.go @@ -79,31 +79,33 @@ func (c *ApiClientImpl) Get(path string) (out interface{}, err error) { } func (c *ApiClientImpl) Post(path string, input interface{}) (out interface{}, err error) { - return c.callWithBody("POST", path, input) + return c.callWithBody("api", "POST", path, input) } func (c *ApiClientImpl) Delete(path string, input interface{}) (out interface{}, err error) { - return c.callWithBody("DELETE", path, input) + return c.callWithBody("api", "DELETE", path, input) } func (c *ApiClientImpl) Put(path string, input interface{}) (out interface{}, err error) { - return c.callWithBody("PUT", path, input) + return c.callWithBody("api", "PUT", path, input) } func (c *ApiClientImpl) Patch(path string, input interface{}) (out interface{}, err error) { - return c.callWithBody("PATCH", path, input) + return c.callWithBody("api", "PATCH", path, input) } -func (c *ApiClientImpl) callWithBody(method string, path string, input interface{}) (out interface{}, err error) { +func (c *ApiClientImpl) callWithBody(prefix string, method string, path string, input interface{}) (out interface{}, err error) { pbytes, _ := json.Marshal(input) buff := bytes.NewBuffer(pbytes) - req, err := http.NewRequest(method, fmt.Sprintf("%s/api/1.0/%s", c.url, path), buff) + req, err := http.NewRequest(method, fmt.Sprintf("%s/%s/1.0/%s", c.url, prefix, path), buff) if err != nil { return nil, err } - req.Header.Add("Authorization", "Bearer "+c.token) + if prefix == "api" { + req.Header.Add("Authorization", "Bearer "+c.token) + } res, err := c.client.Do(req) if err != nil { return nil, err diff --git a/pkg/domain/cluster.go b/pkg/domain/cluster.go index 4c26cd32..81da5e16 100644 --- a/pkg/domain/cluster.go +++ b/pkg/domain/cluster.go @@ -105,6 +105,7 @@ type Cluster struct { CreatedAt time.Time UpdatedAt time.Time ClusterEndpoint string + IsStack bool } type ClusterConf struct { @@ -153,6 +154,7 @@ type CreateClusterRequest struct { CloudAccountId string `json:"cloudAccountId"` ClusterType string `json:"clusterType"` ClusterEndpoint string `json:"userClusterEndpoint,omitempty"` + IsStack bool `json:"isStack,omitempty"` TksCpNode int `json:"tksCpNode"` TksCpNodeMax int `json:"tksCpNodeMax,omitempty"` TksCpNodeType string `json:"tksCpNodeType,omitempty"` @@ -197,6 +199,7 @@ type ClusterResponse struct { CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` ClusterEndpoint string `json:"userClusterEndpoint,omitempty"` + IsStack bool `json:"isStack,omitempty"` } type SimpleClusterResponse struct { @@ -231,3 +234,8 @@ type GetClusterResponse struct { type GetClusterSiteValuesResponse struct { ClusterSiteValues ClusterSiteValuesResponse `json:"clusterSiteValues"` } + +type InstallClusterRequest struct { + ClusterId string `json:"clusterId" validate:"required"` + OrganizationId string `json:"organizationId" validate:"required"` +} diff --git a/pkg/domain/stack.go b/pkg/domain/stack.go index 1b4c1a61..770160ee 100644 --- a/pkg/domain/stack.go +++ b/pkg/domain/stack.go @@ -79,6 +79,7 @@ type Stack = struct { ID StackId Name string Description string + ClusterId string OrganizationId string CloudService string CloudAccountId uuid.UUID @@ -139,6 +140,7 @@ type StackNode struct { type CreateStackRequest struct { Name string `json:"name" validate:"required,name,rfc1123"` Description string `json:"description"` + ClusterId string `json:"clusterId"` CloudService string `json:"cloudService" validate:"required,oneof=AWS BYOH"` StackTemplateId string `json:"stackTemplateId" validate:"required"` CloudAccountId string `json:"cloudAccountId"` diff --git a/pkg/httpErrors/errorCode.go b/pkg/httpErrors/errorCode.go index 94b3ed6f..06d6f8c1 100644 --- a/pkg/httpErrors/errorCode.go +++ b/pkg/httpErrors/errorCode.go @@ -14,6 +14,7 @@ var errorMap = map[ErrorCode]string{ "C_INVALID_STACK_TEMPLATE_ID": "유효하지 않은 스택템플릿 아이디입니다. 스택템플릿 아이디를 확인하세요.", "C_INVALID_ASA_ID": "유효하지 않은 앱서빙앱 아이디입니다. 앱서빙앱 아이디를 확인하세요.", "C_INVALID_ASA_TASK_ID": "유효하지 않은 테스크 아이디입니다. 테스크 아이디를 확인하세요.", + "C_INVALID_CLOUD_SERVICE": "유효하지 않은 클라우드서비스입니다.", // Auth "A_INVALID_ID": "아이디가 존재하지 않습니다.", @@ -55,7 +56,9 @@ var errorMap = map[ErrorCode]string{ "S_FAILED_GET_CLUSTERS": "클러스터를 가져오는데 실패했습니다.", "S_FAILED_DELETE_EXISTED_ASA": "지우고자 하는 스택에 남아 있는 앱서빙앱이 있습니다.", "S_NOT_ENOUGH_QUOTA": "AWS 의 resource quota 가 부족합니다. 관리자에게 문의하세요.", - "C_INVALID_ADMINCLUSTER_URL": "BYOH 타입의 클러스터 생성은 반드시 userClusterEndpoint 값이 필요합니다.", + "S_INVALID_CLUSTER_URL": "BYOH 타입의 클러스터 생성은 반드시 userClusterEndpoint 값이 필요합니다.", + "S_INVALID_CLUSTER_ID": "BYOH 타입의 클러스터 생성은 반드시 clusterId 값이 필요합니다.", + "S_INVALID_CLOUD_SERVICE": "클라우드 서비스 타입이 잘못되었습니다.", // Alert "AL_NOT_FOUND_ALERT": "지정한 앨럿이 존재하지 않습니다.",