diff --git a/.github/workflows/iso.yml b/.github/workflows/iso.yml index 7294872fc2..0245476855 100644 --- a/.github/workflows/iso.yml +++ b/.github/workflows/iso.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 136bf31af8..b4b99403be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Makefile b/Makefile index 25543b7200..95969958c4 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/deploy/addons/gcp-auth/gcp-auth-webhook.yaml b/deploy/addons/gcp-auth/gcp-auth-webhook.yaml index 7513442603..c91266a6e0 100644 --- a/deploy/addons/gcp-auth/gcp-auth-webhook.yaml +++ b/deploy/addons/gcp-auth/gcp-auth-webhook.yaml @@ -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 diff --git a/deploy/addons/storage-provisioner/storage-provisioner.yaml.tmpl b/deploy/addons/storage-provisioner/storage-provisioner.yaml.tmpl index afee101046..61d7adcdc0 100644 --- a/deploy/addons/storage-provisioner/storage-provisioner.yaml.tmpl +++ b/deploy/addons/storage-provisioner/storage-provisioner.yaml.tmpl @@ -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 diff --git a/deploy/iso/minikube-iso/configs/minikube_defconfig b/deploy/iso/minikube-iso/configs/minikube_defconfig index b1b8252df0..c3a8a26c38 100644 --- a/deploy/iso/minikube-iso/configs/minikube_defconfig +++ b/deploy/iso/minikube-iso/configs/minikube_defconfig @@ -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 diff --git a/deploy/iso/minikube-iso/package/cni-plugins/Config.in b/deploy/iso/minikube-iso/package/cni-plugins/Config.in index 9566518974..cdca8f72dd 100644 --- a/deploy/iso/minikube-iso/package/cni-plugins/Config.in +++ b/deploy/iso/minikube-iso/package/cni-plugins/Config.in @@ -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 diff --git a/deploy/iso/minikube-iso/package/cni-plugins/cni-plugins.mk b/deploy/iso/minikube-iso/package/cni-plugins/cni-plugins.mk index c670efeaa6..659e186e87 100644 --- a/deploy/iso/minikube-iso/package/cni-plugins/cni-plugins.mk +++ b/deploy/iso/minikube-iso/package/cni-plugins/cni-plugins.mk @@ -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 diff --git a/deploy/iso/minikube-iso/package/cni/Config.in b/deploy/iso/minikube-iso/package/cni/Config.in index ca60da49fc..b711219701 100644 --- a/deploy/iso/minikube-iso/package/cni/Config.in +++ b/deploy/iso/minikube-iso/package/cni/Config.in @@ -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 diff --git a/deploy/iso/minikube-iso/package/cni/cni.mk b/deploy/iso/minikube-iso/package/cni/cni.mk index 14599e0876..396e966ed0 100644 --- a/deploy/iso/minikube-iso/package/cni/cni.mk +++ b/deploy/iso/minikube-iso/package/cni/cni.mk @@ -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)" \ diff --git a/deploy/iso/minikube-iso/package/conmon/Config.in b/deploy/iso/minikube-iso/package/conmon/Config.in index 0dd4344dc9..6d4df09cf9 100644 --- a/deploy/iso/minikube-iso/package/conmon/Config.in +++ b/deploy/iso/minikube-iso/package/conmon/Config.in @@ -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 diff --git a/deploy/iso/minikube-iso/package/containerd-bin/Config.in b/deploy/iso/minikube-iso/package/containerd-bin/Config.in index 988ab2c4db..fc5f328248 100644 --- a/deploy/iso/minikube-iso/package/containerd-bin/Config.in +++ b/deploy/iso/minikube-iso/package/containerd-bin/Config.in @@ -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 diff --git a/deploy/iso/minikube-iso/package/containerd-bin/containerd-bin.mk b/deploy/iso/minikube-iso/package/containerd-bin/containerd-bin.mk index cba797685d..e9af06aab8 100644 --- a/deploy/iso/minikube-iso/package/containerd-bin/containerd-bin.mk +++ b/deploy/iso/minikube-iso/package/containerd-bin/containerd-bin.mk @@ -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)" \ diff --git a/deploy/iso/minikube-iso/package/crio-bin/Config.in b/deploy/iso/minikube-iso/package/crio-bin/Config.in index 3dd6743912..22d2c45e9d 100644 --- a/deploy/iso/minikube-iso/package/crio-bin/Config.in +++ b/deploy/iso/minikube-iso/package/crio-bin/Config.in @@ -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 diff --git a/deploy/iso/minikube-iso/package/crio-bin/crio-bin.mk b/deploy/iso/minikube-iso/package/crio-bin/crio-bin.mk index 630304a387..814b31e4dd 100644 --- a/deploy/iso/minikube-iso/package/crio-bin/crio-bin.mk +++ b/deploy/iso/minikube-iso/package/crio-bin/crio-bin.mk @@ -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)" \ diff --git a/deploy/iso/minikube-iso/package/podman/Config.in b/deploy/iso/minikube-iso/package/podman/Config.in index f0d92a2cdc..d73162af7f 100644 --- a/deploy/iso/minikube-iso/package/podman/Config.in +++ b/deploy/iso/minikube-iso/package/podman/Config.in @@ -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 diff --git a/deploy/iso/minikube-iso/package/podman/podman.mk b/deploy/iso/minikube-iso/package/podman/podman.mk index 6300a3723d..a5c297b7f2 100644 --- a/deploy/iso/minikube-iso/package/podman/podman.mk +++ b/deploy/iso/minikube-iso/package/podman/podman.mk @@ -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" \ diff --git a/deploy/iso/minikube-iso/package/runc-master/Config.in b/deploy/iso/minikube-iso/package/runc-master/Config.in index c33153880e..e3e4a58454 100644 --- a/deploy/iso/minikube-iso/package/runc-master/Config.in +++ b/deploy/iso/minikube-iso/package/runc-master/Config.in @@ -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 diff --git a/deploy/iso/minikube-iso/package/runc-master/runc-master.mk b/deploy/iso/minikube-iso/package/runc-master/runc-master.mk index 78f8402105..ca12a62b97 100644 --- a/deploy/iso/minikube-iso/package/runc-master/runc-master.mk +++ b/deploy/iso/minikube-iso/package/runc-master/runc-master.mk @@ -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)" \ diff --git a/deploy/minikube/releases.json b/deploy/minikube/releases.json index a37571ded3..74ddc029ba 100644 --- a/deploy/minikube/releases.json +++ b/deploy/minikube/releases.json @@ -1,4 +1,12 @@ [ + { + "name": "v1.14.0", + "checksums": { + "darwin": "71dee6241a93945b40ea7188ad15459e50e7b65eab09fed7302d8cacdc58585c", + "linux": "8727635489be895d9b9cfaa5cb599f45799a28fb07e0a2aac351a9aa1c4b46c1", + "windows": "0317e6c338da23ccf0aba698668c6d919f22e1482340a09d1269220063937aeb" + } + }, { "name": "v1.13.1", "checksums": { diff --git a/hack/kubernetes_version/update_kubernetes_version.go b/hack/kubernetes_version/update_kubernetes_version.go index e20da69ba3..146506e9b4 100644 --- a/hack/kubernetes_version/update_kubernetes_version.go +++ b/hack/kubernetes_version/update_kubernetes_version.go @@ -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=: 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=: 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..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) } diff --git a/pkg/addons/addons.go b/pkg/addons/addons.go index cf1ef671ff..14a3b4e913 100644 --- a/pkg/addons/addons.go +++ b/pkg/addons/addons.go @@ -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) diff --git a/test/integration/addons_test.go b/test/integration/addons_test.go index b0d03fe5d6..d9c686dd2d 100644 --- a/test/integration/addons_test.go +++ b/test/integration/addons_test.go @@ -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) + } +} diff --git a/test/integration/testdata/gcp-creds.json b/test/integration/testdata/gcp-creds.json new file mode 100644 index 0000000000..e2115d8062 --- /dev/null +++ b/test/integration/testdata/gcp-creds.json @@ -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" +}