Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 0 additions & 53 deletions alicloud/resource_alicloud_cs_managed_kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@
"runtime": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Resource{

Check failure on line 409 in alicloud/resource_alicloud_cs_managed_kubernetes.go

View workflow job for this annotation

GitHub Actions / tflint

S022: schema of TypeMap should not use Elem of *schema.Resource
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Expand Down Expand Up @@ -522,7 +522,7 @@
"certificate_authority": {
Type: schema.TypeMap,
Computed: true,
Elem: &schema.Resource{

Check failure on line 525 in alicloud/resource_alicloud_cs_managed_kubernetes.go

View workflow job for this annotation

GitHub Actions / tflint

S022: schema of TypeMap should not use Elem of *schema.Resource
Schema: map[string]*schema.Schema{
"cluster_cert": {
Type: schema.TypeString,
Expand All @@ -547,7 +547,7 @@
"connections": {
Type: schema.TypeMap,
Computed: true,
Elem: &schema.Resource{

Check failure on line 550 in alicloud/resource_alicloud_cs_managed_kubernetes.go

View workflow job for this annotation

GitHub Actions / tflint

S022: schema of TypeMap should not use Elem of *schema.Resource
Schema: map[string]*schema.Schema{
"api_server_internet": {
Type: schema.TypeString,
Expand Down Expand Up @@ -1562,59 +1562,6 @@
return nil
}

// versionCompare check version,
// if cueVersion is newer than neededVersion return 1
// if curVersion is equal neededVersion return 0
// if curVersion is older than neededVersion return -1
// example: neededVersion = 1.20.11-aliyun.1, curVersion = 1.22.3-aliyun.1, it will return 1
func versionCompare(neededVersion, curVersion string) (int, error) {
if neededVersion == "" || curVersion == "" {
if neededVersion == "" && curVersion == "" {
return 0, nil
} else {
if neededVersion == "" {
return 1, nil
} else {
return -1, nil
}
}
}

// 取出版本号
regx := regexp.MustCompile(`[0-9]+\.[0-9]+\.[0-9]+`)
neededVersion = regx.FindString(neededVersion)
curVersion = regx.FindString(curVersion)

currentVersions := strings.Split(neededVersion, ".")
newVersions := strings.Split(curVersion, ".")

compare := 0

for index, val := range currentVersions {
newVal := newVersions[index]
v1, err1 := strconv.Atoi(val)
v2, err2 := strconv.Atoi(newVal)

if err1 != nil || err2 != nil {
return -2, fmt.Errorf("NotSupport, current cluster version is not support: %s", curVersion)
}

if v1 > v2 {
compare = -1
} else if v1 == v2 {
compare = 0
} else {
compare = 1
}

if compare != 0 {
break
}
}

return compare, nil
}

func updateControlPlaneLog(d *schema.ResourceData, meta interface{}) error {
request := &roacs.UpdateControlPlaneLogRequest{}
client := meta.(*connectivity.AliyunClient)
Expand Down
69 changes: 69 additions & 0 deletions alicloud/resource_alicloud_cs_managed_kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1154,4 +1154,73 @@ variable "cluster_type" {
`, name)
}

func Test_versionCompare(t *testing.T) {
tests := []struct {
name string
oldVersion string
newVersion string
expectedResult int
expectError bool
}{
// basic semver
{name: "equal versions", oldVersion: "1.2.3", newVersion: "1.2.3", expectedResult: 0},
{name: "new version newer patch", oldVersion: "1.2.3", newVersion: "1.2.4", expectedResult: 1},
{name: "new version older patch", oldVersion: "1.2.4", newVersion: "1.2.3", expectedResult: -1},
{name: "new version newer minor", oldVersion: "1.2.3", newVersion: "1.3.0", expectedResult: 1},
{name: "new version older minor", oldVersion: "1.3.0", newVersion: "1.2.3", expectedResult: -1},
{name: "new version newer major", oldVersion: "1.2.3", newVersion: "2.0.0", expectedResult: 1},
{name: "new version older major", oldVersion: "2.0.0", newVersion: "1.2.3", expectedResult: -1},

// v prefix
{name: "v prefix both", oldVersion: "v1.2.3", newVersion: "v1.2.4", expectedResult: 1},
{name: "v prefix old only", oldVersion: "v1.2.3", newVersion: "1.2.4", expectedResult: 1},
{name: "v prefix new only", oldVersion: "1.2.3", newVersion: "v1.2.4", expectedResult: 1},
{name: "v prefix equal", oldVersion: "v1.2.3", newVersion: "v1.2.3", expectedResult: 0},

// pre-release suffix (per semver: release > pre-release, e.g. 1.2.3 > 1.2.3-alpha.1)
{name: "release vs pre-release", oldVersion: "1.2.3-alpha.1", newVersion: "1.2.3", expectedResult: 1},
{name: "pre-release vs release", oldVersion: "1.2.3", newVersion: "1.2.3-alpha.1", expectedResult: -1},
{name: "pre-release equal", oldVersion: "1.2.3-alpha.1", newVersion: "1.2.3-alpha.1", expectedResult: 0},
{name: "pre-release numeric diff", oldVersion: "1.2.3-alpha.1", newVersion: "1.2.3-alpha.2", expectedResult: 1},
{name: "pre-release string diff", oldVersion: "1.2.3-alpha.1", newVersion: "1.2.3-beta.1", expectedResult: 1},
{name: "pre-release with aliyun", oldVersion: "1.9.3-aliyun.1", newVersion: "1.9.7-aliyun.2", expectedResult: 1},
{name: "pre-release newer major version", oldVersion: "1.9.3-aliyun.1", newVersion: "2.0.0-aliyun.1", expectedResult: 1},

// apsara format
{name: "apsara same main version, suffix newer", oldVersion: "v1.31.0-apsara.6.11.6.ad796663", newVersion: "v1.31.0-apsara.6.11.7.17d202a9", expectedResult: 1},
{name: "apsara same main version, suffix older", oldVersion: "v1.31.0-apsara.6.11.7.17d202a9", newVersion: "v1.31.0-apsara.6.11.6.ad796663", expectedResult: -1},
{name: "apsara same numeric suffix, diff commit hash", oldVersion: "v1.31.0-apsara.6.11.6.ad796663", newVersion: "v1.31.0-apsara.6.11.6.bf123456", expectedResult: 1},
{name: "apsara different main version", oldVersion: "v1.30.0-apsara.6.11.6.ad796663", newVersion: "v1.31.0-apsara.6.11.6.ad796663", expectedResult: 1},

// empty versions
{name: "both empty", oldVersion: "", newVersion: "", expectedResult: 0},
{name: "old empty", oldVersion: "", newVersion: "1.2.3", expectedResult: 1},
{name: "new empty", oldVersion: "1.2.3", newVersion: "", expectedResult: -1},

// invalid format
{name: "invalid old version", oldVersion: "latest", newVersion: "1.2.3", expectError: true},
{name: "invalid new version", oldVersion: "1.2.3", newVersion: "latest", expectError: true},
{name: "both invalid", oldVersion: "abc", newVersion: "xyz", expectError: true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := versionCompare(tt.oldVersion, tt.newVersion)
if tt.expectError {
if err == nil {
t.Errorf("expected error but got nil, result=%d", result)
}
return
}
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
if result != tt.expectedResult {
t.Errorf("versionCompare(%q, %q) = %d, want %d", tt.oldVersion, tt.newVersion, result, tt.expectedResult)
}
})
}
}

// Test Ack Cluster. <<< Resource test cases, automatically generated.
67 changes: 65 additions & 2 deletions alicloud/service_alicloud_cs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"
"time"

"github.com/Masterminds/semver"
"github.com/alibabacloud-go/cs-20151215/v7/client"
"github.com/alibabacloud-go/tea/tea"
"github.com/aliyun/terraform-provider-alicloud/alicloud/connectivity"
Expand Down Expand Up @@ -428,14 +429,29 @@ func (s *CsClient) DescribeCsKubernetesAllAvailableAddons(clusterId string) (map
// DescribeAddon
addonInfoDetail, err := s.DescribeAddon(clusterId, name, addonInstance.Version)
if err != nil {
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeAddon", err)
if IsExpectedErrors(err, []string{"AddonNotFound"}) {
// If the specified version is not found, retry without version
addonInfoDetail, err = s.DescribeAddon(clusterId, name, "")
if err != nil {
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeAddon", err)
}
} else {
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeAddon", err)
}
}

if addonInfoDetail == nil || addonInfoDetail.Body == nil {
return nil, WrapErrorf(fmt.Errorf("DescribeAddon response body is nil"), DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeAddon", err)
}

addon.NextVersion = getNextVersion(addonInfoDetail)
// If retried without version, use the response version as next_version only if it's newer
if addon.NextVersion == "" && addonInfoDetail.Body.Version != nil {
targetVersion := tea.StringValue(addonInfoDetail.Body.Version)
if cmp, err := versionCompare(addonInstance.Version, targetVersion); err == nil && cmp == 1 {
addon.NextVersion = targetVersion
}
}

// Update if addon installed
addon.Version = addonInstance.Version
Expand Down Expand Up @@ -487,7 +503,15 @@ func (s *CsClient) DescribeCsKubernetesAddon(id string) (*Component, error) {

addonInfo, err := s.DescribeAddon(clusterId, addonName, addonInstance.Version)
if err != nil {
return nil, err
if IsExpectedErrors(err, []string{"AddonNotFound"}) {
// If the specified version is not found, retry without version
addonInfo, err = s.DescribeAddon(clusterId, addonName, "")
if err != nil {
return nil, err
}
} else {
return nil, err
}
}

if addonInfo == nil || addonInfo.Body == nil {
Expand All @@ -500,6 +524,13 @@ func (s *CsClient) DescribeCsKubernetesAddon(id string) (*Component, error) {
if nextVersion := getNextVersion(addonInfo); nextVersion != "" {
addonInstance.NextVersion = nextVersion
}
// If retried without version, use the response version as next_version only if it's newer
if addonInstance.NextVersion == addonInstance.Version && addonInfo.Body.Version != nil {
targetVersion := tea.StringValue(addonInfo.Body.Version)
if cmp, err := versionCompare(addonInstance.Version, targetVersion); err == nil && cmp == 1 {
addonInstance.NextVersion = targetVersion
}
}
addonInstance.CanUpgrade = addonInstance.Version != addonInstance.NextVersion
addonInstance.SupportedActions = tea.StringSliceValue(addonInfo.Body.SupportedActions)

Expand All @@ -517,6 +548,38 @@ func getNextVersion(addonInfo *client.DescribeAddonResponse) string {
return ""
}

// versionCompare compares two semantic versions using github.com/Masterminds/semver.
// Supports formats: "1.2.3", "v1.2.3", "1.2.3-aliyun.1", "v1.31.0-apsara.6.11.6.ad796663", etc.
// Returns:
//
// 1 if newVersion > oldVersion
// 0 if newVersion == oldVersion
// -1 if newVersion < oldVersion
//
// Example: versionCompare("1.20.11-aliyun.1", "1.22.3-aliyun.1") returns 1
func versionCompare(oldVersion, newVersion string) (int, error) {
if oldVersion == "" || newVersion == "" {
if oldVersion == "" && newVersion == "" {
return 0, nil
}
if oldVersion == "" {
return 1, nil
}
return -1, nil
}

oldSemver, err := semver.NewVersion(oldVersion)
if err != nil {
return -2, fmt.Errorf("failed to parse version %q: %v", oldVersion, err)
}
newSemver, err := semver.NewVersion(newVersion)
if err != nil {
return -2, fmt.Errorf("failed to parse version %q: %v", newVersion, err)
}

return newSemver.Compare(oldSemver), nil
}

func (s *CsClient) CsKubernetesAddonTaskRefreshFunc(clusterId string, addonName string, failStates []string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
object, err := s.DescribeCsKubernetesAddonStatus(clusterId, addonName)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ require (
cloud.google.com/go/iam v1.1.7 // indirect
cloud.google.com/go/storage v1.39.1 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/semver v1.5.0
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/PaesslerAG/gval v1.0.0 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
Expand Down
Loading