diff --git a/test/e2e/cluster_upgrade.go b/test/e2e/cluster_upgrade.go index cb4615ac5ffc..097abc6d3017 100644 --- a/test/e2e/cluster_upgrade.go +++ b/test/e2e/cluster_upgrade.go @@ -92,6 +92,7 @@ func ClusterUpgradeConformanceSpec(ctx context.Context, inputGetter func() Clust Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName) input = inputGetter() Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName) + Expect(input.E2EConfig.ResolveReleases()).To(Succeed(), "Failed to resolve release markers in e2e test config file") Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName) Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName) Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName) diff --git a/test/e2e/config/docker.yaml b/test/e2e/config/docker.yaml index 61140d0d6722..ae3c3e19f638 100644 --- a/test/e2e/config/docker.yaml +++ b/test/e2e/config/docker.yaml @@ -35,8 +35,8 @@ providers: - name: cluster-api type: CoreProvider versions: - - name: v1.0.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.0.5/core-components.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.0}" # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.0}/core-components.yaml" type: "url" contract: v1beta1 replacements: @@ -44,8 +44,8 @@ providers: new: --metrics-addr=:8080 files: - sourcePath: "../data/shared/v1.0/metadata.yaml" - - name: v1.4.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.4.5/core-components.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.4}" # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.4}/core-components.yaml" type: "url" contract: v1beta1 replacements: @@ -53,8 +53,8 @@ providers: new: --metrics-addr=:8080 files: - sourcePath: "../data/shared/v1.4/metadata.yaml" - - name: v1.5.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.0/core-components.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.5}" # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.5}/core-components.yaml" type: "url" contract: v1beta1 replacements: @@ -73,8 +73,8 @@ providers: - name: kubeadm type: BootstrapProvider versions: - - name: v1.0.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.0.5/bootstrap-components.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.0}" # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.0}/bootstrap-components.yaml" type: "url" contract: v1beta1 replacements: @@ -82,8 +82,8 @@ providers: new: --metrics-addr=:8080 files: - sourcePath: "../data/shared/v1.0/metadata.yaml" - - name: v1.4.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.4.5/bootstrap-components.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.4}" # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.4}/bootstrap-components.yaml" type: "url" contract: v1beta1 replacements: @@ -91,8 +91,8 @@ providers: new: --metrics-addr=:8080 files: - sourcePath: "../data/shared/v1.4/metadata.yaml" - - name: v1.5.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.0/bootstrap-components.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.5}" # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.5}/bootstrap-components.yaml" type: "url" contract: v1beta1 replacements: @@ -111,8 +111,8 @@ providers: - name: kubeadm type: ControlPlaneProvider versions: - - name: v1.0.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.0.5/control-plane-components.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.0}" # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.0}/control-plane-components.yaml" type: "url" contract: v1beta1 replacements: @@ -120,8 +120,8 @@ providers: new: --metrics-addr=:8080 files: - sourcePath: "../data/shared/v1.0/metadata.yaml" - - name: v1.4.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.4.5/control-plane-components.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.4}" # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.4}/control-plane-components.yaml" type: "url" contract: v1beta1 replacements: @@ -129,8 +129,8 @@ providers: new: --metrics-addr=:8080 files: - sourcePath: "../data/shared/v1.4/metadata.yaml" - - name: v1.5.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.0/control-plane-components.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.5}" # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.5}/control-plane-components.yaml" type: "url" contract: v1beta1 replacements: @@ -149,8 +149,8 @@ providers: - name: docker type: InfrastructureProvider versions: - - name: v1.0.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.0.5/infrastructure-components-development.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.0}" # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.0}/infrastructure-components-development.yaml" type: "url" contract: v1beta1 replacements: @@ -159,8 +159,8 @@ providers: files: - sourcePath: "../data/shared/v1.0/metadata.yaml" - sourcePath: "../data/infrastructure-docker/v1.0/cluster-template.yaml" - - name: v1.4.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.4.5/infrastructure-components-development.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.4}" # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.4}/infrastructure-components-development.yaml" type: "url" contract: v1beta1 replacements: @@ -171,8 +171,8 @@ providers: - sourcePath: "../data/infrastructure-docker/v1.4/cluster-template.yaml" - sourcePath: "../data/infrastructure-docker/v1.4/cluster-template-topology.yaml" - sourcePath: "../data/infrastructure-docker/v1.4/clusterclass-quick-start.yaml" - - name: v1.5.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.0/infrastructure-components-development.yaml" + - name: "{go://sigs.k8s.io/cluster-api@v1.5}" # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only. + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.5}/infrastructure-components-development.yaml" type: "url" contract: v1beta1 replacements: diff --git a/test/framework/clusterctl/e2e_config.go b/test/framework/clusterctl/e2e_config.go index 206fe2c19574..fdf7ef201f97 100644 --- a/test/framework/clusterctl/e2e_config.go +++ b/test/framework/clusterctl/e2e_config.go @@ -19,8 +19,11 @@ package clusterctl import ( "context" "fmt" + "io" + "net/http" "net/url" "os" + "path" "path/filepath" "regexp" "runtime" @@ -29,6 +32,7 @@ import ( "strings" "time" + "github.com/blang/semver/v4" . "github.com/onsi/gomega" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/util/version" @@ -57,6 +61,7 @@ func LoadE2EConfig(_ context.Context, input LoadE2EConfigInput) *E2EConfig { config := &E2EConfig{} Expect(yaml.Unmarshal(configData, config)).To(Succeed(), "Failed to convert the e2e test config file to yaml") + Expect(config.ResolveReleases()).To(Succeed(), "Failed to resolve release markers in e2e test config file") config.Defaults() config.AbsPaths(filepath.Dir(input.ConfigPath)) @@ -245,6 +250,102 @@ type Files struct { TargetName string `json:"targetName,omitempty"` } +// ResolveReleases converts release markers to release version. +func (c *E2EConfig) ResolveReleases() error { + for i := range c.Providers { + provider := &c.Providers[i] + for j := range provider.Versions { + version := &provider.Versions[j] + if version.Type == "url" { + releaseMarker := strings.TrimLeft(strings.TrimRight(version.Name, "}"), "{") + if !strings.EqualFold(version.Name, releaseMarker) { + ver, err := resolveReleaseMarker(releaseMarker) + if err != nil { + return fmt.Errorf("failed resolving release path %s with error: %v", version.Name, err) + } + ver = "v" + ver + version.Value = strings.Replace(version.Value, version.Name, ver, 1) + version.Name = ver + } + } + } + } + return nil +} + +// resolveReleaseMarker resolves releaseMarker string to verion string e.g. +// - Resolves "{go://sigs.k8s.io/cluster-api@v1.0}" to stable minor release of v.1.0. +// - Resolves "{go://sigs.k8s.io/cluster-api@latest-v1.0}" to latest minor release of v.1.0 including rc and pre releases. +func resolveReleaseMarker(releaseMarker string) (string, error) { + parts := strings.Split(releaseMarker, ":") + if len(parts) < 1 { + return "", errors.Errorf("Invalid release url") + } + + host := parts[0] + gomoduleParts := strings.Split(parts[1], "@") + if len(gomoduleParts) < 2 { + return "", errors.Errorf("Invalid release url, go module or version missing") + } + gomodule := gomoduleParts[0] + version := strings.ReplaceAll(gomoduleParts[1], "v", "") + includePrereleases := false + parsedVersion := strings.Split(version, "-") + if len(parsedVersion) > 0 { + if len(parsedVersion) > 1 { + version = parsedVersion[1] + if parsedVersion[0] == "latest" { + includePrereleases = true + } + } + } else { + return "", errors.Errorf("Invalid release url") + } + + minSemVersion := semver.MustParse(version + ".0") + maxSemVersion := minSemVersion + maxSemVersion.Minor++ + + if host == "go" { + host = "proxy.golang.org" + } + rawURL := url.URL{ + Scheme: "https", + Host: host, + Path: path.Join(gomodule, "@v", "/list"), + } + + resp, err := http.Get(rawURL.String()) //nolint:noctx // NB: as we're just implementing an external interface we won't be able to get a context here. + if err != nil { + return "", err + } + defer resp.Body.Close() + + out, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + parsedTags := semver.Versions{} + for _, line := range strings.Split(string(out), "\n") { + if strings.HasPrefix(line, "v") { + parsedTags = append(parsedTags, semver.MustParse(strings.TrimPrefix(line, "v"))) + } + } + sort.Sort(parsedTags) + + var picked semver.Version + for i, tag := range parsedTags { + if !includePrereleases && len(tag.Pre) > 0 { + continue + } + if tag.LT(maxSemVersion) && tag.Minor <= minSemVersion.Minor { + picked = parsedTags[i] + } + } + return picked.String(), nil +} + // Defaults assigns default values to the object. More specifically: // - ManagementClusterName gets a default name if empty. // - Providers version gets type KustomizeSource if not otherwise specified.