Skip to content

Commit

Permalink
Merge pull request #14006 from ckannon/fixes/13136
Browse files Browse the repository at this point in the history
Get latest valid tag for each image during cacheing
  • Loading branch information
spowelljr authored May 12, 2022
2 parents b26dcf2 + 067563e commit 958819c
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 7 deletions.
65 changes: 58 additions & 7 deletions pkg/minikube/bootstrapper/images/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,40 @@ 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"

"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
// Should match `PauseVersion` 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_unix.go
pv := "3.6"
majorMinorVersion := fmt.Sprintf("v%d.%d", v.Major, v.Minor)
imageName := "pause"
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)
Expand All @@ -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)
}

// 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
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"
imageName := "coredns/coredns"
cv := "v1.8.6"
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
Expand All @@ -90,10 +139,12 @@ 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"
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)
Expand Down
36 changes: 36 additions & 0 deletions pkg/minikube/bootstrapper/images/images_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package images

import (
"net/http"
"net/http/httptest"
"strings"
"testing"

Expand Down Expand Up @@ -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(),
Expand Down

0 comments on commit 958819c

Please sign in to comment.