Skip to content

Commit 4ac1964

Browse files
Resolve release markers
1 parent 46412f0 commit 4ac1964

File tree

4 files changed

+216
-24
lines changed

4 files changed

+216
-24
lines changed

test/e2e/cluster_upgrade.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ func ClusterUpgradeConformanceSpec(ctx context.Context, inputGetter func() Clust
9292
Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName)
9393
input = inputGetter()
9494
Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName)
95+
Expect(input.E2EConfig.ResolveReleases()).To(Succeed(), "Failed to resolve release markers in e2e test config file")
9596
Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName)
9697
Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName)
9798
Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName)

test/e2e/config/docker.yaml

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,26 +35,26 @@ providers:
3535
- name: cluster-api
3636
type: CoreProvider
3737
versions:
38-
- name: v1.0.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
39-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.0.5/core-components.yaml"
38+
- 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.
39+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.0}/core-components.yaml"
4040
type: "url"
4141
contract: v1beta1
4242
replacements:
4343
- old: --metrics-addr=127.0.0.1:8080
4444
new: --metrics-addr=:8080
4545
files:
4646
- sourcePath: "../data/shared/v1.0/metadata.yaml"
47-
- name: v1.4.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
48-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.4.5/core-components.yaml"
47+
- 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.
48+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.4}/core-components.yaml"
4949
type: "url"
5050
contract: v1beta1
5151
replacements:
5252
- old: --metrics-addr=127.0.0.1:8080
5353
new: --metrics-addr=:8080
5454
files:
5555
- sourcePath: "../data/shared/v1.4/metadata.yaml"
56-
- name: v1.5.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
57-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.0/core-components.yaml"
56+
- 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.
57+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.5}/core-components.yaml"
5858
type: "url"
5959
contract: v1beta1
6060
replacements:
@@ -73,26 +73,26 @@ providers:
7373
- name: kubeadm
7474
type: BootstrapProvider
7575
versions:
76-
- name: v1.0.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
77-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.0.5/bootstrap-components.yaml"
76+
- 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.
77+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.0}/bootstrap-components.yaml"
7878
type: "url"
7979
contract: v1beta1
8080
replacements:
8181
- old: --metrics-addr=127.0.0.1:8080
8282
new: --metrics-addr=:8080
8383
files:
8484
- sourcePath: "../data/shared/v1.0/metadata.yaml"
85-
- name: v1.4.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
86-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.4.5/bootstrap-components.yaml"
85+
- 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.
86+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.4}/bootstrap-components.yaml"
8787
type: "url"
8888
contract: v1beta1
8989
replacements:
9090
- old: --metrics-addr=127.0.0.1:8080
9191
new: --metrics-addr=:8080
9292
files:
9393
- sourcePath: "../data/shared/v1.4/metadata.yaml"
94-
- name: v1.5.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
95-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.0/bootstrap-components.yaml"
94+
- 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.
95+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.5}/bootstrap-components.yaml"
9696
type: "url"
9797
contract: v1beta1
9898
replacements:
@@ -111,26 +111,26 @@ providers:
111111
- name: kubeadm
112112
type: ControlPlaneProvider
113113
versions:
114-
- name: v1.0.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
115-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.0.5/control-plane-components.yaml"
114+
- 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.
115+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.0}/control-plane-components.yaml"
116116
type: "url"
117117
contract: v1beta1
118118
replacements:
119119
- old: --metrics-addr=127.0.0.1:8080
120120
new: --metrics-addr=:8080
121121
files:
122122
- sourcePath: "../data/shared/v1.0/metadata.yaml"
123-
- name: v1.4.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
124-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.4.5/control-plane-components.yaml"
123+
- 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.
124+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.4}/control-plane-components.yaml"
125125
type: "url"
126126
contract: v1beta1
127127
replacements:
128128
- old: --metrics-addr=127.0.0.1:8080
129129
new: --metrics-addr=:8080
130130
files:
131131
- sourcePath: "../data/shared/v1.4/metadata.yaml"
132-
- name: v1.5.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
133-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.0/control-plane-components.yaml"
132+
- 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.
133+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.5}/control-plane-components.yaml"
134134
type: "url"
135135
contract: v1beta1
136136
replacements:
@@ -149,8 +149,8 @@ providers:
149149
- name: docker
150150
type: InfrastructureProvider
151151
versions:
152-
- name: v1.0.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
153-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.0.5/infrastructure-components-development.yaml"
152+
- 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.
153+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.0}/infrastructure-components-development.yaml"
154154
type: "url"
155155
contract: v1beta1
156156
replacements:
@@ -159,8 +159,8 @@ providers:
159159
files:
160160
- sourcePath: "../data/shared/v1.0/metadata.yaml"
161161
- sourcePath: "../data/infrastructure-docker/v1.0/cluster-template.yaml"
162-
- name: v1.4.5 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
163-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.4.5/infrastructure-components-development.yaml"
162+
- 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.
163+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.4}/infrastructure-components-development.yaml"
164164
type: "url"
165165
contract: v1beta1
166166
replacements:
@@ -171,8 +171,8 @@ providers:
171171
- sourcePath: "../data/infrastructure-docker/v1.4/cluster-template.yaml"
172172
- sourcePath: "../data/infrastructure-docker/v1.4/cluster-template-topology.yaml"
173173
- sourcePath: "../data/infrastructure-docker/v1.4/clusterclass-quick-start.yaml"
174-
- name: v1.5.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
175-
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.0/infrastructure-components-development.yaml"
174+
- 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.
175+
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/{go://sigs.k8s.io/cluster-api@v1.5}/infrastructure-components-development.yaml"
176176
type: "url"
177177
contract: v1beta1
178178
replacements:

test/framework/clusterctl/e2e_config.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"strings"
3030
"time"
3131

32+
"github.com/blang/semver/v4"
3233
. "github.com/onsi/gomega"
3334
"github.com/pkg/errors"
3435
"k8s.io/apimachinery/pkg/util/version"
@@ -37,6 +38,7 @@ import (
3738

3839
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
3940
clusterctlconfig "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
41+
"sigs.k8s.io/cluster-api/internal/goproxy"
4042
"sigs.k8s.io/cluster-api/util"
4143
)
4244

@@ -57,6 +59,7 @@ func LoadE2EConfig(_ context.Context, input LoadE2EConfigInput) *E2EConfig {
5759
config := &E2EConfig{}
5860
Expect(yaml.Unmarshal(configData, config)).To(Succeed(), "Failed to convert the e2e test config file to yaml")
5961

62+
Expect(config.ResolveReleases()).To(Succeed(), "Failed to resolve release markers in e2e test config file")
6063
config.Defaults()
6164
config.AbsPaths(filepath.Dir(input.ConfigPath))
6265

@@ -245,6 +248,83 @@ type Files struct {
245248
TargetName string `json:"targetName,omitempty"`
246249
}
247250

251+
// ResolveReleases converts release markers to release version.
252+
func (c *E2EConfig) ResolveReleases() error {
253+
scheme, host, err := goproxy.GetSchemeAndHost(os.Getenv("GOPROXY"))
254+
if err != nil {
255+
return err
256+
}
257+
if scheme == "" || host == "" {
258+
return fmt.Errorf("releasemarker does not support disabling the go proxy: GOPROXY=%q", os.Getenv("GOPROXY"))
259+
}
260+
goproxyClient := goproxy.NewClient(scheme, host)
261+
for i := range c.Providers {
262+
provider := &c.Providers[i]
263+
for j := range provider.Versions {
264+
version := &provider.Versions[j]
265+
if version.Type == "url" {
266+
releaseMarker := strings.TrimLeft(strings.TrimRight(version.Name, "}"), "{")
267+
if !strings.EqualFold(version.Name, releaseMarker) {
268+
ver, err := resolveReleaseMarker(releaseMarker, goproxyClient)
269+
if err != nil {
270+
return fmt.Errorf("failed resolving release path %s with error: %v", version.Name, err)
271+
}
272+
ver = "v" + ver
273+
version.Value = strings.Replace(version.Value, version.Name, ver, 1)
274+
version.Name = ver
275+
}
276+
}
277+
}
278+
}
279+
return nil
280+
}
281+
282+
// resolveReleaseMarker resolves releaseMarker string to verion string e.g.
283+
// - Resolves "go://sigs.k8s.io/[email protected]" to the latest stable patch release of v1.0.
284+
// - Resolves "go://sigs.k8s.io/[email protected]" to the latest patch release of v.1.0 including rc and pre releases.
285+
func resolveReleaseMarker(releaseMarker string, goproxyClient *goproxy.Client) (string, error) {
286+
parts := strings.Split(releaseMarker, ":")
287+
if len(parts) <= 1 {
288+
return "", errors.Errorf("Invalid release url")
289+
}
290+
291+
gomoduleParts := strings.Split(parts[1], "@")
292+
if len(gomoduleParts) < 2 {
293+
return "", errors.Errorf("Invalid release url, go module or version missing")
294+
}
295+
gomodule := gomoduleParts[0]
296+
version := strings.ReplaceAll(gomoduleParts[1], "v", "")
297+
includePrereleases := false
298+
if strings.HasPrefix(version, "latest-") {
299+
includePrereleases = true
300+
}
301+
version = strings.TrimPrefix(version, "latest-")
302+
303+
minSemVersion, err := semver.Parse(version + ".0")
304+
if err != nil {
305+
return "", err
306+
}
307+
maxSemVersion := minSemVersion
308+
maxSemVersion.Minor++
309+
310+
parsedTags, err := goproxyClient.GetVersions(context.Background(), gomodule)
311+
312+
if err != nil {
313+
return "", err
314+
}
315+
316+
var picked semver.Version
317+
for i, tag := range parsedTags {
318+
if !includePrereleases && len(tag.Pre) > 0 {
319+
continue
320+
}
321+
if tag.LT(maxSemVersion) && tag.Minor <= minSemVersion.Minor {
322+
picked = parsedTags[i]
323+
}
324+
}
325+
return picked.String(), nil
326+
}
327+
248328
// Defaults assigns default values to the object. More specifically:
249329
// - ManagementClusterName gets a default name if empty.
250330
// - Providers version gets type KustomizeSource if not otherwise specified.
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package clusterctl
18+
19+
import (
20+
"fmt"
21+
"net/http"
22+
"net/http/httptest"
23+
"net/url"
24+
"testing"
25+
26+
. "github.com/onsi/gomega"
27+
"sigs.k8s.io/cluster-api/internal/goproxy"
28+
)
29+
30+
func Test_resolveReleaseMarker(t *testing.T) {
31+
32+
clientGoproxy, muxGoproxy, teardownGoproxy := newFakeGoproxy()
33+
defer teardownGoproxy()
34+
35+
// setup an handler with fake releases
36+
muxGoproxy.HandleFunc("/github.com/o/r1/@v/list", func(w http.ResponseWriter, r *http.Request) {
37+
testMethod(t, r, "GET")
38+
fmt.Fprint(w, "v1.2.0\n")
39+
fmt.Fprint(w, "v1.2.1-rc.0\n")
40+
41+
})
42+
tests := []struct {
43+
name string
44+
releaseMarker string
45+
want string
46+
wantErr bool
47+
}{
48+
{
49+
"Invalid url",
50+
"github.com/o/doesntexist",
51+
"",
52+
true,
53+
},
54+
{
55+
"Get stable release",
56+
//"go://sigs.k8s.io/[email protected]",
57+
"go://github.com/o/[email protected]",
58+
"1.2.0",
59+
false,
60+
},
61+
{
62+
"Get latest release",
63+
//"go://sigs.k8s.io/[email protected]",
64+
"go://github.com/o/[email protected]",
65+
"1.2.1-rc.0",
66+
false,
67+
},
68+
}
69+
70+
for _, tt := range tests {
71+
t.Run(tt.name, func(t *testing.T) {
72+
g := NewWithT(t)
73+
74+
got, err := resolveReleaseMarker(tt.releaseMarker, clientGoproxy)
75+
if tt.wantErr {
76+
g.Expect(err).To(HaveOccurred())
77+
return
78+
}
79+
g.Expect(err).ToNot(HaveOccurred())
80+
81+
g.Expect(got).To(BeEquivalentTo(tt.want))
82+
})
83+
}
84+
85+
}
86+
87+
func testMethod(t *testing.T, r *http.Request, want string) {
88+
t.Helper()
89+
90+
if got := r.Method; got != want {
91+
t.Errorf("Request method: %v, want %v", got, want)
92+
}
93+
}
94+
95+
// newFakeGoproxy sets up a test HTTP server along with a github.Client that is
96+
// configured to talk to that test server. Tests should register handlers on
97+
// mux which provide mock responses for the API method being tested.
98+
func newFakeGoproxy() (client *goproxy.Client, mux *http.ServeMux, teardown func()) {
99+
// mux is the HTTP request multiplexer used with the test server.
100+
mux = http.NewServeMux()
101+
102+
apiHandler := http.NewServeMux()
103+
apiHandler.Handle("/", mux)
104+
105+
// server is a test HTTP server used to provide mock API responses.
106+
server := httptest.NewServer(apiHandler)
107+
108+
// client is the GitHub client being tested and is configured to use test server.
109+
url, _ := url.Parse(server.URL + "/")
110+
return goproxy.NewClient(url.Scheme, url.Host), mux, server.Close
111+
}

0 commit comments

Comments
 (0)