diff --git a/src/api/rest/server/resource/k8scluster.go b/src/api/rest/server/resource/k8scluster.go
index 25d38ab6..a371043d 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 5f0dfd61..bbfd7f9c 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 57889bca..c2afeaa4 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 f8774420..45e9a9ba 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 b4bdb6b5..6d849588 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 a3ce9455..e92932bb 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"`
}