Merge pull request #14006 from ckannon/fixes/13136

Get latest valid tag for each image during cacheing
pull/14154/head
Steven Powell 2022-05-12 09:43:05 -07:00 committed by GitHub
commit 958819cba0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 94 additions and 7 deletions

View File

@ -18,9 +18,14 @@ limitations under the License.
package images package images
import ( import (
"encoding/json"
"fmt" "fmt"
"io"
"net/http"
"path" "path"
"k8s.io/klog/v2"
"k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/constants"
"github.com/blang/semver/v4" "github.com/blang/semver/v4"
@ -28,6 +33,11 @@ import (
"k8s.io/minikube/pkg/version" "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 // Pause returns the image name to pull for a given Kubernetes version
func Pause(v semver.Version, mirror string) string { func Pause(v semver.Version, mirror string) string {
// Note: changing this logic requires bumping the preload version // Note: changing this logic requires bumping the preload version
@ -35,10 +45,13 @@ 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.go
// https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/constants/constants_unix.go // https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/constants/constants_unix.go
pv := "3.6" pv := "3.6"
majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor)
imageName := "pause" imageName := "pause"
majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor)
if pVersion, ok := constants.KubeadmImages[majorMinorVersion][imageName]; ok { if pVersion, ok := constants.KubeadmImages[majorMinorVersion][imageName]; ok {
pv = pVersion pv = pVersion
} else {
pv = findLatestTagFromRepository(fmt.Sprintf(tagURLTemplate, kubernetesRepo(mirror), imageName), pv)
} }
return fmt.Sprintf("%s:%s", path.Join(kubernetesRepo(mirror), imageName), pv) return fmt.Sprintf("%s:%s", path.Join(kubernetesRepo(mirror), imageName), pv)
@ -64,24 +77,60 @@ func componentImage(name string, v semver.Version, mirror string) string {
return fmt.Sprintf("%s:v%s", path.Join(kubernetesRepo(mirror), name), v) 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{}
errorMsg := fmt.Sprintf("Failed to get latest image version for %s, reverting to version %s.", url, lastKnownGood)
resp, err := client.Get(url)
if err != nil || resp.StatusCode != http.StatusOK {
klog.Warningf("%s Error %v", errorMsg, err)
return lastKnownGood
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
klog.Warningf("%s Error %v", errorMsg, err)
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.Warningf("%s Error %v", errorMsg, err)
return lastKnownGood
}
lastTagNum := len(tags.Tags) - 1
return tags.Tags[lastTagNum]
}
// coreDNS returns the images used for CoreDNS // coreDNS returns the images used for CoreDNS
func coreDNS(v semver.Version, mirror string) string { func coreDNS(v semver.Version, mirror string) string {
// Note: changing this logic requires bumping the preload version // Note: changing this logic requires bumping the preload version
// Should match `CoreDNSImageName` and `CoreDNSVersion` in // Should match `CoreDNSImageName` and `CoreDNSVersion` in
// 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.go
cv := "1.8.4" imageName := "coredns/coredns"
in := "coredns/coredns" cv := "v1.8.6"
if semver.MustParseRange("<1.21.0-alpha.1")(v) { if semver.MustParseRange("<1.21.0-alpha.1")(v) {
in = "coredns" imageName = "coredns"
} }
majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor) 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 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 // etcd returns the image used for etcd
@ -90,10 +139,12 @@ func etcd(v semver.Version, mirror string) string {
// Should match `DefaultEtcdVersion` in: // Should match `DefaultEtcdVersion` in:
// 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.go
ev := "3.5.0-0" ev := "3.5.0-0"
majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor)
imageName := "etcd" imageName := "etcd"
majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor)
if eVersion, ok := constants.KubeadmImages[majorMinorVersion][imageName]; ok { if eVersion, ok := constants.KubeadmImages[majorMinorVersion][imageName]; ok {
ev = eVersion ev = eVersion
} else {
ev = findLatestTagFromRepository(fmt.Sprintf(tagURLTemplate, kubernetesRepo(mirror), imageName), ev)
} }
return fmt.Sprintf("%s:%s", path.Join(kubernetesRepo(mirror), imageName), ev) return fmt.Sprintf("%s:%s", path.Join(kubernetesRepo(mirror), imageName), ev)

View File

@ -17,6 +17,8 @@ limitations under the License.
package images package images
import ( import (
"net/http"
"net/http/httptest"
"strings" "strings"
"testing" "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) { func TestAuxiliary(t *testing.T) {
want := []string{ want := []string{
"gcr.io/k8s-minikube/storage-provisioner:" + version.GetStorageProvisionerVersion(), "gcr.io/k8s-minikube/storage-provisioner:" + version.GetStorageProvisionerVersion(),