Skip to content

Commit 0cb503c

Browse files
authored
Merge pull request kubernetes-sigs#10220 from Fedosin/fix_latest_release
🐛 Verify that there is a release for the tag
2 parents c88e464 + 1c2aa6c commit 0cb503c

File tree

3 files changed

+247
-27
lines changed

3 files changed

+247
-27
lines changed

cmd/clusterctl/client/repository/repository_github_test.go

Lines changed: 199 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"net/http"
23+
"strings"
2324
"testing"
2425
"time"
2526

@@ -428,6 +429,11 @@ func Test_gitHubRepository_getLatestContractRelease(t *testing.T) {
428429
fmt.Fprint(w, `{"id":13, "tag_name": "v0.5.0", "assets": [{"id": 1, "name": "metadata.yaml"}] }`)
429430
})
430431

432+
mux.HandleFunc("/repos/o/r1/releases/tags/v0.3.2", func(w http.ResponseWriter, r *http.Request) {
433+
goproxytest.HTTPTestMethod(t, r, "GET")
434+
fmt.Fprint(w, `{"id":14, "tag_name": "v0.3.2", "assets": [{"id": 2, "name": "metadata.yaml"}] }`)
435+
})
436+
431437
// Setup a handler for returning a fake release metadata file.
432438
mux.HandleFunc("/repos/o/r1/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
433439
goproxytest.HTTPTestMethod(t, r, "GET")
@@ -436,6 +442,13 @@ func Test_gitHubRepository_getLatestContractRelease(t *testing.T) {
436442
fmt.Fprint(w, "apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3\nreleaseSeries:\n - major: 0\n minor: 4\n contract: v1alpha4\n - major: 0\n minor: 5\n contract: v1alpha4\n - major: 0\n minor: 3\n contract: v1alpha3\n")
437443
})
438444

445+
mux.HandleFunc("/repos/o/r1/releases/assets/2", func(w http.ResponseWriter, r *http.Request) {
446+
goproxytest.HTTPTestMethod(t, r, "GET")
447+
w.Header().Set("Content-Type", "application/octet-stream")
448+
w.Header().Set("Content-Disposition", "attachment; filename=metadata.yaml")
449+
fmt.Fprint(w, "apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3\nreleaseSeries:\n - major: 0\n minor: 4\n contract: v1alpha4\n - major: 0\n minor: 5\n contract: v1alpha4\n - major: 0\n minor: 3\n contract: v1alpha3\n")
450+
})
451+
439452
scheme, host, muxGoproxy, teardownGoproxy := goproxytest.NewFakeGoproxy()
440453
clientGoproxy := goproxy.NewClient(scheme, host)
441454

@@ -534,6 +547,9 @@ func Test_gitHubRepository_getLatestRelease(t *testing.T) {
534547
clientGoproxy := goproxy.NewClient(scheme, host)
535548
defer teardownGoproxy()
536549

550+
client, mux, teardown := test.NewFakeGitHub()
551+
defer teardown()
552+
537553
// Setup a handler for returning 4 fake releases.
538554
muxGoproxy.HandleFunc("/github.com/o/r1/@v/list", func(w http.ResponseWriter, r *http.Request) {
539555
goproxytest.HTTPTestMethod(t, r, "GET")
@@ -543,9 +559,13 @@ func Test_gitHubRepository_getLatestRelease(t *testing.T) {
543559
fmt.Fprint(w, "foo\n") // no semantic version tag
544560
})
545561
// And also expose a release for them
546-
muxGoproxy.HandleFunc("/api.github.com/repos/o/r1/releases/tags/v0.4.2", func(w http.ResponseWriter, r *http.Request) {
562+
mux.HandleFunc("/repos/o/r1/releases/tags/v0.4.2", func(w http.ResponseWriter, r *http.Request) {
547563
goproxytest.HTTPTestMethod(t, r, "GET")
548-
fmt.Fprint(w, "{}\n")
564+
fmt.Fprint(w, `{"id":13, "tag_name": "v0.4.2", "assets": [{"id": 1, "name": "metadata.yaml"}] }`)
565+
})
566+
mux.HandleFunc("/repos/o/r3/releases/tags/v0.1.0-alpha.2", func(w http.ResponseWriter, r *http.Request) {
567+
goproxytest.HTTPTestMethod(t, r, "GET")
568+
fmt.Fprint(w, `{"id":14, "tag_name": "v0.1.0-alpha.2", "assets": [{"id": 2, "name": "metadata.yaml"}] }`)
549569
})
550570

551571
// Setup a handler for returning no releases.
@@ -562,6 +582,21 @@ func Test_gitHubRepository_getLatestRelease(t *testing.T) {
562582
fmt.Fprint(w, "v0.1.0-alpha.2\n")
563583
})
564584

585+
// Setup a handler for returning a fake release metadata file.
586+
mux.HandleFunc("/repos/o/r1/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
587+
goproxytest.HTTPTestMethod(t, r, "GET")
588+
w.Header().Set("Content-Type", "application/octet-stream")
589+
w.Header().Set("Content-Disposition", "attachment; filename=metadata.yaml")
590+
fmt.Fprint(w, "apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3\nreleaseSeries:\n - major: 0\n minor: 4\n contract: v1alpha4\n - major: 0\n minor: 5\n contract: v1alpha4\n - major: 0\n minor: 3\n contract: v1alpha3\n")
591+
})
592+
593+
mux.HandleFunc("/repos/o/r3/releases/assets/2", func(w http.ResponseWriter, r *http.Request) {
594+
goproxytest.HTTPTestMethod(t, r, "GET")
595+
w.Header().Set("Content-Type", "application/octet-stream")
596+
w.Header().Set("Content-Disposition", "attachment; filename=metadata.yaml")
597+
fmt.Fprint(w, "apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3\nreleaseSeries:\n - major: 0\n minor: 4\n contract: v1alpha4\n - major: 0\n minor: 5\n contract: v1alpha4\n - major: 0\n minor: 3\n contract: v1alpha3\n")
598+
})
599+
565600
configVariablesClient := test.NewFakeVariableClient()
566601

567602
type field struct {
@@ -606,7 +641,7 @@ func Test_gitHubRepository_getLatestRelease(t *testing.T) {
606641

607642
resetCaches()
608643

609-
gRepo, err := NewGitHubRepository(ctx, tt.field.providerConfig, configVariablesClient, injectGoproxyClient(clientGoproxy))
644+
gRepo, err := NewGitHubRepository(ctx, tt.field.providerConfig, configVariablesClient, injectGoproxyClient(clientGoproxy), injectGithubClient(client))
610645
g.Expect(err).ToNot(HaveOccurred())
611646

612647
got, err := latestRelease(ctx, gRepo)
@@ -628,6 +663,9 @@ func Test_gitHubRepository_getLatestPatchRelease(t *testing.T) {
628663
clientGoproxy := goproxy.NewClient(scheme, host)
629664
defer teardownGoproxy()
630665

666+
client, mux, teardown := test.NewFakeGitHub()
667+
defer teardown()
668+
631669
// Setup a handler for returning 4 fake releases.
632670
muxGoproxy.HandleFunc("/github.com/o/r1/@v/list", func(w http.ResponseWriter, r *http.Request) {
633671
goproxytest.HTTPTestMethod(t, r, "GET")
@@ -636,6 +674,30 @@ func Test_gitHubRepository_getLatestPatchRelease(t *testing.T) {
636674
fmt.Fprint(w, "v1.3.2\n")
637675
})
638676

677+
// Setup a handler for returning a fake release.
678+
mux.HandleFunc("/repos/o/r1/releases/tags/v0.4.0", func(w http.ResponseWriter, r *http.Request) {
679+
goproxytest.HTTPTestMethod(t, r, "GET")
680+
fmt.Fprint(w, `{"id":13, "tag_name": "v0.4.0", "assets": [{"id": 1, "name": "metadata.yaml"}] }`)
681+
})
682+
683+
mux.HandleFunc("/repos/o/r1/releases/tags/v0.3.2", func(w http.ResponseWriter, r *http.Request) {
684+
goproxytest.HTTPTestMethod(t, r, "GET")
685+
fmt.Fprint(w, `{"id":14, "tag_name": "v0.3.2", "assets": [{"id": 1, "name": "metadata.yaml"}] }`)
686+
})
687+
688+
mux.HandleFunc("/repos/o/r1/releases/tags/v1.3.2", func(w http.ResponseWriter, r *http.Request) {
689+
goproxytest.HTTPTestMethod(t, r, "GET")
690+
fmt.Fprint(w, `{"id":15, "tag_name": "v1.3.2", "assets": [{"id": 1, "name": "metadata.yaml"}] }`)
691+
})
692+
693+
// Setup a handler for returning a fake release metadata file.
694+
mux.HandleFunc("/repos/o/r1/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
695+
goproxytest.HTTPTestMethod(t, r, "GET")
696+
w.Header().Set("Content-Type", "application/octet-stream")
697+
w.Header().Set("Content-Disposition", "attachment; filename=metadata.yaml")
698+
fmt.Fprint(w, "apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3\nreleaseSeries:\n - major: 0\n minor: 4\n contract: v1alpha4\n - major: 0\n minor: 5\n contract: v1alpha4\n - major: 0\n minor: 3\n contract: v1alpha3\n")
699+
})
700+
639701
major0 := uint(0)
640702
minor3 := uint(3)
641703
minor4 := uint(4)
@@ -692,7 +754,7 @@ func Test_gitHubRepository_getLatestPatchRelease(t *testing.T) {
692754

693755
resetCaches()
694756

695-
gRepo, err := NewGitHubRepository(ctx, tt.field.providerConfig, configVariablesClient, injectGoproxyClient(clientGoproxy))
757+
gRepo, err := NewGitHubRepository(ctx, tt.field.providerConfig, configVariablesClient, injectGoproxyClient(clientGoproxy), injectGithubClient(client))
696758
g.Expect(err).ToNot(HaveOccurred())
697759

698760
got, err := latestPatchRelease(ctx, gRepo, tt.major, tt.minor)
@@ -913,3 +975,136 @@ func resetCaches() {
913975
cacheReleases = map[string]*github.RepositoryRelease{}
914976
cacheFiles = map[string][]byte{}
915977
}
978+
979+
func Test_gitHubRepository_releaseNotFound(t *testing.T) {
980+
retryableOperationInterval = 200 * time.Millisecond
981+
retryableOperationTimeout = 1 * time.Second
982+
983+
tests := []struct {
984+
name string
985+
releaseTags []string
986+
ghReleases []string
987+
want string
988+
wantErr bool
989+
}{
990+
{
991+
name: "One release",
992+
releaseTags: []string{"v0.4.2"},
993+
ghReleases: []string{"v0.4.2"},
994+
want: "v0.4.2",
995+
wantErr: false,
996+
},
997+
{
998+
name: "Latest tag without a release",
999+
releaseTags: []string{"v0.5.0", "v0.4.2"},
1000+
ghReleases: []string{"v0.4.2"},
1001+
want: "v0.4.2",
1002+
wantErr: false,
1003+
},
1004+
{
1005+
name: "Two tags without releases",
1006+
releaseTags: []string{"v0.6.0", "v0.5.0", "v0.4.2"},
1007+
ghReleases: []string{"v0.4.2"},
1008+
want: "v0.4.2",
1009+
wantErr: false,
1010+
},
1011+
{
1012+
name: "Five tags without releases",
1013+
releaseTags: []string{"v0.9.0", "v0.8.0", "v0.7.0", "v0.6.0", "v0.5.0", "v0.4.2"},
1014+
ghReleases: []string{"v0.4.2"},
1015+
wantErr: true,
1016+
},
1017+
{
1018+
name: "Pre-releases have lower priority",
1019+
releaseTags: []string{"v0.7.0-alpha", "v0.6.0-alpha", "v0.5.0-alpha", "v0.4.2"},
1020+
ghReleases: []string{"v0.4.2"},
1021+
want: "v0.4.2",
1022+
wantErr: false,
1023+
},
1024+
{
1025+
name: "Two Github releases",
1026+
releaseTags: []string{"v0.7.0", "v0.6.0", "v0.5.0", "v0.4.2"},
1027+
ghReleases: []string{"v0.5.0", "v0.4.2"},
1028+
want: "v0.5.0",
1029+
wantErr: false,
1030+
},
1031+
{
1032+
name: "Github release and prerelease",
1033+
releaseTags: []string{"v0.6.0", "v0.5.0-alpha", "v0.4.2"},
1034+
ghReleases: []string{"v0.5.0-alpha", "v0.4.2"},
1035+
want: "v0.4.2",
1036+
wantErr: false,
1037+
},
1038+
{
1039+
name: "No Github releases",
1040+
releaseTags: []string{"v0.6.0", "v0.5.0", "v0.4.2"},
1041+
ghReleases: []string{},
1042+
wantErr: true,
1043+
},
1044+
{
1045+
name: "Pre-releases only",
1046+
releaseTags: []string{"v0.6.0-alpha", "v0.5.0-alpha", "v0.4.2-alpha"},
1047+
ghReleases: []string{"v0.5.0-alpha"},
1048+
want: "v0.5.0-alpha",
1049+
wantErr: false,
1050+
},
1051+
}
1052+
for _, tt := range tests {
1053+
t.Run(tt.name, func(t *testing.T) {
1054+
g := NewWithT(t)
1055+
1056+
ctx := context.Background()
1057+
1058+
configVariablesClient := test.NewFakeVariableClient()
1059+
1060+
resetCaches()
1061+
1062+
client, mux, teardown := test.NewFakeGitHub()
1063+
defer teardown()
1064+
1065+
providerConfig := config.NewProvider("test", "https://github.com/o/r1/releases/v0.4.1/file.yaml", clusterctlv1.CoreProviderType)
1066+
1067+
scheme, host, muxGoproxy, teardownGoproxy := goproxytest.NewFakeGoproxy()
1068+
clientGoproxy := goproxy.NewClient(scheme, host)
1069+
1070+
defer teardownGoproxy()
1071+
1072+
// First, register tags within goproxy.
1073+
muxGoproxy.HandleFunc("/github.com/o/r1/@v/list", func(w http.ResponseWriter, r *http.Request) {
1074+
goproxytest.HTTPTestMethod(t, r, "GET")
1075+
for _, release := range tt.releaseTags {
1076+
fmt.Fprint(w, release+"\n")
1077+
}
1078+
})
1079+
1080+
// Second, register releases in GitHub.
1081+
for _, release := range tt.ghReleases {
1082+
mux.HandleFunc(fmt.Sprintf("/repos/o/r1/releases/tags/%s", release), func(w http.ResponseWriter, r *http.Request) {
1083+
goproxytest.HTTPTestMethod(t, r, "GET")
1084+
parts := strings.Split(r.RequestURI, "/")
1085+
version := parts[len(parts)-1]
1086+
fmt.Fprintf(w, "{\"id\":13, \"tag_name\": %q, \"assets\": [{\"id\": 1, \"name\": \"metadata.yaml\"}] }", version)
1087+
})
1088+
}
1089+
1090+
// Third, setup a handler for returning a fake release metadata file.
1091+
mux.HandleFunc("/repos/o/r1/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
1092+
goproxytest.HTTPTestMethod(t, r, "GET")
1093+
w.Header().Set("Content-Type", "application/octet-stream")
1094+
w.Header().Set("Content-Disposition", "attachment; filename=metadata.yaml")
1095+
fmt.Fprint(w, "apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3\nreleaseSeries:\n - major: 0\n minor: 4\n contract: v1alpha4\n - major: 0\n minor: 5\n contract: v1alpha4\n - major: 0\n minor: 3\n contract: v1alpha3\n")
1096+
})
1097+
1098+
gRepo, err := NewGitHubRepository(ctx, providerConfig, configVariablesClient, injectGithubClient(client), injectGoproxyClient(clientGoproxy))
1099+
g.Expect(err).ToNot(HaveOccurred())
1100+
1101+
got, err := latestRelease(ctx, gRepo)
1102+
if tt.wantErr {
1103+
g.Expect(err).To(HaveOccurred())
1104+
return
1105+
}
1106+
g.Expect(err).ToNot(HaveOccurred())
1107+
g.Expect(got).To(Equal(tt.want))
1108+
})
1109+
}
1110+
}

cmd/clusterctl/client/repository/repository_local_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ import (
2929
"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
3030
)
3131

32+
const (
33+
metadataContents = "apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3\nreleaseSeries:\n - major: 0\n minor: 4\n contract: v1alpha4\n - major: 0\n minor: 5\n contract: v1alpha4\n - major: 0\n minor: 3\n contract: v1alpha3\n"
34+
)
35+
3236
func Test_localRepository_newLocalRepository(t *testing.T) {
3337
type fields struct {
3438
provider config.Provider
@@ -157,6 +161,7 @@ func Test_localRepository_newLocalRepository_Latest(t *testing.T) {
157161
// Create several release directories
158162
createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/v1.0.0/bootstrap-components.yaml", "foo: bar")
159163
createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/v1.0.1/bootstrap-components.yaml", "foo: bar")
164+
createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/v1.0.1/metadata.yaml", metadataContents)
160165
createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/v2.0.0-alpha.0/bootstrap-components.yaml", "foo: bar")
161166
createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/Foo.Bar/bootstrap-components.yaml", "foo: bar")
162167
createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/foo.file", "foo: bar")
@@ -185,8 +190,10 @@ func Test_localRepository_GetFile(t *testing.T) {
185190
p1 := config.NewProvider("foo", dst1, clusterctlv1.BootstrapProviderType)
186191

187192
// Provider 2: URL is for the latest release
193+
createLocalTestProviderFile(t, tmpDir, "bootstrap-baz/v1.0.0-alpha.0/metadata.yaml", metadataContents)
188194
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v1.0.0/bootstrap-components.yaml", "version: v1.0.0")
189195
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v1.0.1/bootstrap-components.yaml", "version: v1.0.1")
196+
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v1.0.1/metadata.yaml", metadataContents)
190197
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v2.0.0-alpha.0/bootstrap-components.yaml", "version: v2.0.0-alpha.0")
191198
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/Foo.Bar/bootstrap-components.yaml", "version: Foo.Bar")
192199
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/foo.file", "foo: bar")
@@ -323,6 +330,7 @@ func Test_localRepository_GetVersions(t *testing.T) {
323330
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v1.0.0/bootstrap-components.yaml", "version: v1.0.0")
324331
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v1.0.1/bootstrap-components.yaml", "version: v1.0.1")
325332
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v2.0.1/bootstrap-components.yaml", "version: v2.0.1")
333+
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v2.0.2+exp.sha.5114f85/metadata.yaml", metadataContents)
326334
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v2.0.2+exp.sha.5114f85/bootstrap-components.yaml", "version: v2.0.2+exp.sha.5114f85")
327335
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v2.0.3-alpha/bootstrap-components.yaml", "version: v2.0.3-alpha")
328336
createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/Foo.Bar/bootstrap-components.yaml", "version: Foo.Bar")

0 commit comments

Comments
 (0)