Merge pull request #17904 from spowelljr/automateUpdatingKubectlImage

CI: Automate updating kubectl image
pull/17830/head
Medya Ghazizadeh 2024-01-08 11:39:43 -08:00 committed by GitHub
commit 8f736a32a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 220 additions and 133 deletions

View File

@ -25,10 +25,9 @@ jobs:
t=$(make update-kubernetes-version) t=$(make update-kubernetes-version)
t=$(echo $t | head -n 1) t=$(echo $t | head -n 1)
echo "title=$t" >> "$GITHUB_OUTPUT" echo "title=$t" >> "$GITHUB_OUTPUT"
c=$(git status --porcelain)
# The following is to support multiline with GITHUB_OUTPUT, see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings # The following is to support multiline with GITHUB_OUTPUT, see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
echo "changes<<EOF" >> "$GITHUB_OUTPUT" echo "changes<<EOF" >> "$GITHUB_OUTPUT"
echo "$c" >> "$GITHUB_OUTPUT" echo "$(git status --porcelain)" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT"
- name: Create PR - name: Create PR
if: ${{ steps.bumpk8s.outputs.changes != '' }} if: ${{ steps.bumpk8s.outputs.changes != '' }}

View File

@ -29,7 +29,6 @@ jobs:
echo "$c" >> "$GITHUB_OUTPUT" echo "$c" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT"
- name: Create PR - name: Create PR
id: createPR
if: ${{ steps.bumpKindnetd.outputs.changes != '' }} if: ${{ steps.bumpKindnetd.outputs.changes != '' }}
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38
with: with:

View File

@ -23,10 +23,9 @@ jobs:
id: bumpKubeadmConsts id: bumpKubeadmConsts
run: | run: |
make update-kubeadm-constants make update-kubeadm-constants
c=$(git status --porcelain)
# The following is to support multiline with GITHUB_OUTPUT, see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings # The following is to support multiline with GITHUB_OUTPUT, see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
echo "changes<<EOF" >> "$GITHUB_OUTPUT" echo "changes<<EOF" >> "$GITHUB_OUTPUT"
echo "$c" >> "$GITHUB_OUTPUT" echo "$(git status --porcelain)" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT"
- name: Create PR - name: Create PR
if: ${{ steps.bumpKubeadmConsts.outputs.changes != '' }} if: ${{ steps.bumpKubeadmConsts.outputs.changes != '' }}

View File

@ -0,0 +1,49 @@
name: "update-kubectl-version"
on:
workflow_dispatch:
schedule:
# every Monday at around 3 am pacific/10 am UTC
- cron: "0 10 * * 1"
env:
GOPROXY: https://proxy.golang.org
GO_VERSION: '1.21.5'
permissions:
contents: read
jobs:
bump-kubectl-version:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
with:
go-version: ${{env.GO_VERSION}}
cache-dependency-path: ./go.sum
- name: Bump Kubectl version
id: bumpKubectl
run: |
echo "OLD_VERSION=$(DEP=kubectl make get-dependency-version)" >> "$GITHUB_OUTPUT"
make update-kubectl-version
echo "NEW_VERSION=$(DEP=kubectl make get-dependency-version)" >> "$GITHUB_OUTPUT"
# The following is to support multiline with GITHUB_OUTPUT, see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
echo "changes<<EOF" >> "$GITHUB_OUTPUT"
echo "$(git status --porcelain)" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
- name: Create PR
if: ${{ steps.bumpKubectl.outputs.changes != '' }}
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38
with:
token: ${{ secrets.MINIKUBE_BOT_PAT }}
commit-message: 'Addon kubevirt: Update bitnami/kubectl image from ${{ steps.bumpKubectl.outputs.OLD_VERSION }} to ${{ steps.bumpKubectl.outputs.NEW_VERSION }}'
committer: minikube-bot <minikube-bot@google.com>
author: minikube-bot <minikube-bot@google.com>
branch: auto_bump_kubectl_version
push-to-fork: minikube-bot/minikube
base: master
delete-branch: true
title: 'Addon kubevirt: Update bitnami/kubectl image from ${{ steps.bumpKubectl.outputs.OLD_VERSION }} to ${{ steps.bumpKubectl.outputs.NEW_VERSION }}'
labels: ok-to-test
body: |
The [bitnami](https://github.com/bitnami/containers) project released a new bitnami/kubectl image
This PR was auto-generated by `make update-kubectl-version` using [update-kubectl-version.yml](https://github.com/kubernetes/minikube/tree/master/.github/workflows/update-kubectl-version.yml) CI Workflow.

View File

@ -25,9 +25,9 @@ jobs:
echo "OLD_VERSION=$(DEP=ubuntu make get-dependency-version)" >> "$GITHUB_OUTPUT" echo "OLD_VERSION=$(DEP=ubuntu make get-dependency-version)" >> "$GITHUB_OUTPUT"
make update-ubuntu-version make update-ubuntu-version
echo "NEW_VERSION=$(DEP=ubuntu make get-dependency-version)" >> "$GITHUB_OUTPUT" echo "NEW_VERSION=$(DEP=ubuntu make get-dependency-version)" >> "$GITHUB_OUTPUT"
c=$(git status --porcelain) # The following is to support multiline with GITHUB_OUTPUT, see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
echo "changes<<EOF" >> "$GITHUB_OUTPUT" echo "changes<<EOF" >> "$GITHUB_OUTPUT"
echo "$c" >> "$GITHUB_OUTPUT" echo "$(git status --porcelain)" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT"
- name: Create PR - name: Create PR
id: createPR id: createPR

View File

@ -1200,6 +1200,11 @@ update-nerdctld-version:
(cd hack/update/nerdctld_version && \ (cd hack/update/nerdctld_version && \
go run update_nerdctld_version.go) go run update_nerdctld_version.go)
.PHONY: update-kubectl-version
update-kubectl-version:
(cd hack/update/kubectl_version && \
go run update_kubectl_version.go)
.PHONY: get-dependency-verison .PHONY: get-dependency-verison
get-dependency-version: get-dependency-version:
@(cd hack/update/get_version && \ @(cd hack/update/get_version && \

64
hack/update/dockerhub.go Normal file
View File

@ -0,0 +1,64 @@
/*
Copyright 2024 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 update
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
const dockerHubTagsURL = "https://hub.docker.com/v2/repositories/%s/tags?page_size=100"
// Response is used to unmarshal the response from Docker Hub
type Response struct {
Results []struct {
Name string `json:"name"`
}
}
// ImageTagsFromDockerHub returns the 100 latest image tags from Docker Hub for the provided repository
func ImageTagsFromDockerHub(repo string) ([]string, error) {
resp, err := http.Get(fmt.Sprintf(dockerHubTagsURL, repo))
if err != nil {
return nil, fmt.Errorf("unable to get tags list: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("unable to read HTTP response from Docker Hub: %v", err)
}
var content Response
err = json.Unmarshal(body, &content)
if err != nil {
return nil, fmt.Errorf("unable to unmarshal response from Docker Hub: %v", err)
}
versions := make([]string, len(content.Results))
for i, tag := range content.Results {
versions[i] = tag.Name
}
if len(versions) == 0 {
return nil, fmt.Errorf("tag list is empty")
}
return versions, nil
}

View File

@ -54,6 +54,7 @@ var dependencies = map[string]dependency{
"kindnetd": {"pkg/minikube/bootstrapper/images/images.go", `kindnetd:(.*)"`}, "kindnetd": {"pkg/minikube/bootstrapper/images/images.go", `kindnetd:(.*)"`},
"kong": {addonsFile, `kong:(.*)@`}, "kong": {addonsFile, `kong:(.*)@`},
"kong-ingress-controller": {addonsFile, `kong/kubernetes-ingress-controller:(.*)@`}, "kong-ingress-controller": {addonsFile, `kong/kubernetes-ingress-controller:(.*)@`},
"kubectl": {addonsFile, `bitnami/kubectl:(.*)@`},
"metrics-server": {addonsFile, `metrics-server/metrics-server:(.*)@`}, "metrics-server": {addonsFile, `metrics-server/metrics-server:(.*)@`},
"nerdctl": {"deploy/kicbase/Dockerfile", `NERDCTL_VERSION="(.*)"`}, "nerdctl": {"deploy/kicbase/Dockerfile", `NERDCTL_VERSION="(.*)"`},
"nerdctld": {"deploy/kicbase/Dockerfile", `NERDCTLD_VERSION="(.*)"`}, "nerdctld": {"deploy/kicbase/Dockerfile", `NERDCTLD_VERSION="(.*)"`},

View File

@ -17,20 +17,11 @@ limitations under the License.
package main package main
import ( import (
"encoding/json"
"fmt"
"io"
"net/http"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/minikube/hack/update" "k8s.io/minikube/hack/update"
) )
const (
dockerHubKindnetdTags = "https://hub.docker.com/v2/repositories/kindest/kindnetd/tags"
)
var ( var (
schema = map[string]update.Item{ schema = map[string]update.Item{
"pkg/minikube/bootstrapper/images/images.go": { "pkg/minikube/bootstrapper/images/images.go": {
@ -45,43 +36,12 @@ type Data struct {
LatestVersion string LatestVersion string
} }
type Response struct {
Results []struct {
Name string `json:"name"`
}
}
func getLatestVersion() (string, error) {
resp, err := http.Get(dockerHubKindnetdTags)
if err != nil {
return "", fmt.Errorf("failed to get tags: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read HTTP response: %v", err)
}
var content Response
err = json.Unmarshal(body, &content)
if err != nil {
return "", fmt.Errorf("failed to unmarshal response: %v", err)
}
if len(content.Results) == 0 {
return "", fmt.Errorf("tag list is empty")
}
return content.Results[0].Name, nil
}
func main() { func main() {
latest, err := getLatestVersion() tags, err := update.ImageTagsFromDockerHub("kindest/kindnetd")
if err != nil { if err != nil {
klog.Fatalf("failed to get latest version: %v", err) klog.Fatal(err)
} }
data := Data{LatestVersion: latest} data := Data{LatestVersion: tags[0]}
update.Apply(schema, data) update.Apply(schema, data)
} }

View File

@ -0,0 +1,67 @@
/*
Copyright 2024 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 (
"fmt"
"golang.org/x/mod/semver"
"k8s.io/klog/v2"
"k8s.io/minikube/hack/update"
)
var schema = map[string]update.Item{
"pkg/minikube/assets/addons.go": {
Replace: map[string]string{
`"bitnami/kubectl:.*`: `"bitnami/kubectl:{{.Version}}@{{.SHA}}",`,
},
},
}
type Data struct {
Version string
SHA string
}
func main() {
tags, err := update.ImageTagsFromDockerHub("bitnami/kubectl")
if err != nil {
klog.Fatal(err)
}
tag, err := latestStableSemverTag(tags)
if err != nil {
klog.Fatal(err)
}
sha, err := update.GetImageSHA(fmt.Sprintf("docker.io/bitnami/kubectl:%s", tag))
if err != nil {
klog.Fatalf("failed to get image SHA: %v", err)
}
data := Data{Version: tag, SHA: sha}
update.Apply(schema, data)
}
func latestStableSemverTag(tags []string) (string, error) {
for _, tag := range tags {
vTag := fmt.Sprintf("v%s", tag)
if semver.IsValid(vTag) && semver.Prerelease(vTag) == "" {
return tag, nil
}
}
return "", fmt.Errorf("no stable semver tag found")
}

View File

@ -17,18 +17,13 @@ limitations under the License.
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"io"
"net/http"
"strings"
"golang.org/x/mod/semver"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/minikube/hack/update" "k8s.io/minikube/hack/update"
) )
const dockerHubRegistryURL = "https://hub.docker.com/v2/repositories/library/registry/tags"
var schema = map[string]update.Item{ var schema = map[string]update.Item{
"pkg/minikube/assets/addons.go": { "pkg/minikube/assets/addons.go": {
Replace: map[string]string{ Replace: map[string]string{
@ -42,52 +37,31 @@ type Data struct {
SHA string SHA string
} }
// Response is used to unmarshal the response from Docker Hub
type Response struct {
Results []struct {
Name string `json:"name"`
}
}
func main() { func main() {
version, err := getLatestVersion() tags, err := update.ImageTagsFromDockerHub("library/registry")
if err != nil { if err != nil {
klog.Fatalf("failed to get latest version: %v", err) klog.Fatal(err)
} }
version = strings.TrimPrefix(version, "v") tag, err := latestStableSemverTag(tags)
sha, err := update.GetImageSHA(fmt.Sprintf("docker.io/registry:%s", version)) if err != nil {
klog.Fatal(err)
}
sha, err := update.GetImageSHA(fmt.Sprintf("docker.io/registry:%s", tag))
if err != nil { if err != nil {
klog.Fatalf("failed to get image SHA: %v", err) klog.Fatalf("failed to get image SHA: %v", err)
} }
data := Data{Version: version, SHA: sha} data := Data{Version: tag, SHA: sha}
update.Apply(schema, data) update.Apply(schema, data)
} }
func getLatestVersion() (string, error) { func latestStableSemverTag(tags []string) (string, error) {
resp, err := http.Get(dockerHubRegistryURL) for _, tag := range tags {
if err != nil { vTag := fmt.Sprintf("v%s", tag)
return "", fmt.Errorf("failed to get tags: %v", err) if semver.IsValid(vTag) && semver.Prerelease(vTag) == "" {
} return tag, nil
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read response body: %v", err)
}
var content Response
err = json.Unmarshal(body, &content)
if err != nil {
return "", fmt.Errorf("failed to unmarshal response: %v", err)
}
for _, i := range content.Results {
if !strings.Contains(i.Name, "latest") {
return i.Name, nil
} }
} }
return "", fmt.Errorf("no stable semver tag found")
return "", fmt.Errorf("didn't find a non-latest image")
} }

View File

@ -17,10 +17,7 @@ limitations under the License.
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"io"
"net/http"
"strings" "strings"
"k8s.io/klog/v2" "k8s.io/klog/v2"
@ -28,10 +25,6 @@ import (
"k8s.io/minikube/hack/update" "k8s.io/minikube/hack/update"
) )
const (
dockerHubUbuntuBaseURL = "https://hub.docker.com/v2/repositories/library/ubuntu/tags"
)
var ( var (
schema = map[string]update.Item{ schema = map[string]update.Item{
"deploy/kicbase/Dockerfile": { "deploy/kicbase/Dockerfile": {
@ -47,48 +40,25 @@ type Data struct {
LatestVersion string LatestVersion string
} }
// Response is used to unmarshal the response from Docker Hub func latestJammyTag(tags []string) (string, error) {
type Response struct { for _, tag := range tags {
Results []struct { if strings.Contains(tag, "jammy-") {
Name string `json:"name"` return tag, nil
}
}
func getLatestVersion() (string, error) {
resp, err := http.Get(dockerHubUbuntuBaseURL)
if err != nil {
return "", fmt.Errorf("unable to get Ubuntu jammy's latest version: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("unable to read HTTP response from Docker Hub: %v", err)
}
var content Response
err = json.Unmarshal(body, &content)
if err != nil {
return "", fmt.Errorf("unable to unmarshal response from Docker Hub: %v", err)
}
for _, i := range content.Results {
if strings.Contains(i.Name, "jammy-") {
return i.Name, nil
} }
} }
return "", fmt.Errorf("no tag found that matches: jammy-")
return "", fmt.Errorf("response from Docker Hub does not contain a latest jammy image")
} }
func main() { func main() {
// get Ubuntu Jammy latest version tags, err := update.ImageTagsFromDockerHub("library/ubuntu")
latest, err := getLatestVersion()
if err != nil { if err != nil {
klog.Fatalf("Unable to find latest ubuntu:jammy version: %v\n", err) klog.Fatal(err)
} }
data := Data{LatestVersion: fmt.Sprintf("ubuntu:%s", latest)} jammyTag, err := latestJammyTag(tags)
klog.Infof("Ubuntu jammy latest version: %s", latest) if err != nil {
klog.Fatal(err)
}
data := Data{LatestVersion: fmt.Sprintf("ubuntu:%s", jammyTag)}
update.Apply(schema, data) update.Apply(schema, data)
} }