Skip to content

Commit 0c4144d

Browse files
committed
envtest: search the assets index for latest of a release series
Signed-off-by: Chris Bandy <[email protected]>
1 parent 95c76a7 commit 0c4144d

File tree

2 files changed

+164
-14
lines changed

2 files changed

+164
-14
lines changed

pkg/envtest/binaries.go

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import (
3232
"path"
3333
"path/filepath"
3434
"runtime"
35-
"sort"
3635
"strings"
3736

3837
"k8s.io/apimachinery/pkg/util/version"
@@ -111,6 +110,25 @@ type archive struct {
111110
SelfLink string `json:"selfLink"`
112111
}
113112

113+
// interpretKubernetesVersion returns:
114+
// 1. the SemVer form of s when it refers to a specific Kubernetes release, or
115+
// 2. the major and minor portions of s when it refers to a release series, or
116+
// 3. zero values
117+
func interpretKubernetesVersion(s string) (exact string, major, minor uint) {
118+
if v, err := version.ParseSemantic(s); err == nil {
119+
return v.String(), 0, 0
120+
}
121+
122+
// See two parseable components and nothing else.
123+
if v, err := version.ParseGeneric(s); err == nil && len(v.Components()) == 2 {
124+
if v.String() == strings.TrimPrefix(s, "v") {
125+
return "", v.Major(), v.Minor()
126+
}
127+
}
128+
129+
return "", 0, 0
130+
}
131+
114132
func downloadBinaryAssets(ctx context.Context, binaryAssetsDirectory, binaryAssetsVersion, binaryAssetsIndexURL string) (string, string, string, error) {
115133
if binaryAssetsIndexURL == "" {
116134
binaryAssetsIndexURL = DefaultBinaryAssetsIndexURL
@@ -124,15 +142,22 @@ func downloadBinaryAssets(ctx context.Context, binaryAssetsDirectory, binaryAsse
124142
}
125143
}
126144

145+
exact, major, minor := interpretKubernetesVersion(binaryAssetsVersion)
146+
127147
var binaryAssetsIndex *index
128-
if binaryAssetsVersion == "" {
148+
if binaryAssetsVersion != "" && exact != "" {
149+
// Look for these specific binaries locally before downloading them from the release index.
150+
// Use the canonical form of the version from here on.
151+
binaryAssetsVersion = exact
152+
} else if binaryAssetsVersion == "" || major != 0 || minor != 0 {
153+
// Select a stable version from the release index before continuing.
129154
var err error
130155
binaryAssetsIndex, err = getIndex(ctx, binaryAssetsIndexURL)
131156
if err != nil {
132157
return "", "", "", err
133158
}
134159

135-
binaryAssetsVersion, err = latestStableVersionFromIndex(binaryAssetsIndex)
160+
binaryAssetsVersion, err = latestStableVersionFromIndex(binaryAssetsIndex, major, minor)
136161
if err != nil {
137162
return "", "", "", err
138163
}
@@ -219,6 +244,8 @@ func fileExists(path string) bool {
219244
}
220245

221246
func downloadBinaryAssetsArchive(ctx context.Context, index *index, version string, out io.Writer) error {
247+
version = "v" + strings.TrimPrefix(version, "v")
248+
222249
archives, ok := index.Releases[version]
223250
if !ok {
224251
return fmt.Errorf("failed to find envtest binaries for version %s", version)
@@ -252,12 +279,20 @@ func downloadBinaryAssetsArchive(ctx context.Context, index *index, version stri
252279
return readBody(resp, out, archiveName, archive.Hash)
253280
}
254281

255-
func latestStableVersionFromIndex(index *index) (string, error) {
282+
func latestStableVersionFromIndex(index *index, major, minor uint) (string, error) {
256283
if len(index.Releases) == 0 {
257284
return "", fmt.Errorf("failed to find latest stable version from index: index is empty")
258285
}
259286

260-
parsedVersions := []*version.Version{}
287+
var found, lower, upper *version.Version
288+
search := "any"
289+
290+
if major != 0 || minor != 0 {
291+
lower = version.MajorMinor(major, minor)
292+
upper = version.MajorMinor(major, minor+1)
293+
search = lower.String()
294+
}
295+
261296
for releaseVersion := range index.Releases {
262297
v, err := version.ParseSemantic(releaseVersion)
263298
if err != nil {
@@ -269,17 +304,24 @@ func latestStableVersionFromIndex(index *index) (string, error) {
269304
continue
270305
}
271306

272-
parsedVersions = append(parsedVersions, v)
307+
// Filter on release series, if any.
308+
if lower != nil && v.LessThan(lower) {
309+
continue
310+
}
311+
if upper != nil && !v.LessThan(upper) {
312+
continue
313+
}
314+
315+
if found == nil || v.GreaterThan(found) {
316+
found = v
317+
}
273318
}
274319

275-
if len(parsedVersions) == 0 {
276-
return "", fmt.Errorf("failed to find latest stable version from index: index does not have stable versions")
320+
if found == nil {
321+
return "", fmt.Errorf("failed to find latest stable version from index: index does not have %s stable versions", search)
277322
}
278323

279-
sort.Slice(parsedVersions, func(i, j int) bool {
280-
return parsedVersions[i].GreaterThan(parsedVersions[j])
281-
})
282-
return "v" + parsedVersions[0].String(), nil
324+
return "v" + found.String(), nil
283325
}
284326

285327
func getIndex(ctx context.Context, indexURL string) (*index, error) {

pkg/envtest/binaries_test.go

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,93 @@ import (
2828
"os"
2929
"path"
3030
"runtime"
31+
"strings"
32+
"testing"
3133

3234
. "github.com/onsi/ginkgo/v2"
3335
. "github.com/onsi/gomega"
3436
"github.com/onsi/gomega/ghttp"
3537
"sigs.k8s.io/yaml"
3638
)
3739

40+
func TestInterpretKubernetesVersion(t *testing.T) {
41+
t.Parallel()
42+
43+
testCases := []struct {
44+
name string
45+
in []string
46+
exact bool
47+
major uint
48+
minor uint
49+
}{
50+
{
51+
name: `SemVer and "v" prefix are exact`,
52+
exact: true,
53+
in: []string{
54+
"1.2.3", "v1.2.3", "v1.30.2", "v1.31.0-beta.0", "v1.33.0-alpha.2",
55+
},
56+
},
57+
{
58+
name: "leading zeroes are not a version",
59+
in: []string{
60+
"01.2.0", "00001.2.3", "1.2.03", "v01.02.0003",
61+
},
62+
},
63+
{
64+
name: "weird stuff is not a version",
65+
in: []string{
66+
"asdf", "version", "vegeta4", "the.1", "2ne1", "=7.8.9", "10.x", "*",
67+
"0.0001", "1.00002", "v1.2anything", "1.2.x", "1.2.z", "1.2.*",
68+
},
69+
},
70+
{
71+
name: "one number is not a version",
72+
in: []string{
73+
"1", "v1", "v001", "1.", "v1.", "1.x",
74+
},
75+
},
76+
{
77+
name: "two numbers are a release series",
78+
major: 0, minor: 1,
79+
in: []string{"0.1", "v0.1"},
80+
},
81+
{
82+
name: "two numbers are a release series",
83+
major: 1, minor: 2,
84+
in: []string{"1.2", "v1.2"},
85+
},
86+
}
87+
88+
for _, tc := range testCases {
89+
t.Run(tc.name, func(t *testing.T) {
90+
for _, input := range tc.in {
91+
exact, major, minor := interpretKubernetesVersion(input)
92+
93+
if tc.exact {
94+
if expected := strings.TrimPrefix(input, "v"); exact != expected {
95+
t.Errorf("expected canonical %q for %q, got %q", expected, input, exact)
96+
}
97+
if major != 0 || minor != 0 {
98+
t.Errorf("expected no release series for %q, got (%v, %v)", input, major, minor)
99+
}
100+
}
101+
102+
if !tc.exact {
103+
if major != tc.major {
104+
t.Errorf("expected major %v for %q, got %v", tc.major, input, major)
105+
}
106+
if minor != tc.minor {
107+
t.Errorf("expected minor %v for %q, got %v", tc.minor, input, minor)
108+
}
109+
if exact != "" {
110+
t.Errorf("expected no canonical version for %q, got %q", input, exact)
111+
}
112+
}
113+
}
114+
})
115+
}
116+
}
117+
38118
var _ = Describe("Test download binaries", func() {
39119
var downloadDirectory string
40120
var server *ghttp.Server
@@ -68,11 +148,11 @@ var _ = Describe("Test download binaries", func() {
68148
Expect(actualFiles).To(ConsistOf("some-file"))
69149
})
70150

71-
It("should download v1.32.0 binaries", func(ctx SpecContext) {
151+
It("should download binaries of an exact version", func(ctx SpecContext) {
72152
apiServerPath, etcdPath, kubectlPath, err := downloadBinaryAssets(ctx, downloadDirectory, "v1.31.0", fmt.Sprintf("http://%s/%s", server.Addr(), "envtest-releases.yaml"))
73153
Expect(err).ToNot(HaveOccurred())
74154

75-
// Verify latest stable version (v1.32.0) was downloaded
155+
// Verify exact version (v1.31.0) was downloaded
76156
versionDownloadDirectory := path.Join(downloadDirectory, fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH))
77157
Expect(apiServerPath).To(Equal(path.Join(versionDownloadDirectory, "kube-apiserver")))
78158
Expect(etcdPath).To(Equal(path.Join(versionDownloadDirectory, "etcd")))
@@ -86,6 +166,25 @@ var _ = Describe("Test download binaries", func() {
86166
}
87167
Expect(actualFiles).To(ConsistOf("some-file"))
88168
})
169+
170+
It("should download binaries of latest stable version of a release series", func(ctx SpecContext) {
171+
apiServerPath, etcdPath, kubectlPath, err := downloadBinaryAssets(ctx, downloadDirectory, "1.31", fmt.Sprintf("http://%s/%s", server.Addr(), "envtest-releases.yaml"))
172+
Expect(err).ToNot(HaveOccurred())
173+
174+
// Verify stable version (v1.31.4) was downloaded
175+
versionDownloadDirectory := path.Join(downloadDirectory, fmt.Sprintf("1.31.4-%s-%s", runtime.GOOS, runtime.GOARCH))
176+
Expect(apiServerPath).To(Equal(path.Join(versionDownloadDirectory, "kube-apiserver")))
177+
Expect(etcdPath).To(Equal(path.Join(versionDownloadDirectory, "etcd")))
178+
Expect(kubectlPath).To(Equal(path.Join(versionDownloadDirectory, "kubectl")))
179+
180+
dirEntries, err := os.ReadDir(versionDownloadDirectory)
181+
Expect(err).ToNot(HaveOccurred())
182+
var actualFiles []string
183+
for _, e := range dirEntries {
184+
actualFiles = append(actualFiles, e.Name())
185+
}
186+
Expect(actualFiles).To(ConsistOf("some-file"))
187+
})
89188
})
90189

91190
var (
@@ -100,6 +199,15 @@ var (
100199
"envtest-v1.32.0-linux-s390x.tar.gz": {},
101200
"envtest-v1.32.0-windows-amd64.tar.gz": {},
102201
},
202+
"v1.31.4": map[string]archive{
203+
"envtest-v1.31.4-darwin-amd64.tar.gz": {},
204+
"envtest-v1.31.4-darwin-arm64.tar.gz": {},
205+
"envtest-v1.31.4-linux-amd64.tar.gz": {},
206+
"envtest-v1.31.4-linux-arm64.tar.gz": {},
207+
"envtest-v1.31.4-linux-ppc64le.tar.gz": {},
208+
"envtest-v1.31.4-linux-s390x.tar.gz": {},
209+
"envtest-v1.31.4-windows-amd64.tar.gz": {},
210+
},
103211
"v1.31.0": map[string]archive{
104212
"envtest-v1.31.0-darwin-amd64.tar.gz": {},
105213
"envtest-v1.31.0-darwin-arm64.tar.gz": {},

0 commit comments

Comments
 (0)