initial fix on update kubeadm images automatically

pull/12084/head
srikrishnabh93@gmail.com 2021-07-27 23:41:17 +05:30
parent c31bd57f93
commit 5f46b98eae
7 changed files with 501 additions and 45 deletions

View File

@ -0,0 +1,197 @@
/*
Copyright 2021 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"bytes"
"context"
"errors"
"flag"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"strings"
"text/template"
"time"
"golang.org/x/mod/semver"
"k8s.io/klog/v2"
"k8s.io/minikube/hack/update"
"k8s.io/minikube/pkg/minikube/constants"
)
const (
// default context timeout
cxTimeout = 300 * time.Second
kubeadmReleaseURL = "https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/amd64/kubeadm"
kubeadmBinaryName = "kubeadm-linux-amd64-%s"
minikubeConstantsFilePath = "pkg/minikube/constants/constants_kubeadm_images.go"
kubeadmImagesTemplate = `
{{- range $version, $element := .}}
"{{$version}}": {
{{- range $image, $tag := $element}}
"{{$image}}": "{{$tag}}",
{{- end}}
},{{- end}}`
)
// Data contains kubeadm Images map
type Data struct {
ImageMap string `json:"ImageMap"`
}
func main() {
inputVersion := flag.Lookup("kubernetes-version").Value.String()
imageVersions := make([]string, 0)
// set a context with defined timeout
ctx, cancel := context.WithTimeout(context.Background(), cxTimeout)
defer cancel()
if inputVersion == "latest" {
stableImageVersion, latestImageVersion, _, _, err := getK8sVersions(ctx, "kubernetes", "kubernetes")
if err != nil {
klog.Fatal(err)
}
imageVersions = append(imageVersions, stableImageVersion, latestImageVersion)
} else if semver.IsValid(inputVersion) {
imageVersions = append(imageVersions, inputVersion)
} else {
klog.Fatal(errors.New("invalid version"))
}
for _, imageVersion := range imageVersions {
imageMapString, err := getKubeadmImagesMapString(imageVersion)
if err != nil {
klog.Fatalln(err)
}
data := Data{ImageMap: imageMapString}
schema := map[string]update.Item{
minikubeConstantsFilePath: {
Replace: map[string]string{},
},
}
if _, ok := constants.KubeadmImages[imageVersion]; !ok {
schema[minikubeConstantsFilePath].Replace[`KubeadmImages = .*`] =
`KubeadmImages = map[string]map[string]string{ {{.ImageMap}}`
} else {
versionIdentifier := fmt.Sprintf(`"%s": {[^}]+},`, imageVersion)
schema[minikubeConstantsFilePath].Replace[versionIdentifier] = "{{.ImageMap}}"
}
update.Apply(ctx, schema, data, "", "", -1)
}
}
func getKubeadmImagesMapString(version string) (string, error) {
url := fmt.Sprintf(kubeadmReleaseURL, version)
fileName := fmt.Sprintf(kubeadmBinaryName, version)
if err := downloadFile(url, fileName); err != nil {
return "", err
}
kubeadmCommand := fmt.Sprintf("./%s", fileName)
args := []string{"config", "images", "list"}
imageListString, err := executeCommand(kubeadmCommand, args...)
if err != nil {
return "", err
}
if err := os.Remove(fileName); err != nil {
klog.Errorf("failed to remove binary %s", fileName)
}
return formatKubeadmImageList(version, imageListString)
}
func formatKubeadmImageList(version, data string) (string, error) {
templateData := make(map[string]map[string]string)
templateData[version] = make(map[string]string)
lines := strings.Split(data, "\n")
for _, line := range lines {
imageTag := strings.Split(line, ":")
if len(imageTag) == 2 {
templateData[version][imageTag[0]] = imageTag[1]
}
}
imageTemplate := template.New("kubeadmImage")
t, err := imageTemplate.Parse(kubeadmImagesTemplate)
if err != nil {
return "", err
}
var bytesBuffer bytes.Buffer
if err := t.Execute(&bytesBuffer, &templateData); err != nil {
return "", err
}
return bytesBuffer.String(), nil
}
func downloadFile(url, fileName string) error {
file, err := os.Create(fileName)
if err != nil {
return err
}
defer file.Close()
response, err := http.Get(url)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return fmt.Errorf("non success status code, while downloading file: %s from: %s", fileName, url)
}
if _, err := io.Copy(file, response.Body); err != nil {
return err
}
return os.Chmod(fileName, os.ModePerm)
}
func executeCommand(command string, args ...string) (string, error) {
output, err := exec.Command(command, args...).Output()
if err != nil {
return "", err
}
return string(output), nil
}
// getK8sVersion returns Kubernetes versions.
func getK8sVersions(ctx context.Context, owner, repo string) (stable, latest, latestMM, latestP0 string, err error) {
// get Kubernetes versions from GitHub Releases
stable, latest, err = update.GHReleases(ctx, owner, repo)
if err != nil || !semver.IsValid(stable) || !semver.IsValid(latest) {
return "", "", "", "", err
}
latestMM = semver.MajorMinor(latest)
latestP0 = latestMM + ".0"
if semver.Compare(stable, latestP0) == -1 {
latestP0 = latest
}
return stable, latest, latestMM, latestP0, nil
}

View File

@ -62,6 +62,9 @@ func init() {
if err := flag.Set("alsologtostderr", "true"); err != nil { if err := flag.Set("alsologtostderr", "true"); err != nil {
klog.Warningf("Unable to set flag value for alsologtostderr: %v", err) klog.Warningf("Unable to set flag value for alsologtostderr: %v", err)
} }
// used in update_kubeadm_image_versions.go
flag.String("kubernetes-version", "", "kubernetes-version")
flag.Parse() flag.Parse()
defer klog.Flush() defer klog.Flush()

View File

@ -21,6 +21,8 @@ import (
"fmt" "fmt"
"path" "path"
"k8s.io/minikube/pkg/minikube/constants"
"github.com/blang/semver" "github.com/blang/semver"
"k8s.io/minikube/pkg/version" "k8s.io/minikube/pkg/version"
@ -31,10 +33,18 @@ func Pause(v semver.Version, mirror string) string {
// Should match `PauseVersion` in: // Should match `PauseVersion` in:
// 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.2" pv := "3.2"
k8sVersion := fmt.Sprintf("v1.%d.0", v.Minor)
imageName := path.Join(kubernetesRepo(mirror), "pause")
if _, ok := constants.KubeadmImages[k8sVersion]; ok {
pv = constants.KubeadmImages[k8sVersion][imageName]
}
if semver.MustParseRange("<1.18.0-alpha.0")(v) { if semver.MustParseRange("<1.18.0-alpha.0")(v) {
pv = "3.1" pv = "3.1"
} }
return path.Join(kubernetesRepo(mirror), "pause:"+pv)
return fmt.Sprintf("%s:%s", imageName, pv)
} }
// essentials returns images needed too bootstrap a Kubernetes // essentials returns images needed too bootstrap a Kubernetes
@ -60,28 +70,44 @@ func componentImage(name string, v semver.Version, mirror string) string {
func coreDNS(v semver.Version, mirror string) string { func coreDNS(v semver.Version, mirror string) string {
// Should match `CoreDNSVersion` in // Should match `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.7.0"
//switch v.Minor {
//case 22:
// cv = "1.8.0"
//case 10, 20, 21:
// cv = "1.7.0"
//case 18:
// cv = "1.6.7"
//case 17:
// cv = "1.6.5"
//case 16:
// cv = "1.6.2"
//case 15, 14:
// cv = "1.3.1"
//case 13:
// cv = "1.2.6"
//case 12:
// cv = "1.2.2"
//case 11:
// cv = "1.1.3"
//}
*/
cv := "1.7.0" cv := "1.7.0"
switch v.Minor { k8sVersion := fmt.Sprintf("v1.%d.0", v.Minor)
case 22: imageName := path.Join(kubernetesRepo(mirror), "coredns")
cv = "1.8.0"
case 10, 20, 21: if v.Minor >= 21 {
cv = "1.7.0" imageName = path.Join(imageName, "coredns")
case 18:
cv = "1.6.7"
case 17:
cv = "1.6.5"
case 16:
cv = "1.6.2"
case 15, 14:
cv = "1.3.1"
case 13:
cv = "1.2.6"
case 12:
cv = "1.2.2"
case 11:
cv = "1.1.3"
} }
return path.Join(kubernetesRepo(mirror), "coredns:"+cv)
if _, ok := constants.KubeadmImages[k8sVersion]; ok {
cv = constants.KubeadmImages[k8sVersion][imageName]
}
// return path.Join(kubernetesRepo(mirror), "coredns:"+cv)
return fmt.Sprintf("%s:%s", imageName, cv)
} }
// etcd returns the image used for etcd // etcd returns the image used for etcd
@ -90,17 +116,24 @@ func etcd(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
ev := "3.4.13-0" ev := "3.4.13-0"
switch v.Minor { /*switch v.Minor {
case 17, 18: //case 17, 18:
ev = "3.4.3-0" // ev = "3.4.3-0"
case 16: //case 16:
ev = "3.3.15-0" // ev = "3.3.15-0"
case 14, 15: //case 14, 15:
ev = "3.3.10" // ev = "3.3.10"
case 12, 13: //case 12, 13:
ev = "3.2.24" // ev = "3.2.24"
case 11: //case 11:
ev = "3.2.18" // ev = "3.2.18"
//}
*/
k8sVersion := fmt.Sprintf("v1.%d.0", v.Minor)
imageName := path.Join(kubernetesRepo(mirror), "etcd")
if _, ok := constants.KubeadmImages[k8sVersion]; ok {
ev = constants.KubeadmImages[k8sVersion][imageName]
} }
// An awkward special case for v1.19.0 - do not imitate unless necessary // An awkward special case for v1.19.0 - do not imitate unless necessary
@ -108,7 +141,7 @@ func etcd(v semver.Version, mirror string) string {
ev = "3.4.9-1" ev = "3.4.9-1"
} }
return path.Join(kubernetesRepo(mirror), "etcd:"+ev) return fmt.Sprintf("%s:%s", imageName, ev)
} }
// auxiliary returns images that are helpful for running minikube // auxiliary returns images that are helpful for running minikube

View File

@ -17,6 +17,7 @@ limitations under the License.
package images package images
import ( import (
"fmt"
"sort" "sort"
"testing" "testing"
@ -42,17 +43,17 @@ func TestKubeadmImages(t *testing.T) {
"docker.io/kubernetesui/dashboard:v2.1.0", "docker.io/kubernetesui/dashboard:v2.1.0",
"docker.io/kubernetesui/metrics-scraper:v1.0.4", "docker.io/kubernetesui/metrics-scraper:v1.0.4",
}}, }},
{"v1.16.1", "mirror.k8s.io", []string{ {"v1.16.0", "k8s.gcr.io", []string{
"mirror.k8s.io/kube-proxy:v1.16.1", "k8s.gcr.io/kube-proxy:v1.16.0",
"mirror.k8s.io/kube-scheduler:v1.16.1", "k8s.gcr.io/kube-scheduler:v1.16.0",
"mirror.k8s.io/kube-controller-manager:v1.16.1", "k8s.gcr.io/kube-controller-manager:v1.16.0",
"mirror.k8s.io/kube-apiserver:v1.16.1", "k8s.gcr.io/kube-apiserver:v1.16.0",
"mirror.k8s.io/coredns:1.6.2", "k8s.gcr.io/coredns:1.6.2",
"mirror.k8s.io/etcd:3.3.15-0", "k8s.gcr.io/etcd:3.3.15-0",
"mirror.k8s.io/pause:3.1", "k8s.gcr.io/pause:3.1",
"mirror.k8s.io/storage-provisioner:" + version.GetStorageProvisionerVersion(), "k8s.gcr.io/storage-provisioner:" + version.GetStorageProvisionerVersion(),
"mirror.k8s.io/dashboard:v2.1.0", "k8s.gcr.io/dashboard:v2.1.0",
"mirror.k8s.io/metrics-scraper:v1.0.4", "k8s.gcr.io/metrics-scraper:v1.0.4",
}}, }},
{"v1.15.0", "", []string{ {"v1.15.0", "", []string{
"k8s.gcr.io/kube-proxy:v1.15.0", "k8s.gcr.io/kube-proxy:v1.15.0",
@ -111,6 +112,7 @@ func TestKubeadmImages(t *testing.T) {
sort.Strings(got) sort.Strings(got)
sort.Strings(tc.want) sort.Strings(tc.want)
if diff := cmp.Diff(tc.want, got); diff != "" { if diff := cmp.Diff(tc.want, got); diff != "" {
fmt.Println(diff)
t.Errorf("%s images mismatch (-want +got):\n%s", tc.version, diff) t.Errorf("%s images mismatch (-want +got):\n%s", tc.version, diff)
} }
} }

View File

@ -19,6 +19,9 @@ package images
// DefaultKubernetesRepo is the default Kubernetes repository // DefaultKubernetesRepo is the default Kubernetes repository
const DefaultKubernetesRepo = "k8s.gcr.io" const DefaultKubernetesRepo = "k8s.gcr.io"
// DefaultMinikubeRepo is the default Minikube repository
const DefaultMinikubeRepo = "gcr.io/k8s-minikube"
// kubernetesRepo returns the official Kubernetes repository, or an alternate // kubernetesRepo returns the official Kubernetes repository, or an alternate
func kubernetesRepo(mirror string) string { func kubernetesRepo(mirror string) string {
if mirror != "" { if mirror != "" {
@ -32,5 +35,5 @@ func minikubeRepo(mirror string) string {
if mirror != "" { if mirror != "" {
return mirror return mirror
} }
return "gcr.io/k8s-minikube" return DefaultMinikubeRepo
} }

View File

@ -0,0 +1,70 @@
/*
Copyright 2021 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package images
import (
"testing"
"github.com/google/go-cmp/cmp"
)
func Test_kubernetesRepo(t *testing.T) {
tests := []struct {
mirror string
want string
}{
{
"",
DefaultKubernetesRepo,
},
{
"mirror.k8s.io",
"mirror.k8s.io",
},
}
for _, tc := range tests {
got := kubernetesRepo(tc.mirror)
if !cmp.Equal(got, tc.want) {
t.Errorf("mirror miss match, want: %s, got: %s", tc.want, got)
}
}
}
func Test_minikubeRepo(t *testing.T) {
tests := []struct {
mirror string
want string
}{
{
"",
DefaultMinikubeRepo,
},
{
"mirror.k8s.io",
"mirror.k8s.io",
},
}
for _, tc := range tests {
got := minikubeRepo(tc.mirror)
if !cmp.Equal(got, tc.want) {
t.Errorf("mirror miss match, want: %s, got: %s", tc.want, got)
}
}
}

View File

@ -0,0 +1,148 @@
/*
Copyright 2021 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package constants
var (
KubeadmImages = map[string]map[string]string{
"v1.22.0-rc.0": {
"k8s.gcr.io/coredns/coredns": "v1.8.4",
"k8s.gcr.io/etcd": "3.4.13-0",
"k8s.gcr.io/kube-apiserver": "v1.21.3",
"k8s.gcr.io/kube-controller-manager": "v1.21.3",
"k8s.gcr.io/kube-proxy": "v1.21.3",
"k8s.gcr.io/kube-scheduler": "v1.21.3",
"k8s.gcr.io/pause": "3.5",
},
"v1.21.3": {
"k8s.gcr.io/coredns/coredns": "v1.8.0",
"k8s.gcr.io/etcd": "3.4.13-0",
"k8s.gcr.io/kube-apiserver": "v1.21.3",
"k8s.gcr.io/kube-controller-manager": "v1.21.3",
"k8s.gcr.io/kube-proxy": "v1.21.3",
"k8s.gcr.io/kube-scheduler": "v1.21.3",
"k8s.gcr.io/pause": "3.4.1",
},
"v1.21.0": {
"k8s.gcr.io/coredns/coredns": "v1.8.0",
"k8s.gcr.io/etcd": "3.4.13-0",
"k8s.gcr.io/kube-apiserver": "v1.21.3",
"k8s.gcr.io/kube-controller-manager": "v1.21.3",
"k8s.gcr.io/kube-proxy": "v1.21.3",
"k8s.gcr.io/kube-scheduler": "v1.21.3",
"k8s.gcr.io/pause": "3.4.1",
},
"v1.20.0": {
"k8s.gcr.io/coredns": "1.7.0",
"k8s.gcr.io/etcd": "3.4.13-0",
"k8s.gcr.io/kube-apiserver": "v1.20.9",
"k8s.gcr.io/kube-controller-manager": "v1.20.9",
"k8s.gcr.io/kube-proxy": "v1.20.9",
"k8s.gcr.io/kube-scheduler": "v1.20.9",
"k8s.gcr.io/pause": "3.2",
},
"v1.19.0": {
"k8s.gcr.io/coredns": "1.7.0",
"k8s.gcr.io/etcd": "3.4.9-1",
"k8s.gcr.io/kube-apiserver": "v1.19.13",
"k8s.gcr.io/kube-controller-manager": "v1.19.13",
"k8s.gcr.io/kube-proxy": "v1.19.13",
"k8s.gcr.io/kube-scheduler": "v1.19.13",
"k8s.gcr.io/pause": "3.2",
},
"v1.18.0": {
"k8s.gcr.io/coredns": "1.6.7",
"k8s.gcr.io/etcd": "3.4.3-0",
"k8s.gcr.io/kube-apiserver": "v1.18.20",
"k8s.gcr.io/kube-controller-manager": "v1.18.20",
"k8s.gcr.io/kube-proxy": "v1.18.20",
"k8s.gcr.io/kube-scheduler": "v1.18.20",
"k8s.gcr.io/pause": "3.2",
},
"v1.17.0": {
"k8s.gcr.io/coredns": "1.6.5",
"k8s.gcr.io/etcd": "3.4.3-0",
"k8s.gcr.io/kube-apiserver": "v1.17.17",
"k8s.gcr.io/kube-controller-manager": "v1.17.17",
"k8s.gcr.io/kube-proxy": "v1.17.17",
"k8s.gcr.io/kube-scheduler": "v1.17.17",
"k8s.gcr.io/pause": "3.1",
},
"v1.16.1": {
"mirror.k8s.io/kube-proxy": "v1.16.1",
"mirror.k8s.io/kube-scheduler": "v1.16.1",
"mirror.k8s.io/kube-controller-manager": "v1.16.1",
"mirror.k8s.io/kube-apiserver": "v1.16.1",
"mirror.k8s.io/coredns": "1.6.2",
"mirror.k8s.io/etcd": "3.3.15-0",
"mirror.k8s.io/pause": "3.1",
},
"v1.16.0": {
"k8s.gcr.io/coredns": "1.6.2",
"k8s.gcr.io/etcd": "3.3.15-0",
"k8s.gcr.io/kube-apiserver": "v1.16.15",
"k8s.gcr.io/kube-controller-manager": "v1.16.15",
"k8s.gcr.io/kube-proxy": "v1.16.15",
"k8s.gcr.io/kube-scheduler": "v1.16.15",
"k8s.gcr.io/pause": "3.1",
},
"v1.15.0": {
"k8s.gcr.io/coredns": "1.3.1",
"k8s.gcr.io/etcd": "3.3.10",
"k8s.gcr.io/kube-apiserver": "v1.15.12",
"k8s.gcr.io/kube-controller-manager": "v1.15.12",
"k8s.gcr.io/kube-proxy": "v1.15.12",
"k8s.gcr.io/kube-scheduler": "v1.15.12",
"k8s.gcr.io/pause": "3.1",
},
"v1.14.0": {
"k8s.gcr.io/coredns": "1.3.1",
"k8s.gcr.io/etcd": "3.3.10",
"k8s.gcr.io/kube-apiserver": "v1.14.10",
"k8s.gcr.io/kube-controller-manager": "v1.14.10",
"k8s.gcr.io/kube-proxy": "v1.14.10",
"k8s.gcr.io/kube-scheduler": "v1.14.10",
"k8s.gcr.io/pause": "3.1",
},
"v1.13.0": {
"k8s.gcr.io/coredns": "1.2.6",
"k8s.gcr.io/etcd": "3.2.24",
"k8s.gcr.io/kube-apiserver": "v1.13.12",
"k8s.gcr.io/kube-controller-manager": "v1.13.12",
"k8s.gcr.io/kube-proxy": "v1.13.12",
"k8s.gcr.io/kube-scheduler": "v1.13.12",
"k8s.gcr.io/pause": "3.1",
},
"v1.12.0": {
"k8s.gcr.io/coredns": "1.2.2",
"k8s.gcr.io/etcd": "3.2.24",
"k8s.gcr.io/kube-apiserver": "v1.21.3",
"k8s.gcr.io/kube-controller-manager": "v1.21.3",
"k8s.gcr.io/kube-proxy": "v1.21.3",
"k8s.gcr.io/kube-scheduler": "v1.21.3",
"k8s.gcr.io/pause": "3.1",
},
"v1.11.0": {
"k8s.gcr.io/coredns": "1.1.3",
"k8s.gcr.io/etcd-amd64": "3.2.18",
"k8s.gcr.io/kube-apiserver-amd64": "v1.11.10",
"k8s.gcr.io/kube-controller-manager-amd64": "v1.11.10",
"k8s.gcr.io/kube-proxy-amd64": "v1.11.10",
"k8s.gcr.io/kube-scheduler-amd64": "v1.11.10",
"k8s.gcr.io/pause-amd64": "3.1",
},
}
)