Skip to content

Commit f8ef590

Browse files
authored
Merge pull request #9648 from sanyangji/fix-next-version
resource/alicloud_cs_kubernetes_addon: fix get next_version; data-source/alicloud_cs_kubernetes_addons: fix get next_version.
2 parents a611f92 + cb5da56 commit f8ef590

4 files changed

Lines changed: 135 additions & 56 deletions

File tree

alicloud/resource_alicloud_cs_managed_kubernetes.go

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,59 +1562,6 @@ func updateKubernetesClusterTag(d *schema.ResourceData, meta interface{}) error
15621562
return nil
15631563
}
15641564

1565-
// versionCompare check version,
1566-
// if cueVersion is newer than neededVersion return 1
1567-
// if curVersion is equal neededVersion return 0
1568-
// if curVersion is older than neededVersion return -1
1569-
// example: neededVersion = 1.20.11-aliyun.1, curVersion = 1.22.3-aliyun.1, it will return 1
1570-
func versionCompare(neededVersion, curVersion string) (int, error) {
1571-
if neededVersion == "" || curVersion == "" {
1572-
if neededVersion == "" && curVersion == "" {
1573-
return 0, nil
1574-
} else {
1575-
if neededVersion == "" {
1576-
return 1, nil
1577-
} else {
1578-
return -1, nil
1579-
}
1580-
}
1581-
}
1582-
1583-
// 取出版本号
1584-
regx := regexp.MustCompile(`[0-9]+\.[0-9]+\.[0-9]+`)
1585-
neededVersion = regx.FindString(neededVersion)
1586-
curVersion = regx.FindString(curVersion)
1587-
1588-
currentVersions := strings.Split(neededVersion, ".")
1589-
newVersions := strings.Split(curVersion, ".")
1590-
1591-
compare := 0
1592-
1593-
for index, val := range currentVersions {
1594-
newVal := newVersions[index]
1595-
v1, err1 := strconv.Atoi(val)
1596-
v2, err2 := strconv.Atoi(newVal)
1597-
1598-
if err1 != nil || err2 != nil {
1599-
return -2, fmt.Errorf("NotSupport, current cluster version is not support: %s", curVersion)
1600-
}
1601-
1602-
if v1 > v2 {
1603-
compare = -1
1604-
} else if v1 == v2 {
1605-
compare = 0
1606-
} else {
1607-
compare = 1
1608-
}
1609-
1610-
if compare != 0 {
1611-
break
1612-
}
1613-
}
1614-
1615-
return compare, nil
1616-
}
1617-
16181565
func updateControlPlaneLog(d *schema.ResourceData, meta interface{}) error {
16191566
request := &roacs.UpdateControlPlaneLogRequest{}
16201567
client := meta.(*connectivity.AliyunClient)

alicloud/resource_alicloud_cs_managed_kubernetes_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,4 +1154,73 @@ variable "cluster_type" {
11541154
`, name)
11551155
}
11561156

1157+
func Test_versionCompare(t *testing.T) {
1158+
tests := []struct {
1159+
name string
1160+
oldVersion string
1161+
newVersion string
1162+
expectedResult int
1163+
expectError bool
1164+
}{
1165+
// basic semver
1166+
{name: "equal versions", oldVersion: "1.2.3", newVersion: "1.2.3", expectedResult: 0},
1167+
{name: "new version newer patch", oldVersion: "1.2.3", newVersion: "1.2.4", expectedResult: 1},
1168+
{name: "new version older patch", oldVersion: "1.2.4", newVersion: "1.2.3", expectedResult: -1},
1169+
{name: "new version newer minor", oldVersion: "1.2.3", newVersion: "1.3.0", expectedResult: 1},
1170+
{name: "new version older minor", oldVersion: "1.3.0", newVersion: "1.2.3", expectedResult: -1},
1171+
{name: "new version newer major", oldVersion: "1.2.3", newVersion: "2.0.0", expectedResult: 1},
1172+
{name: "new version older major", oldVersion: "2.0.0", newVersion: "1.2.3", expectedResult: -1},
1173+
1174+
// v prefix
1175+
{name: "v prefix both", oldVersion: "v1.2.3", newVersion: "v1.2.4", expectedResult: 1},
1176+
{name: "v prefix old only", oldVersion: "v1.2.3", newVersion: "1.2.4", expectedResult: 1},
1177+
{name: "v prefix new only", oldVersion: "1.2.3", newVersion: "v1.2.4", expectedResult: 1},
1178+
{name: "v prefix equal", oldVersion: "v1.2.3", newVersion: "v1.2.3", expectedResult: 0},
1179+
1180+
// pre-release suffix (per semver: release > pre-release, e.g. 1.2.3 > 1.2.3-alpha.1)
1181+
{name: "release vs pre-release", oldVersion: "1.2.3-alpha.1", newVersion: "1.2.3", expectedResult: 1},
1182+
{name: "pre-release vs release", oldVersion: "1.2.3", newVersion: "1.2.3-alpha.1", expectedResult: -1},
1183+
{name: "pre-release equal", oldVersion: "1.2.3-alpha.1", newVersion: "1.2.3-alpha.1", expectedResult: 0},
1184+
{name: "pre-release numeric diff", oldVersion: "1.2.3-alpha.1", newVersion: "1.2.3-alpha.2", expectedResult: 1},
1185+
{name: "pre-release string diff", oldVersion: "1.2.3-alpha.1", newVersion: "1.2.3-beta.1", expectedResult: 1},
1186+
{name: "pre-release with aliyun", oldVersion: "1.9.3-aliyun.1", newVersion: "1.9.7-aliyun.2", expectedResult: 1},
1187+
{name: "pre-release newer major version", oldVersion: "1.9.3-aliyun.1", newVersion: "2.0.0-aliyun.1", expectedResult: 1},
1188+
1189+
// apsara format
1190+
{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},
1191+
{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},
1192+
{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},
1193+
{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},
1194+
1195+
// empty versions
1196+
{name: "both empty", oldVersion: "", newVersion: "", expectedResult: 0},
1197+
{name: "old empty", oldVersion: "", newVersion: "1.2.3", expectedResult: 1},
1198+
{name: "new empty", oldVersion: "1.2.3", newVersion: "", expectedResult: -1},
1199+
1200+
// invalid format
1201+
{name: "invalid old version", oldVersion: "latest", newVersion: "1.2.3", expectError: true},
1202+
{name: "invalid new version", oldVersion: "1.2.3", newVersion: "latest", expectError: true},
1203+
{name: "both invalid", oldVersion: "abc", newVersion: "xyz", expectError: true},
1204+
}
1205+
1206+
for _, tt := range tests {
1207+
t.Run(tt.name, func(t *testing.T) {
1208+
result, err := versionCompare(tt.oldVersion, tt.newVersion)
1209+
if tt.expectError {
1210+
if err == nil {
1211+
t.Errorf("expected error but got nil, result=%d", result)
1212+
}
1213+
return
1214+
}
1215+
if err != nil {
1216+
t.Errorf("unexpected error: %v", err)
1217+
return
1218+
}
1219+
if result != tt.expectedResult {
1220+
t.Errorf("versionCompare(%q, %q) = %d, want %d", tt.oldVersion, tt.newVersion, result, tt.expectedResult)
1221+
}
1222+
})
1223+
}
1224+
}
1225+
11571226
// Test Ack Cluster. <<< Resource test cases, automatically generated.

alicloud/service_alicloud_cs.go

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010
"time"
1111

12+
"github.com/Masterminds/semver"
1213
"github.com/alibabacloud-go/cs-20151215/v7/client"
1314
"github.com/alibabacloud-go/tea/tea"
1415
"github.com/aliyun/terraform-provider-alicloud/alicloud/connectivity"
@@ -428,14 +429,29 @@ func (s *CsClient) DescribeCsKubernetesAllAvailableAddons(clusterId string) (map
428429
// DescribeAddon
429430
addonInfoDetail, err := s.DescribeAddon(clusterId, name, addonInstance.Version)
430431
if err != nil {
431-
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeAddon", err)
432+
if IsExpectedErrors(err, []string{"AddonNotFound"}) {
433+
// If the specified version is not found, retry without version
434+
addonInfoDetail, err = s.DescribeAddon(clusterId, name, "")
435+
if err != nil {
436+
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeAddon", err)
437+
}
438+
} else {
439+
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeAddon", err)
440+
}
432441
}
433442

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

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

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

488504
addonInfo, err := s.DescribeAddon(clusterId, addonName, addonInstance.Version)
489505
if err != nil {
490-
return nil, err
506+
if IsExpectedErrors(err, []string{"AddonNotFound"}) {
507+
// If the specified version is not found, retry without version
508+
addonInfo, err = s.DescribeAddon(clusterId, addonName, "")
509+
if err != nil {
510+
return nil, err
511+
}
512+
} else {
513+
return nil, err
514+
}
491515
}
492516

493517
if addonInfo == nil || addonInfo.Body == nil {
@@ -500,6 +524,13 @@ func (s *CsClient) DescribeCsKubernetesAddon(id string) (*Component, error) {
500524
if nextVersion := getNextVersion(addonInfo); nextVersion != "" {
501525
addonInstance.NextVersion = nextVersion
502526
}
527+
// If retried without version, use the response version as next_version only if it's newer
528+
if addonInstance.NextVersion == addonInstance.Version && addonInfo.Body.Version != nil {
529+
targetVersion := tea.StringValue(addonInfo.Body.Version)
530+
if cmp, err := versionCompare(addonInstance.Version, targetVersion); err == nil && cmp == 1 {
531+
addonInstance.NextVersion = targetVersion
532+
}
533+
}
503534
addonInstance.CanUpgrade = addonInstance.Version != addonInstance.NextVersion
504535
addonInstance.SupportedActions = tea.StringSliceValue(addonInfo.Body.SupportedActions)
505536

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

551+
// versionCompare compares two semantic versions using github.com/Masterminds/semver.
552+
// Supports formats: "1.2.3", "v1.2.3", "1.2.3-aliyun.1", "v1.31.0-apsara.6.11.6.ad796663", etc.
553+
// Returns:
554+
//
555+
// 1 if newVersion > oldVersion
556+
// 0 if newVersion == oldVersion
557+
// -1 if newVersion < oldVersion
558+
//
559+
// Example: versionCompare("1.20.11-aliyun.1", "1.22.3-aliyun.1") returns 1
560+
func versionCompare(oldVersion, newVersion string) (int, error) {
561+
if oldVersion == "" || newVersion == "" {
562+
if oldVersion == "" && newVersion == "" {
563+
return 0, nil
564+
}
565+
if oldVersion == "" {
566+
return 1, nil
567+
}
568+
return -1, nil
569+
}
570+
571+
oldSemver, err := semver.NewVersion(oldVersion)
572+
if err != nil {
573+
return -2, fmt.Errorf("failed to parse version %q: %v", oldVersion, err)
574+
}
575+
newSemver, err := semver.NewVersion(newVersion)
576+
if err != nil {
577+
return -2, fmt.Errorf("failed to parse version %q: %v", newVersion, err)
578+
}
579+
580+
return newSemver.Compare(oldSemver), nil
581+
}
582+
520583
func (s *CsClient) CsKubernetesAddonTaskRefreshFunc(clusterId string, addonName string, failStates []string) resource.StateRefreshFunc {
521584
return func() (interface{}, string, error) {
522585
object, err := s.DescribeCsKubernetesAddonStatus(clusterId, addonName)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ require (
5858
cloud.google.com/go/iam v1.1.7 // indirect
5959
cloud.google.com/go/storage v1.39.1 // indirect
6060
github.com/Masterminds/goutils v1.1.1 // indirect
61-
github.com/Masterminds/semver v1.5.0 // indirect
61+
github.com/Masterminds/semver v1.5.0
6262
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
6363
github.com/PaesslerAG/gval v1.0.0 // indirect
6464
github.com/PuerkitoBio/purell v1.1.1 // indirect

0 commit comments

Comments
 (0)