Merge branch 'master' of https://github.com/kubernetes/minikube into klog

pull/9355/head
Priya Modali 2020-10-13 09:08:27 -07:00
commit 8936a8c7f8
24 changed files with 579 additions and 162 deletions

View File

@ -24,7 +24,7 @@ jobs:
stable: true
- name: Download Dependencies
run: go mod download
- name: Install KVM
- name: Install KVM
run: |
sudo apt-get update
sudo apt-get install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils
@ -117,11 +117,14 @@ jobs:
GOPOGH_RESULT="${JOB_NAME} : completed with ${FailNum} / ${TestsNum} failures in ${TIME_ELAPSED}"
echo ::set-env name=GOPOGH_RESULT::${GOPOGH_RESULT}
echo ::set-env name=STAT::${STAT}
- uses: actions/upload-artifact@v1
- name: Upload report
uses: actions/upload-artifact@v1
with:
name: iso_functional_test_kvm2_ubuntu
path: out/report
- uses: actions/upload-artifact@v1
- name: Upload iso.log
if: always()
uses: actions/upload-artifact@v1
with:
name: iso log
path: out/iso.log

View File

@ -1,21 +1,51 @@
# Release Notes
## Version 1.14.0 - 2020-10-08
## Features
* Delete context when stopped [#9414](https://github.com/kubernetes/minikube/pull/9414)
* New flag "--ports" to expose ports for docker & podman drivers [#9404](https://github.com/kubernetes/minikube/pull/9404)
## Bug Fixes and minor improvements
* Ingress addon: fix the controller name [#9413](https://github.com/kubernetes/minikube/pull/9413)
* docker/podman drivers: no panic when updating mount-string with no configuration [#9412](https://github.com/kubernetes/minikube/pull/9412)
* Improve solution message when there is no space left on device [#9316](https://github.com/kubernetes/minikube/pull/9316)
* To see more changes checkout the last beta release notes [1.14.0-beta.0](https://github.com/kubernetes/minikube/releases/tag/v1.14.0-beta.0).
Thank you to our contributors for this release.
- Anders F Björklund
- Asare Worae
- Medya Ghazizadeh
- Prajilesh N
- Predrag Rogic
- Priya Wadhwa
- Thomas Strömberg
- ToonvanStrijp
## Version 1.14.0-beta.0 - 2020-10-06
## Features
* add dedicated network for docker driver [#9294](https://github.com/kubernetes/minikube/pull/9294)
* Make sure gcp-auth addon can be enabled on startup [#9318](https://github.com/kubernetes/minikube/pull/9318)
## Bug Fixes
* Fix minikube status bug when cluster is paused [#9383](https://github.com/kubernetes/minikube/pull/9383)
* don't allow profile name to be less than 2 characters [#9367](https://github.com/kubernetes/minikube/pull/9367)
* fix: "profile list" shows paused clusters as "Running" [#8978](https://github.com/kubernetes/minikube/pull/8978)
* Fix error in unittest, as pointed out by warning [#9345](https://github.com/kubernetes/minikube/pull/9345)
## Updates
* update kicbase image to ubuntu-based [#9353](https://github.com/kubernetes/minikube/pull/9353)
Thank you to our contributors for this release!
- Anders F Björklund
- Bob Killen
- Daniel Weibel

View File

@ -15,7 +15,7 @@
# Bump these on release - and please check ISO_VERSION for correctness.
VERSION_MAJOR ?= 1
VERSION_MINOR ?= 14
VERSION_BUILD ?= 0-beta.0
VERSION_BUILD ?= 0
RAW_VERSION=$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_BUILD)
VERSION ?= v$(RAW_VERSION)
@ -32,7 +32,7 @@ RPM_VERSION ?= $(DEB_VERSION)
GO_VERSION ?= 1.14.6
INSTALL_SIZE ?= $(shell du out/minikube-windows-amd64.exe | cut -f1)
BUILDROOT_BRANCH ?= 2019.02.11
BUILDROOT_BRANCH ?= 2020.02.6
REGISTRY?=gcr.io/k8s-minikube
REGISTRY_GH?=docker.pkg.github.com/kubernetes/minikube
@ -58,7 +58,7 @@ MINIKUBE_BUCKET ?= minikube/releases
MINIKUBE_UPLOAD_LOCATION := gs://${MINIKUBE_BUCKET}
MINIKUBE_RELEASES_URL=https://github.com/kubernetes/minikube/releases/download
KERNEL_VERSION ?= 4.19.107
KERNEL_VERSION ?= 4.19.114
# latest from https://github.com/golangci/golangci-lint/releases
GOLINT_VERSION ?= v1.30.0
# Limit number of default jobs, to avoid the CI builds running out of memory

View File

@ -50,8 +50,6 @@ spec:
template:
metadata:
name: gcp-auth-certs-create
labels:
gcp-auth-skip-secret: "true"
spec:
serviceAccountName: minikube-gcp-auth-certs
containers:
@ -79,7 +77,6 @@ spec:
labels:
app: gcp-auth
kubernetes.io/minikube-addons: gcp-auth
gcp-auth-skip-secret: "true"
spec:
containers:
- name: gcp-auth
@ -112,8 +109,6 @@ spec:
template:
metadata:
name: gcp-auth-certs-patch
labels:
gcp-auth-skip-secret: "true"
spec:
serviceAccountName: minikube-gcp-auth-certs
containers:
@ -136,7 +131,7 @@ metadata:
app: gcp-auth
webhooks:
- name: gcp-auth-mutate.k8s.io
failurePolicy: Fail
failurePolicy: Ignore
objectSelector:
matchExpressions:
- key: gcp-auth-skip-secret

View File

@ -95,7 +95,6 @@ metadata:
labels:
integration-test: storage-provisioner
addonmanager.kubernetes.io/mode: Reconcile
gcp-auth-skip-secret: "true"
spec:
serviceAccountName: storage-provisioner
hostNetwork: true

View File

@ -4,8 +4,8 @@ BR2_OPTIMIZE_2=y
BR2_TOOLCHAIN_BUILDROOT_VENDOR="minikube"
BR2_TOOLCHAIN_BUILDROOT_GLIBC=y
BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_19=y
BR2_BINUTILS_VERSION_2_30_X=y
BR2_GCC_VERSION_7_X=y
BR2_BINUTILS_VERSION_2_32_X=y
BR2_GCC_VERSION_8_X=y
BR2_TOOLCHAIN_BUILDROOT_CXX=y
BR2_GCC_ENABLE_LTO=y
BR2_TARGET_GENERIC_HOSTNAME="minikube"
@ -19,6 +19,9 @@ BR2_ROOTFS_USERS_TABLES="$(BR2_EXTERNAL_MINIKUBE_PATH)/board/coreos/minikube/use
BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_MINIKUBE_PATH)/board/coreos/minikube/rootfs-overlay"
BR2_GLOBAL_PATCH_DIR="$(BR2_EXTERNAL_MINIKUBE_PATH)/board/coreos/minikube/patches"
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_LATEST_VERSION=n
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="4.19.114"
BR2_LINUX_KERNEL_BZIMAGE=y
BR2_LINUX_KERNEL_LZ4=y
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
@ -36,7 +39,6 @@ BR2_PACKAGE_SSHFS=y
BR2_PACKAGE_XFSPROGS=y
BR2_PACKAGE_PARTED=y
BR2_PACKAGE_CA_CERTIFICATES=y
BR2_PACKAGE_CURL=y
BR2_PACKAGE_BRIDGE_UTILS=y
BR2_PACKAGE_EBTABLES=y
BR2_PACKAGE_ETHTOOL=y
@ -53,7 +55,6 @@ BR2_PACKAGE_LIBCURL_CURL=y
BR2_PACKAGE_LIBOPENSSL=y
BR2_PACKAGE_LIBOPENSSL_BIN=y
BR2_PACKAGE_OPENVMTOOLS=y
BR2_PACKAGE_OPENVMTOOLS_PROCPS=y
BR2_PACKAGE_SYSTEMD_LOGIND=y
BR2_PACKAGE_SYSTEMD_MACHINED=y
BR2_PACKAGE_SYSTEMD_VCONSOLE=y

View File

@ -2,4 +2,4 @@ config BR2_PACKAGE_CNI_PLUGINS
bool "cni-plugins"
default y
depends on BR2_x86_64
depends on BR2_PACKAGE_HOST_GO_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_ARCH_SUPPORTS

View File

@ -13,6 +13,7 @@ CNI_PLUGINS_LICENSE_FILES = LICENSE
CNI_PLUGINS_DEPENDENCIES = host-go
CNI_PLUGINS_MAKE_ENV = \
$(GO_TARGET_ENV) \
CGO_ENABLED=0 \
GO111MODULE=off

View File

@ -2,4 +2,4 @@ config BR2_PACKAGE_CNI
bool "cni"
default y
depends on BR2_x86_64
depends on BR2_PACKAGE_HOST_GO_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_ARCH_SUPPORTS

View File

@ -14,6 +14,7 @@ CNI_DEPENDENCIES = host-go
CNI_GOPATH = $(@D)/_output
CNI_MAKE_ENV = \
$(GO_TARGET_ENV) \
CGO_ENABLED=0 \
GO111MODULE=off \
GOPATH="$(CNI_GOPATH)" \

View File

@ -1,7 +1,7 @@
config BR2_PACKAGE_CONMON
bool "conmon"
depends on BR2_PACKAGE_HOST_GO_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_CGO_LINKING_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_CGO_LINKING_SUPPORTS
depends on BR2_TOOLCHAIN_HAS_THREADS
select BR2_PACKAGE_LIBGLIB2
select BR2_PACKAGE_SYSTEMD

View File

@ -2,8 +2,8 @@ config BR2_PACKAGE_CONTAINERD_BIN
bool "containerd-bin"
default y
depends on BR2_x86_64
depends on BR2_PACKAGE_HOST_GO_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_CGO_LINKING_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_CGO_LINKING_SUPPORTS
depends on BR2_TOOLCHAIN_HAS_THREADS
depends on BR2_USE_MMU # lvm2
depends on !BR2_STATIC_LIBS # lvm2

View File

@ -10,6 +10,7 @@ CONTAINERD_BIN_SOURCE = $(CONTAINERD_BIN_VERSION).tar.gz
CONTAINERD_BIN_DEPENDENCIES = host-go libgpgme
CONTAINERD_BIN_GOPATH = $(@D)/_output
CONTAINERD_BIN_ENV = \
$(GO_TARGET_ENV) \
CGO_ENABLED=1 \
GO111MODULE=off \
GOPATH="$(CONTAINERD_BIN_GOPATH)" \

View File

@ -2,8 +2,8 @@ config BR2_PACKAGE_CRIO_BIN
bool "crio-bin"
default y
depends on BR2_x86_64
depends on BR2_PACKAGE_HOST_GO_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_CGO_LINKING_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_CGO_LINKING_SUPPORTS
depends on BR2_TOOLCHAIN_HAS_THREADS
depends on BR2_USE_MMU # lvm2
depends on !BR2_STATIC_LIBS # lvm2

View File

@ -11,6 +11,7 @@ CRIO_BIN_SOURCE = $(CRIO_BIN_VERSION).tar.gz
CRIO_BIN_DEPENDENCIES = host-go libgpgme
CRIO_BIN_GOPATH = $(@D)/_output
CRIO_BIN_ENV = \
$(GO_TARGET_ENV) \
CGO_ENABLED=1 \
GO111MODULE=off \
GOPATH="$(CRIO_BIN_GOPATH)" \

View File

@ -2,8 +2,8 @@ config BR2_PACKAGE_PODMAN
bool "podman"
default y
depends on BR2_x86_64
depends on BR2_PACKAGE_HOST_GO_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_CGO_LINKING_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_CGO_LINKING_SUPPORTS
depends on BR2_TOOLCHAIN_HAS_THREADS
select BR2_PACKAGE_RUNC_MASTER
select BR2_PACKAGE_CONMON

View File

@ -10,6 +10,7 @@ PODMAN_DEPENDENCIES = host-go
PODMAN_GOPATH = $(@D)/_output
PODMAN_BIN_ENV = \
$(GO_TARGET_ENV) \
CGO_ENABLED=1 \
GOPATH="$(PODMAN_GOPATH)" \
GOBIN="$(PODMAN_GOPATH)/bin" \

View File

@ -1,7 +1,7 @@
config BR2_PACKAGE_RUNC_MASTER
bool "runc-master"
depends on BR2_PACKAGE_HOST_GO_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_CGO_LINKING_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_CGO_LINKING_SUPPORTS
depends on BR2_TOOLCHAIN_HAS_THREADS
help
runC is a CLI tool for spawning and running containers
@ -12,6 +12,6 @@ config BR2_PACKAGE_RUNC_MASTER
https://github.com/opencontainers/runc
comment "runc needs a toolchain w/ threads"
depends on BR2_PACKAGE_HOST_GO_ARCH_SUPPORTS && \
BR2_PACKAGE_HOST_GO_CGO_LINKING_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_ARCH_SUPPORTS && \
BR2_PACKAGE_HOST_GO_TARGET_CGO_LINKING_SUPPORTS
depends on !BR2_TOOLCHAIN_HAS_THREADS

View File

@ -14,7 +14,8 @@ RUNC_MASTER_LICENSE_FILES = LICENSE
RUNC_MASTER_DEPENDENCIES = host-go
RUNC_MASTER_GOPATH = $(@D)/_output
RUNC_MASTER_MAKE_ENV = $(HOST_GO_TARGET_ENV) \
RUNC_MASTER_MAKE_ENV = \
$(GO_TARGET_ENV) \
CGO_ENABLED=1 \
GO111MODULE=off \
GOPATH="$(RUNC_MASTER_GOPATH)" \

View File

@ -1,4 +1,12 @@
[
{
"name": "v1.14.0",
"checksums": {
"darwin": "71dee6241a93945b40ea7188ad15459e50e7b65eab09fed7302d8cacdc58585c",
"linux": "8727635489be895d9b9cfaa5cb599f45799a28fb07e0a2aac351a9aa1c4b46c1",
"windows": "0317e6c338da23ccf0aba698668c6d919f22e1482340a09d1269220063937aeb"
}
},
{
"name": "v1.13.1",
"checksums": {

View File

@ -1,5 +1,5 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Copyright 2020 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.
@ -14,153 +14,450 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
/*
The script expects the following env variables:
- UPDATE_TARGET=<string>: optional - if unset/absent, default option is "fs"; valid options are:
- "fs" - update only local filesystem repo files [default]
- "gh" - update only remote GitHub repo files and create PR (if one does not exist already)
- "all" - update local and remote repo files and create PR (if one does not exist already)
- GITHUB_TOKEN=<string>: The Github API access token. Injected by the Jenkins credential provider.
- note: GITHUB_TOKEN is needed only if UPDATE_TARGET is "gh" or "all"
*/
package main
import (
"bytes"
"context"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"text/template"
"time"
"golang.org/x/oauth2"
"github.com/google/go-github/v32/github"
"k8s.io/klog/v2"
)
func main() {
// fetch respective current stable (vDefault as DefaultKubernetesVersion) and
// latest rc or beta (vDefault as NewestKubernetesVersion) Kubernetes GitHub Releases
vDefault, vNewest, err := fetchKubernetesReleases()
if err != nil {
klog.Errorf("Fetching current GitHub Releases failed: %v", err)
}
if vDefault == "" || vNewest == "" {
klog.Fatalf("Cannot determine current 'DefaultKubernetesVersion' and 'NewestKubernetesVersion'")
}
klog.Infof("Current Kubernetes GitHub Releases: 'stable' is %s and 'latest' is %s", vDefault, vNewest)
const (
// default context timeout
cxTimeout = 300 * time.Second
if err := updateKubernetesVersions(vDefault, vNewest); err != nil {
klog.Fatalf("Updating 'DefaultKubernetesVersion' and 'NewestKubernetesVersion' failed: %v", err)
}
klog.Infof("Update successful: 'DefaultKubernetesVersion' was set to %s and 'NewestKubernetesVersion' was set to %s", vDefault, vNewest)
// Flush before exiting to guarantee all log output is written
klog.Flush()
}
// fetchKubernetesReleases returns respective current stable (as vDefault) and
// latest rc or beta (as vNewest) Kubernetes GitHub Releases, and any error
func fetchKubernetesReleases() (vDefault, vNewest string, err error) {
client := github.NewClient(nil)
// set a context with a deadline - timeout after at most 10 seconds
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// walk through the paginated list of all 'kubernetes/kubernetes' repo releases
// from latest to older releases, until latest release and pre-release are found
// use max value (100) for PerPage to avoid hitting the rate limits (60 per hour, 10 per minute)
// see https://godoc.org/github.com/google/go-github/github#hdr-Rate_Limiting
opt := &github.ListOptions{PerPage: 100}
for {
rels, resp, err := client.Repositories.ListReleases(ctx, "kubernetes", "kubernetes", opt)
ghListOptionsPerPage = 100
)
var (
// root directory of the local filesystem repo to update
fsRoot = "../../"
// map key corresponds to GitHub TreeEntry.Path and local repo file path (prefixed with fsRoot)
plan = map[string]Patch{
"pkg/minikube/constants/constants.go": {
Replace: map[string]string{
`DefaultKubernetesVersion = \".*`: `DefaultKubernetesVersion = "{{.K8sStableVersion}}"`,
`NewestKubernetesVersion = \".*`: `NewestKubernetesVersion = "{{.K8sLatestVersion}}"`,
},
},
"site/content/en/docs/commands/start.md": {
Replace: map[string]string{
`'stable' for .*,`: `'stable' for {{.K8sStableVersion}},`,
`'latest' for .*\)`: `'latest' for {{.K8sLatestVersion}})`,
},
},
}
target = os.Getenv("UPDATE_TARGET")
// GitHub repo data
ghToken = os.Getenv("GITHUB_TOKEN")
ghOwner = "kubernetes"
ghRepo = "minikube"
ghBase = "master" // could be "main" in the future?
// PR data
prBranchPrefix = "update-kubernetes-version_" // will be appended with first 7 characters of the PR commit SHA
prTitle = `update_kubernetes_version: {stable:"{{.K8sStableVersion}}", latest:"{{.K8sLatestVersion}}"}`
prIssue = 4392
prSearchLimit = 100 // limit the number of previous PRs searched for same prTitle to be <= N * ghListOptionsPerPage
)
// Data holds respective stable (release) and latest (pre-release) Kubernetes versions
type Data struct {
K8sStableVersion string `json:"k8sStableVersion"`
K8sLatestVersion string `json:"k8sLatestVersion"`
}
// Patch defines content where all occurrences of each replace map key should be swapped with its
// respective value. Replace map keys can use RegExp and values can use Golang Text Template
type Patch struct {
Content []byte `json:"-"`
Replace map[string]string `json:"replace"`
}
// apply patch to content by replacing all occurrences of map's keys with their respective values
func (p *Patch) apply(data interface{}) (changed bool, err error) {
if p.Content == nil || p.Replace == nil {
return false, fmt.Errorf("nothing to patch")
}
org := string(p.Content)
str := org
for src, dst := range p.Replace {
re := regexp.MustCompile(src)
tmpl := template.Must(template.New("").Parse(dst))
buf := new(bytes.Buffer)
if err := tmpl.Execute(buf, data); err != nil {
return false, err
}
str = re.ReplaceAllString(str, buf.String())
}
p.Content = []byte(str)
return str != org, nil
}
func main() {
// write log statements to stderr instead of to files
if err := flag.Set("logtostderr", "true"); err != nil {
fmt.Printf("Error setting 'logtostderr' klog flag: %v", err)
}
flag.Parse()
defer klog.Flush()
if target == "" {
target = "fs"
} else if target != "fs" && target != "gh" && target != "all" {
klog.Fatalf("Invalid UPDATE_TARGET option: '%s'; Valid options are: unset/absent (defaults to 'fs'), 'fs', 'gh', or 'all'", target)
} else if (target == "gh" || target == "all") && ghToken == "" {
klog.Fatalf("GITHUB_TOKEN is required if UPDATE_TARGET is 'gh' or 'all'")
}
// set a context with defined timeout
ctx, cancel := context.WithTimeout(context.Background(), cxTimeout)
defer cancel()
// get Kubernetes versions from GitHub Releases
stable, latest, err := ghReleases(ctx, "kubernetes", "kubernetes", ghToken)
if err != nil || stable == "" || latest == "" {
klog.Fatalf("Error getting Kubernetes versions: %v", err)
}
data := Data{K8sStableVersion: stable, K8sLatestVersion: latest}
klog.Infof("Kubernetes versions: 'stable' is %s and 'latest' is %s", data.K8sStableVersion, data.K8sLatestVersion)
klog.Infof("The Plan:\n%s", thePlan(plan, data))
if target == "fs" || target == "all" {
changed, err := fsUpdate(fsRoot, plan, data)
if err != nil {
return "", "", err
klog.Errorf("Error updating local repo: %v", err)
} else if !changed {
klog.Infof("Local repo update skipped: nothing changed")
} else {
klog.Infof("Local repo updated")
}
}
for _, r := range rels {
// GetName returns the Name field if it's non-nil, zero value otherwise.
ver := r.GetName()
if ver == "" {
continue
}
if target == "gh" || target == "all" {
// update prTitle replacing template placeholders with concrete data values
tmpl := template.Must(template.New("prTitle").Parse(prTitle))
buf := new(bytes.Buffer)
if err := tmpl.Execute(buf, data); err != nil {
klog.Fatalf("Error parsing PR Title: %v", err)
}
prTitle = buf.String()
rel := strings.Split(ver, "-")
// check if it is a release channel (ie, 'v1.19.2') or a
// pre-release channel (ie, 'v1.19.3-rc.0' or 'v1.19.0-beta.2')
if len(rel) == 1 && vDefault == "" {
vDefault = ver
} else if len(rel) > 1 && vNewest == "" {
if strings.HasPrefix(rel[1], "rc") || strings.HasPrefix(rel[1], "beta") {
vNewest = ver
}
}
if vDefault != "" && vNewest != "" {
// make sure that vNewest >= vDefault
if vNewest < vDefault {
vNewest = vDefault
}
return vDefault, vNewest, nil
// check if PR already exists
prURL, err := ghFindPR(ctx, prTitle, ghOwner, ghRepo, ghBase, ghToken)
if err != nil {
klog.Errorf("Error checking if PR already exists: %v", err)
} else if prURL != "" {
klog.Infof("PR create skipped: already exists (%s)", prURL)
} else {
// create PR
pr, err := ghCreatePR(ctx, ghOwner, ghRepo, ghBase, prBranchPrefix, prTitle, prIssue, ghToken, plan, data)
if err != nil {
klog.Fatalf("Error creating PR: %v", err)
} else if pr == nil {
klog.Infof("PR create skipped: nothing changed")
} else {
klog.Infof("PR created: %s", *pr.HTMLURL)
}
}
}
}
// fsUpdate updates local filesystem repo files according to the given plan and data,
// returns if the update actually changed anything, and any error occurred
func fsUpdate(fsRoot string, plan map[string]Patch, data Data) (changed bool, err error) {
for path, p := range plan {
path = filepath.Join(fsRoot, path)
blob, err := ioutil.ReadFile(path)
if err != nil {
return false, err
}
info, err := os.Stat(path)
if err != nil {
return false, err
}
mode := info.Mode()
p.Content = blob
chg, err := p.apply(data)
if err != nil {
return false, err
}
if chg {
changed = true
}
if err := ioutil.WriteFile(path, p.Content, mode); err != nil {
return false, err
}
}
return changed, nil
}
// ghCreatePR returns PR created in the GitHub owner/repo, applying the changes to the base head
// commit fork, as defined by the plan and data, and also returns any error occurred
// PR branch will be named by the branch, sufixed by '_' and first 7 characters of fork commit SHA
// PR itself will be named by the title and will reference the issue
func ghCreatePR(ctx context.Context, owner, repo, base, branch, title string, issue int, token string, plan map[string]Patch, data Data) (*github.PullRequest, error) {
ghc := ghClient(ctx, token)
// get base branch
baseBranch, _, err := ghc.Repositories.GetBranch(ctx, owner, repo, base)
if err != nil {
return nil, fmt.Errorf("error getting base branch: %w", err)
}
// get base commit
baseCommit, _, err := ghc.Repositories.GetCommit(ctx, owner, repo, *baseBranch.Commit.SHA)
if err != nil {
return nil, fmt.Errorf("error getting base commit: %w", err)
}
// get base tree
baseTree, _, err := ghc.Git.GetTree(ctx, owner, repo, baseCommit.GetSHA(), true)
if err != nil {
return nil, fmt.Errorf("error getting base tree: %w", err)
}
// update files
changes, err := ghUpdate(ctx, owner, repo, baseTree, token, plan, data)
if err != nil {
return nil, fmt.Errorf("error updating files: %w", err)
}
if changes == nil {
return nil, nil
}
// create fork
fork, resp, err := ghc.Repositories.CreateFork(ctx, owner, repo, nil)
// https://pkg.go.dev/github.com/google/go-github/v32@v32.1.0/github#RepositoriesService.CreateFork
// This method might return an *AcceptedError and a status code of 202. This is because this is
// the status that GitHub returns to signify that it is now computing creating the fork in a
// background task. In this event, the Repository value will be returned, which includes the
// details about the pending fork. A follow up request, after a delay of a second or so, should
// result in a successful request.
if resp.StatusCode == 202 { // *AcceptedError
time.Sleep(time.Second * 5)
} else if err != nil {
return nil, fmt.Errorf("error creating fork: %w", err)
}
// create fork tree from base and changed files
forkTree, _, err := ghc.Git.CreateTree(ctx, *fork.Owner.Login, *fork.Name, *baseTree.SHA, changes)
if err != nil {
return nil, fmt.Errorf("error creating fork tree: %w", err)
}
// create fork commit
forkCommit, _, err := ghc.Git.CreateCommit(ctx, *fork.Owner.Login, *fork.Name, &github.Commit{
Message: github.String(title),
Tree: &github.Tree{SHA: forkTree.SHA},
Parents: []*github.Commit{{SHA: baseCommit.SHA}},
})
if err != nil {
return nil, fmt.Errorf("error creating fork commit: %w", err)
}
klog.Infof("PR commit '%s' created: %s", forkCommit.GetSHA(), forkCommit.GetHTMLURL())
// create PR branch
prBranch := branch + forkCommit.GetSHA()[:7]
prRef, _, err := ghc.Git.CreateRef(ctx, *fork.Owner.Login, *fork.Name, &github.Reference{
Ref: github.String("refs/heads/" + prBranch),
Object: &github.GitObject{
Type: github.String("commit"),
SHA: forkCommit.SHA,
},
})
if err != nil {
return nil, fmt.Errorf("error creating PR branch: %w", err)
}
klog.Infof("PR branch '%s' created: %s", prBranch, prRef.GetURL())
// create PR
modifiable := true
pr, _, err := ghc.PullRequests.Create(ctx, owner, repo, &github.NewPullRequest{
Title: github.String(title),
Head: github.String(*fork.Owner.Login + ":" + prBranch),
Base: github.String(base),
Body: github.String(fmt.Sprintf("fixes #%d\n\nAutomatically created PR to update repo according to the Plan:\n\n```\n%s\n```", issue, thePlan(plan, data))),
MaintainerCanModify: &modifiable,
})
if err != nil {
return nil, fmt.Errorf("error creating pull request: %w", err)
}
return pr, nil
}
// ghUpdate updates remote GitHub owner/repo tree according to the given token, plan and data,
// returns resulting changes, and any error occurred
func ghUpdate(ctx context.Context, owner, repo string, tree *github.Tree, token string, plan map[string]Patch, data Data) (changes []*github.TreeEntry, err error) {
ghc := ghClient(ctx, token)
// load each plan's path content and update it creating new GitHub TreeEntries
cnt := len(plan) // expected number of files to change
for _, org := range tree.Entries {
if *org.Type == "blob" {
if patch, match := plan[*org.Path]; match {
blob, _, err := ghc.Git.GetBlobRaw(ctx, owner, repo, *org.SHA)
if err != nil {
return nil, fmt.Errorf("error getting file: %w", err)
}
patch.Content = blob
changed, err := patch.apply(data)
if err != nil {
return nil, fmt.Errorf("error patching file: %w", err)
}
if changed {
// add github.TreeEntry that will replace original path content with patched one
changes = append(changes, &github.TreeEntry{
Path: org.Path,
Mode: org.Mode,
Type: org.Type,
Content: github.String(string(patch.Content)),
})
}
if cnt--; cnt == 0 {
break
}
}
}
}
if cnt != 0 {
return nil, fmt.Errorf("error finding all the files (%d missing) - check the Plan: %w", cnt, err)
}
return changes, nil
}
// ghFindPR returns URL of the PR if found in the given GitHub ower/repo base and any error occurred
func ghFindPR(ctx context.Context, title, owner, repo, base, token string) (url string, err error) {
ghc := ghClient(ctx, token)
// walk through the paginated list of all pull requests, from latest to older releases
opts := &github.PullRequestListOptions{State: "all", Base: base, ListOptions: github.ListOptions{PerPage: ghListOptionsPerPage}}
for (opts.Page+1)*ghListOptionsPerPage <= prSearchLimit {
prs, resp, err := ghc.PullRequests.List(ctx, owner, repo, opts)
if err != nil {
return "", err
}
for _, pr := range prs {
if pr.GetTitle() == title {
return pr.GetHTMLURL(), nil
}
}
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
opts.Page = resp.NextPage
}
return vDefault, vNewest, nil
return "", nil
}
// updateKubernetesVersions updates DefaultKubernetesVersion to vDefault release and
// NewestKubernetesVersion to vNewest release, and returns any error
func updateKubernetesVersions(vDefault, vNewest string) error {
if err := replaceAllString("../../pkg/minikube/constants/constants.go", map[string]string{
`DefaultKubernetesVersion = \".*`: "DefaultKubernetesVersion = \"" + vDefault + "\"",
`NewestKubernetesVersion = \".*`: "NewestKubernetesVersion = \"" + vNewest + "\"",
}); err != nil {
return err
}
// ghReleases returns current stable release and latest rc or beta pre-release
// from GitHub owner/repo repository, and any error;
// if latest pre-release version is lower than current stable release, then it
// will return current stable release for both
func ghReleases(ctx context.Context, owner, repo, token string) (stable, latest string, err error) {
ghc := ghClient(ctx, token)
if err := replaceAllString("../../site/content/en/docs/commands/start.md", map[string]string{
`'stable' for .*,`: "'stable' for " + vDefault + ",",
`'latest' for .*\)`: "'latest' for " + vNewest + ")",
}); err != nil {
return err
}
// update testData just for the latest 'v<MAJOR>.<MINOR>.0' from vDefault
vDefaultMM := vDefault[:strings.LastIndex(vDefault, ".")]
testData := "../../pkg/minikube/bootstrapper/bsutil/testdata/" + vDefaultMM
return filepath.Walk(testData, func(path string, info os.FileInfo, err error) error {
// walk through the paginated list of all owner/repo releases, from newest to oldest
opts := &github.ListOptions{PerPage: ghListOptionsPerPage}
for {
rls, resp, err := ghc.Repositories.ListReleases(ctx, owner, repo, opts)
if err != nil {
return err
return "", "", err
}
if !strings.HasSuffix(path, "default.yaml") {
return nil
for _, rl := range rls {
ver := rl.GetName()
if ver == "" {
continue
}
// check if ver version is a release (ie, 'v1.19.2') or a
// pre-release (ie, 'v1.19.3-rc.0' or 'v1.19.0-beta.2') channel ch
// note: github.RepositoryRelease GetPrerelease() bool would be useful for all pre-rels
ch := strings.Split(ver, "-")
if len(ch) == 1 && stable == "" {
stable = ver
} else if len(ch) > 1 && latest == "" {
if strings.HasPrefix(ch[1], "rc") || strings.HasPrefix(ch[1], "beta") {
latest = ver
}
}
if stable != "" && latest != "" {
// make sure that v.Latest >= stable
if latest < stable {
latest = stable
}
return stable, latest, nil
}
}
return replaceAllString(path, map[string]string{
`kubernetesVersion: .*`: "kubernetesVersion: " + vDefaultMM + ".0",
})
})
if resp.NextPage == 0 {
break
}
opts.Page = resp.NextPage
}
return stable, latest, nil
}
// replaceAllString replaces all occuranes of map's keys with their respective values in the file
func replaceAllString(path string, pairs map[string]string) error {
fb, err := ioutil.ReadFile(path)
if err != nil {
return err
// ghClient returns GitHub Client with a given context and optional token for authenticated requests
func ghClient(ctx context.Context, token string) *github.Client {
if token == "" {
return github.NewClient(nil)
}
info, err := os.Stat(path)
if err != nil {
return err
}
mode := info.Mode()
f := string(fb)
for org, new := range pairs {
re := regexp.MustCompile(org)
f = re.ReplaceAllString(f, new)
}
if err := ioutil.WriteFile(path, []byte(f), mode); err != nil {
return err
}
return nil
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
tc := oauth2.NewClient(ctx, ts)
return github.NewClient(tc)
}
// thePlan parses and returns updated plan replacing template placeholders with concrete data values
func thePlan(plan map[string]Patch, data Data) (prettyprint string) {
for _, p := range plan {
for src, dst := range p.Replace {
tmpl := template.Must(template.New("").Parse(dst))
buf := new(bytes.Buffer)
if err := tmpl.Execute(buf, data); err != nil {
klog.Fatalf("Error parsing the Plan: %v", err)
return fmt.Sprintf("%+v", plan)
}
p.Replace[src] = buf.String()
}
}
str, err := json.MarshalIndent(plan, "", " ")
if err != nil {
klog.Fatalf("Error parsing the Plan: %v", err)
return fmt.Sprintf("%+v", plan)
}
return string(str)
}

View File

@ -406,18 +406,12 @@ func Start(wg *sync.WaitGroup, cc *config.ClusterConfig, toEnable map[string]boo
var awg sync.WaitGroup
enabledAddons := []string{}
deferredAddons := []string{}
defer func() { // making it show after verifications (see #7613)
register.Reg.SetStep(register.EnablingAddons)
out.T(style.AddonEnable, "Enabled addons: {{.addons}}", out.V{"addons": strings.Join(enabledAddons, ", ")})
}()
for _, a := range toEnableList {
if a == "gcp-auth" {
deferredAddons = append(deferredAddons, a)
continue
}
awg.Add(1)
go func(name string) {
err := RunCallbacks(cc, name, "true")
@ -433,16 +427,6 @@ func Start(wg *sync.WaitGroup, cc *config.ClusterConfig, toEnable map[string]boo
// Wait until all of the addons are enabled before updating the config (not thread safe)
awg.Wait()
// Now run the deferred addons
for _, a := range deferredAddons {
err := RunCallbacks(cc, a, "true")
if err != nil {
out.WarningT("Enabling '{{.name}}' returned an error: {{.error}}", out.V{"name": a, "error": err})
} else {
enabledAddons = append(enabledAddons, a)
}
}
for _, a := range enabledAddons {
if err := Set(cc, a, "true"); err != nil {
klog.Errorf("store failed: %v", err)

View File

@ -19,12 +19,17 @@ limitations under the License.
package integration
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
"reflect"
"runtime"
"strings"
"testing"
"time"
@ -40,8 +45,21 @@ func TestAddons(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), Minutes(40))
defer Cleanup(t, profile, cancel)
args := append([]string{"start", "-p", profile, "--wait=false", "--memory=2600", "--alsologtostderr", "--addons=registry", "--addons=metrics-server", "--addons=helm-tiller", "--addons=olm", "--addons=volumesnapshots", "--addons=csi-hostpath-driver"}, StartArgs()...)
if !NoneDriver() { // none doesn't support ingress
// Set an env var to point to our dummy credentials file
err := os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", filepath.Join(*testdataDir, "gcp-creds.json"))
defer os.Unsetenv("GOOGLE_APPLICATION_CREDENTIALS")
if err != nil {
t.Fatalf("Failed setting GOOGLE_APPLICATION_CREDENTIALS env var: %v", err)
}
err = os.Setenv("GOOGLE_CLOUD_PROJECT", "this_is_fake")
defer os.Unsetenv("GOOGLE_CLOUD_PROJECT")
if err != nil {
t.Fatalf("Failed setting GOOGLE_CLOUD_PROJECT env var: %v", err)
}
args := append([]string{"start", "-p", profile, "--wait=false", "--memory=2600", "--alsologtostderr", "--addons=registry", "--addons=metrics-server", "--addons=helm-tiller", "--addons=olm", "--addons=volumesnapshots", "--addons=csi-hostpath-driver", "--addons=gcp-auth"}, StartArgs()...)
if !NoneDriver() && !(runtime.GOOS == "darwin" && KicDriver()) { // none doesn't support ingress
args = append(args, "--addons=ingress")
}
rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
@ -61,6 +79,7 @@ func TestAddons(t *testing.T) {
{"HelmTiller", validateHelmTillerAddon},
{"Olm", validateOlmAddon},
{"CSI", validateCSIDriverAndSnapshots},
{"GCPAuth", validateGCPAuthAddon},
}
for _, tc := range tests {
tc := tc
@ -92,7 +111,7 @@ func TestAddons(t *testing.T) {
func validateIngressAddon(ctx context.Context, t *testing.T, profile string) {
defer PostMortemLogs(t, profile)
if NoneDriver() {
if NoneDriver() || (runtime.GOOS == "darwin" && KicDriver()) {
t.Skipf("skipping: ssh unsupported by none")
}
@ -504,3 +523,71 @@ func validateCSIDriverAndSnapshots(ctx context.Context, t *testing.T, profile st
t.Errorf("failed to disable volumesnapshots addon: args %q: %v", rr.Command(), err)
}
}
func validateGCPAuthAddon(ctx context.Context, t *testing.T, profile string) {
defer PostMortemLogs(t, profile)
// schedule a pod to check environment variables
rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "create", "-f", filepath.Join(*testdataDir, "busybox.yaml")))
if err != nil {
t.Fatalf("%s failed: %v", rr.Command(), err)
}
// 8 minutes, because 4 is not enough for images to pull in all cases.
names, err := PodWait(ctx, t, profile, "default", "integration-test=busybox", Minutes(8))
if err != nil {
t.Fatalf("wait: %v", err)
}
// Use this pod to confirm that the env vars are set correctly
rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "exec", names[0], "--", "/bin/sh", "-c", "printenv GOOGLE_APPLICATION_CREDENTIALS"))
if err != nil {
t.Fatalf("printenv creds: %v", err)
}
got := strings.TrimSpace(rr.Stdout.String())
expected := "/google-app-creds.json"
if got != expected {
t.Errorf("'printenv GOOGLE_APPLICATION_CREDENTIALS' returned %s, expected %s", got, expected)
}
// Make sure the file contents are correct
rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "exec", names[0], "--", "/bin/sh", "-c", "cat /google-app-creds.json"))
if err != nil {
t.Fatalf("cat creds: %v", err)
}
var gotJSON map[string]string
err = json.Unmarshal(bytes.TrimSpace(rr.Stdout.Bytes()), &gotJSON)
if err != nil {
t.Fatalf("unmarshal json: %v", err)
}
expectedJSON := map[string]string{
"client_id": "haha",
"client_secret": "nice_try",
"quota_project_id": "this_is_fake",
"refresh_token": "maybe_next_time",
"type": "authorized_user",
}
if !reflect.DeepEqual(gotJSON, expectedJSON) {
t.Fatalf("unexpected creds file: got %v, expected %v", gotJSON, expectedJSON)
}
// Check the GOOGLE_CLOUD_PROJECT env var as well
rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "exec", names[0], "--", "/bin/sh", "-c", "printenv GOOGLE_CLOUD_PROJECT"))
if err != nil {
t.Fatalf("print env project: %v", err)
}
got = strings.TrimSpace(rr.Stdout.String())
expected = "this_is_fake"
if got != expected {
t.Errorf("'printenv GOOGLE_APPLICATION_CREDENTIALS' returned %s, expected %s", got, expected)
}
rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "addons", "disable", "gcp-auth", "--alsologtostderr", "-v=1"))
if err != nil {
t.Errorf("failed disabling gcp-auth addon. arg %q.s %v", rr.Command(), err)
}
}

View File

@ -0,0 +1,7 @@
{
"client_id": "haha",
"client_secret": "nice_try",
"quota_project_id": "this_is_fake",
"refresh_token": "maybe_next_time",
"type": "authorized_user"
}