From 61246fc7dcb832d85d8b1f7e45e90d7a010eac5c Mon Sep 17 00:00:00 2001 From: ckannon Date: Thu, 21 Apr 2022 16:26:47 -0400 Subject: [PATCH 1/2] Gets latest valid tag for each image --- pkg/minikube/bootstrapper/images/images.go | 59 ++++++++++++++++++- .../bootstrapper/images/images_test.go | 36 +++++++++++ 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/pkg/minikube/bootstrapper/images/images.go b/pkg/minikube/bootstrapper/images/images.go index 5000478df59e..f92d112e8fc4 100644 --- a/pkg/minikube/bootstrapper/images/images.go +++ b/pkg/minikube/bootstrapper/images/images.go @@ -18,9 +18,14 @@ limitations under the License. package images import ( + "encoding/json" "fmt" + "io" + "net/http" "path" + "k8s.io/klog/v2" + "k8s.io/minikube/pkg/minikube/constants" "github.com/blang/semver/v4" @@ -28,6 +33,11 @@ import ( "k8s.io/minikube/pkg/version" ) +const ( + // builds a docker v2 repository API call in the format https://k8s.gcr.io/v2/coredns/coredns/tags/list + tagURLTemplate = "https://%s/v2/%s/tags/list" +) + // Pause returns the image name to pull for a given Kubernetes version func Pause(v semver.Version, mirror string) string { // Note: changing this logic requires bumping the preload version @@ -35,8 +45,10 @@ func Pause(v semver.Version, mirror string) string { // https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/constants/constants.go // https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/constants/constants_unix.go pv := "3.6" - majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor) imageName := "pause" + pv = findLatestTagFromRepository(fmt.Sprintf(tagURLTemplate, kubernetesRepo(mirror), imageName), pv) + majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor) + if pVersion, ok := constants.KubeadmImages[majorMinorVersion][imageName]; ok { pv = pVersion } @@ -64,14 +76,54 @@ func componentImage(name string, v semver.Version, mirror string) string { return fmt.Sprintf("%s:v%s", path.Join(kubernetesRepo(mirror), name), v) } +// fixes 13136 by getting the latest image version from the k8s.gcr.io repository instead of hardcoded +func findLatestTagFromRepository(url string, lastKnownGood string) string { + client := &http.Client{} + req, err := http.NewRequest("GET", url, nil) + + if err != nil { + klog.Infof("Failed to get latest image version for %s, reverting to version %s\n", url, lastKnownGood) + return lastKnownGood + } + + resp, err := client.Do(req) + + if err != nil || resp.StatusCode != http.StatusOK { + klog.Infof("Failed to get latest image version for %s, reverting to version %s\n", url, lastKnownGood) + return lastKnownGood + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + klog.Infof("Failed to read latest image version for %s, reverting to version %s\n", url, lastKnownGood) + return lastKnownGood + } + + type TagsResponse struct { + Name string `json:"name"` + Tags []string `json:"tags"` + } + + tags := TagsResponse{} + err = json.Unmarshal(body, &tags) + if err != nil || len(tags.Tags) < 1 { + klog.Infof("Failed to read latest image version for %s, reverting to version %s\n", url, lastKnownGood) + return lastKnownGood + } + lastTagNum := len(tags.Tags) - 1 + return tags.Tags[lastTagNum] +} + // coreDNS returns the images used for CoreDNS func coreDNS(v semver.Version, mirror string) string { // Note: changing this logic requires bumping the preload version // Should match `CoreDNSImageName` and `CoreDNSVersion` in // https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/constants/constants.go - cv := "1.8.4" in := "coredns/coredns" + cv := "v1.8.6" + cv = findLatestTagFromRepository(fmt.Sprintf(tagURLTemplate, kubernetesRepo(mirror), in), cv) if semver.MustParseRange("<1.21.0-alpha.1")(v) { in = "coredns" } @@ -90,8 +142,9 @@ func etcd(v semver.Version, mirror string) string { // Should match `DefaultEtcdVersion` in: // https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/constants/constants.go ev := "3.5.0-0" - majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor) imageName := "etcd" + ev = findLatestTagFromRepository(fmt.Sprintf(tagURLTemplate, kubernetesRepo(mirror), ev), ev) + majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor) if eVersion, ok := constants.KubeadmImages[majorMinorVersion][imageName]; ok { ev = eVersion } diff --git a/pkg/minikube/bootstrapper/images/images_test.go b/pkg/minikube/bootstrapper/images/images_test.go index 2d2280016f5a..65fac2e26e92 100644 --- a/pkg/minikube/bootstrapper/images/images_test.go +++ b/pkg/minikube/bootstrapper/images/images_test.go @@ -17,6 +17,8 @@ limitations under the License. package images import ( + "net/http" + "net/http/httptest" "strings" "testing" @@ -91,6 +93,40 @@ k8s.gcr.io/coredns/coredns:v1.8.4 } } +func TestGetLatestTag(t *testing.T) { + serverResp := "{tags: [\"1.8.7\"]}" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(serverResp)) + if err != nil { + t.Errorf("failed to write https response") + } + })) + defer server.Close() + + var testCases = []struct { + name string + url string + lastKnownGood string + wsResponse string + expect string + }{ + {name: "VersionGetSuccess", url: server.URL, lastKnownGood: "v1.8.6", wsResponse: `{"name": "coredns", "tags": ["v1.8.9"]}`, expect: "v1.8.9"}, + {name: "VersionGetFail", url: server.URL, lastKnownGood: "v1.8.6", wsResponse: `{"name": "nah", "nope": ["v1.8.9"]}`, expect: "v1.8.6"}, + {name: "VersionGetFailNone", url: server.URL, lastKnownGood: "v1.8.6", wsResponse: ``, expect: "v1.8.6"}, + {name: "VersionGetSuccessMultiple", url: server.URL, lastKnownGood: "v1.8.6", wsResponse: `{"name": "coredns", "tags": ["1.8.7","v1.8.9"]}`, expect: "v1.8.9"}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + serverResp = tc.wsResponse + resp := findLatestTagFromRepository(tc.url, tc.lastKnownGood) + if diff := cmp.Diff(tc.expect, resp); diff != "" { + t.Errorf("Incorrect response version (-want +got):\n%s", diff) + } + }) + } +} + func TestAuxiliary(t *testing.T) { want := []string{ "gcr.io/k8s-minikube/storage-provisioner:" + version.GetStorageProvisionerVersion(), From 067563ef4058c5b215579086dc409d87e6cad276 Mon Sep 17 00:00:00 2001 From: ckannon Date: Mon, 9 May 2022 11:12:11 -0400 Subject: [PATCH 2/2] Fixed PR comments --- pkg/minikube/bootstrapper/images/images.go | 32 ++++++++++------------ 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/pkg/minikube/bootstrapper/images/images.go b/pkg/minikube/bootstrapper/images/images.go index f92d112e8fc4..60d2e68d6b09 100644 --- a/pkg/minikube/bootstrapper/images/images.go +++ b/pkg/minikube/bootstrapper/images/images.go @@ -46,11 +46,12 @@ func Pause(v semver.Version, mirror string) string { // https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/constants/constants_unix.go pv := "3.6" imageName := "pause" - pv = findLatestTagFromRepository(fmt.Sprintf(tagURLTemplate, kubernetesRepo(mirror), imageName), pv) majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor) if pVersion, ok := constants.KubeadmImages[majorMinorVersion][imageName]; ok { pv = pVersion + } else { + pv = findLatestTagFromRepository(fmt.Sprintf(tagURLTemplate, kubernetesRepo(mirror), imageName), pv) } return fmt.Sprintf("%s:%s", path.Join(kubernetesRepo(mirror), imageName), pv) @@ -79,24 +80,19 @@ func componentImage(name string, v semver.Version, mirror string) string { // fixes 13136 by getting the latest image version from the k8s.gcr.io repository instead of hardcoded func findLatestTagFromRepository(url string, lastKnownGood string) string { client := &http.Client{} - req, err := http.NewRequest("GET", url, nil) + errorMsg := fmt.Sprintf("Failed to get latest image version for %s, reverting to version %s.", url, lastKnownGood) - if err != nil { - klog.Infof("Failed to get latest image version for %s, reverting to version %s\n", url, lastKnownGood) - return lastKnownGood - } - - resp, err := client.Do(req) + resp, err := client.Get(url) if err != nil || resp.StatusCode != http.StatusOK { - klog.Infof("Failed to get latest image version for %s, reverting to version %s\n", url, lastKnownGood) + klog.Warningf("%s Error %v", errorMsg, err) return lastKnownGood } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - klog.Infof("Failed to read latest image version for %s, reverting to version %s\n", url, lastKnownGood) + klog.Warningf("%s Error %v", errorMsg, err) return lastKnownGood } @@ -108,7 +104,7 @@ func findLatestTagFromRepository(url string, lastKnownGood string) string { tags := TagsResponse{} err = json.Unmarshal(body, &tags) if err != nil || len(tags.Tags) < 1 { - klog.Infof("Failed to read latest image version for %s, reverting to version %s\n", url, lastKnownGood) + klog.Warningf("%s Error %v", errorMsg, err) return lastKnownGood } lastTagNum := len(tags.Tags) - 1 @@ -121,19 +117,20 @@ func coreDNS(v semver.Version, mirror string) string { // Should match `CoreDNSImageName` and `CoreDNSVersion` in // https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/constants/constants.go - in := "coredns/coredns" + imageName := "coredns/coredns" cv := "v1.8.6" - cv = findLatestTagFromRepository(fmt.Sprintf(tagURLTemplate, kubernetesRepo(mirror), in), cv) if semver.MustParseRange("<1.21.0-alpha.1")(v) { - in = "coredns" + imageName = "coredns" } majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor) - if cVersion, ok := constants.KubeadmImages[majorMinorVersion][in]; ok { + if cVersion, ok := constants.KubeadmImages[majorMinorVersion][imageName]; ok { cv = cVersion + } else { + cv = findLatestTagFromRepository(fmt.Sprintf(tagURLTemplate, kubernetesRepo(mirror), imageName), cv) } - return fmt.Sprintf("%s:%s", path.Join(kubernetesRepo(mirror), in), cv) + return fmt.Sprintf("%s:%s", path.Join(kubernetesRepo(mirror), imageName), cv) } // etcd returns the image used for etcd @@ -143,10 +140,11 @@ func etcd(v semver.Version, mirror string) string { // https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/constants/constants.go ev := "3.5.0-0" imageName := "etcd" - ev = findLatestTagFromRepository(fmt.Sprintf(tagURLTemplate, kubernetesRepo(mirror), ev), ev) majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor) if eVersion, ok := constants.KubeadmImages[majorMinorVersion][imageName]; ok { ev = eVersion + } else { + ev = findLatestTagFromRepository(fmt.Sprintf(tagURLTemplate, kubernetesRepo(mirror), imageName), ev) } return fmt.Sprintf("%s:%s", path.Join(kubernetesRepo(mirror), imageName), ev)