From b5ccc4373d289b0e19b04b1a80028f0d992eaba7 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 23 Dec 2025 13:21:06 +0800 Subject: [PATCH] Add functions to validate and compare the E2E Velero version. Remove 'self' from MIGRATE_FROM_VELERO_VERSION. * Need to modify the BYOT case to specify MIGRATE_FROM_VELERO_VERSION. Remove the CSI plugin installation check for no older than v1.14 Signed-off-by: Xun Jiang --- test/Makefile | 4 +- test/e2e/e2e_suite_test.go | 34 +++++++++++- test/e2e/migration/migration.go | 56 ++++++++++--------- test/util/velero/install.go | 95 +++++++++++++++++++++++++++++--- test/util/velero/velero_utils.go | 15 ++--- 5 files changed, 157 insertions(+), 47 deletions(-) diff --git a/test/Makefile b/test/Makefile index e24efce95..7c99e4d70 100644 --- a/test/Makefile +++ b/test/Makefile @@ -62,6 +62,8 @@ GINKGO_LABELS ?= # https://onsi.github.io/ginkgo/#mental-model-how-ginkgo-handles-failure FAIL_FAST ?= false +VERSION ?= main + VELERO_CLI ?=$$(pwd)/../_output/bin/$(GOOS)/$(GOARCH)/velero VELERO_IMAGE ?= velero/velero:main @@ -83,7 +85,7 @@ UPGRADE_FROM_VELERO_VERSION ?= v1.15.2,v1.16.2 # to the end, nil string will be set if UPGRADE_FROM_VELERO_CLI is shorter than UPGRADE_FROM_VELERO_VERSION UPGRADE_FROM_VELERO_CLI ?= -MIGRATE_FROM_VELERO_VERSION ?= v1.15.2,self +MIGRATE_FROM_VELERO_VERSION ?= v1.16.2,$(VERSION) MIGRATE_FROM_VELERO_CLI ?= VELERO_NAMESPACE ?= velero diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 5e7be7adc..f0d1c9c2e 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -127,7 +127,7 @@ func init() { flag.StringVar( &test.VeleroCfg.UpgradeFromVeleroVersion, "upgrade-from-velero-version", - "v1.7.1", + "v1.16.2", "comma-separated list of Velero version to be tested with for the pre-upgrade velero server.", ) flag.StringVar( @@ -139,7 +139,7 @@ func init() { flag.StringVar( &test.VeleroCfg.MigrateFromVeleroVersion, "migrate-from-velero-version", - "self", + "v1.17.1", "comma-separated list of Velero version to be tested with on source cluster.", ) flag.StringVar( @@ -727,6 +727,36 @@ func TestE2e(t *testing.T) { } } + // Validate the Velero version + if len(test.VeleroCfg.VeleroVersion) > 0 { + if err := veleroutil.ValidateVeleroVersion(test.VeleroCfg.VeleroVersion); err != nil { + fmt.Println("VeleroVersion is invalid: ", test.VeleroCfg.VeleroVersion) + t.Error(err) + } + } + + // Validate the UpgradeFromVeleroVersion if provided + if len(test.VeleroCfg.UpgradeFromVeleroVersion) > 0 { + versions := strings.Split(test.VeleroCfg.UpgradeFromVeleroVersion, ",") + for _, version := range versions { + if err := veleroutil.ValidateVeleroVersion(version); err != nil { + fmt.Println("UpgradeFromVeleroVersion is invalid: ", version) + t.Error(err) + } + } + } + + // Validate the MigrateFromVeleroVersion if provided + if len(test.VeleroCfg.MigrateFromVeleroVersion) > 0 { + versions := strings.Split(test.VeleroCfg.MigrateFromVeleroVersion, ",") + for _, version := range versions { + if err := veleroutil.ValidateVeleroVersion(version); err != nil { + fmt.Println("MigrateFromVeleroVersion is invalid: ", version) + t.Error(err) + } + } + } + var err error if err = GetKubeConfigContext(); err != nil { fmt.Println(err) diff --git a/test/e2e/migration/migration.go b/test/e2e/migration/migration.go index 08f1bac00..e890189c3 100644 --- a/test/e2e/migration/migration.go +++ b/test/e2e/migration/migration.go @@ -23,7 +23,6 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "golang.org/x/mod/semver" "github.com/vmware-tanzu/velero/test" framework "github.com/vmware-tanzu/velero/test/e2e/test" @@ -47,7 +46,8 @@ type migrationE2E struct { func MigrationWithSnapshots() { for _, veleroCLI2Version := range veleroutil.GetVersionList( test.VeleroCfg.MigrateFromVeleroCLI, - test.VeleroCfg.MigrateFromVeleroVersion) { + test.VeleroCfg.MigrateFromVeleroVersion, + ) { framework.TestFunc( &migrationE2E{ useVolumeSnapshots: true, @@ -60,7 +60,8 @@ func MigrationWithSnapshots() { func MigrationWithFS() { for _, veleroCLI2Version := range veleroutil.GetVersionList( test.VeleroCfg.MigrateFromVeleroCLI, - test.VeleroCfg.MigrateFromVeleroVersion) { + test.VeleroCfg.MigrateFromVeleroVersion, + ) { framework.TestFunc( &migrationE2E{ useVolumeSnapshots: false, @@ -124,23 +125,26 @@ func (m *migrationE2E) Backup() error { var err error if m.veleroCLI2Version.VeleroCLI == "" { - //Assume tag of velero server image is identical to velero CLI version - //Download velero CLI if it's empty according to velero CLI version + // Assume tag of velero server image is identical to velero CLI version + // Download velero CLI if it's empty according to velero CLI version By( - fmt.Sprintf("Install the expected version Velero CLI %s", - m.veleroCLI2Version.VeleroVersion), + fmt.Sprintf( + "Install the expected version Velero CLI %s", + m.veleroCLI2Version.VeleroVersion, + ), func() { - // "self" represents 1.14.x and future versions - if m.veleroCLI2Version.VeleroVersion == "self" { + OriginVeleroCfg, err = veleroutil.SetImagesToDefaultValues( + OriginVeleroCfg, + m.veleroCLI2Version.VeleroVersion, + ) + Expect(err).To(Succeed(), + "Fail to set images for the migrate-from Velero installation.") + + // No need to download Velero CLI if the version is same as the VeleroVersion. + // Uses the local built Velero CLI. + if m.veleroCLI2Version.VeleroVersion == m.VeleroCfg.VeleroVersion { m.veleroCLI2Version.VeleroCLI = m.VeleroCfg.VeleroCLI } else { - OriginVeleroCfg, err = veleroutil.SetImagesToDefaultValues( - OriginVeleroCfg, - m.veleroCLI2Version.VeleroVersion, - ) - Expect(err).To(Succeed(), - "Fail to set images for the migrate-from Velero installation.") - m.veleroCLI2Version.VeleroCLI, err = veleroutil.InstallVeleroCLI( m.Ctx, m.veleroCLI2Version.VeleroVersion) @@ -165,9 +169,11 @@ func (m *migrationE2E) Backup() error { version, err := veleroutil.GetVeleroVersion(m.Ctx, OriginVeleroCfg.VeleroCLI, true) Expect(err).To(Succeed(), "Fail to get Velero version") OriginVeleroCfg.VeleroVersion = version - if OriginVeleroCfg.WorkerOS == common.WorkerOSWindows && - (version != "main" && semver.Compare(version, "v1.16") < 0) { - Skip(fmt.Sprintf("Velero CLI version %s doesn't support Windows migration test.", version)) + if OriginVeleroCfg.WorkerOS == common.WorkerOSWindows { + result, err := veleroutil.VersionNoOlderThan(version, "v1.16") + if err != nil || !result { + Skip(fmt.Sprintf("Velero CLI version %s doesn't support Windows migration test.", version)) + } } if OriginVeleroCfg.SnapshotMoveData { @@ -175,13 +181,11 @@ func (m *migrationE2E) Backup() error { } Expect(veleroutil.VeleroInstall(m.Ctx, &OriginVeleroCfg, false)).To(Succeed()) - if m.veleroCLI2Version.VeleroVersion != "self" { - Expect(veleroutil.CheckVeleroVersion( - m.Ctx, - OriginVeleroCfg.VeleroCLI, - OriginVeleroCfg.MigrateFromVeleroVersion, - )).To(Succeed()) - } + Expect(veleroutil.CheckVeleroVersion( + m.Ctx, + OriginVeleroCfg.VeleroCLI, + OriginVeleroCfg.MigrateFromVeleroVersion, + )).To(Succeed()) }, ) diff --git a/test/util/velero/install.go b/test/util/velero/install.go index 6ffa8812d..b66814ee0 100644 --- a/test/util/velero/install.go +++ b/test/util/velero/install.go @@ -23,6 +23,7 @@ import ( "fmt" "os" "os/exec" + "regexp" "slices" "strings" "time" @@ -311,6 +312,80 @@ func cleanVSpherePluginConfig(c clientset.Interface, ns, secretName, configMapNa return nil } +// ValidateVeleroVersion checks if the given version is valid +// version can be in the format of 'main', 'release-x.y(-dev)', or 'vX.Y(.Z)' +func ValidateVeleroVersion(version string) error { + mainRe := regexp.MustCompile(`^main$`) + releaseRe := regexp.MustCompile(`^release-(\d)\.(\d)(-dev)?$`) + tagRe := regexp.MustCompile(`^v(\d+)\.(\d+)(\.\d+)?$`) + + if mainRe.MatchString(version) || releaseRe.MatchString(version) || tagRe.MatchString(version) { + return nil + } + return fmt.Errorf("invalid Velero version: %s, Velero version must be 'main', 'release-x.y(-dev)', or 'vX.Y.Z'", version) +} + +// VersionNoOlderThan checks if the given version is no older than the targetVersion +// version can be in the format of 'main', 'release-x.y(-dev)', or 'vX.Y(.Z)' +// targetVersion must be in the format of 'main', or 'vX.Y.(Z)' +// return true if version is no older than targetVersion +func VersionNoOlderThan(version string, targetVersion string) (bool, error) { + mainRe := regexp.MustCompile(`^main$`) + releaseRe := regexp.MustCompile(`^release-(\d)\.(\d)(-dev)?$`) + tagRe := regexp.MustCompile(`^v(\d)\.(\d)(\.\d+)?$`) + + if err := ValidateVeleroVersion(version); err != nil { + return false, err + } + if !tagRe.MatchString(targetVersion) && !mainRe.MatchString(targetVersion) { + return false, fmt.Errorf("targetVersion is invalid. it must be in the format of 'main', or 'vX.Y.(Z)'.") + } + + fmt.Printf("version: %s, targetVersion: %s\n", version, targetVersion) + + switch { + case mainRe.MatchString(version): + // main is always the latest + return true, nil + + case releaseRe.MatchString(version): + // release-x.y(-dev) is treated as vX.Y.0 + matches := releaseRe.FindStringSubmatch(version) + major := matches[1] + minor := matches[2] + + switch { + case mainRe.MatchString(targetVersion): + return false, nil + + default: + matches := tagRe.FindStringSubmatch(targetVersion) + targetMajor := matches[1] + targetMinor := matches[2] + if major > targetMajor && minor >= targetMinor { + return true, nil + } else { + return false, nil + } + } + + case tagRe.MatchString(version): + switch { + case mainRe.MatchString(targetVersion): + return false, nil + + default: + if semver.Compare(version, targetVersion) >= 0 { + return true, nil + } else { + return false, nil + } + } + } + + return false, fmt.Errorf("unknown error in VersionNoOlderThan") +} + func installVeleroServer( ctx context.Context, cli string, @@ -333,10 +408,12 @@ func installVeleroServer( // TODO: need to consider align options.UseNodeAgentWindows usage // with options.UseNodeAgent // Only version after v1.16.0 support windows node agent. - if options.WorkerOS == common.WorkerOSWindows && - (semver.Compare(version, "v1.16") >= 0 || version == "main") { - fmt.Println("Install node-agent-windows. The Velero version is ", version) - args = append(args, "--use-node-agent-windows") + if options.WorkerOS == common.WorkerOSWindows { + result, err := VersionNoOlderThan(version, "v1.16") + if err == nil && result { + fmt.Println("Install node-agent-windows. The Velero version is ", version) + args = append(args, "--use-node-agent-windows") + } } if options.DefaultVolumesToFsBackup { @@ -452,10 +529,12 @@ func installVeleroServer( } // Only version no older than v1.15 support --backup-repository-configmap. - if options.BackupRepoConfigMap != "" && - (semver.Compare(version, "v1.15") >= 0 || version == "main") { - fmt.Println("Associate backup repository ConfigMap. The Velero version is ", version) - args = append(args, fmt.Sprintf("--backup-repository-configmap=%s", options.BackupRepoConfigMap)) + if options.BackupRepoConfigMap != "" { + result, err := VersionNoOlderThan(version, "v1.15") + if err == nil && result { + fmt.Println("Associate backup repository ConfigMap. The Velero version is ", version) + args = append(args, fmt.Sprintf("--backup-repository-configmap=%s", options.BackupRepoConfigMap)) + } } if options.RepoMaintenanceJobConfigMap != "" { diff --git a/test/util/velero/velero_utils.go b/test/util/velero/velero_utils.go index 8dbcbb2bd..96925b0d8 100644 --- a/test/util/velero/velero_utils.go +++ b/test/util/velero/velero_utils.go @@ -128,7 +128,11 @@ func SetImagesToDefaultValues(config VeleroConfig, version string) (VeleroConfig ret.Plugins = "" - versionWithoutPatch := semver.MajorMinor(version) + versionWithoutPatch := "main" + if version != "main" { + versionWithoutPatch = semver.MajorMinor(version) + } + // Read migration case needs images from the PluginsMatrix map. images, ok := ImagesMatrix[versionWithoutPatch] if !ok { @@ -153,12 +157,6 @@ func SetImagesToDefaultValues(config VeleroConfig, version string) (VeleroConfig ret.Plugins = images[AWS][0] } - // Because Velero CSI plugin is deprecated in v1.14, - // only need to install it for version lower than v1.14. - if strings.Contains(ret.Features, FeatureCSI) && - semver.Compare(versionWithoutPatch, "v1.14") < 0 { - ret.Plugins = ret.Plugins + "," + images[CSI][0] - } if ret.SnapshotMoveData && ret.CloudProvider == Azure { ret.Plugins = ret.Plugins + "," + images[AWS][0] } @@ -1567,9 +1565,6 @@ func RestorePVRNum(ctx context.Context, veleroNamespace, restoreName string) (in } func IsSupportUploaderType(version string) (bool, error) { - if strings.Contains(version, "self") { - return true, nil - } verSupportUploaderType, err := ver.ParseSemantic("v1.10.0") if err != nil { return false, err