Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature. implementation byoh #156

Merged
merged 1 commit into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion api/swagger/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -4725,7 +4725,6 @@ const docTemplate = `{
"domain.CreateStackRequest": {
"type": "object",
"required": [
"cloudAccountId",
"cloudService",
"name",
"stackTemplateId"
Expand Down
1 change: 0 additions & 1 deletion api/swagger/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -4718,7 +4718,6 @@
"domain.CreateStackRequest": {
"type": "object",
"required": [
"cloudAccountId",
"cloudService",
"name",
"stackTemplateId"
Expand Down
1 change: 0 additions & 1 deletion api/swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,6 @@ definitions:
tksUserNodeType:
type: string
required:
- cloudAccountId
- cloudService
- name
- stackTemplateId
Expand Down
18 changes: 14 additions & 4 deletions internal/delivery/http/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,20 @@ func (h *ClusterHandler) CreateCluster(w http.ResponseWriter, r *http.Request) {
log.InfoWithContext(r.Context(), dto.Conf)

//txHandle := r.Context().Value("txHandle").(*gorm.DB)
clusterId, err := h.usecase.Create(r.Context(), dto)
if err != nil {
ErrorJSON(w, r, err)
return
clusterId := domain.ClusterId("")
if input.CloudService == domain.CloudService_BYOH {
clusterId, err = h.usecase.CreateByoh(r.Context(), dto)
if err != nil {
ErrorJSON(w, r, err)
return
}
} else {
clusterId, err = h.usecase.Create(r.Context(), dto)
if err != nil {
ErrorJSON(w, r, err)
return
}

}

var out domain.CreateClusterResponse
Expand Down
28 changes: 17 additions & 11 deletions internal/delivery/http/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,6 @@ func (h *StackHandler) CreateStack(w http.ResponseWriter, r *http.Request) {
return
}

if input.CloudService == domain.CloudService_BYOH {
if input.AdminClusterUrl == "" {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid adminClusterUrl"), "C_INVALID_ADMINCLUSTER_URL", ""))
return
}
}

var dto domain.Stack
if err = serializer.Map(input, &dto); err != nil {
log.InfoWithContext(r.Context(), err)
Expand All @@ -65,10 +58,23 @@ func (h *StackHandler) CreateStack(w http.ResponseWriter, r *http.Request) {
}
dto.OrganizationId = organizationId

stackId, err := h.usecase.Create(r.Context(), dto)
if err != nil {
ErrorJSON(w, r, err)
return
stackId := domain.StackId("")
if input.CloudService == domain.CloudService_BYOH {
if input.AdminClusterUrl == "" {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid adminClusterUrl"), "C_INVALID_ADMINCLUSTER_URL", ""))
return
}
stackId, err = h.usecase.CreateByoh(r.Context(), dto)
if err != nil {
ErrorJSON(w, r, err)
return
}
} else {
stackId, err = h.usecase.Create(r.Context(), dto)
if err != nil {
ErrorJSON(w, r, err)
return
}
}

out := domain.CreateStackResponse{
Expand Down
103 changes: 68 additions & 35 deletions internal/usecase/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ 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)
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)
Expand Down Expand Up @@ -159,42 +160,74 @@ func (u *ClusterUsecase) Create(ctx context.Context, dto domain.Cluster) (cluste
return "", errors.Wrap(err, "Failed to create cluster")
}

workflowId := ""
if dto.CloudService == domain.CloudService_BYOH {
workflowId, err = u.argo.SumbitWorkflowFromWftpl(
"bootstrap-tks-usercluster",
argowf.SubmitOptions{
Parameters: []string{
fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")),
"contract_id=" + dto.OrganizationId,
"cluster_id=" + clusterId.String(),
"site_name=" + clusterId.String(),
"template_name=" + stackTemplate.Template,
"git_account=" + viper.GetString("git-account"),
"creator=" + user.GetUserId().String(),
"cloud_account_id=" + tksCloudAccountId,
"base_repo_branch=" + viper.GetString("revision"),
//"manifest_repo_url=" + viper.GetString("git-base-url") + "/" + viper.GetString("git-account") + "/" + clusterId + "-manifests",
},
})
} else {
workflowId, err = u.argo.SumbitWorkflowFromWftpl(
"create-tks-usercluster",
argowf.SubmitOptions{
Parameters: []string{
fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")),
"contract_id=" + dto.OrganizationId,
"cluster_id=" + clusterId.String(),
"site_name=" + clusterId.String(),
"template_name=" + stackTemplate.Template,
"git_account=" + viper.GetString("git-account"),
"creator=" + user.GetUserId().String(),
"cloud_account_id=" + tksCloudAccountId,
"base_repo_branch=" + viper.GetString("revision"),
//"manifest_repo_url=" + viper.GetString("git-base-url") + "/" + viper.GetString("git-account") + "/" + clusterId + "-manifests",
},
})
workflowId, err := u.argo.SumbitWorkflowFromWftpl(
"create-tks-usercluster",
argowf.SubmitOptions{
Parameters: []string{
fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")),
"contract_id=" + dto.OrganizationId,
"cluster_id=" + clusterId.String(),
"site_name=" + clusterId.String(),
"template_name=" + stackTemplate.Template,
"git_account=" + viper.GetString("git-account"),
"creator=" + user.GetUserId().String(),
"cloud_account_id=" + tksCloudAccountId,
"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(clusterId, workflowId, domain.ClusterStatus_INSTALLING); err != nil {
return "", errors.Wrap(err, "Failed to initialize status")
}

return clusterId, nil
}

func (u *ClusterUsecase) CreateByoh(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"), "", "")
}

_, err = u.repo.GetByName(dto.OrganizationId, dto.Name)
if err == nil {
return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "", "")
}

stackTemplate, err := u.stackTemplateRepo.Get(dto.StackTemplateId)
if err != nil {
return "", httpErrors.NewBadRequestError(errors.Wrap(err, "Invalid stackTemplateId"), "", "")
}

userId := user.GetUserId()
dto.CreatorId = &userId
clusterId, err = u.repo.Create(dto)
if err != nil {
return "", errors.Wrap(err, "Failed to create cluster")
}

workflowId := ""
workflowId, err = u.argo.SumbitWorkflowFromWftpl(
"bootstrap-tks-usercluster",
argowf.SubmitOptions{
Parameters: []string{
fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")),
"contract_id=" + dto.OrganizationId,
"cluster_id=" + clusterId.String(),
"site_name=" + clusterId.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"),
},
})
if err != nil {
log.ErrorWithContext(ctx, "failed to submit argo workflow template. err : ", err)
return "", err
Expand Down
88 changes: 80 additions & 8 deletions internal/usecase/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,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)
CreateByoh(ctx context.Context, dto domain.Stack) (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)
Expand Down Expand Up @@ -96,16 +97,87 @@ func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId do
log.InfoWithContext(ctx, err)
}

workflow := ""
if dto.CloudService == "AWS" {
workflow = "tks-stack-create-aws"
} else if dto.CloudService == "BYOH" {
workflow = "tks-stack-create-byoh"
} else {
log.ErrorWithContext(ctx, "Invalid cloud service : ", dto.CloudService)
return "", httpErrors.NewInternalServerError(fmt.Errorf("Invalid cloud service. %s", dto.CloudService), "", "")
workflow := "tks-stack-create-aws"
workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{
Parameters: []string{
fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")),
"cluster_name=" + dto.Name,
"description=" + dto.Description,
"organization_id=" + dto.OrganizationId,
"cloud_account_id=" + dto.CloudAccountId.String(),
"stack_template_id=" + dto.StackTemplateId.String(),
"creator=" + user.GetUserId().String(),
"base_repo_branch=" + viper.GetString("revision"),
"infra_conf=" + strings.Replace(helper.ModelToJson(stackConf), "\"", "\\\"", -1),
"cloud_service=" + dto.CloudService,
},
})
if err != nil {
log.ErrorWithContext(ctx, err)
return "", httpErrors.NewInternalServerError(err, "S_FAILED_TO_CALL_WORKFLOW", "")
}
log.DebugWithContext(ctx, "Submitted workflow: ", workflowId)

// wait & get clusterId ( max 1min )
dto.ID = domain.StackId("")
for i := 0; i < 60; i++ {
time.Sleep(time.Second * 5)
workflow, err := u.argo.GetWorkflow("argo", workflowId)
if err != nil {
return "", err
}

log.DebugWithContext(ctx, "workflow ", workflow)
if workflow.Status.Phase != "" && workflow.Status.Phase != "Running" {
return "", fmt.Errorf("Invalid workflow status [%s]", workflow.Status.Phase)
}

cluster, err := u.clusterRepo.GetByName(dto.OrganizationId, dto.Name)
if err != nil {
continue
}
if cluster.Name == dto.Name {
dto.ID = domain.StackId(cluster.ID)
break
}
}

return dto.ID, nil
}

func (u *StackUsecase) CreateByoh(ctx context.Context, dto domain.Stack) (stackId domain.StackId, err error) {
user, ok := request.UserFrom(ctx)
if !ok {
return "", httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "")
}

_, err = u.GetByName(ctx, dto.OrganizationId, dto.Name)
if err == nil {
return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "S_CREATE_ALREADY_EXISTED_NAME", "")
}

_, err = u.stackTemplateRepo.Get(dto.StackTemplateId)
if err != nil {
return "", httpErrors.NewInternalServerError(errors.Wrap(err, "Invalid stackTemplateId"), "S_INVALID_STACK_TEMPLATE", "")
}

clusters, err := u.clusterRepo.FetchByOrganizationId(dto.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)

// Make stack nodes
var stackConf domain.StackConfResponse
if err = domain.Map(dto.Conf, &stackConf); err != nil {
log.InfoWithContext(ctx, err)
}

workflow := "tks-stack-create-byoh"
workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{
Parameters: []string{
fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")),
Expand Down
2 changes: 1 addition & 1 deletion pkg/domain/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ type CreateClusterRequest struct {
StackTemplateId string `json:"stackTemplateId" validate:"required"`
Name string `json:"name" validate:"required,name"`
Description string `json:"description"`
CloudAccountId string `json:"cloudAccountId" validate:"required"`
CloudAccountId string `json:"cloudAccountId"`
ClusterType string `json:"clusterType"`
TksCpNode int `json:"tksCpNode"`
TksCpNodeMax int `json:"tksCpNodeMax,omitempty"`
Expand Down
2 changes: 1 addition & 1 deletion pkg/domain/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ type CreateStackRequest struct {
Description string `json:"description"`
CloudService string `json:"cloudService" validate:"required,oneof=AWS BYOH"`
StackTemplateId string `json:"stackTemplateId" validate:"required"`
CloudAccountId string `json:"cloudAccountId" validate:"required"`
CloudAccountId string `json:"cloudAccountId"`
TksCpNode int `json:"tksCpNode"`
TksCpNodeMax int `json:"tksCpNodeMax,omitempty"`
TksCpNodeType string `json:"tksCpNodeType,omitempty"`
Expand Down
Loading