From 7b75eff7a89c9510f8a13d7910b6b945032dac89 Mon Sep 17 00:00:00 2001 From: Sooyoung Kim Date: Fri, 1 Nov 2024 10:11:54 +0000 Subject: [PATCH] [k8scluster] update k8scluster dynamic - For non required fields, set default values - If no version, apply recent version in k8sclusterinfo.yaml - Filter images with `k8s` infratype - Fix recommend k8s node limit - Update some json examples --- src/api/rest/server/resource/k8scluster.go | 10 +- src/api/rest/server/server.go | 2 +- src/core/common/utility.go | 101 +++++---------------- src/core/infra/provisioning.go | 69 +++++++++----- src/core/infra/recommendation.go | 33 +++++++ src/core/model/k8scluster.go | 8 +- 6 files changed, 111 insertions(+), 112 deletions(-) diff --git a/src/api/rest/server/resource/k8scluster.go b/src/api/rest/server/resource/k8scluster.go index 25d38ab6c..a371043df 100644 --- a/src/api/rest/server/resource/k8scluster.go +++ b/src/api/rest/server/resource/k8scluster.go @@ -576,7 +576,7 @@ func RestPostK8sClusterDynamicCheckRequest(c echo.Context) error { // @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 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/1913" // @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 @@ -640,8 +640,8 @@ func RestGetControlK8sCluster(c echo.Context) error { } } -// RestRecommendNode godoc -// @ID RecommendNode +// RestRecommendK8sNode godoc +// @ID RecommendK8sNode // @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 @@ -652,7 +652,7 @@ func RestGetControlK8sCluster(c echo.Context) error { // @Failure 404 {object} model.SimpleMsg // @Failure 500 {object} model.SimpleMsg // @Router /k8sclusterRecommendNode [post] -func RestRecommendNode(c echo.Context) error { +func RestRecommendK8sNode(c echo.Context) error { nsId := model.SystemCommonNs @@ -661,6 +661,6 @@ func RestRecommendNode(c echo.Context) error { return common.EndRequestWithLog(c, err, nil) } - content, err := infra.RecommendVm(nsId, *u) + content, err := infra.RecommendK8sNode(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 5f0dfd617..bbfd7f9cf 100644 --- a/src/api/rest/server/server.go +++ b/src/api/rest/server/server.go @@ -399,7 +399,7 @@ 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/k8sclusterRecommendNode", rest_resource.RestRecommendK8sNode) e.POST("/tumblebug/k8sclusterDynamicCheckRequest", rest_resource.RestPostK8sClusterDynamicCheckRequest) g.POST("/:nsId/k8sclusterDynamic", rest_resource.RestPostK8sClusterDynamic) g.GET("/:nsId/control/k8scluster/:k8sClusterId", rest_resource.RestGetControlK8sCluster) diff --git a/src/core/common/utility.go b/src/core/common/utility.go index 57889bcad..c2afeaa44 100644 --- a/src/core/common/utility.go +++ b/src/core/common/utility.go @@ -1498,92 +1498,39 @@ func GetModelK8sRequiredSubnetCount(providerName string) (*model.K8sClusterRequi }, nil } -/* -func isValidSpecForK8sCluster(spec *resource.TbSpecInfo) bool { - // - // Check for Provider - // +func FilterDigitsAndDots(input string) string { + re := regexp.MustCompile(`[^0-9.]`) + return re.ReplaceAllString(input, "") +} - providerName := strings.ToLower(spec.ProviderName) +func CompareVersions(version1, version2 string) int { + v1Parts := strings.Split(version1, ".") + v2Parts := strings.Split(version2, ".") - var k8sClusterDetail *common.model.K8sClusterDetail = nil - for provider, detail := range common.RuntimeK8sClusterInfo.CSPs { - provider = strings.ToLower(provider) - if provider == providerName { - k8sClusterDetail = &detail - break - } - } - if k8sClusterDetail == nil { - return false + // Adjust length by appending 0 if necessary + maxLength := len(v1Parts) + if len(v2Parts) > maxLength { + maxLength = len(v2Parts) } - // - // Check for Region - // - - regionName := strings.ToLower(spec.RegionName) + for i := 0; i < maxLength; i++ { + var v1, v2 int - // Check for Version - isExist := false - for _, versionDetail := range k8sClusterDetail.Version { - for _, region := range versionDetail.Region { - region = strings.ToLower(region) - if region == "all" || region == regionName { - if len(versionDetail.Available) > 0 { - isExist = true - break - } - } + // If a part is missing, treat it as 0 + if i < len(v1Parts) { + v1, _ = strconv.Atoi(v1Parts[i]) } - if isExist == true { - break + if i < len(v2Parts) { + v2, _ = strconv.Atoi(v2Parts[i]) } - } - if isExist == false { - return false - } - // Check for NodeImage - isExist = false - for _, nodeImageDetail := range k8sClusterDetail.NodeImage { - for _, region := range nodeImageDetail.Region { - region = strings.ToLower(region) - if region == "all" || region == regionName { - if len(nodeImageDetail.Available) > 0 { - isExist = true - break - } - } - } - if isExist == true { - break + // Compare each part + if v1 > v2 { + return 1 + } else if v1 < v2 { + return -1 } } - if isExist == false { - return false - } - // Check for RootDisk - isExist = false - for _, rootDiskDetail := range k8sClusterDetail.RootDisk { - for _, region := range rootDiskDetail.Region { - region = strings.ToLower(region) - if region == "all" || region == regionName { - if len(rootDiskDetail.Type) > 0 { - isExist = true - break - } - } - } - if isExist == true { - break - } - } - if isExist == false { - return false - } - - return true + return 0 } -*/ diff --git a/src/core/infra/provisioning.go b/src/core/infra/provisioning.go index f87744206..45e9a9ba2 100644 --- a/src/core/infra/provisioning.go +++ b/src/core/infra/provisioning.go @@ -17,7 +17,6 @@ package infra import ( "encoding/json" "fmt" - "regexp" "strconv" "strings" "sync" @@ -1669,15 +1668,17 @@ func filterCheckMciDynamicReqInfoToCheckK8sClusterDynamicReqInfo(mciDReqInfo *mo } } - nodeDReqInfo := model.CheckNodeDynamicReqInfo{ - ConnectionConfigCandidates: k.ConnectionConfigCandidates, - Spec: k.Spec, - Image: imageListForK8s, - Region: k.Region, - SystemMessage: k.SystemMessage, - } + if len(imageListForK8s) > 0 { + nodeDReqInfo := model.CheckNodeDynamicReqInfo{ + ConnectionConfigCandidates: k.ConnectionConfigCandidates, + Spec: k.Spec, + Image: imageListForK8s, + Region: k.Region, + SystemMessage: k.SystemMessage, + } - k8sDReqInfo.ReqCheck = append(k8sDReqInfo.ReqCheck, nodeDReqInfo) + k8sDReqInfo.ReqCheck = append(k8sDReqInfo.ReqCheck, nodeDReqInfo) + } } } } @@ -1703,11 +1704,6 @@ func CheckK8sClusterDynamicReq(req *model.K8sClusterConnectionConfigCandidatesRe 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 { @@ -1718,17 +1714,29 @@ func getK8sRecommendVersion(providerName, regionName, reqVersion string) (string 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 + + if reqVersion == "" { + for _, verDetail := range *availableVersion { + versionIdList = append(versionIdList, verDetail.Id) + filteredRecVersion := common.FilterDigitsAndDots(recVersion) + filteredAvailVersion := common.FilterDigitsAndDots(verDetail.Id) + if common.CompareVersions(filteredRecVersion, filteredAvailVersion) < 0 { + recVersion = verDetail.Id + } + } + } else { + for _, verDetail := range *availableVersion { + versionIdList = append(versionIdList, verDetail.Id) + if strings.EqualFold(reqVersion, verDetail.Id) { + recVersion = verDetail.Id break + } else { + availVersion := common.FilterDigitsAndDots(verDetail.Id) + filteredReqVersion := common.FilterDigitsAndDots(reqVersion) + if strings.HasPrefix(availVersion, filteredReqVersion) { + recVersion = availVersion + break + } } } } @@ -1943,10 +1951,21 @@ func getK8sClusterReqFromDynamicReq(reqID string, nsId string, dReq *model.TbK8s k8sngReq.RootDiskType = dReq.RootDiskType k8sngReq.RootDiskSize = dReq.RootDiskSize k8sngReq.OnAutoScaling = dReq.OnAutoScaling + if k8sngReq.OnAutoScaling == "" { + k8sngReq.OnAutoScaling = "true" + } k8sngReq.DesiredNodeSize = dReq.DesiredNodeSize + if k8sngReq.DesiredNodeSize == "" { + k8sngReq.DesiredNodeSize = "1" + } k8sngReq.MinNodeSize = dReq.MinNodeSize + if k8sngReq.MinNodeSize == "" { + k8sngReq.MinNodeSize = "1" + } k8sngReq.MaxNodeSize = dReq.MaxNodeSize - + if k8sngReq.MaxNodeSize == "" { + k8sngReq.MaxNodeSize = "2" + } k8sReq.Description = dReq.Description k8sReq.Name = dReq.Name if k8sReq.Name == "" { diff --git a/src/core/infra/recommendation.go b/src/core/infra/recommendation.go index b4bdb6b52..6d8495880 100644 --- a/src/core/infra/recommendation.go +++ b/src/core/infra/recommendation.go @@ -617,6 +617,39 @@ func RecommendVmPerformance(nsId string, specList *[]model.TbSpecInfo) ([]model. return result, nil } +// RecommendK8sNode is func to recommend a node for K8sCluster +func RecommendK8sNode(nsId string, plan model.DeploymentPlan) ([]model.TbSpecInfo, error) { + emptyObjList := []model.TbSpecInfo{} + + limitOrig := plan.Limit + plan.Limit = strconv.Itoa(math.MaxInt) + + tbSpecInfoListForVm, err := RecommendVm(nsId, plan) + if err != nil { + return emptyObjList, err + } + + limitNum, err := strconv.Atoi(limitOrig) + if err != nil { + limitNum = math.MaxInt + } + + tbSpecInfoListForK8s := []model.TbSpecInfo{} + count := 0 + for _, tbSpecInfo := range tbSpecInfoListForVm { + if strings.Contains(tbSpecInfo.InfraType, model.StrK8s) || + strings.Contains(tbSpecInfo.InfraType, model.StrKubernetes) { + tbSpecInfoListForK8s = append(tbSpecInfoListForK8s, tbSpecInfo) + count++ + if count == limitNum { + break + } + } + } + + return tbSpecInfoListForK8s, nil +} + // // GetRecommendList is func to get recommendation list // func GetRecommendList(nsId string, cpuSize string, memSize string, diskSize string) ([]TbVmPriority, error) { diff --git a/src/core/model/k8scluster.go b/src/core/model/k8scluster.go index a3ce94557..e92932bbb 100644 --- a/src/core/model/k8scluster.go +++ b/src/core/model/k8scluster.go @@ -460,7 +460,7 @@ type TbK8sAddonsInfo struct { // 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"` + CommonSpecs []string `json:"commonSpec" validate:"required" example:"tencent+ap-seoul+S2.MEDIUM4"` } // CheckK8sClusterDynamicReqInfo is struct to check requirements to create a new K8sCluster instance dynamically (with default resource option) @@ -500,10 +500,10 @@ type TbK8sClusterDynamicReq struct { 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"` + CommonSpec string `json:"commonSpec" validate:"required" example:"tencent+ap-seoul+S2.MEDIUM4"` // CommonImage is field for id of a image in common namespace - CommonImage string `json:"commonImage" validate:"required" example:"default, azure+koreacentrall+ubuntu20.04"` + CommonImage string `json:"commonImage" validate:"required" example:"default, tencent+ap-seoul+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"] @@ -515,5 +515,5 @@ type TbK8sClusterDynamicReq struct { // 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"` + ConnectionName string `json:"connectionName,omitempty" default:"tencent-ap-seoul"` }