From 051f6ab9ca4d74d8659a1fb64d64a40434c038cd Mon Sep 17 00:00:00 2001 From: Sooyoung Kim Date: Thu, 31 Oct 2024 10:24:26 +0000 Subject: [PATCH] [k8scluster] support k8sclusterDynamic like mciDynamic o API changes - (rename) /tumblebug/availableK8sClusterVersion to availableK8sVersion - (rename) /tumblebug/availableK8sClusterNodeImage to availableK8sNodeImage - (rename) /checkNodeGroupsOnK8sCreation to checkK8sNodeGroupsOnK8sCreation - (add) /requiredK8sSubnetCount - (add) /k8sclusterDynamicCheckRequest - (add) /ns/{nsId}/k8sclusterDynamic - (add) /ns/{nsId}/control/k8scluster/{k8sClusterId} - (add) /k8sclusterRecommendNode --- assets/cloudspec.csv | 10 +- assets/k8sclusterinfo.yaml | 9 +- src/api/rest/server/resource/k8scluster.go | 215 +++- src/api/rest/server/server.go | 13 +- src/core/common/utility.go | 109 +- src/core/infra/provisioning.go | 375 ++++++- src/core/model/config.go | 10 + src/core/model/k8scluster.go | 67 ++ src/core/resource/k8scluster.go | 1170 ++++++++------------ 9 files changed, 1246 insertions(+), 732 deletions(-) diff --git a/assets/cloudspec.csv b/assets/cloudspec.csv index bb9fd62d5..5185b9674 100644 --- a/assets/cloudspec.csv +++ b/assets/cloudspec.csv @@ -565,11 +565,11 @@ IBM,jp-tok,bx2-2x8,0.096,34.18,,,,,,,,,,default,default,,,,,,vm IBM,us-east,bx2-2x8,0.096,34.18,,,,,,,,,,default,default,,,,,,vm IBM,us-south,bx2-2x8,0.096,34.18,,,,,,,,,,default,default,,,,,,vm TENCENT,all,S3.SMALL2,0.04,59.98,,,,,,,,,,default,default,,,,,,vm -TENCENT,all,SA2.MEDIUM4,0.04,59.98,,,,,,,,,,default,default,,,,,,vm -TENCENT,all,S2.MEDIUM4,0.04,59.98,,,,,,,,,,default,default,,,,,,vm -TENCENT,all,S5.MEDIUM4,0.03,59.98,,,,,,,,,,default,default,,,,,,vm -TENCENT,all,S3.MEDIUM8,0.04,59.98,,,,,,,,,,default,default,,,,,,vm -TENCENT,all,S8.MEDIUM8,0.04,59.98,,,,,,,,,,default,default,,,,,,vm +TENCENT,all,SA2.MEDIUM4,0.04,59.98,,,,,,,,,,default,default,,,,,,vm|k8s +TENCENT,all,S2.MEDIUM4,0.04,59.98,,,,,,,,,,default,default,,,,,,vm|k8s +TENCENT,all,S5.MEDIUM4,0.03,59.98,,,,,,,,,,default,default,,,,,,vm|k8s +TENCENT,all,S3.MEDIUM8,0.04,59.98,,,,,,,,,,default,default,,,,,,vm|k8s +TENCENT,all,S8.MEDIUM8,0.04,59.98,,,,,,,,,,default,default,,,,,,vm|k8s TENCENT,all,PNV4.7XLARGE116,1.71,64.18,,,,,,,,,,default,default,gpu,NVIDIA A10,1,24,,vm NCP,KR,SPSVRSSD00000005,,,,,,,,,,,,default,default,,,,,,vm NCP,KR,SPSVRSSD00000007,,,,,,,,,,,,default,default,,,,,,vm diff --git a/assets/k8sclusterinfo.yaml b/assets/k8sclusterinfo.yaml index c87e5cfbc..a7cdaede3 100644 --- a/assets/k8sclusterinfo.yaml +++ b/assets/k8sclusterinfo.yaml @@ -4,7 +4,9 @@ # The file is in YAML format and contains the following fields: # k8scluster: Top level key # : Name of the CSP -# nodeGroupsOnCreation: +# nodeGroupsOnCreation: [true/false] +# nodeImageDesignation: [true/false] +# requiredSubnetCount: [required number of subnets to create a kubernetes cluster, default value is 1] # version: # - region: [region1, region2, common(special keyword: most of regions)] # @@ -13,6 +15,7 @@ k8scluster: azure: nodeGroupsOnCreation: true nodeImageDesignation: false + requiredSubnetCount: 1 version: - region: [westeurope,westus] available: @@ -43,6 +46,7 @@ k8scluster: gcp: nodeGroupsOnCreation: true nodeImageDesignation: true + requiredSubnetCount: 1 version: - region: [common] available: @@ -67,6 +71,7 @@ k8scluster: alibaba: nodeGroupsOnCreation: false nodeImageDesignation: true + requiredSubnetCount: 1 version: # ServiceUnavailable or NotSupportedSLB - region: [me-east-1, cn-zhangjiakou, cn-hangzhou, cn-shenzhen, cn-chengdu, ap-south-1, ap-sourtheast-2] @@ -90,6 +95,7 @@ k8scluster: nhncloud: nodeGroupsOnCreation: true nodeImageDesignation: true + requiredSubnetCount: 1 version: - region: [kr1, kr2] available: @@ -112,6 +118,7 @@ k8scluster: tencent: nodeGroupsOnCreation: false nodeImageDesignation: true + requiredSubnetCount: 1 version: - region: [common] available: diff --git a/src/api/rest/server/resource/k8scluster.go b/src/api/rest/server/resource/k8scluster.go index e263ddfc0..25d38ab6c 100644 --- a/src/api/rest/server/resource/k8scluster.go +++ b/src/api/rest/server/resource/k8scluster.go @@ -15,18 +15,20 @@ limitations under the License. package resource import ( + "fmt" "net/http" "github.com/cloud-barista/cb-tumblebug/src/core/common" + "github.com/cloud-barista/cb-tumblebug/src/core/infra" "github.com/cloud-barista/cb-tumblebug/src/core/model" "github.com/cloud-barista/cb-tumblebug/src/core/resource" "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" ) -// RestGetAvailableK8sClusterVersion func is a rest api wrapper for GetAvailableK8sClusterVersion. -// RestGetAvailableK8sClusterVersion godoc -// @ID GetAvailableK8sClusterVersion +// RestGetAvailableK8sVersion func is a rest api wrapper for GetAvailableK8sVersion. +// RestGetAvailableK8sVersion godoc +// @ID GetAvailableK8sVersion // @Summary Get available kubernetes cluster version // @Description Get available kubernetes cluster version // @Tags [Kubernetes] Cluster Management @@ -37,19 +39,19 @@ import ( // @Success 200 {object} model.K8sClusterVersionDetailAvailable // @Failure 404 {object} model.SimpleMsg // @Failure 500 {object} model.SimpleMsg -// @Router /availableK8sClusterVersion [get] -func RestGetAvailableK8sClusterVersion(c echo.Context) error { +// @Router /availableK8sVersion [get] +func RestGetAvailableK8sVersion(c echo.Context) error { providerName := c.QueryParam("providerName") regionName := c.QueryParam("regionName") - content, err := common.GetAvailableK8sClusterVersion(providerName, regionName) + content, err := common.GetAvailableK8sVersion(providerName, regionName) return common.EndRequestWithLog(c, err, content) } -// RestGetAvailableK8sClusterNodeImage func is a rest api wrapper for GetAvailableK8sClusterNodeImage. -// RestGetAvailableK8sClusterNodeImage godoc -// @ID GetAvailableK8sClusterNodeImage +// RestGetAvailableK8sNodeImage func is a rest api wrapper for GetAvailableK8sNodeImage. +// RestGetAvailableK8sNodeImage godoc +// @ID GetAvailableK8sNodeImage // @Summary (UNDER DEVELOPMENT!!!) Get available kubernetes cluster node image // @Description (UNDER DEVELOPMENT!!!) Get available kubernetes cluster node image // @Tags [Kubernetes] Cluster Management @@ -60,19 +62,19 @@ func RestGetAvailableK8sClusterVersion(c echo.Context) error { // @Success 200 {object} model.K8sClusterNodeImageDetailAvailable // @Failure 404 {object} model.SimpleMsg // @Failure 500 {object} model.SimpleMsg -// @Router /availableK8sClusterNodeImage [get] -func RestGetAvailableK8sClusterNodeImage(c echo.Context) error { +// @Router /availableK8sNodeImage [get] +func RestGetAvailableK8sNodeImage(c echo.Context) error { providerName := c.QueryParam("providerName") regionName := c.QueryParam("regionName") - content, err := common.GetAvailableK8sClusterNodeImage(providerName, regionName) + content, err := common.GetAvailableK8sNodeImage(providerName, regionName) return common.EndRequestWithLog(c, err, content) } -// RestCheckNodeGroupsOnK8sCreation func is a rest api wrapper for CheckNodeGroupsOnK8sCreation. -// RestCheckNodeGroupsOnK8sCreation godoc -// @ID CheckNodeGroupsOnK8sCreation +// RestCheckK8sNodeGroupsOnK8sCreation func is a rest api wrapper for GetModelK8sNodeGroupsOnK8sCreation. +// RestCheckK8sNodeGroupsOnK8sCreation godoc +// @ID CheckK8sNodeGroupsOnK8sCreation // @Summary Check whether nodegroups are required during the k8scluster creation // @Description Check whether nodegroups are required during the k8scluster creation // @Tags [Kubernetes] Cluster Management @@ -82,12 +84,54 @@ func RestGetAvailableK8sClusterNodeImage(c echo.Context) error { // @Success 200 {object} model.K8sClusterNodeGroupsOnCreation // @Failure 404 {object} model.SimpleMsg // @Failure 500 {object} model.SimpleMsg -// @Router /checkNodeGroupsOnK8sCreation [get] -func RestCheckNodeGroupsOnK8sCreation(c echo.Context) error { +// @Router /checkK8sNodeGroupsOnK8sCreation [get] +func RestCheckK8sNodeGroupsOnK8sCreation(c echo.Context) error { providerName := c.QueryParam("providerName") - content, err := common.CheckNodeGroupsOnK8sCreation(providerName) + content, err := common.GetModelK8sNodeGroupsOnK8sCreation(providerName) + return common.EndRequestWithLog(c, err, content) +} + +// RestCheckK8sNodeImageDesignation func is a rest api wrapper for GetK8sNodeImageDesignation. +// RestCheckK8sNodeImageDesignation godoc +// @ID CheckK8sNodeImageDesignation +// @Summary Check whether node image designation is possible to create a k8scluster +// @Description Check whether node image designation is possible to create a k8scluster +// @Tags [Kubernetes] Cluster Management +// @Accept json +// @Produce json +// @Param providerName query string true "Name of the CSP to retrieve" +// @Success 200 {object} model.K8sClusterNodeImageDesignation +// @Failure 404 {object} model.SimpleMsg +// @Failure 500 {object} model.SimpleMsg +// @Router /checkK8sNodeImageDesignation [get] +func RestCheckK8sNodeImageDesignation(c echo.Context) error { + + providerName := c.QueryParam("providerName") + + content, err := common.GetModelK8sNodeImageDesignation(providerName) + return common.EndRequestWithLog(c, err, content) +} + +// RestGetRequiredK8sSubnetCount func is a rest api wrapper for GetModelK8sRequiredSubnetCount. +// RestGetRequiredK8sSubnetCount godoc +// @ID GetRequiredK8sSubnetCount +// @Summary Get the required subnet count to create a k8scluster +// @Description Get the required subnet count to create a k8scluster +// @Tags [Kubernetes] Cluster Management +// @Accept json +// @Produce json +// @Param providerName query string true "Name of the CSP to retrieve" +// @Success 200 {object} model.K8sClusterRequiredSubnetCount +// @Failure 404 {object} model.SimpleMsg +// @Failure 500 {object} model.SimpleMsg +// @Router /requiredK8sSubnetCount [get] +func RestGetRequiredK8sSubnetCount(c echo.Context) error { + + providerName := c.QueryParam("providerName") + + content, err := common.GetModelK8sRequiredSubnetCount(providerName) return common.EndRequestWithLog(c, err, content) } @@ -203,6 +247,7 @@ func RestPostK8sNodeGroup(c echo.Context) error { // @Param nsId path string true "Namespace ID" default(default) // @Param k8sClusterId path string true "K8sCluster ID" default(k8scluster-01) // @Param k8sNodeGroupName path string true "K8sNodeGroup Name" default(ng-01) +// @Param option query string false "Option for K8sNodeGroup deletion" Enums(force) // @Success 200 {object} model.SimpleMsg // @Failure 404 {object} model.SimpleMsg // @Router /ns/{nsId}/k8scluster/{k8sClusterId}/k8snodegroup/{k8sNodeGroupName} [delete] @@ -212,9 +257,9 @@ func RestDeleteK8sNodeGroup(c echo.Context) error { k8sClusterId := c.Param("k8sClusterId") k8sNodeGroupName := c.Param("k8sNodeGroupName") - forceFlag := c.QueryParam("force") + optionFlag := c.QueryParam("option") - res, err := resource.RemoveK8sNodeGroup(nsId, k8sClusterId, k8sNodeGroupName, forceFlag) + res, err := resource.RemoveK8sNodeGroup(nsId, k8sClusterId, k8sNodeGroupName, optionFlag) if err != nil { log.Error().Err(err).Msg("") mapA := map[string]string{"message": err.Error()} @@ -404,6 +449,7 @@ func RestGetAllK8sCluster(c echo.Context) error { // @Produce json // @Param nsId path string true "Namespace ID" default(default) // @Param k8sClusterId path string true "K8sCluster ID" default(k8scluster-01) +// @Param option query string false "Option for K8sCluster deletion" Enums(force) // @Success 200 {object} model.SimpleMsg // @Failure 404 {object} model.SimpleMsg // @Router /ns/{nsId}/k8scluster/{k8sClusterId} [delete] @@ -412,9 +458,9 @@ func RestDeleteK8sCluster(c echo.Context) error { nsId := c.Param("nsId") k8sClusterId := c.Param("k8sClusterId") - forceFlag := c.QueryParam("force") + optionFlag := c.QueryParam("option") - res, err := resource.DeleteK8sCluster(nsId, k8sClusterId, forceFlag) + res, err := resource.DeleteK8sCluster(nsId, k8sClusterId, optionFlag) if err != nil { log.Error().Err(err).Msg("") mapA := map[string]string{"message": err.Error()} @@ -440,6 +486,7 @@ func RestDeleteK8sCluster(c echo.Context) error { // @Produce json // @Param nsId path string true "Namespace ID" default(default) // @Param match query string false "Delete resources containing matched ID-substring only" default() +// @Param option query string false "Option for K8sCluster deletion" Enums(force) // @Success 200 {object} model.IdList // @Failure 404 {object} model.SimpleMsg // @Router /ns/{nsId}/k8scluster [delete] @@ -447,10 +494,10 @@ func RestDeleteAllK8sCluster(c echo.Context) error { nsId := c.Param("nsId") - forceFlag := c.QueryParam("force") + optionFlag := c.QueryParam("option") subString := c.QueryParam("match") - output, err := resource.DeleteAllK8sCluster(nsId, subString, forceFlag) + output, err := resource.DeleteAllK8sCluster(nsId, subString, optionFlag) if err != nil { log.Error().Err(err).Msg("") mapA := map[string]string{"message": err.Error()} @@ -497,3 +544,123 @@ func RestPutUpgradeK8sCluster(c echo.Context) error { return c.JSON(http.StatusOK, content) } + +// RestPostK8sClusterDynamicCheckRequest godoc +// @ID PostK8sClusterDynamicCheckRequest +// @Summary Check available ConnectionConfig list for creating K8sCluster Dynamically +// @Description Check available ConnectionConfig list before create K8sCluster Dynamically from common spec and image +// @Tags [Kubernetes] Cluster Management +// @Accept json +// @Produce json +// @Param k8sclusterReq body model.K8sClusterConnectionConfigCandidatesReq true "Details for K8sCluster dynamic request information" +// @Success 200 {object} model.CheckK8sClusterDynamicReqInfo +// @Failure 404 {object} model.SimpleMsg +// @Failure 500 {object} model.SimpleMsg +// @Router /k8sclusterDynamicCheckRequest [post] +func RestPostK8sClusterDynamicCheckRequest(c echo.Context) error { + + req := &model.K8sClusterConnectionConfigCandidatesReq{} + if err := c.Bind(req); err != nil { + return common.EndRequestWithLog(c, err, nil) + } + + result, err := infra.CheckK8sClusterDynamicReq(req) + return common.EndRequestWithLog(c, err, result) +} + +// RestPostK8sClusterDynamic godoc +// @ID PostK8sClusterDynamic +// @Summary Create K8sCluster Dynamically +// @Description Create K8sCluster Dynamically from common spec and image +// @Tags [Kubernetes] Cluster Management +// @Accept json +// @Produce json +// @Param nsId path string true "Namespace ID" default(default) +// @Param k8sclusterReq body model.TbK8sClusterDynamicReq true "Request body to provision K8sCluster dynamically.
Must include commonSpec and commonImage info.
(ex: {name: k8scluster-01, commonImage: azure+koreacentral+ubuntu22.04, commonSpec: azure+koreacentral+Standard_B2s}]})
You can use /k8sclusterRecommendNode and /k8sclusterDynamicCheckRequest to get it.
Check the guide: https://github.com/cloud-barista/cb-tumblebug/discussions/1570" +// @Param option query string false "Option for K8sCluster creation" Enums(hold) +// @Param x-request-id header string false "Custom request ID" +// @Success 200 {object} model.TbK8sClusterInfo +// @Failure 404 {object} model.SimpleMsg +// @Failure 500 {object} model.SimpleMsg +// @Router /ns/{nsId}/k8sclusterDynamic [post] +func RestPostK8sClusterDynamic(c echo.Context) error { + reqID := c.Request().Header.Get(echo.HeaderXRequestID) + + nsId := c.Param("nsId") + optionFlag := c.QueryParam("option") + + req := &model.TbK8sClusterDynamicReq{} + if err := c.Bind(req); err != nil { + log.Warn().Err(err).Msg("invalid request") + return common.EndRequestWithLog(c, err, nil) + } + + result, err := infra.CreateK8sClusterDynamic(reqID, nsId, req, optionFlag) + if err != nil { + log.Error().Err(err).Msg("failed to create K8sCluster dynamically") + return common.EndRequestWithLog(c, err, nil) + } + return c.JSON(http.StatusOK, result) +} + +// RestGetControlK8sCluster godoc +// @ID GetControlK8sCluster +// @Summary Control the creation of K8sCluster (continue, withdraw) +// @Description Control the creation of K8sCluster (continue, withdraw) +// @Tags [Kubernetes] Cluster Management +// @Accept json +// @Produce json +// @Param nsId path string true "Namespace ID" default(default) +// @Param k8sClusterId path string true "K8sCluster ID" default(k8scluster-01) +// @Param action query string true "Action to K8sCluster" Enums(continue, withdraw) +// @Success 200 {object} model.SimpleMsg +// @Failure 404 {object} model.SimpleMsg +// @Failure 500 {object} model.SimpleMsg +// @Router /ns/{nsId}/control/k8scluster/{k8sClusterId} [get] +func RestGetControlK8sCluster(c echo.Context) error { + + nsId := c.Param("nsId") + k8sClusterId := c.Param("k8sClusterId") + + action := c.QueryParam("action") + returnObj := model.SimpleMsg{} + + if action == "continue" || action == "withdraw" { + + resultString, err := resource.HandleK8sClusterAction(nsId, k8sClusterId, action) + if err != nil { + return common.EndRequestWithLog(c, err, returnObj) + } + returnObj.Message = resultString + return common.EndRequestWithLog(c, err, returnObj) + + } else { + err := fmt.Errorf("'action' should be one of these: continue, withdraw") + return common.EndRequestWithLog(c, err, returnObj) + } +} + +// RestRecommendNode godoc +// @ID RecommendNode +// @Summary Recommend K8sCluster's Node plan (filter and priority) +// @Description Recommend K8sCluster's Node plan (filter and priority) Find details from https://github.com/cloud-barista/cb-tumblebug/discussions/1234 +// @Tags [Kubernetes] Cluster Management +// @Accept json +// @Produce json +// @Param deploymentPlan body model.DeploymentPlan false "Recommend K8sCluster's Node plan (filter and priority)" +// @Success 200 {object} []model.TbSpecInfo +// @Failure 404 {object} model.SimpleMsg +// @Failure 500 {object} model.SimpleMsg +// @Router /k8sclusterRecommendNode [post] +func RestRecommendNode(c echo.Context) error { + + nsId := model.SystemCommonNs + + u := &model.DeploymentPlan{} + if err := c.Bind(u); err != nil { + return common.EndRequestWithLog(c, err, nil) + } + + content, err := infra.RecommendVm(nsId, *u) + return common.EndRequestWithLog(c, err, content) +} diff --git a/src/api/rest/server/server.go b/src/api/rest/server/server.go index c1c728f5e..5f0dfd617 100644 --- a/src/api/rest/server/server.go +++ b/src/api/rest/server/server.go @@ -381,9 +381,11 @@ func RunServer() { g.PUT("/:nsId/monitoring/status/mci/:mciId/vm/:vmId", rest_infra.RestPutMonitorAgentStatusInstalled) // K8sCluster - e.GET("/tumblebug/availableK8sClusterVersion", rest_resource.RestGetAvailableK8sClusterVersion) - e.GET("/tumblebug/availableK8sClusterNodeImage", rest_resource.RestGetAvailableK8sClusterNodeImage) - e.GET("/tumblebug/checkNodeGroupsOnK8sCreation", rest_resource.RestCheckNodeGroupsOnK8sCreation) + e.GET("/tumblebug/availableK8sVersion", rest_resource.RestGetAvailableK8sVersion) + e.GET("/tumblebug/availableK8sNodeImage", rest_resource.RestGetAvailableK8sNodeImage) + e.GET("/tumblebug/checkK8sNodeGroupsOnK8sCreation", rest_resource.RestCheckK8sNodeGroupsOnK8sCreation) + e.GET("/tumblebug/checkK8sNodeImageDesignation", rest_resource.RestCheckK8sNodeImageDesignation) + e.GET("/tumblebug/requiredK8sSubnetCount", rest_resource.RestGetRequiredK8sSubnetCount) g.POST("/:nsId/k8scluster", rest_resource.RestPostK8sCluster) g.POST("/:nsId/k8scluster/:k8sClusterId/k8snodegroup", rest_resource.RestPostK8sNodeGroup) g.DELETE("/:nsId/k8scluster/:k8sClusterId/k8snodegroup/:k8sNodeGroupName", rest_resource.RestDeleteK8sNodeGroup) @@ -397,6 +399,11 @@ func RunServer() { g.DELETE("/:nsId/k8scluster", rest_resource.RestDeleteAllK8sCluster) g.PUT("/:nsId/k8scluster/:k8sClusterId/upgrade", rest_resource.RestPutUpgradeK8sCluster) + e.POST("/tumblebug/k8sclusterRecommendNode", rest_resource.RestRecommendNode) + e.POST("/tumblebug/k8sclusterDynamicCheckRequest", rest_resource.RestPostK8sClusterDynamicCheckRequest) + g.POST("/:nsId/k8sclusterDynamic", rest_resource.RestPostK8sClusterDynamic) + g.GET("/:nsId/control/k8scluster/:k8sClusterId", rest_resource.RestGetControlK8sCluster) + // Network Load Balancer g.POST("/:nsId/mci/:mciId/mcSwNlb", rest_infra.RestPostMcNLB) g.POST("/:nsId/mci/:mciId/nlb", rest_infra.RestPostNLB) diff --git a/src/core/common/utility.go b/src/core/common/utility.go index 8871462c9..57889bcad 100644 --- a/src/core/common/utility.go +++ b/src/core/common/utility.go @@ -232,6 +232,23 @@ func GenResourceKey(nsId string, resourceType string, resourceId string) string } } +// GenK8sClusterKey is func to generate a key from K8sCluster ID +func GenK8sClusterKey(nsId string, k8sClusterId string) string { + err := CheckString(nsId) + if err != nil { + log.Error().Err(err).Msg("") + return "/invalidKey" + } + + err = CheckString(k8sClusterId) + if err != nil { + log.Err(err).Msg("Failed to Generate K8sCluster Key") + return "/invalidKey" + } + + return fmt.Sprintf("/ns/%s/k8scluster/%s", nsId, k8sClusterId) +} + // GenChildResourceKey is func to generate a key from resource type and id func GenChildResourceKey(nsId string, resourceType string, parentResourceId string, resourceId string) string { @@ -1288,8 +1305,8 @@ func getK8sClusterDetail(providerName string) *model.K8sClusterDetail { return k8sClusterDetail } -// GetAvailableK8sClusterVersion is func to get available kubernetes cluster versions for provider and region from model.K8sClusterInfo -func GetAvailableK8sClusterVersion(providerName string, regionName string) (*[]model.K8sClusterVersionDetailAvailable, error) { +// GetAvailableK8sVersion is func to get available kubernetes cluster versions for provider and region from model.K8sClusterInfo +func GetAvailableK8sVersion(providerName string, regionName string) (*[]model.K8sClusterVersionDetailAvailable, error) { // // Check available K8sCluster version in k8sclusterinfo.yaml // @@ -1337,8 +1354,8 @@ func GetAvailableK8sClusterVersion(providerName string, regionName string) (*[]m return nil, fmt.Errorf("no entry for provider(%s):region(%s)", providerName, regionName) } -// GetAvailableK8sClusterNodeImage is func to get available kubernetes cluster node images for provider and region from model.K8sClusterInfo -func GetAvailableK8sClusterNodeImage(providerName string, regionName string) (*[]model.K8sClusterNodeImageDetailAvailable, error) { +// GetAvailableK8sNodeImage is func to get available kubernetes cluster node images for provider and region from model.K8sClusterInfo +func GetAvailableK8sNodeImage(providerName string, regionName string) (*[]model.K8sClusterNodeImageDetailAvailable, error) { // // Check available K8sCluster node image in k8sclusterinfo.yaml // @@ -1388,10 +1405,10 @@ func GetAvailableK8sClusterNodeImage(providerName string, regionName string) (*[ return nil, fmt.Errorf("no available kubernetes cluster node image for region(%s) of provider(%s)", regionName, providerName) } -// CheckNodeGroupsOnK8sCreation is func to check whether nodegroups are required during the k8scluster creation -func CheckNodeGroupsOnK8sCreation(providerName string) (*model.K8sClusterNodeGroupsOnCreation, error) { +// GetK8sNodeGroupsOnK8sCreation is func to get whether nodegroups are required during the k8scluster creation +func GetK8sNodeGroupsOnK8sCreation(providerName string) (bool, error) { // - // Check nodeGroupsOnCreation field in k8sclusterinfo.yaml + // Get nodeGroupsOnCreation field in k8sclusterinfo.yaml // providerName = strings.ToLower(providerName) @@ -1399,11 +1416,85 @@ func CheckNodeGroupsOnK8sCreation(providerName string) (*model.K8sClusterNodeGro // Get model.K8sClusterDetail for providerName k8sClusterDetail := getK8sClusterDetail(providerName) if k8sClusterDetail == nil { - return nil, fmt.Errorf("unsupported provider(%s) for kubernetes cluster", providerName) + return false, fmt.Errorf("unsupported provider(%s) for kubernetes cluster", providerName) + } + + return k8sClusterDetail.NodeGroupsOnCreation, nil +} + +// GetModelK8sNodeGroupsOnK8sCreation is to convert a NodeGroupsOnK8sCreation value to model.K8sClusterNodeGroupsOnK8sCreation +func GetModelK8sNodeGroupsOnK8sCreation(providerName string) (*model.K8sClusterNodeGroupsOnCreation, error) { + k8sNodeGroupsOnK8sCreation, err := GetK8sNodeGroupsOnK8sCreation(providerName) + if err != nil { + return nil, err } return &model.K8sClusterNodeGroupsOnCreation{ - Result: strconv.FormatBool(k8sClusterDetail.NodeGroupsOnCreation), + Result: strconv.FormatBool(k8sNodeGroupsOnK8sCreation), + }, nil +} + +// GetK8sNodeImageDesignation is func to get whether node image designation is possible to create a k8scluster +func GetK8sNodeImageDesignation(providerName string) (bool, error) { + // + // Get nodeGroupsOnCreation field in k8sclusterinfo.yaml + // + + providerName = strings.ToLower(providerName) + + // Get model.K8sClusterDetail for providerName + k8sClusterDetail := getK8sClusterDetail(providerName) + if k8sClusterDetail == nil { + return false, fmt.Errorf("unsupported provider(%s) for kubernetes cluster", providerName) + } + + return k8sClusterDetail.NodeImageDesignation, nil +} + +// GetModelK8sNodeImageDesignation is to convert a NodeImageDesignation value to model.K8sClusterNodeImageDesignation +func GetModelK8sNodeImageDesignation(providerName string) (*model.K8sClusterNodeImageDesignation, error) { + k8sNodeImageDesignation, err := GetK8sNodeImageDesignation(providerName) + if err != nil { + return nil, err + } + + return &model.K8sClusterNodeImageDesignation{ + Result: strconv.FormatBool(k8sNodeImageDesignation), + }, nil +} + +// GetK8sRequiredSubnetCount is func to get the required subnet count to create a k8scluster +func GetK8sRequiredSubnetCount(providerName string) (int, error) { + // + // Get requiredSubnetCount field in k8sclusterinfo.yaml + // + + providerName = strings.ToLower(providerName) + + // Get model.K8sClusterDetail for providerName + k8sClusterDetail := getK8sClusterDetail(providerName) + if k8sClusterDetail == nil { + return 0, fmt.Errorf("unsupported provider(%s) for kubernetes cluster", providerName) + } + + // Set default value is 1 + requiredSubnetCount := 1 + if k8sClusterDetail.RequiredSubnetCount > 1 { + requiredSubnetCount = k8sClusterDetail.RequiredSubnetCount + } + + return requiredSubnetCount, nil +} + +// GetModelK8sRequiredSubnetCount is func to get the required subnet count to create a k8scluster +func GetModelK8sRequiredSubnetCount(providerName string) (*model.K8sClusterRequiredSubnetCount, error) { + k8sRequiredSubnetCount, err := GetK8sRequiredSubnetCount(providerName) + if err != nil { + return nil, err + } + + return &model.K8sClusterRequiredSubnetCount{ + Result: strconv.FormatInt(int64(k8sRequiredSubnetCount), 10), }, nil } diff --git a/src/core/infra/provisioning.go b/src/core/infra/provisioning.go index 8e88fabb1..e819395e3 100644 --- a/src/core/infra/provisioning.go +++ b/src/core/infra/provisioning.go @@ -17,6 +17,7 @@ package infra import ( "encoding/json" "fmt" + "regexp" "strconv" "strings" "sync" @@ -945,7 +946,7 @@ func CreateMciDynamic(reqID string, nsId string, req *model.TbMciDynamicReq, dep // Check whether VM names meet requirement. errStr := "" for i, k := range vmRequest { - err = checkCommonResAvailable(&k) + err = checkCommonResAvailableForVmDynamicReq(&k) if err != nil { log.Error().Err(err).Msgf("[%d] Failed to find common resource for MCI provision", i) errStr += "{[" + strconv.Itoa(i+1) + "] " + err.Error() + "} " @@ -1012,8 +1013,8 @@ func CreateMciVmDynamic(nsId string, mciId string, req *model.TbVmDynamicReq) (* return CreateMciGroupVm(nsId, mciId, vmReq, true) } -// checkCommonResAvailable is func to check common resources availability -func checkCommonResAvailable(req *model.TbVmDynamicReq) error { +// checkCommonResAvailableForVmDynamicReq is func to check common resources availability for VmDynamicReq +func checkCommonResAvailableForVmDynamicReq(req *model.TbVmDynamicReq) error { vmRequest := req // Check whether VM names meet requirement. @@ -1619,3 +1620,371 @@ func CreateVm(wg *sync.WaitGroup, nsId string, mciId string, vmInfoData *model.T return nil } + +func filterCheckMciDynamicReqInfoToCheckK8sClusterDynamicReqInfo(mciDReqInfo *model.CheckMciDynamicReqInfo) *model.CheckK8sClusterDynamicReqInfo { + k8sDReqInfo := model.CheckK8sClusterDynamicReqInfo{} + + if mciDReqInfo != nil { + for _, k := range mciDReqInfo.ReqCheck { + if strings.Contains(k.Spec.InfraType, model.StrK8s) || + strings.Contains(k.Spec.InfraType, model.StrKubernetes) { + + imageListForK8s := []model.TbImageInfo{} + for _, i := range k.Image { + if strings.Contains(i.InfraType, model.StrK8s) || + strings.Contains(i.InfraType, model.StrKubernetes) { + imageListForK8s = append(imageListForK8s, i) + } + } + + nodeDReqInfo := model.CheckNodeDynamicReqInfo{ + ConnectionConfigCandidates: k.ConnectionConfigCandidates, + Spec: k.Spec, + Image: imageListForK8s, + Region: k.Region, + SystemMessage: k.SystemMessage, + } + + k8sDReqInfo.ReqCheck = append(k8sDReqInfo.ReqCheck, nodeDReqInfo) + } + } + } + + return &k8sDReqInfo +} + +// CheckK8sClusterDynamicReq is func to check request info to create K8sCluster obeject and deploy requested Nodes in a dynamic way +func CheckK8sClusterDynamicReq(req *model.K8sClusterConnectionConfigCandidatesReq) (*model.CheckK8sClusterDynamicReqInfo, error) { + if len(req.CommonSpecs) != 1 { + err := fmt.Errorf("Only one CommonSpec should be defined.") + log.Error().Err(err).Msg("") + return &model.CheckK8sClusterDynamicReqInfo{}, err + } + + mciCCCReq := model.MciConnectionConfigCandidatesReq{ + CommonSpecs: req.CommonSpecs, + } + mciDReqInfo, err := CheckMciDynamicReq(&mciCCCReq) + + k8sDReqInfo := filterCheckMciDynamicReqInfoToCheckK8sClusterDynamicReqInfo(mciDReqInfo) + + return k8sDReqInfo, err +} + +func filterDigitsAndDots(input string) string { + re := regexp.MustCompile(`[^0-9.]`) + return re.ReplaceAllString(input, "") +} + +func getK8sRecommendVersion(providerName, regionName, reqVersion string) (string, error) { + availableVersion, err := common.GetAvailableK8sVersion(providerName, regionName) + if err != nil { + err := fmt.Errorf("No available K8sCluster version.") + log.Error().Err(err).Msg("") + return "", err + } + + recVersion := model.StrEmpty + versionIdList := []string{} + for _, verDetail := range *availableVersion { + versionIdList = append(versionIdList, verDetail.Id) + if strings.EqualFold(reqVersion, verDetail.Id) { + recVersion = verDetail.Id + break + } else { + availVersion := filterDigitsAndDots(verDetail.Id) + filteredReqVersion := filterDigitsAndDots(reqVersion) + if strings.HasPrefix(availVersion, filteredReqVersion) { + recVersion = availVersion + break + } + } + } + + if strings.EqualFold(recVersion, model.StrEmpty) { + return "", fmt.Errorf("Available K8sCluster Version(k8sclusterinfo.yaml) for Provider/Region(%s/%s): %s", + providerName, regionName, strings.Join(versionIdList, ", ")) + } + + return recVersion, nil +} + +// checkCommonResAvailableForK8sClusterDynamicReq is func to check common resources availability for K8sClusterDynamicReq +func checkCommonResAvailableForK8sClusterDynamicReq(dReq *model.TbK8sClusterDynamicReq) error { + specInfo, err := resource.GetSpec(model.SystemCommonNs, dReq.CommonSpec) + if err != nil { + log.Error().Err(err).Msg("") + return err + } + + connName := specInfo.ConnectionName + // If ConnectionName is specified by the request, Use ConnectionName from the request + if dReq.ConnectionName != "" { + connName = dReq.ConnectionName + } + + // validate the GetConnConfig for spec + connConfig, err := common.GetConnConfig(connName) + if err != nil { + err := fmt.Errorf("Failed to get ConnectionName (" + connName + ") for Spec (" + dReq.CommonSpec + ") is not found.") + log.Error().Err(err).Msg("") + return err + } + + niDesignation, err := common.GetK8sNodeImageDesignation(connConfig.ProviderName) + if err != nil { + log.Error().Err(err).Msg("") + } + + if niDesignation == false { + // if node image designation is not supported by CSP, CommonImage should be "default" or ""(blank) + if !(strings.EqualFold(dReq.CommonImage, "default") || strings.EqualFold(dReq.CommonImage, "")) { + err := fmt.Errorf("The NodeImageDesignation is not supported by CSP(%s). CommonImage's value should be \"default\" or \"\"", connConfig.ProviderName) + log.Error().Err(err).Msg("") + return err + } + } + + // In K8sCluster, allows dReq.CommonImage to be set to "default" or "" + if strings.EqualFold(dReq.CommonImage, "default") || + strings.EqualFold(dReq.CommonImage, "") { + // do nothing + } else { + osType := strings.ReplaceAll(dReq.CommonImage, " ", "") + imageId := resource.GetProviderRegionZoneResourceKey(connConfig.ProviderName, connConfig.RegionDetail.RegionName, "", osType) + // incase of user provided image id completely (e.g. aws+ap-northeast-2+ubuntu22.04) + if strings.Contains(dReq.CommonImage, "+") { + imageId = dReq.CommonImage + } + _, err = resource.GetImage(model.SystemCommonNs, imageId) + if err != nil { + err := fmt.Errorf("Failed to get Image " + dReq.CommonImage + " from " + connName) + log.Error().Err(err).Msg("") + return err + } + } + + return nil +} + +// getK8sClusterReqFromDynamicReq is func to get TbK8sClusterReq from TbK8sClusterDynamicReq +func getK8sClusterReqFromDynamicReq(reqID string, nsId string, dReq *model.TbK8sClusterDynamicReq) (*model.TbK8sClusterReq, error) { + onDemand := true + + emptyK8sReq := &model.TbK8sClusterReq{} + k8sReq := &model.TbK8sClusterReq{} + k8sngReq := &model.TbK8sNodeGroupReq{} + + specInfo, err := resource.GetSpec(model.SystemCommonNs, dReq.CommonSpec) + if err != nil { + log.Err(err).Msg("") + return emptyK8sReq, err + } + k8sngReq.SpecId = specInfo.Id + + k8sRecVersion, err := getK8sRecommendVersion(specInfo.ProviderName, specInfo.RegionName, dReq.Version) + if err != nil { + log.Err(err).Msg("") + return emptyK8sReq, err + } + + // If ConnectionName is specified by the request, Use ConnectionName from the request + k8sReq.ConnectionName = specInfo.ConnectionName + if dReq.ConnectionName != "" { + k8sReq.ConnectionName = dReq.ConnectionName + } + + // validate the GetConnConfig for spec + connection, err := common.GetConnConfig(k8sReq.ConnectionName) + if err != nil { + err := fmt.Errorf("Failed to Get ConnectionName (" + k8sReq.ConnectionName + ") for Spec (" + dReq.CommonSpec + ") is not found.") + log.Err(err).Msg("") + return emptyK8sReq, err + } + + k8sNgOnCreation, err := common.GetK8sNodeGroupsOnK8sCreation(connection.ProviderName) + if err != nil { + log.Err(err).Msgf("Failed to Get Nodegroups on K8sCluster Creation") + return emptyK8sReq, err + } + + // In K8sCluster, allows dReq.CommonImage to be set to "default" or "" + if strings.EqualFold(dReq.CommonImage, "default") || + strings.EqualFold(dReq.CommonImage, "") { + // do nothing + } else { + osType := strings.ReplaceAll(dReq.CommonImage, " ", "") + k8sngReq.ImageId = resource.GetProviderRegionZoneResourceKey(connection.ProviderName, connection.RegionDetail.RegionName, "", osType) + // incase of user provided image id completely (e.g. aws+ap-northeast-2+ubuntu22.04) + if strings.Contains(dReq.CommonImage, "+") { + k8sngReq.ImageId = dReq.CommonImage + } + _, err = resource.GetImage(model.SystemCommonNs, k8sngReq.ImageId) + if err != nil { + err := fmt.Errorf("Failed to get the Image " + k8sngReq.ImageId + " from " + k8sReq.ConnectionName) + log.Err(err).Msg("") + return emptyK8sReq, err + } + } + // Default resource name has this pattern (nsId + "-shared-" + vmReq.ConnectionName) + resourceName := nsId + model.StrSharedResourceName + k8sReq.ConnectionName + + common.UpdateRequestProgress(reqID, common.ProgressInfo{Title: "Setting vNet:" + resourceName, Time: time.Now()}) + + k8sReq.VNetId = resourceName + _, err = resource.GetResource(nsId, model.StrVNet, k8sReq.VNetId) + if err != nil { + if !onDemand { + err := fmt.Errorf("Failed to get the vNet " + k8sReq.VNetId + " from " + k8sReq.ConnectionName) + log.Err(err).Msg("Failed to get the vNet") + return emptyK8sReq, err + } + + common.UpdateRequestProgress(reqID, common.ProgressInfo{Title: "Loading default vNet:" + resourceName, Time: time.Now()}) + + err2 := resource.CreateSharedResource(nsId, model.StrVNet, k8sReq.ConnectionName) + if err2 != nil { + log.Err(err2).Msg("Failed to create new default vNet " + k8sReq.VNetId + " from " + k8sReq.ConnectionName) + return emptyK8sReq, err2 + } else { + log.Info().Msg("Created new default vNet: " + k8sReq.VNetId) + } + } else { + log.Info().Msg("Found and utilize default vNet: " + k8sReq.VNetId) + } + k8sReq.SubnetIds = append(k8sReq.SubnetIds, resourceName) + k8sReq.SubnetIds = append(k8sReq.SubnetIds, resourceName+"-01") + + common.UpdateRequestProgress(reqID, common.ProgressInfo{Title: "Setting SSHKey:" + resourceName, Time: time.Now()}) + + k8sngReq.SshKeyId = resourceName + _, err = resource.GetResource(nsId, model.StrSSHKey, k8sngReq.SshKeyId) + if err != nil { + if !onDemand { + err := fmt.Errorf("Failed to get the SSHKey " + k8sngReq.SshKeyId + " from " + k8sReq.ConnectionName) + log.Err(err).Msg("Failed to get the SSHKey") + return emptyK8sReq, err + } + + common.UpdateRequestProgress(reqID, common.ProgressInfo{Title: "Loading default SSHKey:" + resourceName, Time: time.Now()}) + + err2 := resource.CreateSharedResource(nsId, model.StrSSHKey, k8sReq.ConnectionName) + if err2 != nil { + log.Err(err2).Msg("Failed to create new default SSHKey " + k8sngReq.SshKeyId + " from " + k8sReq.ConnectionName) + return emptyK8sReq, err2 + } else { + log.Info().Msg("Created new default SSHKey: " + k8sReq.VNetId) + } + } else { + log.Info().Msg("Found and utilize default SSHKey: " + k8sReq.VNetId) + } + + common.UpdateRequestProgress(reqID, common.ProgressInfo{Title: "Setting securityGroup:" + resourceName, Time: time.Now()}) + + securityGroup := resourceName + k8sReq.SecurityGroupIds = append(k8sReq.SecurityGroupIds, securityGroup) + _, err = resource.GetResource(nsId, model.StrSecurityGroup, securityGroup) + if err != nil { + if !onDemand { + err := fmt.Errorf("Failed to get the securityGroup " + securityGroup + " from " + k8sReq.ConnectionName) + log.Err(err).Msg("Failed to get the securityGroup") + return emptyK8sReq, err + } + + common.UpdateRequestProgress(reqID, common.ProgressInfo{Title: "Loading default securityGroup:" + resourceName, Time: time.Now()}) + + err2 := resource.CreateSharedResource(nsId, model.StrSecurityGroup, k8sReq.ConnectionName) + if err2 != nil { + log.Err(err2).Msg("Failed to create new default securityGroup " + securityGroup + " from " + k8sReq.ConnectionName) + return emptyK8sReq, err2 + } else { + log.Info().Msg("Created new default securityGroup: " + securityGroup) + } + } else { + log.Info().Msg("Found and utilize default securityGroup: " + securityGroup) + } + + k8sngReq.Name = dReq.NodeGroupName + if k8sngReq.Name == "" { + k8sngReq.Name = common.GenUid() + } + k8sngReq.RootDiskType = dReq.RootDiskType + k8sngReq.RootDiskSize = dReq.RootDiskSize + k8sngReq.OnAutoScaling = dReq.OnAutoScaling + k8sngReq.DesiredNodeSize = dReq.DesiredNodeSize + k8sngReq.MinNodeSize = dReq.MinNodeSize + k8sngReq.MaxNodeSize = dReq.MaxNodeSize + + k8sReq.Description = dReq.Description + k8sReq.Name = dReq.Name + if k8sReq.Name == "" { + k8sReq.Name = common.GenUid() + } + k8sReq.Version = k8sRecVersion + if k8sNgOnCreation { + k8sReq.K8sNodeGroupList = append(k8sReq.K8sNodeGroupList, *k8sngReq) + } else { + log.Info().Msg("Need to Add NodeGroups To Use This K8sCluster") + } + k8sReq.Label = dReq.Label + + common.PrintJsonPretty(k8sReq) + common.UpdateRequestProgress(reqID, common.ProgressInfo{Title: "Prepared resources for K8sCluster:" + k8sReq.Name, Info: k8sReq, Time: time.Now()}) + + return k8sReq, nil +} + +// CreateK8sClusterDynamic is func to create K8sCluster obeject and deploy requested K8sCluster and NodeGroup in a dynamic way +func CreateK8sClusterDynamic(reqID string, nsId string, dReq *model.TbK8sClusterDynamicReq, deployOption string) (*model.TbK8sClusterInfo, error) { + emptyK8sCluster := &model.TbK8sClusterInfo{} + err := common.CheckString(nsId) + if err != nil { + log.Err(err).Msg("") + return emptyK8sCluster, err + } + check, err := resource.CheckK8sCluster(nsId, dReq.Name) + if err != nil { + log.Err(err).Msg("") + return emptyK8sCluster, err + } + if check { + err := fmt.Errorf("already exists") + log.Err(err).Msgf("Failed to Create K8sCluster(%s) Dynamically", dReq.Name) + return emptyK8sCluster, err + } + + err = checkCommonResAvailableForK8sClusterDynamicReq(dReq) + if err != nil { + log.Err(err).Msgf("Failed to find common resource for K8sCluster provision") + return emptyK8sCluster, err + } + + //If not, generate default resources dynamically. + k8sReq, err := getK8sClusterReqFromDynamicReq(reqID, nsId, dReq) + if err != nil { + log.Err(err).Msg("Failed to prefare resources for dynamic K8sCluster creation") + // Rollback created default resources + time.Sleep(5 * time.Second) + log.Info().Msg("Try rollback created default resources") + rollbackResult, rollbackErr := resource.DelAllSharedResources(nsId) + if rollbackErr != nil { + err = fmt.Errorf("Failed in rollback operation: %w", rollbackErr) + } else { + ids := strings.Join(rollbackResult.IdList, ", ") + err = fmt.Errorf("Rollback results [%s]: %w", ids, err) + } + return emptyK8sCluster, err + } + + common.PrintJsonPretty(k8sReq) + common.UpdateRequestProgress(reqID, common.ProgressInfo{Title: "Prepared all resources for provisioning K8sCluster:" + k8sReq.Name, Info: k8sReq, Time: time.Now()}) + common.UpdateRequestProgress(reqID, common.ProgressInfo{Title: "Start provisioning", Time: time.Now()}) + + // Run create K8sCluster with the generated K8sCluster request (option != register) + option := "create" + if deployOption == "hold" { + option = "hold" + } + + return resource.CreateK8sCluster(nsId, k8sReq, option) +} diff --git a/src/core/model/config.go b/src/core/model/config.go index b8d10d55f..d6f4c3dd5 100644 --- a/src/core/model/config.go +++ b/src/core/model/config.go @@ -61,9 +61,19 @@ type K8sClusterNodeGroupsOnCreation struct { Result string `json:"result" example:"true"` } +type K8sClusterNodeImageDesignation struct { + Result string `json:"result" example:"true"` +} + +type K8sClusterRequiredSubnetCount struct { + Result string `json:"result" example:"1"` +} + // K8sClusterDetail is structure for kubernetes cluster detail information type K8sClusterDetail struct { NodeGroupsOnCreation bool `mapstructure:"nodeGroupsOnCreation" json:"nodegroups_on_creation"` + NodeImageDesignation bool `mapstructure:"nodeImageDesignation" json:"node_image_designation"` + RequiredSubnetCount int `mapstructure:"requiredSubnetCount" json:"required_subnet_count"` Version []K8sClusterVersionDetail `mapstructure:"version" json:"versions"` NodeImage []K8sClusterNodeImageDetail `mapstructure:"nodeImage" json:"node_images"` RootDisk []K8sClusterRootDiskDetail `mapstructure:"rootDisk" json:"root_disks"` diff --git a/src/core/model/k8scluster.go b/src/core/model/k8scluster.go index 67a22851c..a3ce94557 100644 --- a/src/core/model/k8scluster.go +++ b/src/core/model/k8scluster.go @@ -96,6 +96,12 @@ type TbK8sClusterReq struct { // Tumblebug // Fields for "Register existing K8sCluster" feature // @description CspResourceId is required to register a k8s cluster from CSP (option=register) CspResourceId string `json:"cspResourceId" example:"required when option is register"` + + // Label is for describing the object by keywords + Label map[string]string `json:"label"` + + // SystemLabel is for describing the k8scluster in a keyword (any string can be used) for special System purpose + SystemLabel string `json:"systemLabel" example:"" default:""` } // 2023-11-13 https://github.com/cloud-barista/cb-spider/blob/fa4bd91fdaa6bb853ea96eca4a7b4f58a2abebf2/api-runtime/rest-runtime/ClusterRest.go#L441 @@ -450,3 +456,64 @@ type SpiderAddonsInfo struct { type TbK8sAddonsInfo struct { KeyValueList []KeyValue `json:"keyValueList"` } + +// K8sClusterConnectionConfigCandidatesReq is struct for a request to check requirements to create a new K8sCluster instance dynamically (with default resource option) +type K8sClusterConnectionConfigCandidatesReq struct { + // CommonSpec is field for id of a spec in common namespace + CommonSpecs []string `json:"commonSpec" validate:"required" example:"azure+koreacentral+Standard_B2s"` +} + +// CheckK8sClusterDynamicReqInfo is struct to check requirements to create a new K8sCluster instance dynamically (with default resource option) +type CheckK8sClusterDynamicReqInfo struct { + ReqCheck []CheckNodeDynamicReqInfo `json:"reqCheck" validate:"required"` +} + +// CheckNodeDynamicReqInfo is struct to check requirements to create a new server instance dynamically (with default resource option) +type CheckNodeDynamicReqInfo struct { + + // ConnectionConfigCandidates will provide ConnectionConfig options + ConnectionConfigCandidates []string `json:"connectionConfigCandidates" default:""` + + Spec TbSpecInfo `json:"spec" default:""` + Image []TbImageInfo `json:"image" default:""` + Region RegionDetail `json:"region" default:""` + + // Latest system message such as error message + SystemMessage string `json:"systemMessage" example:"Failed because ..." default:""` // systeam-given string message + +} + +// TbK8sClusterDynamicReq is struct for requirements to create K8sCluster dynamically (with default resource option) +type TbK8sClusterDynamicReq struct { + // K8sCluster name if it is not empty. + Name string `json:"name" validate:"required" example:"k8scluster-01"` + + // K8s Clsuter version + Version string `json:"version" example:"1.29"` + + // Label is for describing the object by keywords + Label map[string]string `json:"label"` + + Description string `json:"description" example:"Description"` + + // NodeGroup name if it is not empty + NodeGroupName string `json:"nodeGroupName" example:"nodegroup-01"` + + // CommonSpec is field for id of a spec in common namespace + CommonSpec string `json:"commonSpec" validate:"required" example:"azure+koreacentral+standard_b2s"` + + // CommonImage is field for id of a image in common namespace + CommonImage string `json:"commonImage" validate:"required" example:"default, azure+koreacentrall+ubuntu20.04"` + + RootDiskType string `json:"rootDiskType,omitempty" example:"default, TYPE1, ..." default:"default"` // "", "default", "TYPE1", AWS: ["standard", "gp2", "gp3"], Azure: ["PremiumSSD", "StandardSSD", "StandardHDD"], GCP: ["pd-standard", "pd-balanced", "pd-ssd", "pd-extreme"], ALIBABA: ["cloud_efficiency", "cloud", "cloud_essd"], TENCENT: ["CLOUD_PREMIUM", "CLOUD_SSD"] + RootDiskSize string `json:"rootDiskSize,omitempty" example:"default, 30, 42, ..." default:"default"` // "default", Integer (GB): ["50", ..., "1000"] + + OnAutoScaling string `json:"onAutoScaling,omitempty" default:"true" example:"true"` + DesiredNodeSize string `json:"desiredNodeSize,omitempty" default:"1" example:"1"` + MinNodeSize string `json:"minNodeSize,omitempty" default:"1" example:"1"` + MaxNodeSize string `json:"maxNodeSize,omitempty" default:"2" example:"3"` + + // if ConnectionName is given, the VM tries to use associtated credential. + // if not, it will use predefined ConnectionName in Spec objects + ConnectionName string `json:"connectionName,omitempty" default:"azure-koreacentral"` +} diff --git a/src/core/resource/k8scluster.go b/src/core/resource/k8scluster.go index 1005d879c..a4b9e0cd8 100644 --- a/src/core/resource/k8scluster.go +++ b/src/core/resource/k8scluster.go @@ -19,6 +19,7 @@ import ( "fmt" "reflect" "strings" + "sync" "time" "github.com/cloud-barista/cb-tumblebug/src/core/common" @@ -43,33 +44,174 @@ func TbK8sClusterReqStructLevelValidation(sl validator.StructLevel) { } } +var holdingK8sClusterMap sync.Map + +// HandleK8sClusterAction is func to handle actions to K8sCluster +func HandleK8sClusterAction(nsId string, k8sClusterId string, action string) (string, error) { + action = common.ToLower(action) + + err := common.CheckString(nsId) + if err != nil { + log.Error().Err(err).Msg("") + return "", err + } + + err = common.CheckString(k8sClusterId) + if err != nil { + log.Error().Err(err).Msg("") + return "", err + } + + check, _ := CheckK8sCluster(nsId, k8sClusterId) + + if !check { + err := fmt.Errorf("The K8sCluster " + k8sClusterId + " does not exist.") + return err.Error(), err + } + + log.Debug().Msgf("[Get K8sCluster requested action] %s", action) + if action == "continue" { + log.Debug().Msg("[continue K8sCluster provisioning]") + key := common.GenK8sClusterKey(nsId, k8sClusterId) + holdingK8sClusterMap.Store(key, action) + + return "Continue the holding K8sCluster", nil + } else if action == "withdraw" { + log.Debug().Msg("[withdraw K8sCluster provisioning]") + key := common.GenK8sClusterKey(nsId, k8sClusterId) + holdingK8sClusterMap.Store(key, action) + + return "Withdraw the holding K8sCluster", nil + } else { + return "", fmt.Errorf(action + " not supported") + } +} + +func createK8sClusterInfo(nsId string, tbK8sCInfo model.TbK8sClusterInfo) error { + log.Debug().Msg("[Create TbK8sClusterInfo] " + tbK8sCInfo.Id) + + k8sClusterId := tbK8sCInfo.Id + k := common.GenK8sClusterKey(nsId, k8sClusterId) + kv, err := kvstore.GetKv(k) + if err != nil { + err := fmt.Errorf("failed to create K8sClusterInfo(%s): %v", k8sClusterId, err) + return err + } + + if kv != (kvstore.KeyValue{}) { + err := fmt.Errorf("failed to create K8sClusterInfo(%s): already exists", k8sClusterId) + return err + } + + val, err := json.Marshal(&tbK8sCInfo) + if err != nil { + err := fmt.Errorf("failed to create K8sClusterInfo(%s): %v", k8sClusterId, err) + return err + } + + err = kvstore.Put(k, string(val)) + if err != nil { + err := fmt.Errorf("failed to create K8sClusterInfo(%s): %v", k8sClusterId, err) + return err + } + + return nil +} + +func getK8sClusterInfo(nsId, k8sClusterId string) (model.TbK8sClusterInfo, error) { + log.Debug().Msg("[Get K8sClusterInfo] " + k8sClusterId) + + emptyObj := model.TbK8sClusterInfo{} + + k := common.GenK8sClusterKey(nsId, k8sClusterId) + kv, err := kvstore.GetKv(k) + if err != nil { + err := fmt.Errorf("failed to get K8sClusterInfo(%s): %v", k8sClusterId, err) + return emptyObj, err + } + + tbK8sCInfo := model.TbK8sClusterInfo{} + if kv == (kvstore.KeyValue{}) { + err := fmt.Errorf("failed to get K8sClusterInfo(%s): empty keyvalue", k8sClusterId) + return emptyObj, err + } + + err = json.Unmarshal([]byte(kv.Value), &tbK8sCInfo) + if err != nil { + err := fmt.Errorf("failed to get K8sClusterInfo(%s): %v", k8sClusterId, err) + return emptyObj, err + } + + return tbK8sCInfo, nil +} + +// updateK8sClusterInfo is func to update TbK8sClusterInfo +func updateK8sClusterInfo(nsId string, newTbK8sCInfo model.TbK8sClusterInfo) { + k8sClusterId := newTbK8sCInfo.Id + log.Debug().Msg("[Update K8sClusterInfo] " + k8sClusterId) + + k := common.GenK8sClusterKey(nsId, k8sClusterId) + + // Check existence of the key. If no key, no update. + kv, err := kvstore.GetKv(k) + if kv == (kvstore.KeyValue{}) || err != nil { + return + } + + oldTbK8sCInfo := model.TbK8sClusterInfo{} + json.Unmarshal([]byte(kv.Value), &oldTbK8sCInfo) + + if !reflect.DeepEqual(&oldTbK8sCInfo, &newTbK8sCInfo) { + val, _ := json.Marshal(&newTbK8sCInfo) + err = kvstore.Put(k, string(val)) + if err != nil { + err := fmt.Errorf("failed to update K8sClusterInfo(%s): %v", k8sClusterId, err) + log.Err(err).Msgf("nsId=%s", nsId) + } + } +} + +// deleteK8sClusterInfo is func to delete TbK8sClusterInfo +func deleteK8sClusterInfo(nsId, k8sClusterId string) error { + log.Debug().Msg("[Delete K8sClusterInfo] " + k8sClusterId) + + k := common.GenK8sClusterKey(nsId, k8sClusterId) + err := kvstore.Delete(k) + if err != nil { + err := fmt.Errorf("failed to delete K8sClusterInfo(%s): %v", k8sClusterId, err) + return err + } + + return nil +} + // CreateK8sCluster create a k8s cluster -func CreateK8sCluster(nsId string, req *model.TbK8sClusterReq, option string) (model.TbK8sClusterInfo, error) { +func CreateK8sCluster(nsId string, req *model.TbK8sClusterReq, option string) (*model.TbK8sClusterInfo, error) { log.Info().Msg("CreateK8sCluster") - emptyObj := model.TbK8sClusterInfo{} + emptyObj := &model.TbK8sClusterInfo{} - reqId := req.Name + k8sClusterId := req.Name err := validate.Struct(req) if err != nil { if _, ok := err.(*validator.InvalidValidationError); ok { - log.Err(err).Msg("Failed to Create a K8sCluster") + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } return emptyObj, err } - check, err := CheckK8sCluster(nsId, reqId) + check, err := CheckK8sCluster(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Create a K8sCluster") + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } if check { - err := fmt.Errorf("The k8s cluster " + reqId + " already exists.") - log.Err(err).Msg("Failed to Create a K8sCluster") + err := fmt.Errorf("already exists", k8sClusterId) + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } @@ -78,65 +220,93 @@ func CreateK8sCluster(nsId string, req *model.TbK8sClusterReq, option string) (m */ err = checkK8sClusterEnablement(req.ConnectionName) if err != nil { - log.Err(err).Msg("Failed to Create a K8sCluster") + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } - connectionConfig, err := common.GetConnConfig(req.ConnectionName) + uid := common.GenUid() + connConfig, err := common.GetConnConfig(req.ConnectionName) if err != nil { - err = fmt.Errorf("Cannot retrieve ConnectionConfig" + err.Error()) - log.Error().Err(err).Msg("") + err := fmt.Errorf("Cannot retrieve ConnectionConfig" + err.Error()) + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) + return emptyObj, err } - /* - * Build RequestBody for model.SpiderClusterReq{} - */ + tbK8sCInfo := &model.TbK8sClusterInfo{ + ResourceType: model.StrK8s, + Id: k8sClusterId, + Uid: uid, + Name: k8sClusterId, + ConnectionName: req.ConnectionName, + ConnectionConfig: connConfig, + Description: req.Description, + } + + err = createK8sClusterInfo(nsId, *tbK8sCInfo) + if err != nil { + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) + return emptyObj, err + } + + // hold option will hold the K8sCluster creation process until the user releases it. + if option == "hold" { + key := common.GenK8sClusterKey(nsId, k8sClusterId) + holdingK8sClusterMap.Store(key, "holding") + for { + value, ok := holdingK8sClusterMap.Load(key) + if !ok { + break + } + if value == "continue" { + holdingK8sClusterMap.Delete(key) + break + } else if value == "withdraw" { + holdingK8sClusterMap.Delete(key) + DeleteK8sCluster(nsId, k8sClusterId, "force") + err := fmt.Errorf("Withdrawed K8sCluster creation") + log.Error().Err(err).Msg("") + return nil, err + } + + log.Info().Msgf("K8sCluster: %s (holding)", key) + time.Sleep(5 * time.Second) + } + option = "create" + } // Validate err = validateAtCreateK8sCluster(req) if err != nil { - log.Err(err).Msgf("Failed to Create a K8sCluster: Requested K8sVersion(%s)", req.Version) + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } + + // Build RequestBody for model.SpiderClusterReq{} spVersion := req.Version spVPCName, err := GetCspResourceName(nsId, model.StrVNet, req.VNetId) if spVPCName == "" { - log.Err(err).Msg("Failed to Create a K8sCluster") + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } - /* - var spSubnetNames []string - for _, v := range u.SubnetIds { - spSnName, err := GetCspResourceId(nsId, model.StrSubnet, v) - if spSnName == "" { - log.Error().Err(err).Msg("") - return emptyObj, err - } - - spSubnetNames = append(spSubnetNames, spSnName) - } - */ - - var spSnName string - var spSubnetNames []string - var found bool - tmpInf, err := GetResource(nsId, model.StrVNet, req.VNetId) if err != nil { - log.Err(err).Msg("Failed to Create a K8sCluster") + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } tbVNetInfo := model.TbVNetInfo{} err = common.CopySrcToDest(&tmpInf, &tbVNetInfo) if err != nil { - log.Err(err).Msg("Failed to Create a K8sCluster") + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } + var spSnName string + var spSubnetNames []string + for _, v := range req.SubnetIds { - found = false + found := false for _, w := range tbVNetInfo.SubnetInfoList { if v == w.Name { spSnName = w.CspResourceName @@ -147,11 +317,21 @@ func CreateK8sCluster(nsId string, req *model.TbK8sClusterReq, option string) (m if found == true { spSubnetNames = append(spSubnetNames, spSnName) + + k8sRequiredSubnetCount, err := common.GetK8sRequiredSubnetCount(connConfig.ProviderName) + if err != nil { + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) + return emptyObj, err + } + + if k8sRequiredSubnetCount <= len(spSubnetNames) { + break + } } } if len(spSubnetNames) == 0 { - err := fmt.Errorf("No valid subnets in VNetId(%s)", req.VNetId) - log.Err(err).Msg("Failed to Create a K8sCluster") + err := fmt.Errorf("no valid subnets in vnet(%s)", req.VNetId) + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } @@ -159,7 +339,7 @@ func CreateK8sCluster(nsId string, req *model.TbK8sClusterReq, option string) (m for _, v := range req.SecurityGroupIds { spSgName, err := GetCspResourceName(nsId, model.StrSecurityGroup, v) if spSgName == "" { - log.Err(err).Msg("Failed to Create a K8sCluster") + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } @@ -171,7 +351,7 @@ func CreateK8sCluster(nsId string, req *model.TbK8sClusterReq, option string) (m spName := v.Name err := common.CheckString(spName) if err != nil { - log.Err(err).Msg("Failed to Create a K8sCluster") + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } @@ -180,26 +360,48 @@ func CreateK8sCluster(nsId string, req *model.TbK8sClusterReq, option string) (m spImgName = "" } else { spImgName, err = GetCspResourceName(nsId, model.StrImage, v.ImageId) - if spImgName == "" { - log.Err(err).Msg("Failed to Create a K8sCluster") - return emptyObj, err + if spImgName == "" || err != nil { + log.Warn().Msgf("Not found the Image %s in ns %s, find it from SystemCommonNs", v.ImageId, nsId) + errAgg := err.Error() + // If cannot find the resource, use common resource + spImgName, err = GetCspResourceName(model.SystemCommonNs, model.StrImage, v.ImageId) + if spImgName == "" || err != nil { + errAgg += err.Error() + err = fmt.Errorf(errAgg) + log.Err(err).Msgf("Not found the Image %s both from ns %s and SystemCommonNs", v.ImageId, nsId) + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) + return emptyObj, err + } else { + log.Info().Msgf("Use the CommonImage %s in SystemCommonNs", spImgName) + } + } else { + log.Info().Msgf("Use the Image %s in ns %s", spImgName, nsId) } } spSpecName := "" spSpecName, err = GetCspResourceName(nsId, model.StrSpec, v.SpecId) - if err != nil { - log.Info().Err(err) + if spSpecName == "" || err != nil { + log.Warn().Msgf("Not found the Spec %s in ns %s, find it from SystemCommonNs", v.SpecId, nsId) + errAgg := err.Error() + // If cannot find resource, use common resource spSpecName, err = GetCspResourceName(model.SystemCommonNs, model.StrSpec, v.SpecId) - if err != nil { - log.Err(err).Msg("Failed to Create a K8sCluster") + if spSpecName == "" || err != nil { + errAgg += err.Error() + err = fmt.Errorf(errAgg) + log.Err(err).Msgf("Not found the Spec %s both from ns %s and SystemCommonNs", v.SpecId, nsId) + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err + } else { + log.Info().Msgf("Use the CommonSpec %s in SystemCommonNs", spSpecName) } + } else { + log.Info().Msgf("Use the Spec %s in ns %s", spSpecName, nsId) } spKpName, err := GetCspResourceName(nsId, model.StrSSHKey, v.SshKeyId) if spKpName == "" { - log.Err(err).Msg("Failed to Create a K8sCluster") + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } @@ -217,8 +419,6 @@ func CreateK8sCluster(nsId string, req *model.TbK8sClusterReq, option string) (m }) } - uid := common.GenUid() - requestBody := model.SpiderClusterReq{ ConnectionName: req.ConnectionName, ReqInfo: model.SpiderClusterReqInfo{ @@ -259,26 +459,14 @@ func CreateK8sCluster(nsId string, req *model.TbK8sClusterReq, option string) (m ) if err != nil { - log.Err(err).Msg("Failed to Create a K8sCluster") + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) return emptyObj, err } - /* - * Extract SpiderClusterInfo from Response & Build model.TbK8sClusterInfo object - */ - - tbK8sCInfo := model.TbK8sClusterInfo{ - ResourceType: model.StrK8s, - Id: reqId, - Uid: uid, - CspResourceName: spClusterRes.SpiderClusterInfo.IId.NameId, - CspResourceId: spClusterRes.SpiderClusterInfo.IId.SystemId, - Name: reqId, - ConnectionName: req.ConnectionName, - ConnectionConfig: connectionConfig, - Description: req.Description, - CspViewK8sClusterDetail: spClusterRes.SpiderClusterInfo, - } + // Update model.TbK8sClusterInfo object + tbK8sCInfo.CspResourceName = spClusterRes.SpiderClusterInfo.IId.NameId + tbK8sCInfo.CspResourceId = spClusterRes.SpiderClusterInfo.IId.SystemId + tbK8sCInfo.CspViewK8sClusterDetail = spClusterRes.SpiderClusterInfo if option == "register" && req.CspResourceId == "" { tbK8sCInfo.SystemLabel = "Registered from CB-Spider resource" @@ -287,22 +475,7 @@ func CreateK8sCluster(nsId string, req *model.TbK8sClusterReq, option string) (m tbK8sCInfo.SystemLabel = "Registered from CSP resource" } - /* - * Put/Get model.TbK8sClusterInfo to/from kvstore - */ - k := GenK8sClusterKey(nsId, tbK8sCInfo.Id) - Val, _ := json.Marshal(tbK8sCInfo) - - err = kvstore.Put(k, string(Val)) - if err != nil { - log.Err(err).Msg("Failed to Create a K8sCluster") - return tbK8sCInfo, err - } - - storedTbK8sCInfo, err := GetK8sCluster(nsId, tbK8sCInfo.Id) - if err != nil { - log.Err(err).Msg("") - } + updateK8sClusterInfo(nsId, *tbK8sCInfo) // Store label info using CreateOrUpdateLabel labels := map[string]string{ @@ -319,37 +492,26 @@ func CreateK8sCluster(nsId string, req *model.TbK8sClusterReq, option string) (m model.LabelCreatedTime: tbK8sCInfo.CspViewK8sClusterDetail.CreatedTime.String(), model.LabelConnectionName: tbK8sCInfo.ConnectionName, } - err = label.CreateOrUpdateLabel(model.StrK8s, uid, k, labels) + k8sClusterKey := common.GenK8sClusterKey(nsId, k8sClusterId) + err = label.CreateOrUpdateLabel(model.StrK8s, uid, k8sClusterKey, labels) if err != nil { - log.Error().Err(err).Msg("") - return storedTbK8sCInfo, err + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) + return emptyObj, err } - return storedTbK8sCInfo, nil + return tbK8sCInfo, nil } // AddK8sNodeGroup adds a NodeGroup -func AddK8sNodeGroup(nsId string, k8sClusterId string, u *model.TbK8sNodeGroupReq) (model.TbK8sClusterInfo, error) { +func AddK8sNodeGroup(nsId string, k8sClusterId string, u *model.TbK8sNodeGroupReq) (*model.TbK8sClusterInfo, error) { log.Info().Msg("AddK8sNodeGroup") - emptyObj := model.TbK8sClusterInfo{} - /* - err := common.CheckString(nsId) - if err != nil { - log.Error().Err(err).Msg("") - return emptyObj, err - } + emptyObj := &model.TbK8sClusterInfo{} - err = common.CheckString(k8sClusterId) - if err != nil { - log.Error().Err(err).Msg("") - return emptyObj, err - } - */ err := validate.Struct(u) if err != nil { if _, ok := err.(*validator.InvalidValidationError); ok { - log.Err(err).Msg("Failed to Add K8sNodeGroup") + log.Err(err).Msgf("Failed to Add K8sNodeGroup(k8scluster=%s)", k8sClusterId) return emptyObj, err } @@ -358,82 +520,90 @@ func AddK8sNodeGroup(nsId string, k8sClusterId string, u *model.TbK8sNodeGroupRe check, err := CheckK8sCluster(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Add K8sNodeGroup") + log.Err(err).Msgf("Failed to Add K8sNodeGroup(k8scluster=%s)", k8sClusterId) return emptyObj, err } if !check { - err := fmt.Errorf("The K8sCluster " + k8sClusterId + " does not exist.") - log.Err(err).Msg("Failed to Add K8sNodeGroup") + err := fmt.Errorf("not exist") + log.Err(err).Msgf("Failed to Add K8sNodeGroup(k8scluster=%s)", k8sClusterId) return emptyObj, err } - /* - * Get model.TbK8sClusterInfo from kvstore - */ - oldTbK8sCInfo := model.TbK8sClusterInfo{} - k := GenK8sClusterKey(nsId, k8sClusterId) - kv, err := kvstore.GetKv(k) - if err != nil { - err = fmt.Errorf("In AddK8sNodeGroup(); kvstore.GetKv() returned an error: " + err.Error()) - log.Err(err).Msg("Failed to Add K8sNodeGroup") - return emptyObj, err - } - - log.Debug().Msg("<" + kv.Key + "> \n" + kv.Value) - - err = json.Unmarshal([]byte(kv.Value), &oldTbK8sCInfo) + // Get model.TbK8sClusterInfo from kvstore + tbK8sCInfo, err := getK8sClusterInfo(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Add K8sNodeGroup") + log.Err(err).Msgf("Failed to Add K8sNodeGroup(k8scluster=%s)", k8sClusterId) return emptyObj, err } - /* - * Check for K8sCluster Enablement from ClusterSetting - */ - - err = checkK8sClusterEnablement(oldTbK8sCInfo.ConnectionName) + // Check for K8sCluster Enablement from ClusterSetting + err = checkK8sClusterEnablement(tbK8sCInfo.ConnectionName) if err != nil { - log.Err(err).Msg("Failed to Add K8sNodeGroup") + log.Err(err).Msgf("Failed to Add K8sNodeGroup(k8scluster=%s)", k8sClusterId) return emptyObj, err } - /* - * Build RequestBody for SpiderNodeGroupReq{} - */ - + // Build RequestBody for SpiderNodeGroupReq{} spName := u.Name err = common.CheckString(spName) if err != nil { - log.Err(err).Msg("Failed to Add K8sNodeGroup") + log.Err(err).Msgf("Failed to Add K8sNodeGroup(k8scluster=%s)", k8sClusterId) return emptyObj, err } - spImgName := "" // Some CSPs do not require ImageName for creating a cluster + spImgName := "" // Some CSPs do not require ImageName for creating a k8s cluster if u.ImageId == "" || u.ImageId == "default" { spImgName = "" } else { spImgName, err = GetCspResourceName(nsId, model.StrImage, u.ImageId) - if spImgName == "" { - log.Err(err).Msg("Failed to Add K8sNodeGroup") - return emptyObj, err + if spImgName == "" || err != nil { + log.Warn().Msgf("Not found the Image %s in ns %s, find it from SystemCommonNs", u.ImageId, nsId) + errAgg := err.Error() + // If cannot find the resource, use common resource + spImgName, err = GetCspResourceName(model.SystemCommonNs, model.StrImage, u.ImageId) + if spImgName == "" || err != nil { + errAgg += err.Error() + err = fmt.Errorf(errAgg) + log.Err(err).Msgf("Not found the Image %s both from ns %s and SystemCommonNs", u.ImageId, nsId) + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) + return emptyObj, err + } else { + log.Info().Msgf("Use the CommonImage %s in SystemCommonNs", spImgName) + } + } else { + log.Info().Msgf("Use the Image %s in ns %s", spImgName, nsId) } } - spSpecName, err := GetCspResourceName(nsId, model.StrSpec, u.SpecId) - if err != nil { - log.Err(err).Msg("Failed to Create a K8sCluster") - return emptyObj, err + spSpecName := "" + spSpecName, err = GetCspResourceName(nsId, model.StrSpec, u.SpecId) + if spSpecName == "" || err != nil { + log.Warn().Msgf("Not found the Spec %s in ns %s, find it from SystemCommonNs", u.SpecId, nsId) + errAgg := err.Error() + // If cannot find resource, use common resource + spSpecName, err = GetCspResourceName(model.SystemCommonNs, model.StrSpec, u.SpecId) + if spSpecName == "" || err != nil { + errAgg += err.Error() + err = fmt.Errorf(errAgg) + log.Err(err).Msgf("Not found the Spec %s both from ns %s and SystemCommonNs", u.SpecId, nsId) + log.Err(err).Msgf("Failed to Create a K8sCluster(%s)", k8sClusterId) + return emptyObj, err + } else { + log.Info().Msgf("Use the CommonSpec %s in SystemCommonNs", spSpecName) + } + } else { + log.Info().Msgf("Use the Spec %s in ns %s", spSpecName, nsId) } spKpName, err := GetCspResourceName(nsId, model.StrSSHKey, u.SshKeyId) if spKpName == "" { - log.Err(err).Msg("Failed to Add K8sNodeGroup") + log.Err(err).Msgf("Failed to Add K8sNodeGroup(k8scluster=%s)", k8sClusterId) return emptyObj, err } requestBody := model.SpiderNodeGroupReq{ - ConnectionName: oldTbK8sCInfo.ConnectionName, + ConnectionName: tbK8sCInfo.ConnectionName, ReqInfo: model.SpiderNodeGroupReqInfo{ Name: spName, ImageName: spImgName, @@ -454,7 +624,7 @@ func AddK8sNodeGroup(nsId string, k8sClusterId string, u *model.TbK8sNodeGroupRe method := "POST" client.SetTimeout(20 * time.Minute) - url := model.SpiderRestUrl + "/cluster/" + oldTbK8sCInfo.CspResourceName + "/nodegroup" + url := model.SpiderRestUrl + "/cluster/" + tbK8sCInfo.CspResourceName + "/nodegroup" var spClusterRes model.SpiderClusterRes @@ -470,109 +640,58 @@ func AddK8sNodeGroup(nsId string, k8sClusterId string, u *model.TbK8sNodeGroupRe ) if err != nil { - log.Err(err).Msg("Failed to Add K8sNodeGroup") + log.Err(err).Msgf("Failed to Add K8sNodeGroup(k8scluster=%s)", k8sClusterId) return emptyObj, err } - /* - * Extract SpiderClusterInfo from Response & Build model.TbK8sClusterInfo object - */ - - newTbK8sCInfo := model.TbK8sClusterInfo{ - ResourceType: model.StrK8s, - Id: oldTbK8sCInfo.Id, - Uid: oldTbK8sCInfo.Uid, - CspResourceName: oldTbK8sCInfo.CspResourceName, - CspResourceId: oldTbK8sCInfo.CspResourceId, - Name: oldTbK8sCInfo.Name, - ConnectionName: oldTbK8sCInfo.ConnectionName, - Description: oldTbK8sCInfo.Description, - CspViewK8sClusterDetail: spClusterRes.SpiderClusterInfo, - } - - /* - * Put/Get model.TbK8sClusterInfo to/from kvstore - */ - k = GenK8sClusterKey(nsId, newTbK8sCInfo.Id) - Val, _ := json.Marshal(newTbK8sCInfo) + // Update/Get model.TbK8sClusterInfo object to/from kvstore + tbK8sCInfo.CspViewK8sClusterDetail = spClusterRes.SpiderClusterInfo + updateK8sClusterInfo(nsId, tbK8sCInfo) - err = kvstore.Put(k, string(Val)) + storedTbK8sCInfo, err := getK8sClusterInfo(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Add K8sNodeGroup") - return newTbK8sCInfo, err - } - - kv, err = kvstore.GetKv(k) - if err != nil { - err = fmt.Errorf("In AddK8sNodeGroup(); kvstore.GetKv() returned an error: " + err.Error()) - log.Err(err).Msg("") - // return nil, err + log.Err(err).Msgf("Failed to Add K8sNodeGroup(k8scluster=%s)", k8sClusterId) + return emptyObj, err } - log.Debug().Msg("<" + kv.Key + "> \n" + kv.Value) - - storedTbK8sCInfo := model.TbK8sClusterInfo{} - err = json.Unmarshal([]byte(kv.Value), &storedTbK8sCInfo) - if err != nil { - log.Err(err).Msg("") - } - return storedTbK8sCInfo, nil + return &storedTbK8sCInfo, nil } // RemoveK8sNodeGroup removes a specified NodeGroup -func RemoveK8sNodeGroup(nsId string, k8sClusterId string, k8sNodeGroupName string, forceFlag string) (bool, error) { +func RemoveK8sNodeGroup(nsId, k8sClusterId, k8sNodeGroupName, option string) (bool, error) { log.Info().Msg("RemoveK8sNodeGroup") - /* - err := common.CheckString(nsId) - if err != nil { - log.Error().Err(err).Msg("") - return err - } - err = common.CheckString(k8sClusterId) - if err != nil { - log.Error().Err(err).Msg("") - return err - } - */ check, err := CheckK8sCluster(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Remove K8sNodeGroup") + log.Err(err).Msgf("Failed to Remove K8sNodeGroup(k8scluster=%s)", k8sClusterId) return false, err } if !check { - err := fmt.Errorf("The K8sCluster " + k8sClusterId + " does not exist.") - log.Err(err).Msg("Failed to Remove K8sNodeGroup") + err := fmt.Errorf("not exist") + log.Err(err).Msgf("Failed to Remove K8sNodeGroup(k8scluster=%s)", k8sClusterId) return false, err } - k := GenK8sClusterKey(nsId, k8sClusterId) - log.Debug().Msg("key: " + k) - - kv, _ := kvstore.GetKv(k) + // Get model.TbK8sClusterInfo from kvstore + tbK8sCInfo, err := getK8sClusterInfo(nsId, k8sClusterId) + if err != nil { + log.Err(err).Msgf("Failed to Remove K8sNodeGroup(k8scluster=%s)", k8sClusterId) + return false, err + } - // Create Req body + // Create Request body for RemoveK8sNodeGroup of CB-Spider type JsonTemplate struct { - NameSpace string ConnectionName string } - requestBody := JsonTemplate{} - - tbK8sCInfo := model.TbK8sClusterInfo{} - err = json.Unmarshal([]byte(kv.Value), &tbK8sCInfo) - if err != nil { - log.Err(err).Msg("Failed to Remove K8sNodeGroup") - return false, err + requestBody := JsonTemplate{ + ConnectionName: tbK8sCInfo.ConnectionName, } - requestBody.NameSpace = "" // should be empty string from Tumblebug - requestBody.ConnectionName = tbK8sCInfo.ConnectionName - client := resty.New() url := model.SpiderRestUrl + "/cluster/" + tbK8sCInfo.CspResourceName + "/nodegroup/" + k8sNodeGroupName - if forceFlag == "true" { + if option == "force" { url += "?force=true" } method := "DELETE" @@ -590,7 +709,7 @@ func RemoveK8sNodeGroup(nsId string, k8sClusterId string, k8sNodeGroupName strin ) if err != nil { - log.Err(err).Msg("Failed to Remove K8sNodeGroup") + log.Err(err).Msgf("Failed to Remove K8sNodeGroup(k8scluster=%s)", k8sClusterId) return false, err } @@ -607,58 +726,39 @@ func RemoveK8sNodeGroup(nsId string, k8sClusterId string, k8sNodeGroupName strin } // SetK8sNodeGroupAutoscaling set NodeGroup's Autoscaling On/Off -func SetK8sNodeGroupAutoscaling(nsId string, k8sClusterId string, k8sNodeGroupName string, u *model.TbSetK8sNodeGroupAutoscalingReq) (model.TbSetK8sNodeGroupAutoscalingRes, error) { +func SetK8sNodeGroupAutoscaling(nsId string, k8sClusterId string, k8sNodeGroupName string, u *model.TbSetK8sNodeGroupAutoscalingReq) (*model.TbSetK8sNodeGroupAutoscalingRes, error) { log.Info().Msg("SetK8sNodeGroupAutoscaling") - emptyObj := model.TbSetK8sNodeGroupAutoscalingRes{} - /* - err := common.CheckString(nsId) - if err != nil { - log.Error().Err(err).Msg("") - return err - } + emptyObj := &model.TbSetK8sNodeGroupAutoscalingRes{} - err = common.CheckString(k8sClusterId) - if err != nil { - log.Error().Err(err).Msg("") - return err - } - */ check, err := CheckK8sCluster(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Set K8sNodeGroup Autoscaling") + log.Err(err).Msgf("Failed to Set K8sNodeGroup Autoscaling(k8scluster=%s)", k8sClusterId) return emptyObj, err } if !check { - err := fmt.Errorf("The K8sCluster " + k8sClusterId + " does not exist.") - log.Err(err).Msg("Failed to Set K8sNodeGroup Autoscaling") + err := fmt.Errorf("not exist") + log.Err(err).Msgf("Failed to Set K8sNodeGroup Autoscaling(k8scluster=%s)", k8sClusterId) return emptyObj, err } err = common.CheckString(k8sNodeGroupName) if err != nil { - log.Err(err).Msg("Failed to Set K8sNodeGroup Autoscaling") + log.Err(err).Msgf("Failed to Set K8sNodeGroup Autoscaling(k8scluster=%s)", k8sClusterId) return emptyObj, err } - /* - * Get model.TbK8sClusterInfo object from kvstore - */ - - k := GenK8sClusterKey(nsId, k8sClusterId) - log.Debug().Msg("key: " + k) - - kv, _ := kvstore.GetKv(k) - - tbK8sCInfo := model.TbK8sClusterInfo{} - err = json.Unmarshal([]byte(kv.Value), &tbK8sCInfo) + // Get model.TbK8sClusterInfo from kvstore + tbK8sCInfo, err := getK8sClusterInfo(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Set K8sNodeGroup Autoscaling") + log.Err(err).Msgf("Failed to Set K8sNodeGroup Autoscaling(k8scluster=%s)", k8sClusterId) + log.Err(err).Msg("") return emptyObj, err } + // Create Request body for SetAutoScaling of CB-Spider requestBody := model.SpiderSetAutoscalingReq{ ConnectionName: tbK8sCInfo.ConnectionName, ReqInfo: model.SpiderSetAutoscalingReqInfo{ @@ -683,66 +783,46 @@ func SetK8sNodeGroupAutoscaling(nsId string, k8sClusterId string, k8sNodeGroupNa ) if err != nil { - log.Err(err).Msg("Failed to Set K8sNodeGroup Autoscaling") + log.Err(err).Msgf("Failed to Set K8sNodeGroup Autoscaling(k8scluster=%s)", k8sClusterId) return emptyObj, err } - var tbK8sSetAutoscalingRes model.TbSetK8sNodeGroupAutoscalingRes - tbK8sSetAutoscalingRes.Result = spSetAutoscalingRes.Result + tbK8sSetAutoscalingRes := &model.TbSetK8sNodeGroupAutoscalingRes{ + Result: spSetAutoscalingRes.Result, + } return tbK8sSetAutoscalingRes, nil } // ChangeK8sNodeGroupAutoscaleSize change NodeGroup's Autoscaling Size -func ChangeK8sNodeGroupAutoscaleSize(nsId string, k8sClusterId string, k8sNodeGroupName string, u *model.TbChangeK8sNodeGroupAutoscaleSizeReq) (model.TbChangeK8sNodeGroupAutoscaleSizeRes, error) { +func ChangeK8sNodeGroupAutoscaleSize(nsId string, k8sClusterId string, k8sNodeGroupName string, u *model.TbChangeK8sNodeGroupAutoscaleSizeReq) (*model.TbChangeK8sNodeGroupAutoscaleSizeRes, error) { log.Info().Msg("ChangeK8sNodeGroupAutoscaleSize") - emptyObj := model.TbChangeK8sNodeGroupAutoscaleSizeRes{} - /* - err := common.CheckString(nsId) - if err != nil { - log.Error().Err(err).Msg("") - return err - } + emptyObj := &model.TbChangeK8sNodeGroupAutoscaleSizeRes{} - err = common.CheckString(k8sClusterId) - if err != nil { - log.Error().Err(err).Msg("") - return err - } - */ check, err := CheckK8sCluster(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Change K8sNodeGroup AutoscaleSize") + log.Err(err).Msgf("Failed to Change K8sNodeGroup AutoscaleSize(k8scluster=%s)", k8sClusterId) return emptyObj, err } if !check { - err := fmt.Errorf("The K8sCluster " + k8sClusterId + " does not exist.") - log.Err(err).Msg("Failed to Change K8sNodeGroup AutoscaleSize") + err := fmt.Errorf("not exist") + log.Err(err).Msgf("Failed to Change K8sNodeGroup AutoscaleSize(k8scluster=%s)", k8sClusterId) return emptyObj, err } err = common.CheckString(k8sNodeGroupName) if err != nil { - log.Err(err).Msg("Failed to Change K8sNodeGroup AutoscaleSize") + log.Err(err).Msgf("Failed to Change K8sNodeGroup AutoscaleSize(k8scluster=%s)", k8sClusterId) return emptyObj, err } - /* - * Get model.TbK8sClusterInfo object from kvstore - */ - - k := GenK8sClusterKey(nsId, k8sClusterId) - log.Debug().Msg("key: " + k) - - kv, _ := kvstore.GetKv(k) - - tbK8sCInfo := model.TbK8sClusterInfo{} - err = json.Unmarshal([]byte(kv.Value), &tbK8sCInfo) + // Get model.TbK8sClusterInfo from kvstore + tbK8sCInfo, err := getK8sClusterInfo(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Change K8sNodeGroup AutoscaleSize") + log.Err(err).Msgf("Failed to Change K8sNodeGroup AutoscaleSize(k8scluster=%s)", k8sClusterId) return emptyObj, err } @@ -772,80 +852,48 @@ func ChangeK8sNodeGroupAutoscaleSize(nsId string, k8sClusterId string, k8sNodeGr ) if err != nil { - log.Err(err).Msg("Failed to Change K8sNodeGroup AutoscaleSize") + log.Err(err).Msgf("Failed to Change K8sNodeGroup AutoscaleSize(k8scluster=%s)", k8sClusterId) return emptyObj, err } - var tbK8sCAutoscaleSizeRes model.TbChangeK8sNodeGroupAutoscaleSizeRes - tbK8sCAutoscaleSizeRes.TbK8sNodeGroupInfo = model.TbK8sNodeGroupInfo{ - CspViewK8sNodeGroupDetail: spChangeAutoscaleSizeRes.SpiderNodeGroupInfo, + tbK8sCAutoscaleSizeRes := &model.TbChangeK8sNodeGroupAutoscaleSizeRes{ + TbK8sNodeGroupInfo: model.TbK8sNodeGroupInfo{ + CspViewK8sNodeGroupDetail: spChangeAutoscaleSizeRes.SpiderNodeGroupInfo, + }, } return tbK8sCAutoscaleSizeRes, nil } // GetK8sCluster retrives a k8s cluster information -func GetK8sCluster(nsId string, k8sClusterId string) (model.TbK8sClusterInfo, error) { +func GetK8sCluster(nsId string, k8sClusterId string) (*model.TbK8sClusterInfo, error) { + log.Debug().Msg("[Get K8sCluster] " + k8sClusterId) - emptyObj := model.TbK8sClusterInfo{} - /* - err := common.CheckString(nsId) - if err != nil { - log.Error().Err(err).Msg("") - return emptyObj, err - } + emptyObj := &model.TbK8sClusterInfo{} - err = common.CheckString(k8sClusterId) - if err != nil { - log.Error().Err(err).Msg("") - return emptyObj, err - } - */ check, err := CheckK8sCluster(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Get K8sCluster") + log.Err(err).Msgf("Failed to Get K8sCluster(%s)", k8sClusterId) return emptyObj, err } if !check { - err := fmt.Errorf("The K8sCluster " + k8sClusterId + " does not exist.") - log.Err(err).Msg("Failed to Get K8sCluster") + err := fmt.Errorf("not exist") + log.Err(err).Msgf("Failed to Get K8sCluster(%s)", k8sClusterId) return emptyObj, err } - log.Debug().Msg("[Get K8sCluster] " + k8sClusterId) - - /* - * Get model.TbK8sClusterInfo object from kvstore - */ - k := GenK8sClusterKey(nsId, k8sClusterId) - - kv, err := kvstore.GetKv(k) + // Get model.TbK8sClusterInfo from kvstore + tbK8sCInfo, err := getK8sClusterInfo(nsId, k8sClusterId) if err != nil { - log.Error().Err(err).Msg("") + log.Err(err).Msgf("Failed to Get K8sCluster(%s)", k8sClusterId) return emptyObj, err } - storedTbK8sCInfo := model.TbK8sClusterInfo{} - if kv == (kvstore.KeyValue{}) { - err = fmt.Errorf("Cannot get the k8s cluster " + k8sClusterId + ".") - log.Err(err).Msg("Failed to Get K8sCluster") - return storedTbK8sCInfo, err - } - - err = json.Unmarshal([]byte(kv.Value), &storedTbK8sCInfo) - if err != nil { - log.Err(err).Msg("Failed to Get K8sCluster") - return storedTbK8sCInfo, err - } - - /* - * Get model.TbK8sClusterInfo object from CB-Spider - */ - + // Update model.TbK8sClusterInfo from CB-Spider client := resty.New() client.SetTimeout(10 * time.Minute) - url := model.SpiderRestUrl + "/cluster/" + storedTbK8sCInfo.CspResourceName + url := model.SpiderRestUrl + "/cluster/" + tbK8sCInfo.CspResourceName method := "GET" // Create Request body for GetK8sCluster of CB-Spider @@ -853,7 +901,7 @@ func GetK8sCluster(nsId string, k8sClusterId string) (model.TbK8sClusterInfo, er ConnectionName string } requestBody := JsonTemplate{ - ConnectionName: storedTbK8sCInfo.ConnectionName, + ConnectionName: tbK8sCInfo.ConnectionName, } var spClusterRes model.SpiderClusterRes @@ -869,65 +917,30 @@ func GetK8sCluster(nsId string, k8sClusterId string) (model.TbK8sClusterInfo, er ) if err != nil { - log.Err(err).Msg("Failed to Get K8sCluster") + log.Err(err).Msgf("Failed to Get K8sCluster(%s)", k8sClusterId) return emptyObj, err } - tbK8sCInfo := model.TbK8sClusterInfo{ - ResourceType: model.StrK8s, - Id: storedTbK8sCInfo.Id, - Uid: storedTbK8sCInfo.Uid, - CspResourceName: storedTbK8sCInfo.CspResourceName, - CspResourceId: storedTbK8sCInfo.CspResourceId, - Name: storedTbK8sCInfo.Name, - ConnectionName: storedTbK8sCInfo.ConnectionName, - ConnectionConfig: storedTbK8sCInfo.ConnectionConfig, - Description: storedTbK8sCInfo.Description, - CspViewK8sClusterDetail: spClusterRes.SpiderClusterInfo, - } + // Update/Get model.TbK8sClusterInfo object to/from kvstore + tbK8sCInfo.CspViewK8sClusterDetail = spClusterRes.SpiderClusterInfo + updateK8sClusterInfo(nsId, tbK8sCInfo) - /* - * FIXME: Do not compare, just store? - * Compare tbK8sCInfo with storedTbK8sCInfo - */ - /* - if !isEqualTbK8sClusterInfoExceptStatus(storedTbK8sCInfo, tbK8sCInfo) { - err := fmt.Errorf("The k8s cluster " + k8sClusterId + " has been changed something.") - log.Err(err).Msg("Failed to Get K8sCluster") - return emptyObj, err - } - */ - - // add label info - labelInfo, err := label.GetLabels(model.StrK8s, tbK8sCInfo.Uid) + storedTbK8sCInfo, err := getK8sClusterInfo(nsId, k8sClusterId) if err != nil { - log.Error().Err(err).Msg("Cannot get the label info") - return tbK8sCInfo, err + log.Err(err).Msgf("Failed to Get K8sCluster(%s)", k8sClusterId) + return emptyObj, err } - tbK8sCInfo.Label = labelInfo.Labels - - return tbK8sCInfo, nil -} - -/* -func isEqualTbK8sClusterInfoExceptStatus(info1 model.TbK8sClusterInfo, info2 model.TbK8sClusterInfo) bool { - - // FIX: now compare some fields only - - if info1.Id != info2.Id || - info1.Name != info2.Name || - info1.ConnectionName != info2.ConnectionName || - info1.Description != info2.Description || - info1.CspResourceId != info2.CspResourceId || - info1.CspResourceName != info2.CspResourceName || - info1.CreatedTime != info2.CreatedTime { - return false + // add label info + labelInfo, err := label.GetLabels(model.StrK8s, storedTbK8sCInfo.Uid) + if err != nil { + log.Err(err).Msgf("Failed to Get K8sCluster(%s)", k8sClusterId) + return emptyObj, err } + storedTbK8sCInfo.Label = labelInfo.Labels - return true + return &storedTbK8sCInfo, nil } -*/ // CheckK8sCluster returns the existence of the TB K8sCluster object in bool form. func CheckK8sCluster(nsId string, k8sClusterId string) (bool, error) { @@ -955,7 +968,7 @@ func CheckK8sCluster(nsId string, k8sClusterId string) (bool, error) { log.Debug().Msg("[Check K8sCluster] " + k8sClusterId) - key := GenK8sClusterKey(nsId, k8sClusterId) + key := common.GenK8sClusterKey(nsId, k8sClusterId) keyValue, err := kvstore.GetKv(key) if err != nil { @@ -968,23 +981,6 @@ func CheckK8sCluster(nsId string, k8sClusterId string) (bool, error) { return false, nil } -// GenK8sClusterKey is func to generate a key from K8sCluster ID -func GenK8sClusterKey(nsId string, k8sClusterId string) string { - err := common.CheckString(nsId) - if err != nil { - log.Error().Err(err).Msg("") - return "/invalidKey" - } - - err = common.CheckString(k8sClusterId) - if err != nil { - log.Err(err).Msg("Failed to Generate K8sCluster Key") - return "/invalidKey" - } - - return fmt.Sprintf("/ns/%s/k8scluster/%s", nsId, k8sClusterId) -} - // ListK8sClusterId returns the list of TB K8sCluster object IDs of given nsId func ListK8sClusterId(nsId string) ([]string, error) { @@ -1006,14 +1002,6 @@ func ListK8sClusterId(nsId string) ([]string, error) { return nil, err } - /* if keyValue == nil, then for-loop below will not be executed, and the empty array will be returned in `resourceList` placeholder. - if keyValue == nil { - err = fmt.Errorf("ListResourceId(); %s is empty.", key) - log.Error().Err(err).Msg("") - return nil, err - } - */ - var k8sClusterIds []string for _, v := range kv { trimmed := strings.TrimPrefix(v.Key, (k + "k8scluster/")) @@ -1028,7 +1016,7 @@ func ListK8sClusterId(nsId string) ([]string, error) { // ListK8sCluster returns the list of TB K8sCluster objects of given nsId func ListK8sCluster(nsId string, filterKey string, filterVal string) (interface{}, error) { - //log.Info().Msg("ListK8sCluster") + log.Info().Msg("ListK8sCluster") err := common.CheckString(nsId) if err != nil { @@ -1040,9 +1028,7 @@ func ListK8sCluster(nsId string, filterKey string, filterVal string) (interface{ k := fmt.Sprintf("/ns/%s/k8scluster", nsId) //log.Debug().Msg(k) - /* - * Get model.TbK8sClusterInfo objects from kvstore - */ + // Get model.TbK8sClusterInfo objects from kvstore kv, err := kvstore.GetKvList(k) kv = kvutil.FilterKvListBy(kv, k, 1) @@ -1079,61 +1065,40 @@ func ListK8sCluster(nsId string, filterKey string, filterVal string) (interface{ } // DeleteK8sCluster deletes a k8s cluster -func DeleteK8sCluster(nsId string, k8sClusterId string, forceFlag string) (bool, error) { +func DeleteK8sCluster(nsId, k8sClusterId, option string) (bool, error) { log.Info().Msg("DeleteK8sCluster") - /* - err := common.CheckString(nsId) - if err != nil { - log.Error().Err(err).Msg("") - return err - } - err = common.CheckString(k8sClusterId) - if err != nil { - log.Error().Err(err).Msg("") - return err - } - */ check, err := CheckK8sCluster(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Delete K8sCluster") + log.Err(err).Msgf("Failed to Delete K8sCluster(%s)", k8sClusterId) return false, err } if !check { - err := fmt.Errorf("The K8sCluster " + k8sClusterId + " does not exist.") - log.Err(err).Msg("Failed to Delete K8sCluster") + err := fmt.Errorf("not exist") + log.Err(err).Msgf("Failed to Delete K8sCluster(%s)", k8sClusterId) return false, err } - /* - * Get model.TbK8sClusterInfo object from kvstore - */ - - k := GenK8sClusterKey(nsId, k8sClusterId) - log.Debug().Msg("key: " + k) - - kv, _ := kvstore.GetKv(k) + // Get model.TbK8sClusterInfo object from kvstore + tbK8sCInfo, err := getK8sClusterInfo(nsId, k8sClusterId) + if err != nil { + log.Err(err).Msgf("Failed to Delete K8sCluster(%s)", k8sClusterId) + return false, err + } // Create Req body type JsonTemplate struct { ConnectionName string } - requestBody := JsonTemplate{} - - tbK8sCInfo := model.TbK8sClusterInfo{} - err = json.Unmarshal([]byte(kv.Value), &tbK8sCInfo) - if err != nil { - log.Err(err).Msg("Failed to Delete K8sCluster") - return false, err + requestBody := JsonTemplate{ + ConnectionName: tbK8sCInfo.ConnectionName, } - requestBody.ConnectionName = tbK8sCInfo.ConnectionName - client := resty.New() url := model.SpiderRestUrl + "/cluster/" + tbK8sCInfo.CspResourceName - if forceFlag == "true" { + if option == "force" { url += "?force=true" } method := "DELETE" @@ -1150,16 +1115,17 @@ func DeleteK8sCluster(nsId string, k8sClusterId string, forceFlag string) (bool, common.VeryShortDuration, ) - if forceFlag == "true" { - err = kvstore.Delete(k) + log.Debug().Msgf("option=%s", option) + if option == "force" { + err = deleteK8sClusterInfo(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Delete K8sCluster") + log.Err(err).Msgf("Failed to Delete K8sCluster(%s)", k8sClusterId) return false, err } } if err != nil { - log.Err(err).Msg("Failed to Delete K8sCluster") + log.Err(err).Msgf("Failed to Delete K8sCluster(%s)", k8sClusterId) return false, err } @@ -1167,10 +1133,10 @@ func DeleteK8sCluster(nsId string, k8sClusterId string, forceFlag string) (bool, if mapRes, ok := ifRes.(map[string]interface{}); ok { result := mapRes["Result"] if result == "true" { - if forceFlag != "true" { - err = kvstore.Delete(k) + if option != "force" { + err = deleteK8sClusterInfo(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Delete K8sCluster") + log.Err(err).Msgf("Failed to Delete K8sCluster(%s)", k8sClusterId) return false, err } } @@ -1184,14 +1150,14 @@ func DeleteK8sCluster(nsId string, k8sClusterId string, forceFlag string) (bool, } // DeleteAllK8sCluster deletes all clusters -func DeleteAllK8sCluster(nsId string, subString string, forceFlag string) (model.IdList, error) { +func DeleteAllK8sCluster(nsId, subString, option string) (*model.IdList, error) { log.Info().Msg("DeleteAllK8sCluster") - deletedK8sClusters := model.IdList{} + deletedK8sClusters := &model.IdList{} err := common.CheckString(nsId) if err != nil { - log.Err(err).Msg("Failed to Delete All K8sCluster") + log.Err(err).Msgf("Failed to Delete All K8sCluster") return deletedK8sClusters, err } @@ -1205,7 +1171,7 @@ func DeleteAllK8sCluster(nsId string, subString string, forceFlag string) (model if subString == "" || strings.Contains(v, subString) { deleteStatus := "" - res, err := DeleteK8sCluster(nsId, v, forceFlag) + res, err := DeleteK8sCluster(nsId, v, option) if err != nil { deleteStatus = err.Error() @@ -1220,15 +1186,15 @@ func DeleteAllK8sCluster(nsId string, subString string, forceFlag string) (model } // UpgradeK8sCluster upgrades an existing k8s cluster to the specified version -func UpgradeK8sCluster(nsId string, k8sClusterId string, u *model.TbUpgradeK8sClusterReq) (model.TbK8sClusterInfo, error) { +func UpgradeK8sCluster(nsId string, k8sClusterId string, u *model.TbUpgradeK8sClusterReq) (*model.TbK8sClusterInfo, error) { log.Info().Msg("UpgradeK8sCluster") - emptyObj := model.TbK8sClusterInfo{} + emptyObj := &model.TbK8sClusterInfo{} err := validate.Struct(u) if err != nil { if _, ok := err.(*validator.InvalidValidationError); ok { - log.Err(err).Msg("Failed to Upgrade a K8sCluster") + log.Err(err).Msgf("Failed to Upgrade a K8sCluster(%s)", k8sClusterId) return emptyObj, err } @@ -1237,67 +1203,48 @@ func UpgradeK8sCluster(nsId string, k8sClusterId string, u *model.TbUpgradeK8sCl check, err := CheckK8sCluster(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Upgrade a K8sCluster") + log.Err(err).Msgf("Failed to Upgrade a K8sCluster(%s)", k8sClusterId) return emptyObj, err } if !check { - err := fmt.Errorf("The K8sCluster " + k8sClusterId + " does not exist.") - log.Err(err).Msg("Failed to Upgrade a K8sCluster") - return emptyObj, err - } - - /* - * Get model.TbK8sClusterInfo from kvstore - */ - oldTbK8sCInfo := model.TbK8sClusterInfo{} - k := GenK8sClusterKey(nsId, k8sClusterId) - kv, err := kvstore.GetKv(k) - if err != nil { - err = fmt.Errorf("In UpgradeK8sCluster(); kvstore.GetKv() returned an error: " + err.Error()) - log.Err(err).Msg("Failed to Upgrade a K8sCluster") + err := fmt.Errorf("not exist") + log.Err(err).Msgf("Failed to Upgrade a K8sCluster(%s)", k8sClusterId) return emptyObj, err } - log.Debug().Msg("<" + kv.Key + "> \n" + kv.Value) - - err = json.Unmarshal([]byte(kv.Value), &oldTbK8sCInfo) + // Get model.TbK8sClusterInfo from kvstore + tbK8sCInfo, err := getK8sClusterInfo(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Upgrade a K8sCluster") + log.Err(err).Msgf("Failed to Upgrade a K8sCluster(%s)", k8sClusterId) return emptyObj, err } - /* - * Check for K8sCluster Enablement from K8sClusterSetting - */ - - err = checkK8sClusterEnablement(oldTbK8sCInfo.ConnectionName) + // Check for K8sCluster Enablement from K8sClusterSetting + err = checkK8sClusterEnablement(tbK8sCInfo.ConnectionName) if err != nil { - log.Err(err).Msg("Failed to Upgrade a K8sCluster") + log.Err(err).Msgf("Failed to Upgrade a K8sCluster(%s)", k8sClusterId) return emptyObj, err } - /* - * Build RequestBody for model.SpiderUpgradeClusterReq{} - */ - // Validate - err = validateAtUpgradeK8sCluster(oldTbK8sCInfo.ConnectionName, u) + err = validateAtUpgradeK8sCluster(tbK8sCInfo.ConnectionName, u) if err != nil { - log.Err(err).Msg("Failed to Upgrade a K8sCluster") + log.Err(err).Msgf("Failed to Upgrade a K8sCluster(%s)", k8sClusterId) return emptyObj, err } - spVersion := u.Version + // Build RequestBody for model.SpiderUpgradeClusterReq{} + spVersion := u.Version requestBody := model.SpiderUpgradeClusterReq{ - ConnectionName: oldTbK8sCInfo.ConnectionName, + ConnectionName: tbK8sCInfo.ConnectionName, ReqInfo: model.SpiderUpgradeClusterReqInfo{ Version: spVersion, }, } client := resty.New() - url := model.SpiderRestUrl + "/cluster/" + oldTbK8sCInfo.CspResourceName + "/upgrade" + url := model.SpiderRestUrl + "/cluster/" + tbK8sCInfo.CspResourceName + "/upgrade" method := "PUT" var spClusterRes model.SpiderClusterRes @@ -1313,206 +1260,34 @@ func UpgradeK8sCluster(nsId string, k8sClusterId string, u *model.TbUpgradeK8sCl ) if err != nil { - log.Err(err).Msg("Failed to Upgrade a K8sCluster") + log.Err(err).Msgf("Failed to Upgrade a K8sCluster(%s)", k8sClusterId) return emptyObj, err } - /* - * Extract SpiderClusterInfo from Response & Build model.TbK8sClusterInfo object - */ - - newTbK8sCInfo := model.TbK8sClusterInfo{ - ResourceType: model.StrK8s, - Id: oldTbK8sCInfo.Id, - Uid: oldTbK8sCInfo.Uid, - CspResourceName: oldTbK8sCInfo.CspResourceName, - CspResourceId: oldTbK8sCInfo.CspResourceId, - Name: oldTbK8sCInfo.Name, - ConnectionName: oldTbK8sCInfo.ConnectionName, - Description: oldTbK8sCInfo.Description, - CspViewK8sClusterDetail: spClusterRes.SpiderClusterInfo, - } + // Update/Get model.TbK8sClusterInfo object to/from kvstore + tbK8sCInfo.CspViewK8sClusterDetail = spClusterRes.SpiderClusterInfo + updateK8sClusterInfo(nsId, tbK8sCInfo) - /* - * Put/Get model.TbK8sClusterInfo to/from kvstore - */ - k = GenK8sClusterKey(nsId, newTbK8sCInfo.Id) - Val, _ := json.Marshal(newTbK8sCInfo) - - err = kvstore.Put(k, string(Val)) + storedTbK8sCInfo, err := getK8sClusterInfo(nsId, k8sClusterId) if err != nil { - log.Err(err).Msg("Failed to Upgrade a K8sCluster") + log.Err(err).Msgf("Failed to Upgrade a K8sCluster(%s)", k8sClusterId) return emptyObj, err } - kv, err = kvstore.GetKv(k) - if err != nil { - err = fmt.Errorf("In UpgradeK8sCluster(); kvstore.GetKv() returned an error: " + err.Error()) - log.Err(err).Msg("") - // return nil, err + // Update label info using CreateOrUpdateLabel + labels := map[string]string{ + model.LabelVersion: tbK8sCInfo.CspViewK8sClusterDetail.Version, + model.LabelCreatedTime: tbK8sCInfo.CspViewK8sClusterDetail.CreatedTime.String(), } - - log.Debug().Msg("<" + kv.Key + "> \n" + kv.Value) - - storedTbK8sCInfo := model.TbK8sClusterInfo{} - err = json.Unmarshal([]byte(kv.Value), &storedTbK8sCInfo) + k8sClusterKey := common.GenK8sClusterKey(nsId, k8sClusterId) + err = label.CreateOrUpdateLabel(model.StrK8s, storedTbK8sCInfo.Uid, k8sClusterKey, labels) if err != nil { - log.Err(err).Msg("") - } - - return storedTbK8sCInfo, nil -} - -/* -func convertSpiderNetworkInfoToTbK8sClusterNetworkInfo(spNetworkInfo model.SpiderNetworkInfo) model.TbK8sClusterNetworkInfo { - tbVNetId := spNetworkInfo.VpcIID.SystemId - - var tbSubnetIds []string - for _, v := range spNetworkInfo.SubnetIIDs { - tbSubnetIds = append(tbSubnetIds, v.SystemId) - } - - var tbSecurityGroupIds []string - for _, v := range spNetworkInfo.SecurityGroupIIDs { - tbSecurityGroupIds = append(tbSecurityGroupIds, v.SystemId) - } - - tbKeyValueList := convertSpiderKeyValueListToTbKeyValueList(spNetworkInfo.KeyValueList) - - tbK8sClusterNetworkInfo := model.TbK8sClusterNetworkInfo{ - VNetId: tbVNetId, - SubnetIds: tbSubnetIds, - SecurityGroupIds: tbSecurityGroupIds, - KeyValueList: tbKeyValueList, - } - - return tbK8sClusterNetworkInfo -} - -func convertSpiderNodeGroupInfoToTbK8sNodeGroupInfo(spNodeGroupInfo *model.SpiderNodeGroupInfo) model.TbK8sNodeGroupInfo { - tbNodeId := spNodeGroupInfo.IId.SystemId - tbImageId := spNodeGroupInfo.ImageIID.SystemId - tbSpecId := spNodeGroupInfo.VMSpecName - tbRootDiskType := spNodeGroupInfo.RootDiskType - tbRootDiskSize := spNodeGroupInfo.RootDiskSize - tbSshKeyId := spNodeGroupInfo.KeyPairIID.SystemId - tbOnAutoScaling := spNodeGroupInfo.OnAutoScaling - tbDesiredNodeSize := spNodeGroupInfo.DesiredNodeSize - tbMinNodeSize := spNodeGroupInfo.MinNodeSize - tbMaxNodeSize := spNodeGroupInfo.MaxNodeSize - tbStatus := convertSpiderNodeGroupStatusToTbK8sNodeGroupStatus(spNodeGroupInfo.Status) - - var tbK8sNodes []string - for _, v := range spNodeGroupInfo.Nodes { - tbK8sNodes = append(tbK8sNodes, v.SystemId) - } - - tbKeyValueList := convertSpiderKeyValueListToTbKeyValueList(spNodeGroupInfo.KeyValueList) - tbK8sNodeGroupInfo := model.TbK8sNodeGroupInfo{ - Id: tbNodeId, - ImageId: tbImageId, - SpecId: tbSpecId, - RootDiskType: tbRootDiskType, - RootDiskSize: tbRootDiskSize, - SshKeyId: tbSshKeyId, - OnAutoScaling: tbOnAutoScaling, - DesiredNodeSize: tbDesiredNodeSize, - MinNodeSize: tbMinNodeSize, - MaxNodeSize: tbMaxNodeSize, - Status: tbStatus, - K8sNodes: tbK8sNodes, - KeyValueList: tbKeyValueList, - } - - return tbK8sNodeGroupInfo -} - -func convertSpiderNodeGroupListToTbK8sNodeGroupList(spNodeGroupList []model.SpiderNodeGroupInfo) []model.TbK8sNodeGroupInfo { - var tbK8sNodeGroupList []model.TbK8sNodeGroupInfo - for _, v := range spNodeGroupList { - tbK8sNodeGroupInfo := convertSpiderNodeGroupInfoToTbK8sNodeGroupInfo(&v) - tbK8sNodeGroupList = append(tbK8sNodeGroupList, tbK8sNodeGroupInfo) - } - - return tbK8sNodeGroupList -} - -func convertSpiderClusterAccessInfoToTbK8sAccessInfo(spAccessInfo model.SpiderAccessInfo) model.TbK8sAccessInfo { - return model.TbK8sAccessInfo{spAccessInfo.Endpoint, spAccessInfo.Kubeconfig} -} - -func convertSpiderClusterAddonsInfoToTbK8sAddonsInfo(spAddonsInfo model.SpiderAddonsInfo) model.TbK8sAddonsInfo { - tbKeyValueList := convertSpiderKeyValueListToTbKeyValueList(spAddonsInfo.KeyValueList) - return model.TbK8sAddonsInfo{tbKeyValueList} -} - -func convertSpiderKeyValueListToTbKeyValueList(spKeyValueList []model.KeyValue) []model.KeyValue { - var tbKeyValueList []model.KeyValue - for _, v := range spKeyValueList { - tbKeyValueList = append(tbKeyValueList, v) - } - return tbKeyValueList -} - -func convertSpiderClusterInfoToTbK8sClusterInfo(spClusterInfo *model.SpiderClusterInfo, id string, connectionName string, description string) model.TbK8sClusterInfo { - tbK8sCNInfo := convertSpiderNetworkInfoToTbK8sClusterNetworkInfo(spClusterInfo.Network) - tbK8sNGList := convertSpiderNodeGroupListToTbK8sNodeGroupList(spClusterInfo.NodeGroupList) - tbK8sCAccInfo := convertSpiderClusterAccessInfoToTbK8sAccessInfo(spClusterInfo.AccessInfo) - tbK8sCAddInfo := convertSpiderClusterAddonsInfoToTbK8sAddonsInfo(spClusterInfo.Addons) - tbK8sCStatus := convertSpiderClusterStatusToTbK8sClusterStatus(spClusterInfo.Status) - tbKVList := convertSpiderKeyValueListToTbKeyValueList(spClusterInfo.KeyValueList) - tbK8sCInfo := model.TbK8sClusterInfo{ - Id: id, - Name: id, - ConnectionName: connectionName, - Version: spClusterInfo.Version, - Network: tbK8sCNInfo, - K8sNodeGroupList: tbK8sNGList, - AccessInfo: tbK8sCAccInfo, - Addons: tbK8sCAddInfo, - Status: tbK8sCStatus, - CreatedTime: spClusterInfo.CreatedTime, - KeyValueList: tbKVList, - Description: description, - CspResourceId: spClusterInfo.IId.SystemId, - CspResourceName: spClusterInfo.IId.NameId, - } - - return tbK8sCInfo -} - -func convertSpiderClusterStatusToTbK8sClusterStatus(spClusterStatus model.SpiderClusterStatus) model.TbK8sClusterStatus { - if spClusterStatus == model.SpiderClusterCreating { - return model.TbK8sClusterCreating - } else if spClusterStatus == model.SpiderClusterActive { - return model.TbK8sClusterActive - } else if spClusterStatus == model.SpiderClusterInactive { - return model.TbK8sClusterInactive - } else if spClusterStatus == model.SpiderClusterUpdating { - return model.TbK8sClusterUpdating - } else if spClusterStatus == model.SpiderClusterDeleting { - return model.TbK8sClusterDeleting - } - - return model.TbK8sClusterInactive -} - -func convertSpiderNodeGroupStatusToTbK8sNodeGroupStatus(spNodeGroupStatus model.SpiderNodeGroupStatus) model.TbK8sNodeGroupStatus { - if spNodeGroupStatus == model.SpiderNodeGroupCreating { - return model.TbK8sNodeGroupCreating - } else if spNodeGroupStatus == model.SpiderNodeGroupActive { - return model.TbK8sNodeGroupActive - } else if spNodeGroupStatus == model.SpiderNodeGroupInactive { - return model.TbK8sNodeGroupInactive - } else if spNodeGroupStatus == model.SpiderNodeGroupUpdating { - return model.TbK8sNodeGroupUpdating - } else if spNodeGroupStatus == model.SpiderNodeGroupDeleting { - return model.TbK8sNodeGroupDeleting + log.Err(err).Msgf("Failed to Upgrade a K8sCluster(%s)", k8sClusterId) + return emptyObj, err } - return model.TbK8sNodeGroupInactive + return &storedTbK8sCInfo, nil } -*/ // checkK8sClusterEnablement returns the enablement status(nil or error) for K8sCluster related to Connection. func checkK8sClusterEnablement(connectionName string) error { @@ -1556,12 +1331,33 @@ func validateAtCreateK8sCluster(tbK8sClusterReq *model.TbK8sClusterReq) error { connConfig, err := common.GetConnConfig(tbK8sClusterReq.ConnectionName) // Validate K8sCluster Version - err = validateK8sClusterVersion(connConfig.ProviderName, connConfig.RegionDetail.RegionName, tbK8sClusterReq.Version) + err = validateK8sVersion(connConfig.ProviderName, connConfig.RegionDetail.RegionName, tbK8sClusterReq.Version) + if err != nil { + log.Err(err).Msgf("Requested K8sVersion(%s)", tbK8sClusterReq.Version) + return err + } + + // Validate K8sNodeGroups On K8s Creation + k8sNgOnCreation, err := common.GetK8sNodeGroupsOnK8sCreation(connConfig.ProviderName) if err != nil { - log.Err(err).Msgf("Failed to Create a K8sCluster: Requested K8sVersion(%s)", tbK8sClusterReq.Version) + log.Err(err).Msgf("Failed to Get Nodegroups on K8sCluster Creation") return err } + if k8sNgOnCreation { + if len(tbK8sClusterReq.K8sNodeGroupList) <= 0 { + err := fmt.Errorf("Need to Set One more K8sNodeGroupList") + log.Err(err).Msgf("Provider(%s)", connConfig.ProviderName) + return err + } + } else { + if len(tbK8sClusterReq.K8sNodeGroupList) > 0 { + err := fmt.Errorf("Need to Set Empty K8sNodeGroupList") + log.Err(err).Msgf("Provider(%s)", connConfig.ProviderName) + return err + } + } + return nil } @@ -1569,17 +1365,17 @@ func validateAtUpgradeK8sCluster(connectionName string, tbUpgradeK8sClusterReq * connConfig, err := common.GetConnConfig(connectionName) // Validate K8sCluster Version - err = validateK8sClusterVersion(connConfig.ProviderName, connConfig.RegionDetail.RegionName, tbUpgradeK8sClusterReq.Version) + err = validateK8sVersion(connConfig.ProviderName, connConfig.RegionDetail.RegionName, tbUpgradeK8sClusterReq.Version) if err != nil { - log.Err(err).Msgf("Failed to Create a K8sCluster: Requested K8sVersion(%s)", tbUpgradeK8sClusterReq.Version) + log.Err(err).Msgf("Requested K8sVersion(%s)", tbUpgradeK8sClusterReq.Version) return err } return nil } -func validateK8sClusterVersion(providerName, regionName, version string) error { - availableVersion, err := common.GetAvailableK8sClusterVersion(providerName, regionName) +func validateK8sVersion(providerName, regionName, version string) error { + availableVersion, err := common.GetAvailableK8sVersion(providerName, regionName) if err != nil { return err } @@ -1595,10 +1391,10 @@ func validateK8sClusterVersion(providerName, regionName, version string) error { } } - if valid { - return nil - } else { + if valid == false { return fmt.Errorf("Available K8sCluster Version(k8sclusterinfo.yaml) for Provider/Region(%s/%s): %s", providerName, regionName, strings.Join(versionIdList, ", ")) } + + return nil }