Skip to content

Commit 56a6e95

Browse files
authored
test(e2e): Resolve latest upstream provider releases in e2e config (#388)
1 parent 658f01e commit 56a6e95

File tree

5 files changed

+387
-14
lines changed

5 files changed

+387
-14
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ require (
3232
sigs.k8s.io/cluster-api-addon-provider-helm v0.1.1-alpha.1
3333
sigs.k8s.io/cluster-api/test v1.6.1
3434
sigs.k8s.io/controller-runtime v0.17.1
35+
sigs.k8s.io/yaml v1.4.0
3536
)
3637

3738
require (
@@ -141,5 +142,4 @@ require (
141142
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
142143
sigs.k8s.io/kind v0.20.0 // indirect
143144
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
144-
sigs.k8s.io/yaml v1.4.0 // indirect
145145
)

test/e2e/config/cre.yaml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ providers:
1111
- name: cluster-api
1212
type: CoreProvider
1313
versions:
14-
- name: v1.6.1
15-
value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.6.1/core-components.yaml
14+
- name: "{go://sigs.k8s.io/cluster-api@v1.6}"
15+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.6}/core-components.yaml"
1616
type: url
1717
contract: v1beta1
1818
files:
@@ -24,8 +24,8 @@ providers:
2424
- name: kubeadm
2525
type: BootstrapProvider
2626
versions:
27-
- name: v1.6.1
28-
value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.6.1/bootstrap-components.yaml
27+
- name: "{go://sigs.k8s.io/cluster-api@v1.6}"
28+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.6}/bootstrap-components.yaml"
2929
type: url
3030
contract: v1beta1
3131
files:
@@ -37,8 +37,8 @@ providers:
3737
- name: kubeadm
3838
type: ControlPlaneProvider
3939
versions:
40-
- name: v1.6.1
41-
value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.6.1/control-plane-components.yaml
40+
- name: "{go://sigs.k8s.io/cluster-api@v1.6}"
41+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.6}/control-plane-components.yaml"
4242
type: url
4343
contract: v1beta1
4444
files:
@@ -50,8 +50,8 @@ providers:
5050
- name: docker
5151
type: InfrastructureProvider
5252
versions:
53-
- name: v1.6.1
54-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.6.1/infrastructure-components-development.yaml"
53+
- name: "{go://sigs.k8s.io/cluster-api@v1.6}"
54+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.6}/infrastructure-components-development.yaml"
5555
type: "url"
5656
contract: v1beta1
5757
files:
@@ -73,8 +73,8 @@ providers:
7373
- name: helm
7474
type: AddonProvider
7575
versions:
76-
- name: v0.1.1-alpha.1
77-
value: "https://github.com/kubernetes-sigs/cluster-api-addon-provider-helm/releases/download/v0.1.1-alpha.1/addon-components.yaml"
76+
- name: "{go://sigs.k8s.io/[email protected]}"
77+
value: "https://github.com/kubernetes-sigs/cluster-api-addon-provider-helm/releases/download/{go://sigs.k8s.io/[email protected]}/addon-components.yaml"
7878
type: "url"
7979
contract: v1beta1
8080
files:
@@ -86,8 +86,8 @@ providers:
8686
- name: cre
8787
type: RuntimeExtensionProvider
8888
versions:
89-
- name: v0.5.0
90-
value: "https://github.com/d2iq-labs/capi-runtime-extensions/releases/download/v0.5.0/runtime-extension-components.yaml"
89+
- name: "{go://github.com/d2iq-labs/capi-runtime-extensions@v0.5}"
90+
value: "https://github.com/d2iq-labs/capi-runtime-extensions/releases/download/{go://github.com/d2iq-labs/capi-runtime-extensions@v0.5}/runtime-extension-components.yaml"
9191
type: "url"
9292
contract: v1beta1
9393
files:

test/e2e/e2e_suite_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import (
2626
"sigs.k8s.io/cluster-api/test/framework/bootstrap"
2727
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
2828
ctrl "sigs.k8s.io/controller-runtime"
29+
30+
clusterctltemp "github.com/d2iq-labs/capi-runtime-extensions/test/framework/clusterctl"
2931
)
3032

3133
func init() { //nolint:gochecknoinits // Idiomatically used to set up flags.
@@ -154,7 +156,7 @@ var _ = SynchronizedAfterSuite(func() {
154156
})
155157

156158
func loadE2EConfig(configPath string) *clusterctl.E2EConfig {
157-
config := clusterctl.LoadE2EConfig(
159+
config := clusterctltemp.LoadE2EConfig(
158160
context.TODO(),
159161
clusterctl.LoadE2EConfigInput{ConfigPath: configPath},
160162
)
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Copyright 2023 D2iQ, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/*
5+
Copyright 2020 The Kubernetes Authors.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
*/
19+
20+
// This file is a copy of the original file from the cluster-api repository, to allow resolving latest patch releases
21+
// versions. Once this is released upstream in CPAI v1.7, this file can be removed and code switched to use the upstream
22+
// config loader.
23+
24+
package clusterctl
25+
26+
import (
27+
"context"
28+
"errors"
29+
"fmt"
30+
"os"
31+
"path/filepath"
32+
"strings"
33+
34+
"github.com/blang/semver/v4"
35+
. "github.com/onsi/gomega"
36+
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
37+
"sigs.k8s.io/yaml"
38+
39+
"github.com/d2iq-labs/capi-runtime-extensions/test/framework/goproxy"
40+
)
41+
42+
// LoadE2EConfig loads the configuration for the e2e test environment.
43+
func LoadE2EConfig(ctx context.Context, input clusterctl.LoadE2EConfigInput) *clusterctl.E2EConfig {
44+
configData, err := os.ReadFile(input.ConfigPath)
45+
Expect(err).ToNot(HaveOccurred(), "Failed to read the e2e test config file")
46+
Expect(configData).ToNot(BeEmpty(), "The e2e test config file should not be empty")
47+
48+
config := &clusterctl.E2EConfig{}
49+
Expect(
50+
yaml.Unmarshal(configData, config),
51+
).To(Succeed(), "Failed to convert the e2e test config file to yaml")
52+
53+
Expect(
54+
ResolveReleases(ctx, config),
55+
).To(Succeed(), "Failed to resolve release markers in e2e test config file")
56+
config.Defaults()
57+
config.AbsPaths(filepath.Dir(input.ConfigPath))
58+
59+
Expect(config.Validate()).To(Succeed(), "The e2e test config file is not valid")
60+
61+
return config
62+
}
63+
64+
// ResolveReleases converts release markers to release version.
65+
func ResolveReleases(ctx context.Context, config *clusterctl.E2EConfig) error {
66+
for i := range config.Providers {
67+
provider := &config.Providers[i]
68+
for j := range provider.Versions {
69+
version := &provider.Versions[j]
70+
if version.Type != clusterctl.URLSource {
71+
continue
72+
}
73+
// Skipping versions that are not a resolvable marker. Resolvable markers are surrounded by `{}`
74+
if !strings.HasPrefix(version.Name, "{") || !strings.HasSuffix(version.Name, "}") {
75+
continue
76+
}
77+
releaseMarker := strings.TrimLeft(strings.TrimRight(version.Name, "}"), "{")
78+
ver, err := ResolveRelease(ctx, releaseMarker)
79+
if err != nil {
80+
return fmt.Errorf("failed resolving release url %q: %w", version.Name, err)
81+
}
82+
ver = "v" + ver
83+
version.Value = strings.Replace(version.Value, version.Name, ver, 1)
84+
version.Name = ver
85+
}
86+
}
87+
return nil
88+
}
89+
90+
func ResolveRelease(ctx context.Context, releaseMarker string) (string, error) {
91+
scheme, host, err := goproxy.GetSchemeAndHost(os.Getenv("GOPROXY"))
92+
if err != nil {
93+
return "", err
94+
}
95+
if scheme == "" || host == "" {
96+
return "", fmt.Errorf(
97+
"releasemarker does not support disabling the go proxy: GOPROXY=%q",
98+
os.Getenv("GOPROXY"),
99+
)
100+
}
101+
goproxyClient := goproxy.NewClient(scheme, host)
102+
return resolveReleaseMarker(ctx, releaseMarker, goproxyClient)
103+
}
104+
105+
// resolveReleaseMarker resolves releaseMarker string to verion string e.g.
106+
// - Resolves "go://sigs.k8s.io/[email protected]" to the latest stable patch release of v1.0.
107+
// - Resolves "go://sigs.k8s.io/[email protected]" to the latest patch release of v1.0 including rc and
108+
// pre releases.
109+
func resolveReleaseMarker(
110+
ctx context.Context,
111+
releaseMarker string,
112+
goproxyClient *goproxy.Client,
113+
) (string, error) {
114+
if !strings.HasPrefix(releaseMarker, "go://") {
115+
return "", errors.New("unknown release marker scheme")
116+
}
117+
118+
releaseMarker = strings.TrimPrefix(releaseMarker, "go://")
119+
if releaseMarker == "" {
120+
return "", errors.New("empty release url")
121+
}
122+
123+
gomoduleParts := strings.Split(releaseMarker, "@")
124+
if len(gomoduleParts) < 2 {
125+
return "", errors.New("go module or version missing")
126+
}
127+
gomodule := gomoduleParts[0]
128+
129+
includePrereleases := false
130+
if strings.HasPrefix(gomoduleParts[1], "latest-") {
131+
includePrereleases = true
132+
}
133+
version := strings.TrimPrefix(gomoduleParts[1], "latest-") + ".0"
134+
version = strings.TrimPrefix(version, "v")
135+
semVersion, err := semver.Parse(version)
136+
if err != nil {
137+
return "", fmt.Errorf("parsing semver for %s: %w", version, err)
138+
}
139+
140+
parsedTags, err := goproxyClient.GetVersions(ctx, gomodule)
141+
if err != nil {
142+
return "", err
143+
}
144+
145+
var picked semver.Version
146+
for i, tag := range parsedTags {
147+
if !includePrereleases && len(tag.Pre) > 0 {
148+
continue
149+
}
150+
if tag.Major == semVersion.Major && tag.Minor == semVersion.Minor {
151+
picked = parsedTags[i]
152+
}
153+
}
154+
if picked.Major == 0 && picked.Minor == 0 && picked.Patch == 0 {
155+
return "", fmt.Errorf(
156+
"no suitable release available for release marker %s",
157+
releaseMarker,
158+
)
159+
}
160+
return picked.String(), nil
161+
}

0 commit comments

Comments
 (0)