diff --git a/.codecov.yml b/.codecov.yml index 69cb76019a..17faa8c54f 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1 +1,7 @@ -comment: false +comment: + layout: "reach, diff, flags, files" + behavior: default + require_changes: false # if true: only post the comment if coverage changes + require_base: no # [yes :: must have a base report to post] + require_head: yes # [yes :: must have a head report to post] + branches: null # branch names that can post comment \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..77ab7bfbc2 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,8 @@ + diff --git a/.gitignore b/.gitignore index 35e907bf71..27689109f1 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ _testmain.go #iso version file deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/etc/VERSION +/pkg/minikube/assets/assets.go-e /pkg/minikube/assets/assets.go /pkg/minikube/translate/translations.go /pkg/minikube/translate/translations.go-e diff --git a/.travis.yml b/.travis.yml index 6bbc93422d..ca4772639c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,45 @@ -language: go +--- +# linted with yamllint os: linux - +language: go +go: + - 1.12.12 env: - - GOPROXY=https://proxy.golang.org + global: + - GOPROXY=https://proxy.golang.org matrix: include: - - go: 1.12.9 - - language: python - before_install: pip install flake8 - script: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + - language: go + name: Code Lint + go: 1.12.12 + env: + - TESTSUITE=lintall + before_install: + - sudo apt-get install -y libvirt-dev + script: make test -before_install: - - sudo apt-get install -y libvirt-dev -install: - - echo "Don't run anything." -script: - - make test + - language: go + name: Unit Test + go: 1.12.12 + env: + - TESTSUITE=unittest + before_install: + - sudo apt-get install -y libvirt-dev + script: make test + + - language: go + name: Build + go: 1.12.12 + script: make after_success: - bash <(curl -s https://codecov.io/bash) +travisBuddy: + regex: (FAIL:|\.go:\d+:|^panic:|failed$) notifications: - webhooks: https://www.travisbuddy.com/ - on_success: never # travisbuddy don't comment on successfull \ No newline at end of file + webhooks: + urls: + - https://www.travisbuddy.com?only=failed,errored + on_success: never # don't comment on successful builds. + on_failure: always + on_cancel: always + on_error: always diff --git a/CHANGELOG.md b/CHANGELOG.md index d222a7373c..6c8312bc93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,121 @@ # Release Notes +## Version 1.5.2 - 2019-10-31 (Happy Halloween!) + +* service: fix --url mode [#5790](https://github.com/kubernetes/minikube/pull/5790) +* Refactor command runner interface, allow stdin writes [#5530](https://github.com/kubernetes/minikube/pull/5530) +* macOS install docs: minikube is a normal Homebrew formula now [#5750](https://github.com/kubernetes/minikube/pull/5750) +* Allow CPU count check to be disabled using --force [#5803](https://github.com/kubernetes/minikube/pull/5803) +* Make network validation friendlier, especially to corp networks [#5802](https://github.com/kubernetes/minikube/pull/5802) + +Thank you to our contributors for this release: + +- Anders F Björklund +- Issy Long +- Medya Ghazizadeh +- Thomas Strömberg + +## Version 1.5.1 - 2019-10-29 + +* Set Docker open-files limit ( 'ulimit -n') to be consistent with other runtimes [#5761](https://github.com/kubernetes/minikube/pull/5761) +* Use fixed uid/gid for the default user account [#5767](https://github.com/kubernetes/minikube/pull/5767) +* Set --wait=false to default but still wait for apiserver [#5757](https://github.com/kubernetes/minikube/pull/5757) +* kubelet: Pass --config to use kubeadm generated configuration [#5697](https://github.com/kubernetes/minikube/pull/5697) +* Refactor to remove opening browser and just return url(s) [#5718](https://github.com/kubernetes/minikube/pull/5718) + +Huge thank you for this release towards our contributors: + +- Anders F Björklund +- Medya Ghazizadeh +- Nanik T +- Priya Wadhwa +- Sharif Elgamal +- Thomas Strömberg + +## Version 1.5.0 - 2019-10-25 + +* Default to best-available local hypervisor rather than VirtualBox [#5700](https://github.com/kubernetes/minikube/pull/5700) +* Update default Kubernetes version to v1.16.2 [#5731](https://github.com/kubernetes/minikube/pull/5731) +* Add json output for status [#5611](https://github.com/kubernetes/minikube/pull/5611) +* gvisor: Use chroot instead of LD_LIBRARY_PATH [#5735](https://github.com/kubernetes/minikube/pull/5735) +* Hide innocuous viper ConfigFileNotFoundError [#5732](https://github.com/kubernetes/minikube/pull/5732) + +Thank you to our contributors! + +- Anders F Björklund +- duohedron +- Javis Zhou +- Josh Woodcock +- Kenta Iso +- Marek Schwarz +- Medya Ghazizadeh +- Nanik T +- Rob Bruce +- Sharif Elgamal +- Thomas Strömberg + +## Version 1.5.0-beta.0 - 2019-10-21 + +* Fix node InternalIP not matching host-only address [#5427](https://github.com/kubernetes/minikube/pull/5427) +* Add helm-tiller addon [#5363](https://github.com/kubernetes/minikube/pull/5363) +* Add ingress-dns addon [#5507](https://github.com/kubernetes/minikube/pull/5507) +* Add validation checking for minikube profile [#5624](https://github.com/kubernetes/minikube/pull/5624) +* add ability to override autoupdating drivers [#5640](https://github.com/kubernetes/minikube/pull/5640) +* Add option to configure dnsDomain in kubeAdm [#5566](https://github.com/kubernetes/minikube/pull/5566) +* Added flags to purge configuration with minikube delete [#5548](https://github.com/kubernetes/minikube/pull/5548) +* Upgrade Buildroot to 2019.02 and VirtualBox to 5.2 [#5609](https://github.com/kubernetes/minikube/pull/5609) +* Add libmachine debug logs back [#5574](https://github.com/kubernetes/minikube/pull/5574) +* Add JSON output for addons list [#5601](https://github.com/kubernetes/minikube/pull/5601) +* Update default Kubernetes version to 1.16.1 [#5593](https://github.com/kubernetes/minikube/pull/5593) +* Upgrade nginx ingress controller to 0.26.1 [#5514](https://github.com/kubernetes/minikube/pull/5514) +* Initial translations for fr, es, de, ja, and zh-CN [#5466](https://github.com/kubernetes/minikube/pull/5466) +* PL translation [#5491](https://github.com/kubernetes/minikube/pull/5491) +* Warn if incompatible kubectl version is in use [#5596](https://github.com/kubernetes/minikube/pull/5596) +* Fix crash when deleting the cluster but it doesn't exist [#4980](https://github.com/kubernetes/minikube/pull/4980) +* Add json output for profile list [#5554](https://github.com/kubernetes/minikube/pull/5554) +* Allow addon enabling and disabling when minikube is not running [#5565](https://github.com/kubernetes/minikube/pull/5565) +* Added option to delete all profiles [#4780](https://github.com/kubernetes/minikube/pull/4780) +* Replace registry-creds addon ReplicationController with Deployment [#5586](https://github.com/kubernetes/minikube/pull/5586) +* Performance and security enhancement for ingress-dns addon [#5614](https://github.com/kubernetes/minikube/pull/5614) +* Add addons flag to 'minikube start' in order to enable specified addons [#5543](https://github.com/kubernetes/minikube/pull/5543) +* Warn when a user tries to set a profile name that is unlikely to be valid [#4999](https://github.com/kubernetes/minikube/pull/4999) +* Make error message more human readable [#5563](https://github.com/kubernetes/minikube/pull/5563) +* Adjusted Terminal Style Detection [#5508](https://github.com/kubernetes/minikube/pull/5508) +* Fixes image repository flags when using CRI-O and containerd runtime [#5447](https://github.com/kubernetes/minikube/pull/5447) +* fix "minikube update-context" command fail [#5626](https://github.com/kubernetes/minikube/pull/5626) +* Fix pods not being scheduled when ingress deployment is patched [#5519](https://github.com/kubernetes/minikube/pull/5519) +* Fix order of parameters to CurrentContext funcs [#5439](https://github.com/kubernetes/minikube/pull/5439) +* Add solution for VERR_VMX_MSR_ALL_VMX_DISABLED [#5460](https://github.com/kubernetes/minikube/pull/5460) +* fr: fix translations of environment & existent [#5483](https://github.com/kubernetes/minikube/pull/5483) +* optimizing Chinese translation [#5201](https://github.com/kubernetes/minikube/pull/5201) +* Change systemd unit files perm to 644 [#5492](https://github.com/kubernetes/minikube/pull/5492) + +Huge thank you for this release towards our contributors: +- Anders F Björklund +- bhanu011 +- chentanjun +- Cornelius Weig +- Doug A +- hwdef +- James Peach +- Josh Woodcock +- Kenta Iso +- Marcin Niemira +- Medya Ghazizadeh +- Nanik T +- Pranav Jituri +- Samuel Almeida +- serhatcetinkaya +- Sharif Elgamal +- tanjunchen +- Thomas Strömberg +- u5surf +- yugo horie +- yuxiaobo +- Zhongcheng Lao +- Zoltán Reegn + + ## Version 1.4.0 - 2019-09-17 Notable user-facing changes: @@ -784,7 +900,7 @@ Huge thank you for this release towards our contributors: * Issue #3037 change dependency management to dep [#3136](https://github.com/kubernetes/minikube/pull/3136) * Update dashboard version to v1.10.0 [#3122](https://github.com/kubernetes/minikube/pull/3122) -* fix: --format outputs any string, --https only subsitute http URL scheme [#3114](https://github.com/kubernetes/minikube/pull/3114) +* fix: --format outputs any string, --https only substitute http URL scheme [#3114](https://github.com/kubernetes/minikube/pull/3114) * Change default docker storage driver to overlay2 [#3121](https://github.com/kubernetes/minikube/pull/3121) * Add env variable for default ES_JAVA_OPTS [#3086](https://github.com/kubernetes/minikube/pull/3086) * fix(cli): `minikube start --mount --mountsting` without write permission [#2671](https://github.com/kubernetes/minikube/pull/2671) diff --git a/Makefile b/Makefile index 89ace1bfa0..9468331188 100755 --- a/Makefile +++ b/Makefile @@ -14,22 +14,22 @@ # Bump these on release - and please check ISO_VERSION for correctness. VERSION_MAJOR ?= 1 -VERSION_MINOR ?= 4 -VERSION_BUILD ?= 0 +VERSION_MINOR ?= 5 +VERSION_BUILD ?= 2 RAW_VERSION=$(VERSION_MAJOR).$(VERSION_MINOR).${VERSION_BUILD} VERSION ?= v$(RAW_VERSION) # Default to .0 for higher cache hit rates, as build increments typically don't require new ISO versions -ISO_VERSION ?= v$(VERSION_MAJOR).$(VERSION_MINOR).0 +ISO_VERSION ?= v$(VERSION_MAJOR).$(VERSION_MINOR).1 # Dashes are valid in semver, but not Linux packaging. Use ~ to delimit alpha/beta DEB_VERSION ?= $(subst -,~,$(RAW_VERSION)) RPM_VERSION ?= $(DEB_VERSION) # used by hack/jenkins/release_build_and_upload.sh and KVM_BUILD_IMAGE, see also BUILD_IMAGE below -GO_VERSION ?= 1.12.9 +GO_VERSION ?= 1.12.12 INSTALL_SIZE ?= $(shell du out/minikube-windows-amd64.exe | cut -f1) -BUILDROOT_BRANCH ?= 2018.05.3 +BUILDROOT_BRANCH ?= 2019.02.6 REGISTRY?=gcr.io/k8s-minikube # Get git commit id @@ -49,19 +49,21 @@ MINIKUBE_BUCKET ?= minikube/releases MINIKUBE_UPLOAD_LOCATION := gs://${MINIKUBE_BUCKET} MINIKUBE_RELEASES_URL=https://github.com/kubernetes/minikube/releases/download -KERNEL_VERSION ?= 4.15 +KERNEL_VERSION ?= 4.19.76 # latest from https://github.com/golangci/golangci-lint/releases -GOLINT_VERSION ?= v1.18.0 +GOLINT_VERSION ?= v1.21.0 # Limit number of default jobs, to avoid the CI builds running out of memory GOLINT_JOBS ?= 4 # see https://github.com/golangci/golangci-lint#memory-usage-of-golangci-lint -GOLINT_GOGC ?= 8 +GOLINT_GOGC ?= 100 # options for lint (golangci-lint) -GOLINT_OPTIONS = --deadline 4m \ +GOLINT_OPTIONS = --timeout 4m \ --build-tags "${MINIKUBE_INTEGRATION_BUILD_TAGS}" \ - --enable goimports,gocritic,golint,gocyclo,interfacer,misspell,nakedret,stylecheck,unconvert,unparam \ + --enable goimports,gocritic,golint,gocyclo,misspell,nakedret,stylecheck,unconvert,unparam,dogsled \ --exclude 'variable on range scope.*in function literal|ifElseChain' +# Major version of gvisor image. Increment when there are breaking changes. +GVISOR_IMAGE_VERSION ?= 2 export GO111MODULE := on @@ -97,6 +99,7 @@ MARKDOWNLINT ?= markdownlint MINIKUBE_MARKDOWN_FILES := README.md docs CONTRIBUTING.md CHANGELOG.md MINIKUBE_BUILD_TAGS := container_image_ostree_stub containers_image_openpgp +MINIKUBE_BUILD_TAGS += go_getter_nos3 go_getter_nogcs MINIKUBE_INTEGRATION_BUILD_TAGS := integration $(MINIKUBE_BUILD_TAGS) CMD_SOURCE_DIRS = cmd pkg @@ -127,6 +130,12 @@ endif ifeq ($(GOOS),windows) IS_EXE = .exe + DIRSEP_ = \\ + DIRSEP = $(strip $(DIRSEP_)) + PATHSEP = ; +else + DIRSEP = / + PATHSEP = : endif @@ -136,8 +145,15 @@ out/minikube$(IS_EXE): out/minikube-$(GOOS)-$(GOARCH)$(IS_EXE) out/minikube-windows-amd64.exe: out/minikube-windows-amd64 cp $< $@ -.PHONY: minikube-linux-amd64 minikube-darwin-amd64 minikube-windows-amd64.exe +out/minikube-linux-x86_64: out/minikube-linux-amd64 + cp $< $@ + +out/minikube-linux-aarch64: out/minikube-linux-arm64 + cp $< $@ + +.PHONY: minikube-linux-amd64 minikube-linux-arm64 minikube-darwin-amd64 minikube-windows-amd64.exe minikube-linux-amd64: out/minikube-linux-amd64 +minikube-linux-arm64: out/minikube-linux-arm64 minikube-darwin-amd64: out/minikube-darwin-amd64 minikube-windows-amd64.exe: out/minikube-windows-amd64.exe @@ -239,33 +255,27 @@ extract: pkg/minikube/assets/assets.go: $(shell find "deploy/addons" -type f) ifeq ($(MINIKUBE_BUILD_IN_DOCKER),y) $(call DOCKER,$(BUILD_IMAGE),/usr/bin/make $@) -else ifeq ($(GOOS),windows) - which go-bindata || GO111MODULE=off GOBIN="$(GOPATH)\bin" go get github.com/jteeuwen/go-bindata/... - PATH="$(PATH);$(GOPATH)\bin" go-bindata -nomemcopy -o $@ -pkg assets deploy/addons/... - -gofmt -s -w $@ -else - which go-bindata || GO111MODULE=off GOBIN=$(GOPATH)/bin go get github.com/jteeuwen/go-bindata/... - PATH="$(PATH):$(GOPATH)/bin" go-bindata -nomemcopy -o $@ -pkg assets deploy/addons/... - -gofmt -s -w $@ endif + which go-bindata || GO111MODULE=off GOBIN="$(GOPATH)$(DIRSEP)bin" go get github.com/jteeuwen/go-bindata/... + PATH="$(PATH)$(PATHSEP)$(GOPATH)$(DIRSEP)bin" go-bindata -nomemcopy -o $@ -pkg assets deploy/addons/... + -gofmt -s -w $@ + @#golint: Dns should be DNS (compat sed) + @sed -i -e 's/Dns/DNS/g' $@ && rm -f ./-e + @#golint: Html should be HTML (compat sed) + @sed -i -e 's/Html/HTML/g' $@ && rm -f ./-e pkg/minikube/translate/translations.go: $(shell find "translations/" -type f) ifeq ($(MINIKUBE_BUILD_IN_DOCKER),y) $(call DOCKER,$(BUILD_IMAGE),/usr/bin/make $@) -else ifeq ($(GOOS),windows) - which go-bindata || GO111MODULE=off GOBIN="$(GOPATH)\bin" go get github.com/jteeuwen/go-bindata/... - PATH="$(PATH);$(GOPATH)\bin" go-bindata -nomemcopy -o $@ -pkg translate translations/... - -gofmt -s -w $@ -else - which go-bindata || GO111MODULE=off GOBIN=$(GOPATH)/bin go get github.com/jteeuwen/go-bindata/... - PATH="$(PATH):$(GOPATH)/bin" go-bindata -nomemcopy -o $@ -pkg translate translations/... - -gofmt -s -w $@ endif + which go-bindata || GO111MODULE=off GOBIN="$(GOPATH)$(DIRSEP)bin" go get github.com/jteeuwen/go-bindata/... + PATH="$(PATH)$(PATHSEP)$(GOPATH)$(DIRSEP)bin" go-bindata -nomemcopy -o $@ -pkg translate translations/... + -gofmt -s -w $@ @#golint: Json should be JSON (compat sed) @sed -i -e 's/Json/JSON/' $@ && rm -f ./-e .PHONY: cross -cross: minikube-linux-amd64 minikube-darwin-amd64 minikube-windows-amd64.exe +cross: minikube-linux-amd64 minikube-linux-arm64 minikube-darwin-amd64 minikube-windows-amd64.exe .PHONY: windows windows: minikube-windows-amd64.exe @@ -281,7 +291,8 @@ e2e-cross: e2e-linux-amd64 e2e-darwin-amd64 e2e-windows-amd64.exe .PHONY: checksum checksum: - for f in out/minikube-linux-amd64 out/minikube-darwin-amd64 out/minikube-windows-amd64.exe out/minikube.iso \ + for f in out/minikube.iso out/minikube-linux-amd64 minikube-linux-arm64 \ + out/minikube-darwin-amd64 out/minikube-windows-amd64.exe \ out/docker-machine-driver-kvm2 out/docker-machine-driver-hyperkit; do \ if [ -f "$${f}" ]; then \ openssl sha256 "$${f}" | awk '{print $$2}' > "$${f}.sha256" ; \ @@ -312,7 +323,7 @@ vet: @go vet $(SOURCE_PACKAGES) .PHONY: golint -golint: +golint: pkg/minikube/assets/assets.go pkg/minikube/translate/translations.go @golint -set_exit_status $(SOURCE_PACKAGES) .PHONY: gocyclo @@ -348,21 +359,29 @@ mdlint: out/docs/minikube.md: $(shell find "cmd") $(shell find "pkg/minikube/constants") pkg/minikube/assets/assets.go pkg/minikube/translate/translations.go go run -ldflags="$(MINIKUBE_LDFLAGS)" -tags gendocs hack/help_text/gen_help_text.go -out/minikube_$(DEB_VERSION).deb: out/minikube-linux-amd64 +out/minikube_$(DEB_VERSION).deb: out/minikube_$(DEB_VERSION)-0_amd64.deb + cp $< $@ + +out/minikube_$(DEB_VERSION)-0_%.deb: out/minikube-linux-% cp -r installers/linux/deb/minikube_deb_template out/minikube_$(DEB_VERSION) chmod 0755 out/minikube_$(DEB_VERSION)/DEBIAN sed -E -i 's/--VERSION--/'$(DEB_VERSION)'/g' out/minikube_$(DEB_VERSION)/DEBIAN/control + sed -E -i 's/--ARCH--/'$*'/g' out/minikube_$(DEB_VERSION)/DEBIAN/control mkdir -p out/minikube_$(DEB_VERSION)/usr/bin - cp out/minikube-linux-amd64 out/minikube_$(DEB_VERSION)/usr/bin/minikube - fakeroot dpkg-deb --build out/minikube_$(DEB_VERSION) + cp $< out/minikube_$(DEB_VERSION)/usr/bin/minikube + fakeroot dpkg-deb --build out/minikube_$(DEB_VERSION) $@ rm -rf out/minikube_$(DEB_VERSION) -out/minikube-$(RPM_VERSION).rpm: out/minikube-linux-amd64 +out/minikube-$(RPM_VERSION).rpm: out/minikube-$(RPM_VERSION)-0.x86_64.rpm + cp $< $@ + +out/minikube-$(RPM_VERSION)-0.%.rpm: out/minikube-linux-% cp -r installers/linux/rpm/minikube_rpm_template out/minikube-$(RPM_VERSION) sed -E -i 's/--VERSION--/'$(RPM_VERSION)'/g' out/minikube-$(RPM_VERSION)/minikube.spec sed -E -i 's|--OUT--|'$(PWD)/out'|g' out/minikube-$(RPM_VERSION)/minikube.spec - rpmbuild -bb -D "_rpmdir $(PWD)/out" -D "_rpmfilename minikube-$(RPM_VERSION).rpm" \ + rpmbuild -bb -D "_rpmdir $(PWD)/out" --target $* \ out/minikube-$(RPM_VERSION)/minikube.spec + @mv out/$*/minikube-$(RPM_VERSION)-0.$*.rpm out/ && rmdir out/$* rm -rf out/minikube-$(RPM_VERSION) .PHONY: apt @@ -380,14 +399,16 @@ out/repodata/repomd.xml: out/minikube-$(RPM_VERSION).rpm -u "$(MINIKUBE_RELEASES_URL)/$(VERSION)/" out .SECONDEXPANSION: -TAR_TARGETS_linux := out/minikube-linux-amd64 out/docker-machine-driver-kvm2 -TAR_TARGETS_darwin := out/minikube-darwin-amd64 out/docker-machine-driver-hyperkit -TAR_TARGETS_windows := out/minikube-windows-amd64.exe -out/minikube-%-amd64.tar.gz: $$(TAR_TARGETS_$$*) +TAR_TARGETS_linux-amd64 := out/minikube-linux-amd64 out/docker-machine-driver-kvm2 +TAR_TARGETS_linux-arm64 := out/minikube-linux-arm64 +TAR_TARGETS_darwin-amd64 := out/minikube-darwin-amd64 out/docker-machine-driver-hyperkit +TAR_TARGETS_windows-amd64 := out/minikube-windows-amd64.exe +out/minikube-%.tar.gz: $$(TAR_TARGETS_$$*) tar -cvzf $@ $^ .PHONY: cross-tars -cross-tars: out/minikube-windows-amd64.tar.gz out/minikube-linux-amd64.tar.gz out/minikube-darwin-amd64.tar.gz +cross-tars: out/minikube-linux-amd64.tar.gz out/minikube-linux-arm64.tar.gz \ + out/minikube-windows-amd64.tar.gz out/minikube-darwin-amd64.tar.gz -cd out && $(SHA512SUM) *.tar.gz > SHA512SUM out/minikube-installer.exe: out/minikube-windows-amd64.exe @@ -464,11 +485,11 @@ out/gvisor-addon: pkg/minikube/assets/assets.go pkg/minikube/translate/translati .PHONY: gvisor-addon-image gvisor-addon-image: out/gvisor-addon - docker build -t $(REGISTRY)/gvisor-addon:latest -f deploy/gvisor/Dockerfile . + docker build -t $(REGISTRY)/gvisor-addon:$(GVISOR_IMAGE_VERSION) -f deploy/gvisor/Dockerfile . .PHONY: push-gvisor-addon-image push-gvisor-addon-image: gvisor-addon-image - gcloud docker -- push $(REGISTRY)/gvisor-addon:latest + gcloud docker -- push $(REGISTRY)/gvisor-addon:$(GVISOR_IMAGE_VERSION) .PHONY: release-iso release-iso: minikube_iso checksum @@ -551,3 +572,7 @@ site: site/themes/docsy/assets/vendor/bootstrap/package.js out/hugo/hugo --navigateToChanged \ --ignoreCache \ --buildFuture) + +.PHONY: out/mkcmp +out/mkcmp: + GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $@ cmd/performance/main.go diff --git a/OWNERS b/OWNERS index 52d291dc92..64b59d393c 100644 --- a/OWNERS +++ b/OWNERS @@ -7,12 +7,16 @@ reviewers: - RA489 - medyagh - josedonizetti + - blueelvis + - priyawadhwa approvers: - tstromberg - afbjorklund - sharifelgamal - RA489 - medyagh + - josedonizetti + - priyawadhwa emeritus_approvers: - dlorenc - luxas diff --git a/cmd/minikube/cmd/cache.go b/cmd/minikube/cmd/cache.go index b2795e4851..3952b8185a 100644 --- a/cmd/minikube/cmd/cache.go +++ b/cmd/minikube/cmd/cache.go @@ -22,9 +22,13 @@ import ( "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/exit" + "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/machine" ) +// cacheImageConfigKey is the config field name used to store which images we have previously cached +const cacheImageConfigKey = "cache" + // cacheCmd represents the cache command var cacheCmd = &cobra.Command{ Use: "cache", @@ -43,7 +47,7 @@ var addCacheCmd = &cobra.Command{ exit.WithError("Failed to cache and load images", err) } // Add images to config file - if err := cmdConfig.AddToConfigMap(constants.Cache, args); err != nil { + if err := cmdConfig.AddToConfigMap(cacheImageConfigKey, args); err != nil { exit.WithError("Failed to update config", err) } }, @@ -56,7 +60,7 @@ var deleteCacheCmd = &cobra.Command{ Long: "Delete an image from the local cache.", Run: func(cmd *cobra.Command, args []string) { // Delete images from config file - if err := cmdConfig.DeleteFromConfigMap(constants.Cache, args); err != nil { + if err := cmdConfig.DeleteFromConfigMap(cacheImageConfigKey, args); err != nil { exit.WithError("Failed to delete images from config", err) } // Delete images from cache/images directory @@ -67,11 +71,11 @@ var deleteCacheCmd = &cobra.Command{ } func imagesInConfigFile() ([]string, error) { - configFile, err := config.ReadConfig(constants.ConfigFile) + configFile, err := config.ReadConfig(localpath.ConfigFile) if err != nil { return nil, err } - if values, ok := configFile[constants.Cache]; ok { + if values, ok := configFile[cacheImageConfigKey]; ok { var images []string for key := range values.(map[string]interface{}) { images = append(images, key) diff --git a/cmd/minikube/cmd/cache_list.go b/cmd/minikube/cmd/cache_list.go index 2d7466061e..d557733c4f 100644 --- a/cmd/minikube/cmd/cache_list.go +++ b/cmd/minikube/cmd/cache_list.go @@ -22,10 +22,11 @@ import ( "github.com/spf13/cobra" cmdConfig "k8s.io/minikube/cmd/minikube/cmd/config" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/exit" ) +const defaultCacheListFormat = "{{.CacheImage}}\n" + var cacheListFormat string // CacheListTemplate represents the cache list template @@ -39,7 +40,7 @@ var listCacheCmd = &cobra.Command{ Short: "List all available images from the local cache.", Long: "List all available images from the local cache.", Run: func(cmd *cobra.Command, args []string) { - images, err := cmdConfig.ListConfigMap(constants.Cache) + images, err := cmdConfig.ListConfigMap(cacheImageConfigKey) if err != nil { exit.WithError("Failed to get image map", err) } @@ -50,7 +51,7 @@ var listCacheCmd = &cobra.Command{ } func init() { - listCacheCmd.Flags().StringVar(&cacheListFormat, "format", constants.DefaultCacheListFormat, + listCacheCmd.Flags().StringVar(&cacheListFormat, "format", defaultCacheListFormat, `Go template format string for the cache list output. The format for Go templates can be found here: https://golang.org/pkg/text/template/ For the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#CacheListTemplate`) cacheCmd.AddCommand(listCacheCmd) diff --git a/cmd/minikube/cmd/config/addons.go b/cmd/minikube/cmd/config/addons.go index e176f7dfc2..ddcf9ae076 100644 --- a/cmd/minikube/cmd/config/addons.go +++ b/cmd/minikube/cmd/config/addons.go @@ -25,7 +25,7 @@ import ( var AddonsCmd = &cobra.Command{ Use: "addons SUBCOMMAND [flags]", Short: "Modify minikube's kubernetes addons", - Long: `addons modifies minikube addons files using subcommands like "minikube addons enable heapster"`, + Long: `addons modifies minikube addons files using subcommands like "minikube addons enable dashboard"`, Run: func(cmd *cobra.Command, args []string) { if err := cmd.Help(); err != nil { glog.Errorf("help: %v", err) diff --git a/cmd/minikube/cmd/config/addons_list.go b/cmd/minikube/cmd/config/addons_list.go index bf05d976a7..08492a3678 100644 --- a/cmd/minikube/cmd/config/addons_list.go +++ b/cmd/minikube/cmd/config/addons_list.go @@ -17,17 +17,23 @@ limitations under the License. package config import ( + "encoding/json" + "fmt" "os" "sort" + "strings" "text/template" "github.com/spf13/cobra" "k8s.io/minikube/pkg/minikube/assets" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/exit" + "k8s.io/minikube/pkg/minikube/out" ) +const defaultAddonListFormat = "- {{.AddonName}}: {{.AddonStatus}}\n" + var addonListFormat string +var addonListOutput string // AddonListTemplate represents the addon list template type AddonListTemplate struct { @@ -43,28 +49,49 @@ var addonsListCmd = &cobra.Command{ if len(args) != 0 { exit.UsageT("usage: minikube addons list") } - err := addonList() - if err != nil { - exit.WithError("addon list failed", err) + + if addonListOutput != "list" && addonListFormat != defaultAddonListFormat { + exit.UsageT("Cannot use both --output and --format options") + } + + switch strings.ToLower(addonListOutput) { + case "list": + printAddonsList() + case "json": + printAddonsJSON() + default: + exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'list', 'json'", addonListOutput)) } }, } func init() { - AddonsCmd.Flags().StringVar(&addonListFormat, "format", constants.DefaultAddonListFormat, + addonsListCmd.Flags().StringVarP( + &addonListFormat, + "format", + "f", + defaultAddonListFormat, `Go template format string for the addon list output. The format for Go templates can be found here: https://golang.org/pkg/text/template/ For the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd/config#AddonListTemplate`) + + addonsListCmd.Flags().StringVarP( + &addonListOutput, + "output", + "o", + "list", + `minikube addons list --output OUTPUT. json, list`) + AddonsCmd.AddCommand(addonsListCmd) } -func stringFromStatus(addonStatus bool) string { +var stringFromStatus = func(addonStatus bool) string { if addonStatus { return "enabled" } return "disabled" } -func addonList() error { +var printAddonsList = func() { addonNames := make([]string, 0, len(assets.Addons)) for addonName := range assets.Addons { addonNames = append(addonNames, addonName) @@ -75,7 +102,7 @@ func addonList() error { addonBundle := assets.Addons[addonName] addonStatus, err := addonBundle.IsEnabled() if err != nil { - return err + exit.WithError("Error getting addons status", err) } tmpl, err := template.New("list").Parse(addonListFormat) if err != nil { @@ -87,5 +114,30 @@ func addonList() error { exit.WithError("Error executing list template", err) } } - return nil +} + +var printAddonsJSON = func() { + addonNames := make([]string, 0, len(assets.Addons)) + for addonName := range assets.Addons { + addonNames = append(addonNames, addonName) + } + sort.Strings(addonNames) + + addonsMap := map[string]map[string]interface{}{} + + for _, addonName := range addonNames { + addonBundle := assets.Addons[addonName] + + addonStatus, err := addonBundle.IsEnabled() + if err != nil { + exit.WithError("Error getting addons status", err) + } + + addonsMap[addonName] = map[string]interface{}{ + "Status": stringFromStatus(addonStatus), + } + } + jsonString, _ := json.Marshal(addonsMap) + + out.String(string(jsonString)) } diff --git a/cmd/minikube/cmd/config/config.go b/cmd/minikube/cmd/config/config.go index c282909deb..5b13061e2f 100644 --- a/cmd/minikube/cmd/config/config.go +++ b/cmd/minikube/cmd/config/config.go @@ -22,7 +22,7 @@ import ( "github.com/golang/glog" "github.com/spf13/cobra" "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/localpath" ) // Bootstrapper is the name for bootstrapper @@ -159,12 +159,6 @@ var settings = []Setting{ validations: []setFn{IsValidAddon}, callbacks: []setFn{EnableOrDisableStorageClasses}, }, - { - name: "heapster", - set: SetBool, - validations: []setFn{IsValidAddon}, - callbacks: []setFn{EnableOrDisableAddon}, - }, { name: "efk", set: SetBool, @@ -201,12 +195,6 @@ var settings = []Setting{ validations: []setFn{IsValidAddon}, callbacks: []setFn{EnableOrDisableAddon}, }, - { - name: "default-storageclass", - set: SetBool, - validations: []setFn{IsValidAddon}, - callbacks: []setFn{EnableOrDisableStorageClasses}, - }, { name: "storage-provisioner", set: SetBool, @@ -248,6 +236,18 @@ var settings = []Setting{ validations: []setFn{IsValidAddon, IsContainerdRuntime}, callbacks: []setFn{EnableOrDisableAddon}, }, + { + name: "helm-tiller", + set: SetBool, + validations: []setFn{IsValidAddon}, + callbacks: []setFn{EnableOrDisableAddon}, + }, + { + name: "ingress-dns", + set: SetBool, + validations: []setFn{IsValidAddon}, + callbacks: []setFn{EnableOrDisableAddon}, + }, { name: "hyperv-virtual-switch", set: SetString, @@ -294,7 +294,7 @@ func configurableFields() string { // ListConfigMap list entries from config file func ListConfigMap(name string) ([]string, error) { - configFile, err := config.ReadConfig(constants.ConfigFile) + configFile, err := config.ReadConfig(localpath.ConfigFile) if err != nil { return nil, err } @@ -314,7 +314,7 @@ func AddToConfigMap(name string, images []string) error { return err } // Set the values - cfg, err := config.ReadConfig(constants.ConfigFile) + cfg, err := config.ReadConfig(localpath.ConfigFile) if err != nil { return err } @@ -331,7 +331,7 @@ func AddToConfigMap(name string, images []string) error { return err } // Write the values - return config.WriteConfig(constants.ConfigFile, cfg) + return config.WriteConfig(localpath.ConfigFile, cfg) } // DeleteFromConfigMap deletes entries from a map in the config file @@ -341,7 +341,7 @@ func DeleteFromConfigMap(name string, images []string) error { return err } // Set the values - cfg, err := config.ReadConfig(constants.ConfigFile) + cfg, err := config.ReadConfig(localpath.ConfigFile) if err != nil { return err } @@ -356,5 +356,5 @@ func DeleteFromConfigMap(name string, images []string) error { return err } // Write the values - return config.WriteConfig(constants.ConfigFile, cfg) + return config.WriteConfig(localpath.ConfigFile, cfg) } diff --git a/cmd/minikube/cmd/config/disable_test.go b/cmd/minikube/cmd/config/disable_test.go index 3141605972..dbde3850c7 100644 --- a/cmd/minikube/cmd/config/disable_test.go +++ b/cmd/minikube/cmd/config/disable_test.go @@ -16,10 +16,24 @@ limitations under the License. package config -import "testing" +import ( + "testing" + + "gotest.tools/assert" + pkgConfig "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/localpath" +) func TestDisableUnknownAddon(t *testing.T) { if err := Set("InvalidAddon", "false"); err == nil { t.Fatalf("Disable did not return error for unknown addon") } } + +func TestDisableAddon(t *testing.T) { + if err := Set("dashboard", "false"); err != nil { + t.Fatalf("Disable returned unexpected error: " + err.Error()) + } + config, _ := pkgConfig.ReadConfig(localpath.ConfigFile) + assert.Equal(t, config["dashboard"], false) +} diff --git a/cmd/minikube/cmd/config/enable_test.go b/cmd/minikube/cmd/config/enable_test.go index 007d59516d..28556dc1d8 100644 --- a/cmd/minikube/cmd/config/enable_test.go +++ b/cmd/minikube/cmd/config/enable_test.go @@ -16,10 +16,24 @@ limitations under the License. package config -import "testing" +import ( + "testing" + + "gotest.tools/assert" + pkgConfig "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/localpath" +) func TestEnableUnknownAddon(t *testing.T) { if err := Set("InvalidAddon", "false"); err == nil { t.Fatalf("Enable did not return error for unknown addon") } } + +func TestEnableAddon(t *testing.T) { + if err := Set("ingress", "true"); err != nil { + t.Fatalf("Enable returned unexpected error: " + err.Error()) + } + config, _ := pkgConfig.ReadConfig(localpath.ConfigFile) + assert.Equal(t, config["ingress"], true) +} diff --git a/cmd/minikube/cmd/config/open.go b/cmd/minikube/cmd/config/open.go index 53d87aa8ad..458950b917 100644 --- a/cmd/minikube/cmd/config/open.go +++ b/cmd/minikube/cmd/config/open.go @@ -17,12 +17,15 @@ limitations under the License. package config import ( + "fmt" + "os" "text/template" + "github.com/pkg/browser" + "github.com/spf13/cobra" "k8s.io/minikube/pkg/minikube/assets" "k8s.io/minikube/pkg/minikube/cluster" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/machine" "k8s.io/minikube/pkg/minikube/out" @@ -63,7 +66,9 @@ var addonsOpenCmd = &cobra.Command{ } defer api.Close() - cluster.EnsureMinikubeRunningOrExit(api, 1) + if !cluster.IsMinikubeRunning(api) { + os.Exit(1) + } addon, ok := assets.Addons[addonName] // validate addon input if !ok { exit.WithCodeT(exit.Data, `addon '{{.name}}' is not a valid addon packaged with minikube. @@ -93,9 +98,20 @@ You can add one by annotating a service with the label {{.labelName}}:{{.addonNa } for i := range serviceList.Items { svc := serviceList.Items[i].ObjectMeta.Name - if err := service.WaitAndMaybeOpenService(api, namespace, svc, addonsURLTemplate, addonsURLMode, https, wait, interval); err != nil { + var urlString []string + + if urlString, err = service.WaitForService(api, namespace, svc, addonsURLTemplate, addonsURLMode, https, wait, interval); err != nil { exit.WithCodeT(exit.Unavailable, "Wait failed: {{.error}}", out.V{"error": err}) } + + if len(urlString) != 0 { + out.T(out.Celebrate, "Opening kubernetes service {{.namespace_name}}/{{.service_name}} in default browser...", out.V{"namespace_name": namespace, "service_name": svc}) + for _, url := range urlString { + if err := browser.OpenURL(url); err != nil { + exit.WithError(fmt.Sprintf("browser failed to open url %s", url), err) + } + } + } } }, } @@ -103,8 +119,8 @@ You can add one by annotating a service with the label {{.labelName}}:{{.addonNa func init() { addonsOpenCmd.Flags().BoolVar(&addonsURLMode, "url", false, "Display the kubernetes addons URL in the CLI instead of opening it in the default browser") addonsOpenCmd.Flags().BoolVar(&https, "https", false, "Open the addons URL with https instead of http") - addonsOpenCmd.Flags().IntVar(&wait, "wait", constants.DefaultWait, "Amount of time to wait for service in seconds") - addonsOpenCmd.Flags().IntVar(&interval, "interval", constants.DefaultInterval, "The time interval for each check that wait performs in seconds") + addonsOpenCmd.Flags().IntVar(&wait, "wait", service.DefaultWait, "Amount of time to wait for service in seconds") + addonsOpenCmd.Flags().IntVar(&interval, "interval", service.DefaultInterval, "The time interval for each check that wait performs in seconds") addonsOpenCmd.PersistentFlags().StringVar(&addonsURLFormat, "format", defaultAddonsFormatTemplate, "Format to output addons URL in. This format will be applied to each url individually and they will be printed one at a time.") AddonsCmd.AddCommand(addonsOpenCmd) } diff --git a/cmd/minikube/cmd/config/profile.go b/cmd/minikube/cmd/config/profile.go index 4842ec5345..64f0f372f5 100644 --- a/cmd/minikube/cmd/config/profile.go +++ b/cmd/minikube/cmd/config/profile.go @@ -45,8 +45,23 @@ var ProfileCmd = &cobra.Command{ } profile := args[0] + /** + we need to add code over here to check whether the profile + name is in the list of reserved keywords + */ + if pkgConfig.ProfileNameInReservedKeywords(profile) { + out.ErrT(out.FailureType, `Profile name "{{.profilename}}" is minikube keyword. To delete profile use command minikube delete -p `, out.V{"profilename": profile}) + os.Exit(0) + } + if profile == "default" { profile = "minikube" + } else { + // not validating when it is default profile + errProfile, ok := ValidateProfile(profile) + if !ok && errProfile != nil { + out.FailureT(errProfile.Msg) + } } if !pkgConfig.ProfileExists(profile) { @@ -71,7 +86,7 @@ var ProfileCmd = &cobra.Command{ out.SuccessT("Skipped switching kubectl context for {{.profile_name}} because --keep-context was set.", out.V{"profile_name": profile}) out.SuccessT("To connect to this cluster, use: kubectl --context={{.profile_name}}", out.V{"profile_name": profile}) } else { - err := kubeconfig.SetCurrentContext(constants.KubeconfigPath, profile) + err := kubeconfig.SetCurrentContext(profile, constants.KubeconfigPath) if err != nil { out.ErrT(out.Sad, `Error while setting kubectl current context : {{.error}}`, out.V{"error": err}) } diff --git a/cmd/minikube/cmd/config/profile_list.go b/cmd/minikube/cmd/config/profile_list.go index b6aa8b91e7..b79bf162cc 100644 --- a/cmd/minikube/cmd/config/profile_list.go +++ b/cmd/minikube/cmd/config/profile_list.go @@ -17,9 +17,11 @@ limitations under the License. package config import ( + "encoding/json" "fmt" "os" "strconv" + "strings" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/exit" @@ -29,48 +31,101 @@ import ( "github.com/spf13/cobra" ) +var ( + output string +) + var profileListCmd = &cobra.Command{ Use: "list", Short: "Lists all minikube profiles.", Long: "Lists all valid minikube profiles and detects all possible invalid profiles.", Run: func(cmd *cobra.Command, args []string) { - var validData [][]string - - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"Profile", "VM Driver", "NodeIP", "Node Port", "Kubernetes Version"}) - table.SetAutoFormatHeaders(false) - table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true}) - table.SetCenterSeparator("|") - validProfiles, invalidProfiles, err := config.ListProfiles() - - if len(validProfiles) == 0 || err != nil { - exit.UsageT("No minikube profile was found. You can create one using `minikube start`.") - } - for _, p := range validProfiles { - validData = append(validData, []string{p.Name, p.Config.MachineConfig.VMDriver, p.Config.KubernetesConfig.NodeIP, strconv.Itoa(p.Config.KubernetesConfig.NodePort), p.Config.KubernetesConfig.KubernetesVersion}) + switch strings.ToLower(output) { + case "json": + printProfilesJSON() + case "table": + printProfilesTable() + default: + exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'table', 'json'", output)) } - table.AppendBulk(validData) - table.Render() - - if invalidProfiles != nil { - out.T(out.WarningType, "Found {{.number}} invalid profile(s) ! ", out.V{"number": len(invalidProfiles)}) - for _, p := range invalidProfiles { - out.T(out.Empty, "\t "+p.Name) - } - out.T(out.Tip, "You can delete them using the following command(s): ") - for _, p := range invalidProfiles { - out.String(fmt.Sprintf("\t $ minikube delete -p %s \n", p.Name)) - } - - } - if err != nil { - exit.WithCodeT(exit.Config, fmt.Sprintf("error loading profiles: %v", err)) - } }, } +var printProfilesTable = func() { + + var validData [][]string + + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Profile", "VM Driver", "NodeIP", "Node Port", "Kubernetes Version"}) + table.SetAutoFormatHeaders(false) + table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true}) + table.SetCenterSeparator("|") + validProfiles, invalidProfiles, err := config.ListProfiles() + + if len(validProfiles) == 0 || err != nil { + exit.UsageT("No minikube profile was found. You can create one using `minikube start`.") + } + for _, p := range validProfiles { + validData = append(validData, []string{p.Name, p.Config.MachineConfig.VMDriver, p.Config.KubernetesConfig.NodeIP, strconv.Itoa(p.Config.KubernetesConfig.NodePort), p.Config.KubernetesConfig.KubernetesVersion}) + } + + table.AppendBulk(validData) + table.Render() + + if invalidProfiles != nil { + out.T(out.WarningType, "Found {{.number}} invalid profile(s) ! ", out.V{"number": len(invalidProfiles)}) + for _, p := range invalidProfiles { + out.T(out.Empty, "\t "+p.Name) + } + out.T(out.Tip, "You can delete them using the following command(s): ") + for _, p := range invalidProfiles { + out.String(fmt.Sprintf("\t $ minikube delete -p %s \n", p.Name)) + } + + } + + if err != nil { + exit.WithCodeT(exit.Config, fmt.Sprintf("error loading profiles: %v", err)) + } + +} + +var printProfilesJSON = func() { + validProfiles, invalidProfiles, err := config.ListProfiles() + + var valid []*config.Profile + var invalid []*config.Profile + + if validProfiles != nil { + valid = validProfiles + } else { + valid = []*config.Profile{} + } + + if invalidProfiles != nil { + invalid = invalidProfiles + } else { + invalid = []*config.Profile{} + } + + var body = map[string]interface{}{} + + if err == nil { + body["valid"] = valid + body["invalid"] = invalid + jsonString, _ := json.Marshal(body) + out.String(string(jsonString)) + } else { + body["error"] = err + jsonString, _ := json.Marshal(body) + out.String(string(jsonString)) + os.Exit(exit.Failure) + } +} + func init() { + profileListCmd.Flags().StringVarP(&output, "output", "o", "table", "The output format. One of 'json', 'table'") ProfileCmd.AddCommand(profileListCmd) } diff --git a/cmd/minikube/cmd/config/prompt.go b/cmd/minikube/cmd/config/prompt.go index 91cbae6b77..10c00a7f0f 100644 --- a/cmd/minikube/cmd/config/prompt.go +++ b/cmd/minikube/cmd/config/prompt.go @@ -149,7 +149,7 @@ func posString(slice []string, element string) int { return -1 } -// containsString returns true iff slice contains element +// containsString returns true if slice contains element func containsString(slice []string, element string) bool { - return !(posString(slice, element) == -1) + return posString(slice, element) != -1 } diff --git a/cmd/minikube/cmd/config/set.go b/cmd/minikube/cmd/config/set.go index b936f8cc5b..c870b4dbd4 100644 --- a/cmd/minikube/cmd/config/set.go +++ b/cmd/minikube/cmd/config/set.go @@ -19,8 +19,8 @@ package config import ( "github.com/spf13/cobra" pkgConfig "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/exit" + "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/out" ) @@ -60,7 +60,7 @@ func Set(name string, value string) error { } // Set the value - config, err := pkgConfig.ReadConfig(constants.ConfigFile) + config, err := pkgConfig.ReadConfig(localpath.ConfigFile) if err != nil { return err } @@ -76,5 +76,5 @@ func Set(name string, value string) error { } // Write the value - return pkgConfig.WriteConfig(constants.ConfigFile, config) + return pkgConfig.WriteConfig(localpath.ConfigFile, config) } diff --git a/cmd/minikube/cmd/config/set_test.go b/cmd/minikube/cmd/config/set_test.go index b85057e27a..e027f7aebb 100644 --- a/cmd/minikube/cmd/config/set_test.go +++ b/cmd/minikube/cmd/config/set_test.go @@ -19,7 +19,7 @@ package config import "testing" func TestNotFound(t *testing.T) { - err := Set("nonexistant", "10") + err := Set("nonexistent", "10") if err == nil { t.Fatalf("Set did not return error for unknown property") } diff --git a/cmd/minikube/cmd/config/unset.go b/cmd/minikube/cmd/config/unset.go index ee9cff74f4..c3294eac9f 100644 --- a/cmd/minikube/cmd/config/unset.go +++ b/cmd/minikube/cmd/config/unset.go @@ -19,8 +19,8 @@ package config import ( "github.com/spf13/cobra" pkgConfig "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/exit" + "k8s.io/minikube/pkg/minikube/localpath" ) var configUnsetCmd = &cobra.Command{ @@ -44,10 +44,10 @@ func init() { // Unset unsets a property func Unset(name string) error { - m, err := pkgConfig.ReadConfig(constants.ConfigFile) + m, err := pkgConfig.ReadConfig(localpath.ConfigFile) if err != nil { return err } delete(m, name) - return pkgConfig.WriteConfig(constants.ConfigFile, m) + return pkgConfig.WriteConfig(localpath.ConfigFile, m) } diff --git a/cmd/minikube/cmd/config/util.go b/cmd/minikube/cmd/config/util.go index f04222d901..cb26c7349c 100644 --- a/cmd/minikube/cmd/config/util.go +++ b/cmd/minikube/cmd/config/util.go @@ -27,13 +27,15 @@ import ( "k8s.io/minikube/pkg/minikube/cluster" "k8s.io/minikube/pkg/minikube/command" "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/machine" "k8s.io/minikube/pkg/minikube/out" "k8s.io/minikube/pkg/minikube/storageclass" ) +// defaultStorageClassProvisioner is the name of the default storage class provisioner +const defaultStorageClassProvisioner = "standard" + // Runs all the validation or callback functions and collects errors func run(name string, value string, fns []setFn) error { var errors []error @@ -109,6 +111,18 @@ func EnableOrDisableAddon(name string, val string) error { if err != nil { return errors.Wrapf(err, "parsing bool: %s", name) } + addon := assets.Addons[name] + + // check addon status before enabling/disabling it + alreadySet, err := isAddonAlreadySet(addon, enable) + if err != nil { + out.ErrT(out.Conflict, "{{.error}}", out.V{"error": err}) + return err + } + //if addon is already enabled or disabled, do nothing + if alreadySet { + return nil + } // TODO(r2d4): config package should not reference API, pull this out api, err := machine.NewAPIClient() @@ -116,13 +130,18 @@ func EnableOrDisableAddon(name string, val string) error { return errors.Wrap(err, "machine client") } defer api.Close() - cluster.EnsureMinikubeRunningOrExit(api, 0) - addon := assets.Addons[name] + //if minikube is not running, we return and simply update the value in the addon + //config and rewrite the file + if !cluster.IsMinikubeRunning(api) { + return nil + } + host, err := cluster.CheckIfHostExistsAndLoad(api, config.GetMachineName()) if err != nil { return errors.Wrap(err, "getting host") } + cmd, err := machine.CommandRunner(host) if err != nil { return errors.Wrap(err, "command runner") @@ -137,30 +156,24 @@ func EnableOrDisableAddon(name string, val string) error { return enableOrDisableAddonInternal(addon, cmd, data, enable) } -func isAddonAlreadySet(addon *assets.Addon, enable bool) error { - +func isAddonAlreadySet(addon *assets.Addon, enable bool) (bool, error) { addonStatus, err := addon.IsEnabled() if err != nil { - return errors.Wrap(err, "get the addon status") + return false, errors.Wrap(err, "get the addon status") } if addonStatus && enable { - return fmt.Errorf("addon %s was already enabled", addon.Name()) + return true, nil } else if !addonStatus && !enable { - return fmt.Errorf("addon %s was already disabled", addon.Name()) + return true, nil } - return nil + return false, nil } func enableOrDisableAddonInternal(addon *assets.Addon, cmd command.Runner, data interface{}, enable bool) error { var err error - // check addon status before enabling/disabling it - if err := isAddonAlreadySet(addon, enable); err != nil { - out.ErrT(out.Conflict, "{{.error}}", out.V{"error": err}) - os.Exit(0) - } if enable { for _, addon := range addon.Assets { @@ -205,7 +218,7 @@ func EnableOrDisableStorageClasses(name, val string) error { return errors.Wrap(err, "Error parsing boolean") } - class := constants.DefaultStorageClassProvisioner + class := defaultStorageClassProvisioner if name == "storage-provisioner-gluster" { class = "glusterfile" } @@ -230,3 +243,42 @@ func EnableOrDisableStorageClasses(name, val string) error { return EnableOrDisableAddon(name, val) } + +// ErrValidateProfile Error to validate profile +type ErrValidateProfile struct { + Name string + Msg string +} + +func (e ErrValidateProfile) Error() string { + return e.Msg +} + +// ValidateProfile checks if the profile user is trying to switch exists, else throws error +func ValidateProfile(profile string) (*ErrValidateProfile, bool) { + + validProfiles, invalidProfiles, err := config.ListProfiles() + if err != nil { + out.FailureT(err.Error()) + } + + // handling invalid profiles + for _, invalidProf := range invalidProfiles { + if profile == invalidProf.Name { + return &ErrValidateProfile{Name: profile, Msg: fmt.Sprintf("%q is an invalid profile", profile)}, false + } + } + + profileFound := false + // valid profiles if found, setting profileFound to trueexpectedMsg + for _, prof := range validProfiles { + if prof.Name == profile { + profileFound = true + break + } + } + if !profileFound { + return &ErrValidateProfile{Name: profile, Msg: fmt.Sprintf("profile %q not found", profile)}, false + } + return nil, true +} diff --git a/cmd/minikube/cmd/config/util_test.go b/cmd/minikube/cmd/config/util_test.go index 0f0cf189c8..5a47c15798 100644 --- a/cmd/minikube/cmd/config/util_test.go +++ b/cmd/minikube/cmd/config/util_test.go @@ -17,21 +17,22 @@ limitations under the License. package config import ( + "fmt" "testing" "k8s.io/minikube/pkg/minikube/assets" pkgConfig "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" ) var minikubeConfig = pkgConfig.MinikubeConfig{ - "vm-driver": constants.DriverKvm2, + "vm-driver": driver.KVM2, "cpus": 12, "show-libmachine-logs": true, } func TestFindSettingNotFound(t *testing.T) { - s, err := findSetting("nonexistant") + s, err := findSetting("nonexistent") if err == nil { t.Fatalf("Shouldn't have found setting, but did. [%+v]", s) } @@ -48,9 +49,9 @@ func TestFindSetting(t *testing.T) { } func TestSetString(t *testing.T) { - err := SetString(minikubeConfig, "vm-driver", constants.DriverVirtualbox) + err := SetString(minikubeConfig, "vm-driver", driver.VirtualBox) if err != nil { - t.Fatalf("Couldnt set string: %v", err) + t.Fatalf("Couldn't set string: %v", err) } } @@ -85,15 +86,13 @@ func TestSetBool(t *testing.T) { func TestIsAddonAlreadySet(t *testing.T) { testCases := []struct { addonName string - expectErr string }{ { addonName: "ingress", - expectErr: "addon ingress was already ", }, + { - addonName: "heapster", - expectErr: "addon heapster was already ", + addonName: "registry", }, } @@ -101,13 +100,39 @@ func TestIsAddonAlreadySet(t *testing.T) { addon := assets.Addons[test.addonName] addonStatus, _ := addon.IsEnabled() - expectMsg := test.expectErr + "enabled" - if !addonStatus { - expectMsg = test.expectErr + "disabled" + alreadySet, err := isAddonAlreadySet(addon, addonStatus) + if !alreadySet { + if addonStatus { + t.Errorf("Did not get expected status, \n\n expected %+v already enabled", test.addonName) + } else { + t.Errorf("Did not get expected status, \n\n expected %+v already disabled", test.addonName) + } } - err := isAddonAlreadySet(addon, addonStatus) - if err.Error() != expectMsg { - t.Errorf("Did not get expected error, \n\n expected: %+v \n\n actual: %+v", expectMsg, err) + if err != nil { + t.Errorf("Got unexpected error: %+v", err) + } + } +} + +func TestValidateProfile(t *testing.T) { + testCases := []struct { + profileName string + }{ + { + profileName: "82374328742_2974224498", + }, + { + profileName: "minikube", + }, + } + + for _, test := range testCases { + profileNam := test.profileName + expectedMsg := fmt.Sprintf("profile %q not found", test.profileName) + + err, ok := ValidateProfile(profileNam) + if !ok && err.Error() != expectedMsg { + t.Errorf("Didnt receive expected message") } } } diff --git a/cmd/minikube/cmd/config/validations.go b/cmd/minikube/cmd/config/validations.go index aa6ca13ae5..89cc043250 100644 --- a/cmd/minikube/cmd/config/validations.go +++ b/cmd/minikube/cmd/config/validations.go @@ -28,8 +28,8 @@ import ( "github.com/pkg/errors" "k8s.io/minikube/pkg/minikube/assets" "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/cruntime" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/out" ) @@ -44,13 +44,11 @@ and then start minikube again with the following flags: minikube start --container-runtime=containerd --docker-opt containerd=/var/run/containerd/containerd.sock` // IsValidDriver checks if a driver is supported -func IsValidDriver(string, driver string) error { - for _, d := range constants.SupportedVMDrivers { - if driver == d { - return nil - } +func IsValidDriver(string, name string) error { + if driver.Supported(name) { + return nil } - return fmt.Errorf("driver %q is not supported", driver) + return fmt.Errorf("driver %q is not supported", name) } // RequiresRestartMsg returns the "requires restart" message diff --git a/cmd/minikube/cmd/config/view.go b/cmd/minikube/cmd/config/view.go index 33f79e12cd..288160a168 100644 --- a/cmd/minikube/cmd/config/view.go +++ b/cmd/minikube/cmd/config/view.go @@ -22,10 +22,12 @@ import ( "github.com/spf13/cobra" "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/exit" + "k8s.io/minikube/pkg/minikube/localpath" ) +const defaultConfigViewFormat = "- {{.ConfigKey}}: {{.ConfigValue}}\n" + var viewFormat string // ViewTemplate represents the view template @@ -47,7 +49,7 @@ var configViewCmd = &cobra.Command{ } func init() { - configViewCmd.Flags().StringVar(&viewFormat, "format", constants.DefaultConfigViewFormat, + configViewCmd.Flags().StringVar(&viewFormat, "format", defaultConfigViewFormat, `Go template format string for the config view output. The format for Go templates can be found here: https://golang.org/pkg/text/template/ For the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd/config#ConfigViewTemplate`) ConfigCmd.AddCommand(configViewCmd) @@ -55,7 +57,7 @@ For the list of accessible variables for the template, see the struct values her // View displays the current config func View() error { - cfg, err := config.ReadConfig(constants.ConfigFile) + cfg, err := config.ReadConfig(localpath.ConfigFile) if err != nil { return err } diff --git a/cmd/minikube/cmd/dashboard.go b/cmd/minikube/cmd/dashboard.go index 4908e319f1..27a472d80b 100644 --- a/cmd/minikube/cmd/dashboard.go +++ b/cmd/minikube/cmd/dashboard.go @@ -93,7 +93,9 @@ var dashboardCmd = &cobra.Command{ exit.WithCodeT(exit.NoInput, "kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/") } - cluster.EnsureMinikubeRunningOrExit(api, 1) + if !cluster.IsMinikubeRunning(api) { + os.Exit(1) + } // Check dashboard status before enabling it dashboardAddon := assets.Addons["dashboard"] diff --git a/cmd/minikube/cmd/delete.go b/cmd/minikube/cmd/delete.go index b088c51fd4..18e621dd45 100644 --- a/cmd/minikube/cmd/delete.go +++ b/cmd/minikube/cmd/delete.go @@ -23,17 +23,19 @@ import ( "path/filepath" "strconv" - "github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/mcnerror" "github.com/golang/glog" - ps "github.com/mitchellh/go-ps" + "github.com/mitchellh/go-ps" "github.com/pkg/errors" + + "github.com/docker/machine/libmachine" "github.com/spf13/cobra" "github.com/spf13/viper" cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config" "k8s.io/minikube/pkg/minikube/cluster" pkg_config "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/kubeconfig" "k8s.io/minikube/pkg/minikube/localpath" @@ -41,6 +43,9 @@ import ( "k8s.io/minikube/pkg/minikube/out" ) +var deleteAll bool +var purge bool + // deleteCmd represents the delete command var deleteCmd = &cobra.Command{ Use: "delete", @@ -50,26 +55,156 @@ associated files.`, Run: runDelete, } +type typeOfError int + +const ( + // Fatal is a type of DeletionError + Fatal typeOfError = 0 + // MissingProfile is a type of DeletionError + MissingProfile typeOfError = 1 + // MissingCluster is a type of DeletionError + MissingCluster typeOfError = 2 +) + +// DeletionError can be returned from DeleteProfiles +type DeletionError struct { + Err error + Errtype typeOfError +} + +func (error DeletionError) Error() string { + return error.Err.Error() +} + +func init() { + deleteCmd.Flags().BoolVar(&deleteAll, "all", false, "Set flag to delete all profiles") + deleteCmd.Flags().BoolVar(&purge, "purge", false, "Set this flag to delete the '.minikube' folder from your user directory.") + + if err := viper.BindPFlags(deleteCmd.Flags()); err != nil { + exit.WithError("unable to bind flags", err) + } + RootCmd.AddCommand(deleteCmd) +} + // runDelete handles the executes the flow of "minikube delete" func runDelete(cmd *cobra.Command, args []string) { if len(args) > 0 { exit.UsageT("Usage: minikube delete") } - profile := viper.GetString(pkg_config.MachineProfile) + profileFlag, err := cmd.Flags().GetString("profile") + if err != nil { + exit.WithError("Could not get profile flag", err) + } + + validProfiles, invalidProfiles, err := pkg_config.ListProfiles() + profilesToDelete := append(validProfiles, invalidProfiles...) + + // If the purge flag is set, go ahead and delete the .minikube directory. + if purge && len(profilesToDelete) > 1 && !deleteAll { + out.ErrT(out.Notice, "Multiple minikube profiles were found - ") + for _, p := range profilesToDelete { + out.T(out.Notice, " - {{.profile}}", out.V{"profile": p.Name}) + } + exit.UsageT("Usage: minikube delete --all --purge") + } + + if deleteAll { + if profileFlag != constants.DefaultMachineName { + exit.UsageT("usage: minikube delete --all") + } + + if err != nil { + exit.WithError("Error getting profiles to delete", err) + } + + errs := DeleteProfiles(profilesToDelete) + if len(errs) > 0 { + HandleDeletionErrors(errs) + } else { + out.T(out.DeletingHost, "Successfully deleted all profiles") + } + } else { + if len(args) > 0 { + exit.UsageT("usage: minikube delete") + } + + profileName := viper.GetString(pkg_config.MachineProfile) + profile, err := pkg_config.LoadProfile(profileName) + if err != nil { + out.ErrT(out.Meh, `"{{.name}}" profile does not exist`, out.V{"name": profileName}) + } + + errs := DeleteProfiles([]*pkg_config.Profile{profile}) + if len(errs) > 0 { + HandleDeletionErrors(errs) + } else { + out.T(out.DeletingHost, "Successfully deleted profile \"{{.name}}\"", out.V{"name": profileName}) + } + } + + // If the purge flag is set, go ahead and delete the .minikube directory. + if purge { + purgeMinikubeDirectory() + } +} + +func purgeMinikubeDirectory() { + glog.Infof("Purging the '.minikube' directory located at %s", localpath.MiniPath()) + if err := os.RemoveAll(localpath.MiniPath()); err != nil { + exit.WithError("unable to delete minikube config folder", err) + } + out.T(out.Crushed, "Successfully purged minikube directory located at - [{{.minikubeDirectory}}]", out.V{"minikubeDirectory": localpath.MiniPath()}) +} + +// DeleteProfiles deletes one or more profiles +func DeleteProfiles(profiles []*pkg_config.Profile) []error { + var errs []error + for _, profile := range profiles { + err := deleteProfile(profile) + + if err != nil { + mm, loadErr := cluster.LoadMachine(profile.Name) + + if !profile.IsValid() || (loadErr != nil || !mm.IsValid()) { + invalidProfileDeletionErrs := deleteInvalidProfile(profile) + if len(invalidProfileDeletionErrs) > 0 { + errs = append(errs, invalidProfileDeletionErrs...) + } + } else { + errs = append(errs, err) + } + } + } + return errs +} + +func deleteProfile(profile *pkg_config.Profile) error { + viper.Set(pkg_config.MachineProfile, profile.Name) + api, err := machine.NewAPIClient() if err != nil { - exit.WithError("Error getting client", err) + delErr := profileDeletionErr(profile.Name, fmt.Sprintf("error getting client %v", err)) + return DeletionError{Err: delErr, Errtype: Fatal} } defer api.Close() cc, err := pkg_config.Load() if err != nil && !os.IsNotExist(err) { out.ErrT(out.Sad, "Error loading profile {{.name}}: {{.error}}", out.V{"name": profile, "error": err}) + delErr := profileDeletionErr(profile.Name, fmt.Sprintf("error loading profile config: %v", err)) + return DeletionError{Err: delErr, Errtype: MissingProfile} } - // In the case of "none", we want to uninstall Kubernetes as there is no VM to delete - if err == nil && cc.MachineConfig.VMDriver == constants.DriverNone { - uninstallKubernetes(api, cc.KubernetesConfig, viper.GetString(cmdcfg.Bootstrapper)) + if err == nil && driver.BareMetal(cc.MachineConfig.VMDriver) { + if err := uninstallKubernetes(api, cc.KubernetesConfig, viper.GetString(cmdcfg.Bootstrapper)); err != nil { + deletionError, ok := err.(DeletionError) + if ok { + delErr := profileDeletionErr(profile.Name, fmt.Sprintf("%v", err)) + deletionError.Err = delErr + return deletionError + } + return err + } } if err := killMountProcess(); err != nil { @@ -79,14 +214,125 @@ func runDelete(cmd *cobra.Command, args []string) { if err = cluster.DeleteHost(api); err != nil { switch errors.Cause(err).(type) { case mcnerror.ErrHostDoesNotExist: - out.T(out.Meh, `"{{.name}}" cluster does not exist`, out.V{"name": profile}) + out.T(out.Meh, `"{{.name}}" cluster does not exist. Proceeding ahead with cleanup.`, out.V{"name": profile.Name}) default: out.T(out.FailureType, "Failed to delete cluster: {{.error}}", out.V{"error": err}) - out.T(out.Notice, `You may need to manually remove the "{{.name}}" VM from your hypervisor`, out.V{"name": profile}) + out.T(out.Notice, `You may need to manually remove the "{{.name}}" VM from your hypervisor`, out.V{"name": profile.Name}) } } // In case DeleteHost didn't complete the job. + deleteProfileDirectory(profile.Name) + + if err := pkg_config.DeleteProfile(profile.Name); err != nil { + if os.IsNotExist(err) { + delErr := profileDeletionErr(profile.Name, fmt.Sprintf("\"%s\" profile does not exist", profile.Name)) + return DeletionError{Err: delErr, Errtype: MissingProfile} + } + delErr := profileDeletionErr(profile.Name, fmt.Sprintf("failed to remove profile %v", err)) + return DeletionError{Err: delErr, Errtype: Fatal} + } + + out.T(out.Crushed, `The "{{.name}}" cluster has been deleted.`, out.V{"name": profile.Name}) + + machineName := pkg_config.GetMachineName() + if err := deleteContext(machineName); err != nil { + return err + } + return nil +} + +func deleteContext(machineName string) error { + if err := kubeconfig.DeleteContext(constants.KubeconfigPath, machineName); err != nil { + return DeletionError{Err: fmt.Errorf("update config: %v", err), Errtype: Fatal} + } + + if err := cmdcfg.Unset(pkg_config.MachineProfile); err != nil { + return DeletionError{Err: fmt.Errorf("unset minikube profile: %v", err), Errtype: Fatal} + } + return nil +} + +func deleteInvalidProfile(profile *pkg_config.Profile) []error { + out.T(out.DeletingHost, "Trying to delete invalid profile {{.profile}}", out.V{"profile": profile.Name}) + + var errs []error + pathToProfile := pkg_config.ProfileFolderPath(profile.Name, localpath.MiniPath()) + if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) { + err := os.RemoveAll(pathToProfile) + if err != nil { + errs = append(errs, DeletionError{err, Fatal}) + } + } + + pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath()) + if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) { + err := os.RemoveAll(pathToMachine) + if err != nil { + errs = append(errs, DeletionError{err, Fatal}) + } + } + return errs +} + +func profileDeletionErr(profileName string, additionalInfo string) error { + return fmt.Errorf("error deleting profile \"%s\": %s", profileName, additionalInfo) +} + +func uninstallKubernetes(api libmachine.API, kc pkg_config.KubernetesConfig, bsName string) error { + out.T(out.Resetting, "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...", out.V{"kubernetes_version": kc.KubernetesVersion, "bootstrapper_name": bsName}) + clusterBootstrapper, err := getClusterBootstrapper(api, bsName) + if err != nil { + return DeletionError{Err: fmt.Errorf("unable to get bootstrapper: %v", err), Errtype: Fatal} + } else if err = clusterBootstrapper.DeleteCluster(kc); err != nil { + return DeletionError{Err: fmt.Errorf("failed to delete cluster: %v", err), Errtype: Fatal} + } + return nil +} + +// HandleDeletionErrors handles deletion errors from DeleteProfiles +func HandleDeletionErrors(errors []error) { + if len(errors) == 1 { + handleSingleDeletionError(errors[0]) + } else { + handleMultipleDeletionErrors(errors) + } +} + +func handleSingleDeletionError(err error) { + deletionError, ok := err.(DeletionError) + + if ok { + switch deletionError.Errtype { + case Fatal: + out.FatalT(deletionError.Error()) + case MissingProfile: + out.ErrT(out.Sad, deletionError.Error()) + case MissingCluster: + out.ErrT(out.Meh, deletionError.Error()) + default: + out.FatalT(deletionError.Error()) + } + } else { + exit.WithError("Could not process error from failed deletion", err) + } +} + +func handleMultipleDeletionErrors(errors []error) { + out.ErrT(out.Sad, "Multiple errors deleting profiles") + + for _, err := range errors { + deletionError, ok := err.(DeletionError) + + if ok { + glog.Errorln(deletionError.Error()) + } else { + exit.WithError("Could not process errors from failed deletion", err) + } + } +} + +func deleteProfileDirectory(profile string) { machineDir := filepath.Join(localpath.MiniPath(), "machines", profile) if _, err := os.Stat(machineDir); err == nil { out.T(out.DeletingHost, `Removing {{.directory}} ...`, out.V{"directory": machineDir}) @@ -95,34 +341,6 @@ func runDelete(cmd *cobra.Command, args []string) { exit.WithError("Unable to remove machine directory: %v", err) } } - - if err := pkg_config.DeleteProfile(profile); err != nil { - if os.IsNotExist(err) { - out.T(out.Meh, `"{{.name}}" profile does not exist`, out.V{"name": profile}) - os.Exit(0) - } - exit.WithError("Failed to remove profile", err) - } - out.T(out.Crushed, `The "{{.name}}" cluster has been deleted.`, out.V{"name": profile}) - - machineName := pkg_config.GetMachineName() - if err := kubeconfig.DeleteContext(constants.KubeconfigPath, machineName); err != nil { - exit.WithError("update config", err) - } - - if err := cmdcfg.Unset(pkg_config.MachineProfile); err != nil { - exit.WithError("unset minikube profile", err) - } -} - -func uninstallKubernetes(api libmachine.API, kc pkg_config.KubernetesConfig, bsName string) { - out.T(out.Resetting, "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...", out.V{"kubernetes_version": kc.KubernetesVersion, "bootstrapper_name": bsName}) - clusterBootstrapper, err := getClusterBootstrapper(api, bsName) - if err != nil { - out.ErrT(out.Empty, "Unable to get bootstrapper: {{.error}}", out.V{"error": err}) - } else if err = clusterBootstrapper.DeleteCluster(kc); err != nil { - out.ErrT(out.Empty, "Failed to delete cluster: {{.error}}", out.V{"error": err}) - } } // killMountProcess kills the mount process, if it is running diff --git a/cmd/minikube/cmd/delete_test.go b/cmd/minikube/cmd/delete_test.go new file mode 100644 index 0000000000..69a37be735 --- /dev/null +++ b/cmd/minikube/cmd/delete_test.go @@ -0,0 +1,216 @@ +/* +Copyright 2016 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 cmd + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/otiai10/copy" + "github.com/spf13/viper" + + "k8s.io/minikube/pkg/minikube/cluster" + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/localpath" +) + +// except returns a list of strings, minus the excluded ones +func exclude(vals []string, exclude []string) []string { + result := []string{} + for _, v := range vals { + excluded := false + for _, e := range exclude { + if e == v { + excluded = true + continue + } + } + if !excluded { + result = append(result, v) + } + } + return result +} + +func fileNames(path string) ([]string, error) { + result := []string{} + fis, err := ioutil.ReadDir(path) + if err != nil { + return result, err + } + for _, fi := range fis { + result = append(result, fi.Name()) + } + return result, nil +} + +func TestDeleteProfile(t *testing.T) { + td, err := ioutil.TempDir("", "single") + if err != nil { + t.Fatalf("tempdir: %v", err) + } + err = copy.Copy("../../../pkg/minikube/config/testdata/delete-single", td) + if err != nil { + t.Fatalf("copy: %v", err) + } + + tests := []struct { + name string + profile string + expected []string + }{ + {"normal", "p1", []string{"p1"}}, + {"empty-profile", "p2_empty_profile_config", []string{"p2_empty_profile_config"}}, + {"invalid-profile", "p3_invalid_profile_config", []string{"p3_invalid_profile_config"}}, + {"partial-profile", "p4_partial_profile_config", []string{"p4_partial_profile_config"}}, + {"missing-mach", "p5_missing_machine_config", []string{"p5_missing_machine_config"}}, + {"empty-mach", "p6_empty_machine_config", []string{"p6_empty_machine_config"}}, + {"invalid-mach", "p7_invalid_machine_config", []string{"p7_invalid_machine_config"}}, + {"partial-mach", "p8_partial_machine_config", []string{"p8_partial_machine_config"}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err = os.Setenv(localpath.MinikubeHome, td) + if err != nil { + t.Errorf("setenv: %v", err) + } + + beforeProfiles, err := fileNames(filepath.Join(localpath.MiniPath(), "profiles")) + if err != nil { + t.Errorf("readdir: %v", err) + } + beforeMachines, err := fileNames(filepath.Join(localpath.MiniPath(), "machines")) + if err != nil { + t.Errorf("readdir: %v", err) + } + + profile, err := config.LoadProfile(tt.profile) + if err != nil { + t.Logf("load failure: %v", err) + } + + errs := DeleteProfiles([]*config.Profile{profile}) + if len(errs) > 0 { + HandleDeletionErrors(errs) + t.Errorf("Errors while deleting profiles: %v", errs) + } + pathToProfile := config.ProfileFolderPath(profile.Name, localpath.MiniPath()) + if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) { + t.Errorf("Profile folder of profile \"%s\" was not deleted", profile.Name) + } + + pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath()) + if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) { + t.Errorf("Profile folder of profile \"%s\" was not deleted", profile.Name) + } + + afterProfiles, err := fileNames(filepath.Join(localpath.MiniPath(), "profiles")) + if err != nil { + t.Errorf("readdir profiles: %v", err) + } + + afterMachines, err := fileNames(filepath.Join(localpath.MiniPath(), "machines")) + if err != nil { + t.Errorf("readdir machines: %v", err) + } + + expectedProfiles := exclude(beforeProfiles, tt.expected) + if diff := cmp.Diff(expectedProfiles, afterProfiles); diff != "" { + t.Errorf("profiles mismatch (-want +got):\n%s", diff) + } + + expectedMachines := exclude(beforeMachines, tt.expected) + if diff := cmp.Diff(expectedMachines, afterMachines); diff != "" { + t.Errorf("machines mismatch (-want +got):\n%s", diff) + } + + viper.Set(config.MachineProfile, "") + }) + } +} + +func TestDeleteAllProfiles(t *testing.T) { + td, err := ioutil.TempDir("", "all") + if err != nil { + t.Fatalf("tempdir: %v", err) + } + err = copy.Copy("../../../pkg/minikube/config/testdata/delete-all", td) + if err != nil { + t.Fatalf("copy: %v", err) + } + + err = os.Setenv(localpath.MinikubeHome, td) + if err != nil { + t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome) + } + + pFiles, err := fileNames(filepath.Join(localpath.MiniPath(), "profiles")) + if err != nil { + t.Errorf("filenames: %v", err) + } + mFiles, err := fileNames(filepath.Join(localpath.MiniPath(), "machines")) + if err != nil { + t.Errorf("filenames: %v", err) + } + + const numberOfTotalProfileDirs = 8 + if numberOfTotalProfileDirs != len(pFiles) { + t.Errorf("got %d test profiles, expected %d: %s", len(pFiles), numberOfTotalProfileDirs, pFiles) + } + const numberOfTotalMachineDirs = 7 + if numberOfTotalMachineDirs != len(mFiles) { + t.Errorf("got %d test machines, expected %d: %s", len(mFiles), numberOfTotalMachineDirs, mFiles) + } + + validProfiles, inValidProfiles, err := config.ListProfiles() + if err != nil { + t.Error(err) + } + + if numberOfTotalProfileDirs != len(validProfiles)+len(inValidProfiles) { + t.Errorf("ListProfiles length = %d, expected %d\nvalid: %v\ninvalid: %v\n", len(validProfiles)+len(inValidProfiles), numberOfTotalProfileDirs, validProfiles, inValidProfiles) + } + + profiles := append(validProfiles, inValidProfiles...) + errs := DeleteProfiles(profiles) + + if errs != nil { + t.Errorf("errors while deleting all profiles: %v", errs) + } + + afterProfiles, err := fileNames(filepath.Join(localpath.MiniPath(), "profiles")) + if err != nil { + t.Errorf("profiles: %v", err) + } + afterMachines, err := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) + if err != nil { + t.Errorf("machines: %v", err) + } + if len(afterProfiles) != 0 { + t.Errorf("Did not delete all profiles, remaining: %v", afterProfiles) + } + + if len(afterMachines) != 0 { + t.Errorf("Did not delete all machines, remaining: %v", afterMachines) + } + + viper.Set(config.MachineProfile, "") +} diff --git a/cmd/minikube/cmd/env.go b/cmd/minikube/cmd/env.go index 8157c35e1b..4af010af7e 100644 --- a/cmd/minikube/cmd/env.go +++ b/cmd/minikube/cmd/env.go @@ -35,7 +35,7 @@ import ( "github.com/spf13/cobra" "k8s.io/minikube/pkg/minikube/cluster" "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/machine" ) @@ -343,7 +343,7 @@ var dockerEnvCmd = &cobra.Command{ if err != nil { exit.WithError("Error getting host", err) } - if host.Driver.DriverName() == constants.DriverNone { + if host.Driver.DriverName() == driver.None { exit.UsageT(`'none' driver does not support 'minikube docker-env' command`) } hostSt, err := cluster.GetHostStatus(api) diff --git a/cmd/minikube/cmd/mount.go b/cmd/minikube/cmd/mount.go index d2942699de..ac9e8ce25a 100644 --- a/cmd/minikube/cmd/mount.go +++ b/cmd/minikube/cmd/mount.go @@ -31,15 +31,19 @@ import ( "github.com/spf13/cobra" "k8s.io/minikube/pkg/minikube/cluster" "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/machine" "k8s.io/minikube/pkg/minikube/out" "k8s.io/minikube/third_party/go9p/ufs" ) -// nineP is the value of --type used for the 9p filesystem. -const nineP = "9p" +const ( + // nineP is the value of --type used for the 9p filesystem. + nineP = "9p" + defaultMountVersion = "9p2000.L" + defaultMsize = 262144 +) // placeholders for flag values var mountIP string @@ -103,7 +107,7 @@ var mountCmd = &cobra.Command{ if err != nil { exit.WithError("Error loading api", err) } - if host.Driver.DriverName() == constants.DriverNone { + if host.Driver.DriverName() == driver.None { exit.UsageT(`'none' driver does not support 'minikube mount' command`) } var ip net.IP @@ -202,13 +206,13 @@ var mountCmd = &cobra.Command{ func init() { mountCmd.Flags().StringVar(&mountIP, "ip", "", "Specify the ip that the mount should be setup on") mountCmd.Flags().StringVar(&mountType, "type", nineP, "Specify the mount filesystem type (supported types: 9p)") - mountCmd.Flags().StringVar(&mountVersion, "9p-version", constants.DefaultMountVersion, "Specify the 9p version that the mount should use") + mountCmd.Flags().StringVar(&mountVersion, "9p-version", defaultMountVersion, "Specify the 9p version that the mount should use") mountCmd.Flags().BoolVar(&isKill, "kill", false, "Kill the mount process spawned by minikube start") mountCmd.Flags().StringVar(&uid, "uid", "docker", "Default user id used for the mount") mountCmd.Flags().StringVar(&gid, "gid", "docker", "Default group id used for the mount") mountCmd.Flags().UintVar(&mode, "mode", 0755, "File permissions used for the mount") mountCmd.Flags().StringSliceVar(&options, "options", []string{}, "Additional mount options, such as cache=fscache") - mountCmd.Flags().IntVar(&mSize, "msize", constants.DefaultMsize, "The number of bytes to use for 9p packet payload") + mountCmd.Flags().IntVar(&mSize, "msize", defaultMsize, "The number of bytes to use for 9p packet payload") } // getPort asks the kernel for a free open port that is ready to use diff --git a/cmd/minikube/cmd/root.go b/cmd/minikube/cmd/root.go index 894ec36398..38f84246d9 100644 --- a/cmd/minikube/cmd/root.go +++ b/cmd/minikube/cmd/root.go @@ -161,7 +161,7 @@ func setFlagsUsingViper() { func init() { translate.DetermineLocale() RootCmd.PersistentFlags().StringP(config.MachineProfile, "p", constants.DefaultMachineName, `The name of the minikube VM being used. This can be set to allow having multiple instances of minikube independently.`) - RootCmd.PersistentFlags().StringP(configCmd.Bootstrapper, "b", constants.DefaultClusterBootstrapper, "The name of the cluster bootstrapper that will set up the kubernetes cluster.") + RootCmd.PersistentFlags().StringP(configCmd.Bootstrapper, "b", "kubeadm", "The name of the cluster bootstrapper that will set up the kubernetes cluster.") groups := templates.CommandGroups{ { @@ -232,18 +232,20 @@ func init() { // initConfig reads in config file and ENV variables if set. func initConfig() { - configPath := constants.ConfigFile + configPath := localpath.ConfigFile viper.SetConfigFile(configPath) viper.SetConfigType("json") - err := viper.ReadInConfig() - if err != nil { - glog.Warningf("Error reading config file at %s: %v", configPath, err) + if err := viper.ReadInConfig(); err != nil { + // This config file is optional, so don't emit errors if missing + if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + glog.Warningf("Error reading config file at %s: %v", configPath, err) + } } setupViper() } func setupViper() { - viper.SetEnvPrefix(constants.MinikubeEnvPrefix) + viper.SetEnvPrefix(minikubeEnvPrefix) // Replaces '-' in flags with '_' in env variables // e.g. iso-url => $ENVPREFIX_ISO_URL viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) diff --git a/cmd/minikube/cmd/root_test.go b/cmd/minikube/cmd/root_test.go index 87a4faa325..b0c29ca4b1 100644 --- a/cmd/minikube/cmd/root_test.go +++ b/cmd/minikube/cmd/root_test.go @@ -26,7 +26,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/tests" ) @@ -97,7 +96,7 @@ func runCommand(f func(*cobra.Command, []string)) { func hideEnv(t *testing.T) func(t *testing.T) { envs := make(map[string]string) for _, env := range os.Environ() { - if strings.HasPrefix(env, constants.MinikubeEnvPrefix) { + if strings.HasPrefix(env, minikubeEnvPrefix) { line := strings.Split(env, "=") key, val := line[0], line[1] envs[key] = val @@ -143,7 +142,7 @@ func TestViperConfig(t *testing.T) { } func getEnvVarName(name string) string { - return constants.MinikubeEnvPrefix + "_" + strings.ToUpper(name) + return minikubeEnvPrefix + "_" + strings.ToUpper(name) } func setValues(tt configTest) error { diff --git a/cmd/minikube/cmd/service.go b/cmd/minikube/cmd/service.go index 6ed380c7e0..8d844deb06 100644 --- a/cmd/minikube/cmd/service.go +++ b/cmd/minikube/cmd/service.go @@ -17,13 +17,19 @@ limitations under the License. package cmd import ( + "fmt" + "net/url" + "os" "text/template" + "github.com/golang/glog" + "github.com/pkg/browser" "github.com/spf13/cobra" + "k8s.io/minikube/pkg/minikube/cluster" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/machine" + "k8s.io/minikube/pkg/minikube/out" "k8s.io/minikube/pkg/minikube/service" ) @@ -65,12 +71,32 @@ var serviceCmd = &cobra.Command{ } defer api.Close() - cluster.EnsureMinikubeRunningOrExit(api, 1) - err = service.WaitAndMaybeOpenService(api, namespace, svc, - serviceURLTemplate, serviceURLMode, https, wait, interval) + if !cluster.IsMinikubeRunning(api) { + os.Exit(1) + } + + urls, err := service.WaitForService(api, namespace, svc, serviceURLTemplate, serviceURLMode, https, wait, interval) if err != nil { exit.WithError("Error opening service", err) } + + for _, u := range urls { + _, err := url.Parse(u) + if err != nil { + glog.Warningf("failed to parse url %q: %v (will not open)", u, err) + out.String(fmt.Sprintf("%s\n", u)) + continue + } + + if serviceURLMode { + out.String(fmt.Sprintf("%s\n", u)) + continue + } + out.T(out.Celebrate, "Opening service {{.namespace_name}}/{{.service_name}} in default browser...", out.V{"namespace_name": namespace, "service_name": svc}) + if err := browser.OpenURL(u); err != nil { + exit.WithError(fmt.Sprintf("open url failed: %s", u), err) + } + } }, } @@ -78,8 +104,8 @@ func init() { serviceCmd.Flags().StringVarP(&namespace, "namespace", "n", "default", "The service namespace") serviceCmd.Flags().BoolVar(&serviceURLMode, "url", false, "Display the kubernetes service URL in the CLI instead of opening it in the default browser") serviceCmd.Flags().BoolVar(&https, "https", false, "Open the service URL with https instead of http") - serviceCmd.Flags().IntVar(&wait, "wait", constants.DefaultWait, "Amount of time to wait for a service in seconds") - serviceCmd.Flags().IntVar(&interval, "interval", constants.DefaultInterval, "The initial time interval for each check that wait performs in seconds") + serviceCmd.Flags().IntVar(&wait, "wait", service.DefaultWait, "Amount of time to wait for a service in seconds") + serviceCmd.Flags().IntVar(&interval, "interval", service.DefaultInterval, "The initial time interval for each check that wait performs in seconds") serviceCmd.PersistentFlags().StringVar(&serviceURLFormat, "format", defaultServiceFormatTemplate, "Format to output service URL in. This format will be applied to each url individually and they will be printed one at a time.") diff --git a/cmd/minikube/cmd/ssh.go b/cmd/minikube/cmd/ssh.go index 84909e92be..f460bb0b45 100644 --- a/cmd/minikube/cmd/ssh.go +++ b/cmd/minikube/cmd/ssh.go @@ -25,7 +25,7 @@ import ( "k8s.io/minikube/pkg/minikube/cluster" "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/machine" "k8s.io/minikube/pkg/minikube/out" @@ -46,7 +46,7 @@ var sshCmd = &cobra.Command{ if err != nil { exit.WithError("Error getting host", err) } - if host.Driver.DriverName() == constants.DriverNone { + if host.Driver.DriverName() == driver.None { exit.UsageT("'none' driver does not support 'minikube ssh' command") } if viper.GetBool(nativeSSH) { diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index 8ee66f820b..523d00ba4c 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -17,7 +17,9 @@ limitations under the License. package cmd import ( + "encoding/json" "fmt" + "math" "net" "net/url" "os" @@ -37,13 +39,13 @@ import ( "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/pkg/errors" "github.com/shirou/gopsutil/cpu" gopshost "github.com/shirou/gopsutil/host" "github.com/spf13/cobra" "github.com/spf13/viper" "golang.org/x/sync/errgroup" cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config" - "k8s.io/minikube/pkg/drivers" "k8s.io/minikube/pkg/minikube/bootstrapper" "k8s.io/minikube/pkg/minikube/bootstrapper/images" "k8s.io/minikube/pkg/minikube/bootstrapper/kubeadm" @@ -52,7 +54,7 @@ import ( cfg "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/cruntime" - "k8s.io/minikube/pkg/minikube/drivers/none" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/kubeconfig" "k8s.io/minikube/pkg/minikube/localpath" @@ -61,6 +63,7 @@ import ( "k8s.io/minikube/pkg/minikube/notify" "k8s.io/minikube/pkg/minikube/out" "k8s.io/minikube/pkg/minikube/proxy" + "k8s.io/minikube/pkg/minikube/translate" pkgutil "k8s.io/minikube/pkg/util" "k8s.io/minikube/pkg/util/lock" "k8s.io/minikube/pkg/util/retry" @@ -85,6 +88,9 @@ const ( kvmQemuURI = "kvm-qemu-uri" kvmGPU = "kvm-gpu" kvmHidden = "kvm-hidden" + minikubeEnvPrefix = "MINIKUBE" + defaultMemorySize = "2000mb" + defaultDiskSize = "20000mb" keepContext = "keep-context" createMount = "mount" featureGates = "feature-gates" @@ -96,6 +102,7 @@ const ( imageMirrorCountry = "image-mirror-country" mountString = "mount-string" disableDriverMounts = "disable-driver-mounts" + addons = "addons" cacheImages = "cache-images" uuid = "uuid" vpnkitSock = "hyperkit-vpnkit-sock" @@ -110,17 +117,21 @@ const ( interactive = "interactive" waitTimeout = "wait-timeout" nativeSSH = "native-ssh" + minimumMemorySize = "1024mb" + minimumCPUS = 2 + minimumDiskSize = "2000mb" + autoUpdate = "auto-update-drivers" ) var ( - registryMirror []string - dockerEnv []string - dockerOpt []string - insecureRegistry []string - apiServerNames []string - apiServerIPs []net.IP - extraOptions cfg.ExtraOptionSlice - enableUpdateNotification = true + registryMirror []string + dockerEnv []string + dockerOpt []string + insecureRegistry []string + apiServerNames []string + addonList []string + apiServerIPs []net.IP + extraOptions cfg.ExtraOptionSlice ) func init() { @@ -136,7 +147,7 @@ func init() { // initMinikubeFlags includes commandline flags for minikube. func initMinikubeFlags() { - viper.SetEnvPrefix(constants.MinikubeEnvPrefix) + viper.SetEnvPrefix(minikubeEnvPrefix) // Replaces '-' in flags with '_' in env variables // e.g. iso-url => $ENVPREFIX_ISO_URL viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) @@ -145,23 +156,25 @@ func initMinikubeFlags() { startCmd.Flags().Bool(force, false, "Force minikube to perform possibly dangerous operations") startCmd.Flags().Bool(interactive, true, "Allow user prompts for more information") - startCmd.Flags().Int(cpus, constants.DefaultCPUS, "Number of CPUs allocated to the minikube VM.") - startCmd.Flags().String(memory, constants.DefaultMemorySize, "Amount of RAM allocated to the minikube VM (format: [], where unit = b, k, m or g).") - startCmd.Flags().String(humanReadableDiskSize, constants.DefaultDiskSize, "Disk size allocated to the minikube VM (format: [], where unit = b, k, m or g).") + startCmd.Flags().Int(cpus, 2, "Number of CPUs allocated to the minikube VM.") + startCmd.Flags().String(memory, defaultMemorySize, "Amount of RAM allocated to the minikube VM (format: [], where unit = b, k, m or g).") + startCmd.Flags().String(humanReadableDiskSize, defaultDiskSize, "Disk size allocated to the minikube VM (format: [], where unit = b, k, m or g).") startCmd.Flags().Bool(downloadOnly, false, "If true, only download and cache files for later use - don't install or start anything.") startCmd.Flags().Bool(cacheImages, true, "If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --vm-driver=none.") startCmd.Flags().String(isoURL, constants.DefaultISOURL, "Location of the minikube iso.") - startCmd.Flags().Bool(keepContext, constants.DefaultKeepContext, "This will keep the existing kubectl context and will create a minikube context.") - startCmd.Flags().Bool(embedCerts, constants.DefaultEmbedCerts, "if true, will embed the certs in kubeconfig.") + startCmd.Flags().Bool(keepContext, false, "This will keep the existing kubectl context and will create a minikube context.") + startCmd.Flags().Bool(embedCerts, false, "if true, will embed the certs in kubeconfig.") startCmd.Flags().String(containerRuntime, "docker", "The container runtime to be used (docker, crio, containerd).") startCmd.Flags().Bool(createMount, false, "This will start the mount daemon and automatically mount files into minikube.") - startCmd.Flags().String(mountString, constants.DefaultMountDir+":"+constants.DefaultMountEndpoint, "The argument to pass the minikube mount command on start.") + startCmd.Flags().String(mountString, constants.DefaultMountDir+":/minikube-host", "The argument to pass the minikube mount command on start.") + startCmd.Flags().StringArrayVar(&addonList, addons, nil, "Enable addons. see `minikube addons list` for a list of valid addon names.") startCmd.Flags().String(criSocket, "", "The cri socket path to be used.") startCmd.Flags().String(networkPlugin, "", "The name of the network plugin.") startCmd.Flags().Bool(enableDefaultCNI, false, "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \"--network-plugin=cni\".") - startCmd.Flags().Bool(waitUntilHealthy, true, "Wait until Kubernetes core services are healthy before exiting.") + startCmd.Flags().Bool(waitUntilHealthy, false, "Wait until Kubernetes core services are healthy before exiting.") startCmd.Flags().Duration(waitTimeout, 6*time.Minute, "max time to wait per Kubernetes core services to be healthy.") startCmd.Flags().Bool(nativeSSH, true, "Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'.") + startCmd.Flags().Bool(autoUpdate, true, "If set, automatically updates drivers to the latest version. Defaults to true.") } // initKubernetesFlags inits the commandline flags for kubernetes related options @@ -182,7 +195,7 @@ func initKubernetesFlags() { // initDriverFlags inits the commandline flags for vm drivers func initDriverFlags() { - startCmd.Flags().String("vm-driver", "", fmt.Sprintf("Driver is one of: %v (defaults to virtualbox)", constants.SupportedVMDrivers)) + startCmd.Flags().String("vm-driver", "", fmt.Sprintf("Driver is one of: %v (defaults to auto-detect)", driver.SupportedDrivers())) startCmd.Flags().Bool(disableDriverMounts, false, "Disables the filesystem mounts provided by the hypervisors") // kvm2 @@ -257,17 +270,7 @@ func platform() string { // runStart handles the executes the flow of "minikube start" func runStart(cmd *cobra.Command, args []string) { - prefix := "" - if viper.GetString(cfg.MachineProfile) != constants.DefaultMachineName { - prefix = fmt.Sprintf("[%s] ", viper.GetString(cfg.MachineProfile)) - } - - versionState := out.Happy - if notify.MaybePrintUpdateTextFromGithub() { - versionState = out.Meh - } - - out.T(versionState, "{{.prefix}}minikube {{.version}} on {{.platform}}", out.V{"prefix": prefix, "version": version.GetVersion(), "platform": platform()}) + displayVersion(version.GetVersion()) displayEnviron(os.Environ()) // if --registry-mirror specified when run minikube start, @@ -286,30 +289,28 @@ func runStart(cmd *cobra.Command, args []string) { exit.WithCodeT(exit.Data, "Unable to load config: {{.error}}", out.V{"error": err}) } - driver := selectDriver(oldConfig) - err = autoSetDriverOptions(driver) + driverName := selectDriver(oldConfig) + glog.Infof("selected: %v", driverName) + err = autoSetDriverOptions(cmd, driverName) if err != nil { glog.Errorf("Error autoSetOptions : %v", err) } - validateFlags(driver) - validateUser(driver) + validateFlags(driverName) + validateUser(driverName) - v, err := version.GetSemverVersion() - if err != nil { - out.WarningT("Error parsing minikube version: {{.error}}", out.V{"error": err}) - } else if err := drivers.InstallOrUpdate(driver, localpath.MakeMiniPath("bin"), v, viper.GetBool(interactive)); err != nil { - out.WarningT("Unable to update {{.driver}} driver: {{.error}}", out.V{"driver": driver, "error": err}) + // No need to install a driver in download-only mode + if !viper.GetBool(downloadOnly) { + updateDriver(driverName) } k8sVersion, isUpgrade := getKubernetesVersion(oldConfig) - config, err := generateCfgFromFlags(cmd, k8sVersion, driver) + config, err := generateCfgFromFlags(cmd, k8sVersion, driverName) if err != nil { exit.WithError("Failed to generate config", err) } - // For non-"none", the ISO is required to boot, so block until it is downloaded - if driver != constants.DriverNone { + if !driver.BareMetal(driverName) { if err := cluster.CacheISO(config.MachineConfig); err != nil { exit.WithError("Failed to cache ISO", err) } @@ -336,7 +337,7 @@ func runStart(cmd *cobra.Command, args []string) { mRunner, preExists, machineAPI, host := startMachine(&config) defer machineAPI.Close() // configure the runtime (docker, containerd, crio) - cr := configureRuntimes(mRunner, driver) + cr := configureRuntimes(mRunner, driverName, config.KubernetesConfig) showVersionInfo(k8sVersion, cr) waitCacheImages(&cacheGroup) @@ -352,20 +353,66 @@ func runStart(cmd *cobra.Command, args []string) { // pull images or restart cluster bootstrapCluster(bs, cr, mRunner, config.KubernetesConfig, preExists, isUpgrade) configureMounts() + + // enable addons with start command + enableAddons() + if err = loadCachedImagesInConfigFile(); err != nil { out.T(out.FailureType, "Unable to load cached images from config file.") } - // special ops for none driver, like change minikube directory. - if driver == constants.DriverNone { + // special ops for none , like change minikube directory. + if driverName == driver.None { prepareNone() } - if viper.GetBool(waitUntilHealthy) { - if err := bs.WaitCluster(config.KubernetesConfig, viper.GetDuration(waitTimeout)); err != nil { - exit.WithError("Wait failed", err) + waitCluster(bs, config) + if err := showKubectlInfo(kubeconfig, k8sVersion); err != nil { + glog.Errorf("kubectl info: %v", err) + } +} + +func updateDriver(driverName string) { + v, err := version.GetSemverVersion() + if err != nil { + out.WarningT("Error parsing minikube version: {{.error}}", out.V{"error": err}) + } else if err := driver.InstallOrUpdate(driverName, localpath.MakeMiniPath("bin"), v, viper.GetBool(interactive), viper.GetBool(autoUpdate)); err != nil { + out.WarningT("Unable to update {{.driver}} driver: {{.error}}", out.V{"driver": driverName, "error": err}) + } +} + +func enableAddons() { + for _, a := range addonList { + err := cmdcfg.Set(a, "true") + if err != nil { + exit.WithError("addon enable failed", err) } } - showKubectlConnectInfo(kubeconfig) +} + +func waitCluster(bs bootstrapper.Bootstrapper, config cfg.Config) { + var podsToWaitFor []string + + if !viper.GetBool(waitUntilHealthy) { + // only wait for apiserver if wait=false + podsToWaitFor = []string{"apiserver"} + } + if err := bs.WaitForPods(config.KubernetesConfig, viper.GetDuration(waitTimeout), podsToWaitFor); err != nil { + exit.WithError("Wait failed", err) + } +} + +func displayVersion(version string) { + prefix := "" + if viper.GetString(cfg.MachineProfile) != constants.DefaultMachineName { + prefix = fmt.Sprintf("[%s] ", viper.GetString(cfg.MachineProfile)) + } + + versionState := out.Happy + if notify.MaybePrintUpdateTextFromGithub() { + versionState = out.Meh + } + + out.T(versionState, "{{.prefix}}minikube {{.version}} on {{.platform}}", out.V{"prefix": prefix, "version": version, "platform": platform()}) } // displayEnviron makes the user aware of environment variables that will affect how minikube operates @@ -431,8 +478,12 @@ func startMachine(config *cfg.Config) (runner command.Runner, preExists bool, ma exit.WithError("Failed to get machine client", err) } host, preExists = startHost(m, config.MachineConfig) + runner, err = machine.CommandRunner(host) + if err != nil { + exit.WithError("Failed to get command runner", err) + } - ip := validateNetwork(host) + ip := validateNetwork(host, runner) // Bypass proxy for minikube's vm host ip err = proxy.ExcludeIP(ip) if err != nil { @@ -443,10 +494,6 @@ func startMachine(config *cfg.Config) (runner command.Runner, preExists bool, ma if err := saveConfig(config); err != nil { exit.WithError("Failed to save config", err) } - runner, err = machine.CommandRunner(host) - if err != nil { - exit.WithError("Failed to get command runner", err) - } return runner, preExists, m, host } @@ -462,59 +509,115 @@ func showVersionInfo(k8sVersion string, cr cruntime.Manager) { } } -func showKubectlConnectInfo(kcs *kubeconfig.Settings) { +func showKubectlInfo(kcs *kubeconfig.Settings, k8sVersion string) error { if kcs.KeepContext { out.T(out.Kubectl, "To connect to this cluster, use: kubectl --context={{.name}}", out.V{"name": kcs.ClusterName}) } else { out.T(out.Ready, `Done! kubectl is now configured to use "{{.name}}"`, out.V{"name": cfg.GetMachineName()}) } - _, err := exec.LookPath("kubectl") + + path, err := exec.LookPath("kubectl") if err != nil { out.T(out.Tip, "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/") + return nil } + + j, err := exec.Command(path, "version", "--client", "--output=json").Output() + if err != nil { + return errors.Wrap(err, "exec") + } + + cv := struct { + ClientVersion struct { + GitVersion string `json:"gitVersion"` + } `json:"clientVersion"` + }{} + err = json.Unmarshal(j, &cv) + if err != nil { + return errors.Wrap(err, "unmarshal") + } + + client, err := semver.Make(strings.TrimPrefix(cv.ClientVersion.GitVersion, version.VersionPrefix)) + if err != nil { + return errors.Wrap(err, "client semver") + } + + cluster := semver.MustParse(strings.TrimPrefix(k8sVersion, version.VersionPrefix)) + minorSkew := int(math.Abs(float64(int(client.Minor) - int(cluster.Minor)))) + glog.Infof("kubectl: %s, cluster: %s (minor skew: %d)", client, cluster, minorSkew) + + if client.Major != cluster.Major || minorSkew > 1 { + out.WarningT("{{.path}} is version {{.client_version}}, and is incompatible with Kubernetes {{.cluster_version}}. You will need to update {{.path}} or use 'minikube kubectl' to connect with this cluster", + out.V{"path": path, "client_version": client, "cluster_version": cluster}) + } + return nil } func selectDriver(oldConfig *cfg.Config) string { - driver := viper.GetString("vm-driver") - // By default, the driver is whatever we used last time - if driver == "" { - driver = constants.DefaultVMDriver + name := viper.GetString("vm-driver") + glog.Infof("selectDriver: flag=%q, old=%v", name, oldConfig) + if name == "" { + // By default, the driver is whatever we used last time if oldConfig != nil { - driver = oldConfig.MachineConfig.VMDriver + return oldConfig.MachineConfig.VMDriver } + options := driver.Choices() + pick, alts := driver.Choose(options) + if len(options) > 1 { + out.T(out.Sparkle, `Automatically selected the '{{.driver}}' driver (alternates: {{.alternates}})`, out.V{"driver": pick.Name, "alternates": alts}) + } else { + out.T(out.Sparkle, `Automatically selected the '{{.driver}}' driver`, out.V{"driver": pick.Name}) + } + + if pick.Name == "" { + exit.WithCodeT(exit.Config, "Unable to determine a default driver to use. Try specifying --vm-driver, or see https://minikube.sigs.k8s.io/docs/start/") + } + + name = pick.Name } - if err := cmdcfg.IsValidDriver(runtime.GOOS, driver); err != nil { - exit.WithCodeT(exit.Failure, "The driver '{{.driver}}' is not supported on {{.os}}", out.V{"driver": driver, "os": runtime.GOOS}) + if !driver.Supported(name) { + exit.WithCodeT(exit.Failure, "The driver '{{.driver}}' is not supported on {{.os}}", out.V{"driver": name, "os": runtime.GOOS}) + } + + st := driver.Status(name) + if st.Error != nil { + out.ErrLn("") + out.WarningT("'{{.driver}}' driver reported a possible issue: {{.error}}", out.V{"driver": name, "error": st.Error, "fix": st.Fix}) + out.ErrT(out.Tip, "Suggestion: {{.fix}}", out.V{"fix": translate.T(st.Fix)}) + if st.Doc != "" { + out.ErrT(out.Documentation, "Documentation: {{.url}}", out.V{"url": st.Doc}) + } + out.ErrLn("") } // Detect if our driver conflicts with a previously created VM. If we run into any errors, just move on. api, err := machine.NewAPIClient() if err != nil { glog.Infof("selectDriver NewAPIClient: %v", err) - return driver + return name } exists, err := api.Exists(cfg.GetMachineName()) if err != nil { glog.Infof("selectDriver api.Exists: %v", err) - return driver + return name } if !exists { - return driver + return name } h, err := api.Load(cfg.GetMachineName()) if err != nil { glog.Infof("selectDriver api.Load: %v", err) - return driver + return name } - if h.Driver.DriverName() == driver || h.Driver.DriverName() == "not-found" { - return driver + if h.Driver.DriverName() == name || h.Driver.DriverName() == "not-found" { + return name } out.ErrT(out.Conflict, `The existing "{{.profile_name}}" VM that was created using the "{{.old_driver}}" driver, and is incompatible with the "{{.driver}}" driver.`, - out.V{"profile_name": cfg.GetMachineName(), "driver": driver, "old_driver": h.Driver.DriverName()}) + out.V{"profile_name": cfg.GetMachineName(), "driver": name, "old_driver": h.Driver.DriverName()}) out.ErrT(out.Workaround, `To proceed, either: 1) Delete the existing VM using: '{{.command}} delete' @@ -552,9 +655,8 @@ func selectImageRepository(mirrorCountry string, k8sVersion string) (bool, strin } checkRepository := func(repo string) error { - podInfraContainerImage, _ := images.CachedImages(repo, k8sVersion) - - ref, err := name.ParseReference(podInfraContainerImage, name.WeakValidation) + pauseImage := images.PauseImage(repo, k8sVersion) + ref, err := name.ParseReference(pauseImage, name.WeakValidation) if err != nil { return err } @@ -585,7 +687,7 @@ func minikubeCmd() string { } // validerUser validates minikube is run by the recommended user (privileged or regular) -func validateUser(driver string) { +func validateUser(drvName string) { u, err := user.Current() if err != nil { glog.Errorf("Error getting the current user: %v", err) @@ -594,15 +696,15 @@ func validateUser(driver string) { useForce := viper.GetBool(force) - if driver == constants.DriverNone && u.Uid != "0" && !useForce { - exit.WithCodeT(exit.Permissions, `The "{{.driver_name}}" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}'.`, out.V{"driver_name": driver}) + if driver.BareMetal(drvName) && u.Uid != "0" && !useForce { + exit.WithCodeT(exit.Permissions, `The "{{.driver_name}}" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}'.`, out.V{"driver_name": drvName}) } - if driver == constants.DriverNone || u.Uid != "0" { + if driver.BareMetal(drvName) || u.Uid != "0" { return } - out.T(out.Stopped, `The "{{.driver_name}}" driver should not be used with root privileges.`, out.V{"driver_name": driver}) + out.T(out.Stopped, `The "{{.driver_name}}" driver should not be used with root privileges.`, out.V{"driver_name": drvName}) out.T(out.Tip, "If you are running minikube within a VM, consider using --vm-driver=none:") out.T(out.Documentation, " https://minikube.sigs.k8s.io/docs/reference/drivers/none/") @@ -619,23 +721,23 @@ func validateUser(driver string) { } // validateFlags validates the supplied flags against known bad combinations -func validateFlags(driver string) { +func validateFlags(drvName string) { diskSizeMB := pkgutil.CalculateSizeInMB(viper.GetString(humanReadableDiskSize)) - if diskSizeMB < pkgutil.CalculateSizeInMB(constants.MinimumDiskSize) && !viper.GetBool(force) { - exit.WithCodeT(exit.Config, "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}", out.V{"requested_size": diskSizeMB, "minimum_size": pkgutil.CalculateSizeInMB(constants.MinimumDiskSize)}) + if diskSizeMB < pkgutil.CalculateSizeInMB(minimumDiskSize) && !viper.GetBool(force) { + exit.WithCodeT(exit.Config, "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}", out.V{"requested_size": diskSizeMB, "minimum_size": pkgutil.CalculateSizeInMB(minimumDiskSize)}) } memorySizeMB := pkgutil.CalculateSizeInMB(viper.GetString(memory)) - if memorySizeMB < pkgutil.CalculateSizeInMB(constants.MinimumMemorySize) && !viper.GetBool(force) { - exit.UsageT("Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}", out.V{"requested_size": memorySizeMB, "minimum_size": pkgutil.CalculateSizeInMB(constants.MinimumMemorySize)}) + if memorySizeMB < pkgutil.CalculateSizeInMB(minimumMemorySize) && !viper.GetBool(force) { + exit.UsageT("Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}", out.V{"requested_size": memorySizeMB, "minimum_size": pkgutil.CalculateSizeInMB(minimumMemorySize)}) } - if memorySizeMB < pkgutil.CalculateSizeInMB(constants.DefaultMemorySize) && !viper.GetBool(force) { + if memorySizeMB < pkgutil.CalculateSizeInMB(defaultMemorySize) && !viper.GetBool(force) { out.T(out.Notice, "Requested memory allocation ({{.memory}}MB) is less than the default memory allocation of {{.default_memorysize}}MB. Beware that minikube might not work correctly or crash unexpectedly.", - out.V{"memory": memorySizeMB, "default_memorysize": pkgutil.CalculateSizeInMB(constants.DefaultMemorySize)}) + out.V{"memory": memorySizeMB, "default_memorysize": pkgutil.CalculateSizeInMB(defaultMemorySize)}) } var cpuCount int - if driver == constants.DriverNone { + if driver.BareMetal(drvName) { if cfg.GetMachineName() != constants.DefaultMachineName { exit.WithCodeT(exit.Config, "The 'none' driver does not support multiple profiles: https://minikube.sigs.k8s.io/docs/reference/drivers/none/") } @@ -650,8 +752,8 @@ func validateFlags(driver string) { } else { cpuCount = viper.GetInt(cpus) } - if cpuCount < constants.MinimumCPUS { - exit.UsageT("Requested CPU count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}", out.V{"requested_cpus": cpuCount, "minimum_cpus": constants.MinimumCPUS}) + if cpuCount < minimumCPUS && !viper.GetBool(force) { + exit.UsageT("Requested cpu count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}", out.V{"requested_cpus": cpuCount, "minimum_cpus": minimumCPUS}) } // check that kubeadm extra args contain only whitelisted parameters @@ -710,7 +812,7 @@ func waitCacheImages(g *errgroup.Group) { } // generateCfgFromFlags generates cfg.Config based on flags and supplied arguments -func generateCfgFromFlags(cmd *cobra.Command, k8sVersion string, driver string) (cfg.Config, error) { +func generateCfgFromFlags(cmd *cobra.Command, k8sVersion string, drvName string) (cfg.Config, error) { r, err := cruntime.New(cruntime.Config{Type: viper.GetString(containerRuntime)}) if err != nil { return cfg.Config{}, err @@ -727,23 +829,8 @@ func generateCfgFromFlags(cmd *cobra.Command, k8sVersion string, driver string) } // Feed Docker our host proxy environment by default, so that it can pull images - if _, ok := r.(*cruntime.Docker); ok { - if !cmd.Flags().Changed("docker-env") { - for _, k := range proxy.EnvVars { - if v := os.Getenv(k); v != "" { - // convert https_proxy to HTTPS_PROXY for linux - // TODO (@medyagh): if user has both http_proxy & HTTPS_PROXY set merge them. - k = strings.ToUpper(k) - if k == "HTTP_PROXY" || k == "HTTPS_PROXY" { - if strings.HasPrefix(v, "localhost") || strings.HasPrefix(v, "127.0") { - out.WarningT("Not passing {{.name}}={{.value}} to docker env.", out.V{"name": k, "value": v}) - continue - } - } - dockerEnv = append(dockerEnv, fmt.Sprintf("%s=%s", k, v)) - } - } - } + if _, ok := r.(*cruntime.Docker); ok && !cmd.Flags().Changed("docker-env") { + setDockerProxy() } repository := viper.GetString(imageRepository) @@ -765,7 +852,7 @@ func generateCfgFromFlags(cmd *cobra.Command, k8sVersion string, driver string) repository = autoSelectedRepository } - if repository != "" { + if cmd.Flags().Changed(imageRepository) { out.T(out.SuccessType, "Using image repository {{.name}}", out.V{"name": repository}) } @@ -777,7 +864,7 @@ func generateCfgFromFlags(cmd *cobra.Command, k8sVersion string, driver string) Memory: pkgutil.CalculateSizeInMB(viper.GetString(memory)), CPUs: viper.GetInt(cpus), DiskSize: pkgutil.CalculateSizeInMB(viper.GetString(humanReadableDiskSize)), - VMDriver: driver, + VMDriver: drvName, ContainerRuntime: viper.GetString(containerRuntime), HyperkitVpnKitSock: viper.GetString(vpnkitSock), HyperkitVSockPorts: viper.GetStringSlice(vsockPorts), @@ -822,13 +909,33 @@ func generateCfgFromFlags(cmd *cobra.Command, k8sVersion string, driver string) return cfg, nil } -// autoSetDriverOptions sets the options needed for specific vm-driver automatically. -func autoSetDriverOptions(driver string) error { - if driver == constants.DriverNone { - if o := none.AutoOptions(); o != "" { - return extraOptions.Set(o) +// setDockerProxy sets the proxy environment variables in the docker environment. +func setDockerProxy() { + for _, k := range proxy.EnvVars { + if v := os.Getenv(k); v != "" { + // convert https_proxy to HTTPS_PROXY for linux + // TODO (@medyagh): if user has both http_proxy & HTTPS_PROXY set merge them. + k = strings.ToUpper(k) + if k == "HTTP_PROXY" || k == "HTTPS_PROXY" { + if strings.HasPrefix(v, "localhost") || strings.HasPrefix(v, "127.0") { + out.WarningT("Not passing {{.name}}={{.value}} to docker env.", out.V{"name": k, "value": v}) + continue + } + } + dockerEnv = append(dockerEnv, fmt.Sprintf("%s=%s", k, v)) } - viper.Set(cacheImages, false) + } +} + +// autoSetDriverOptions sets the options needed for specific vm-driver automatically. +func autoSetDriverOptions(cmd *cobra.Command, drvName string) error { + hints := driver.FlagDefaults(drvName) + if !cmd.Flags().Changed("extra-config") && hints.ExtraOptions != "" { + return extraOptions.Set(hints.ExtraOptions) + } + + if !cmd.Flags().Changed(cacheImages) { + viper.Set(cacheImages, hints.CacheImages) } return nil } @@ -888,7 +995,7 @@ func startHost(api libmachine.API, mc cfg.MachineConfig) (*host.Host, bool) { } // validateNetwork tries to catch network problems as soon as possible -func validateNetwork(h *host.Host) string { +func validateNetwork(h *host.Host, r command.Runner) string { ip, err := h.Driver.GetIP() if err != nil { exit.WithError("Unable to get VM IP address", err) @@ -912,10 +1019,64 @@ func validateNetwork(h *host.Host) string { } } - // Here is where we should be checking connectivity to/from the VM + if !driver.BareMetal(h.Driver.DriverName()) { + trySSH(h, ip) + } + + tryLookup(r) + tryRegistry(r) return ip } +func trySSH(h *host.Host, ip string) { + sshAddr := fmt.Sprintf("%s:22", ip) + conn, err := net.Dial("tcp", sshAddr) + if err != nil { + exit.WithCodeT(exit.IO, `minikube is unable to connect to the VM: {{.error}} + +This is likely due to one of two reasons: + +- VPN or firewall interference +- {{.hypervisor}} network configuration issue + +Suggested workarounds: + +- Disable your local VPN or firewall software +- Configure your local VPN or firewall to allow access to {{.ip}} +- Restart or reinstall {{.hypervisor}} +- Use an alternative --vm-driver`, out.V{"error": err, "hypervisor": h.Driver.DriverName(), "ip": ip}) + } + defer conn.Close() +} + +func tryLookup(r command.Runner) { + // DNS check + if rr, err := r.RunCmd(exec.Command("nslookup", "kubernetes.io")); err != nil { + glog.Warningf("%s failed: %v", rr.Args, err) + out.WarningT("VM may be unable to resolve external DNS records") + } +} + +func tryRegistry(r command.Runner) { + // Try an HTTPS connection to the image repository + proxy := os.Getenv("HTTPS_PROXY") + opts := []string{"-sS"} + if proxy != "" && !strings.HasPrefix(proxy, "localhost") && !strings.HasPrefix(proxy, "127.0") { + opts = append([]string{"-x", proxy}, opts...) + } + + repo := viper.GetString(imageRepository) + if repo == "" { + repo = images.DefaultImageRepo + } + + opts = append(opts, fmt.Sprintf("https://%s/", repo)) + if rr, err := r.RunCmd(exec.Command("curl", opts...)); err != nil { + glog.Warningf("%s failed: %v", rr.Args, err) + out.WarningT("VM is unable to access {{.repository}}, you may need to configure a proxy or set --image-repository", out.V{"repository": repo}) + } +} + // getKubernetesVersion ensures that the requested version is reasonable func getKubernetesVersion(old *cfg.Config) (string, bool) { rawVersion := viper.GetString(kubernetesVersion) @@ -993,15 +1154,15 @@ func setupKubeAdm(mAPI libmachine.API, kc cfg.KubernetesConfig) bootstrapper.Boo } // configureRuntimes does what needs to happen to get a runtime going. -func configureRuntimes(runner cruntime.CommandRunner, driver string) cruntime.Manager { - config := cruntime.Config{Type: viper.GetString(containerRuntime), Runner: runner} +func configureRuntimes(runner cruntime.CommandRunner, drvName string, k8s cfg.KubernetesConfig) cruntime.Manager { + config := cruntime.Config{Type: viper.GetString(containerRuntime), Runner: runner, ImageRepository: k8s.ImageRepository, KubernetesVersion: k8s.KubernetesVersion} cr, err := cruntime.New(config) if err != nil { exit.WithError("Failed runtime", err) } disableOthers := true - if driver == constants.DriverNone { + if driver.BareMetal(drvName) { disableOthers = false } err = cr.Enable(disableOthers) diff --git a/cmd/minikube/cmd/start_test.go b/cmd/minikube/cmd/start_test.go index b5522df450..0f4fe397d2 100644 --- a/cmd/minikube/cmd/start_test.go +++ b/cmd/minikube/cmd/start_test.go @@ -26,8 +26,8 @@ import ( ) func TestGenerateCfgFromFlagsHTTPProxyHandling(t *testing.T) { - viper.SetDefault(memory, constants.DefaultMemorySize) - viper.SetDefault(humanReadableDiskSize, constants.DefaultDiskSize) + viper.SetDefault(memory, defaultMemorySize) + viper.SetDefault(humanReadableDiskSize, defaultDiskSize) originalEnv := os.Getenv("HTTP_PROXY") defer func() { err := os.Setenv("HTTP_PROXY", originalEnv) diff --git a/cmd/minikube/cmd/status.go b/cmd/minikube/cmd/status.go index c34abb9869..27c5e373e0 100644 --- a/cmd/minikube/cmd/status.go +++ b/cmd/minikube/cmd/status.go @@ -17,7 +17,10 @@ limitations under the License. package cmd import ( + "encoding/json" + "fmt" "os" + "strings" "text/template" "github.com/docker/machine/libmachine/state" @@ -35,6 +38,16 @@ import ( ) var statusFormat string +var output string + +// KubeconfigStatus represents the kubeconfig status +var KubeconfigStatus = struct { + Configured string + Misconfigured string +}{ + Configured: `Configured`, + Misconfigured: `Misconfigured`, +} // Status represents the status type Status struct { @@ -48,6 +61,11 @@ const ( minikubeNotRunningStatusFlag = 1 << 0 clusterNotRunningStatusFlag = 1 << 1 k8sNotRunningStatusFlag = 1 << 2 + defaultStatusFormat = `host: {{.Host}} +kubelet: {{.Kubelet}} +apiserver: {{.APIServer}} +kubeconfig: {{.Kubeconfig}} +` ) // statusCmd represents the status command @@ -58,6 +76,11 @@ var statusCmd = &cobra.Command{ Exit status contains the status of minikube's VM, cluster and kubernetes encoded on it's bits in this order from right to left. Eg: 7 meaning: 1 (for minikube NOK) + 2 (for cluster NOK) + 4 (for kubernetes NOK)`, Run: func(cmd *cobra.Command, args []string) { + + if output != "text" && statusFormat != defaultStatusFormat { + exit.UsageT("Cannot use both --output and --format options") + } + var returnCode = 0 api, err := machine.NewAPIClient() if err != nil { @@ -110,10 +133,9 @@ var statusCmd = &cobra.Command{ glog.Errorln("Error kubeconfig status:", err) } if ks { - kubeconfigSt = "Correctly Configured: pointing to minikube-vm at " + ip.String() + kubeconfigSt = KubeconfigStatus.Configured } else { - kubeconfigSt = "Misconfigured: pointing to stale minikube-vm." + - "\nTo fix the kubectl context, run minikube update-context" + kubeconfigSt = KubeconfigStatus.Misconfigured returnCode |= k8sNotRunningStatusFlag } } else { @@ -126,13 +148,14 @@ var statusCmd = &cobra.Command{ APIServer: apiserverSt, Kubeconfig: kubeconfigSt, } - tmpl, err := template.New("status").Parse(statusFormat) - if err != nil { - exit.WithError("Error creating status template", err) - } - err = tmpl.Execute(os.Stdout, status) - if err != nil { - exit.WithError("Error executing status template", err) + + switch strings.ToLower(output) { + case "text": + printStatusText(status) + case "json": + printStatusJSON(status) + default: + exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'text', 'json'", output)) } os.Exit(returnCode) @@ -140,7 +163,32 @@ var statusCmd = &cobra.Command{ } func init() { - statusCmd.Flags().StringVar(&statusFormat, "format", constants.DefaultStatusFormat, + statusCmd.Flags().StringVarP(&statusFormat, "format", "f", defaultStatusFormat, `Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/ For the list accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#Status`) + statusCmd.Flags().StringVarP(&output, "output", "o", "text", + `minikube status --output OUTPUT. json, text`) +} + +var printStatusText = func(status Status) { + tmpl, err := template.New("status").Parse(statusFormat) + if err != nil { + exit.WithError("Error creating status template", err) + } + err = tmpl.Execute(os.Stdout, status) + if err != nil { + exit.WithError("Error executing status template", err) + } + if status.Kubeconfig == KubeconfigStatus.Misconfigured { + out.WarningT("Warning: Your kubectl is pointing to stale minikube-vm.\nTo fix the kubectl context, run `minikube update-context`") + } +} + +var printStatusJSON = func(status Status) { + + jsonString, err := json.Marshal(status) + if err != nil { + exit.WithError("Error converting status to json", err) + } + out.String(string(jsonString)) } diff --git a/cmd/minikube/cmd/stop.go b/cmd/minikube/cmd/stop.go index c8688bcbae..13efef0079 100644 --- a/cmd/minikube/cmd/stop.go +++ b/cmd/minikube/cmd/stop.go @@ -77,7 +77,7 @@ func runStop(cmd *cobra.Command, args []string) { } machineName := pkg_config.GetMachineName() - err = kubeconfig.UnsetCurrentContext(constants.KubeconfigPath, machineName) + err = kubeconfig.UnsetCurrentContext(machineName, constants.KubeconfigPath) if err != nil { exit.WithError("update config", err) } diff --git a/cmd/minikube/cmd/update-check.go b/cmd/minikube/cmd/update-check.go index 7fa4ebcbde..ec519cae35 100644 --- a/cmd/minikube/cmd/update-check.go +++ b/cmd/minikube/cmd/update-check.go @@ -18,7 +18,6 @@ package cmd import ( "github.com/spf13/cobra" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/notify" "k8s.io/minikube/pkg/minikube/out" @@ -29,12 +28,8 @@ var updateCheckCmd = &cobra.Command{ Use: "update-check", Short: "Print current and latest version number", Long: `Print current and latest version number`, - PersistentPreRun: func(cmd *cobra.Command, args []string) { - // Explicitly disable update checking for the version command - enableUpdateNotification = false - }, Run: func(command *cobra.Command, args []string) { - url := constants.GithubMinikubeReleasesURL + url := notify.GithubMinikubeReleasesURL r, err := notify.GetAllVersionsFromURL(url) if err != nil { exit.WithError("Unable to fetch latest version info", err) diff --git a/cmd/minikube/cmd/update-context.go b/cmd/minikube/cmd/update-context.go index 3a6eeda815..9474bf7ab5 100644 --- a/cmd/minikube/cmd/update-context.go +++ b/cmd/minikube/cmd/update-context.go @@ -44,7 +44,7 @@ var updateContextCmd = &cobra.Command{ if err != nil { exit.WithError("Error host driver ip status", err) } - updated, err := kubeconfig.UpdateIP(ip, constants.KubeconfigPath, machineName) + updated, err := kubeconfig.UpdateIP(ip, machineName, constants.KubeconfigPath) if err != nil { exit.WithError("update config", err) } diff --git a/cmd/minikube/cmd/version.go b/cmd/minikube/cmd/version.go index a7cb944d0d..00c61efd88 100644 --- a/cmd/minikube/cmd/version.go +++ b/cmd/minikube/cmd/version.go @@ -26,10 +26,6 @@ var versionCmd = &cobra.Command{ Use: "version", Short: "Print the version of minikube", Long: `Print the version of minikube.`, - PersistentPreRun: func(cmd *cobra.Command, args []string) { - // Explicitly disable update checking for the version command - enableUpdateNotification = false - }, Run: func(command *cobra.Command, args []string) { out.Ln("minikube version: %v", version.GetVersion()) gitCommitID := version.GetGitCommitID() diff --git a/cmd/minikube/main.go b/cmd/minikube/main.go index b3162c7613..8ef1b3a007 100644 --- a/cmd/minikube/main.go +++ b/cmd/minikube/main.go @@ -28,6 +28,9 @@ import ( // Fix for https://github.com/kubernetes/minikube/issues/4866 _ "k8s.io/minikube/pkg/initflag" + // Register drivers + _ "k8s.io/minikube/pkg/minikube/registry/drvs" + mlog "github.com/docker/machine/libmachine/log" "github.com/golang/glog" @@ -42,7 +45,9 @@ import ( const minikubeEnableProfile = "MINIKUBE_ENABLE_PROFILING" var ( - machineLogErrorRe = regexp.MustCompile(`(?i) (failed|error|fatal)`) + // This regex is intentionally very specific, it's supposed to surface + // unexpected errors from libmachine to the user. + machineLogErrorRe = regexp.MustCompile(`VirtualizationException`) machineLogWarningRe = regexp.MustCompile(`(?i)warning`) ) @@ -67,6 +72,7 @@ func bridgeLogMessages() { log.SetOutput(stdLogBridge{}) mlog.SetErrWriter(machineLogBridge{}) mlog.SetOutWriter(machineLogBridge{}) + mlog.SetDebug(true) } type stdLogBridge struct{} diff --git a/cmd/performance/cmd/mkcmp.go b/cmd/performance/cmd/mkcmp.go new file mode 100644 index 0000000000..f1a26fe953 --- /dev/null +++ b/cmd/performance/cmd/mkcmp.go @@ -0,0 +1,49 @@ +/* +Copyright 2017 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 cmd + +import ( + "errors" + "fmt" + "os" + + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: "mkcmp [path to first binary] [path to second binary]", + Short: "mkcmp is used to compare performance of two minikube binaries", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + return validateArgs(args) + }, + Run: func(cmd *cobra.Command, args []string) {}, +} + +func validateArgs(args []string) error { + if len(args) != 2 { + return errors.New("mkcmp requires two minikube binaries to compare: mkcmp [path to first binary] [path to second binary]") + } + return nil +} + +// Execute runs the mkcmp command +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/pkg/minikube/drivers/virtualbox/doc.go b/cmd/performance/main.go similarity index 78% rename from pkg/minikube/drivers/virtualbox/doc.go rename to cmd/performance/main.go index 0e6eff9e8c..82c0efb0ac 100644 --- a/pkg/minikube/drivers/virtualbox/doc.go +++ b/cmd/performance/main.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +Copyright 2017 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,4 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -package virtualbox +package main + +import "k8s.io/minikube/cmd/performance/cmd" + +func main() { + cmd.Execute() +} diff --git a/deploy/addons/gvisor/gvisor-pod.yaml.tmpl b/deploy/addons/gvisor/gvisor-pod.yaml.tmpl index 652dbb6810..1b7d69cd84 100644 --- a/deploy/addons/gvisor/gvisor-pod.yaml.tmpl +++ b/deploy/addons/gvisor/gvisor-pod.yaml.tmpl @@ -24,50 +24,28 @@ spec: hostPID: true containers: - name: gvisor - image: {{default "gcr.io/k8s-minikube" .ImageRepository}}/gvisor-addon:latest + image: {{default "gcr.io/k8s-minikube" .ImageRepository}}/gvisor-addon:2 securityContext: privileged: true volumeMounts: - mountPath: /node/ - name: node - - mountPath: /usr/libexec/sudo - name: sudo - - mountPath: /var/run - name: varrun - - mountPath: /usr/bin - name: usrbin - - mountPath: /usr/lib - name: usrlib - - mountPath: /bin - name: bin + name: node-root + - mountPath: /node/run + name: node-run - mountPath: /tmp/gvisor - name: gvisor + name: node-tmp env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/node/bin - name: SYSTEMD_IGNORE_CHROOT value: "yes" imagePullPolicy: IfNotPresent volumes: - - name: node + - name: node-root hostPath: path: / - - name: sudo + - name: node-run hostPath: - path: /usr/libexec/sudo - - name: varrun - hostPath: - path: /var/run - - name: usrlib - hostPath: - path: /usr/lib - - name: usrbin - hostPath: - path: /usr/bin - - name: bin - hostPath: - path: /bin - - name: gvisor + path: /run + - name: node-tmp hostPath: path: /tmp/gvisor restartPolicy: Always diff --git a/deploy/addons/heapster/heapster-rc.yaml.tmpl b/deploy/addons/heapster/heapster-rc.yaml.tmpl deleted file mode 100644 index 3eeb3cc202..0000000000 --- a/deploy/addons/heapster/heapster-rc.yaml.tmpl +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright 2017 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. - -apiVersion: v1 -kind: ReplicationController -metadata: - labels: - k8s-app: heapster - kubernetes.io/minikube-addons: heapster - addonmanager.kubernetes.io/mode: Reconcile - version: v1.5.3 - name: heapster - namespace: kube-system -spec: - replicas: 1 - selector: - k8s-app: heapster - version: v1.5.3 - addonmanager.kubernetes.io/mode: Reconcile - template: - metadata: - labels: - k8s-app: heapster - version: v1.5.3 - addonmanager.kubernetes.io/mode: Reconcile - spec: - containers: - - name: heapster - image: {{default "k8s.gcr.io" .ImageRepository}}/heapster-{{.Arch}}:v1.5.3 - imagePullPolicy: IfNotPresent - command: - - /heapster - - --source=kubernetes.summary_api:'' - - --sink=influxdb:http://monitoring-influxdb:8086 - - --metric_resolution=60s - volumeMounts: - - name: ssl-certs - mountPath: /etc/ssl/certs - readOnly: true - volumes: - - name: ssl-certs - hostPath: - path: /etc/ssl/certs diff --git a/deploy/addons/heapster/influx-grafana-rc.yaml.tmpl b/deploy/addons/heapster/influx-grafana-rc.yaml.tmpl deleted file mode 100644 index bfd2bcdd1b..0000000000 --- a/deploy/addons/heapster/influx-grafana-rc.yaml.tmpl +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright 2017 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. - -apiVersion: v1 -kind: ReplicationController -metadata: - name: influxdb-grafana - labels: - k8s-app: influx-grafana - kubernetes.io/minikube-addons: heapster - addonmanager.kubernetes.io/mode: Reconcile - namespace: kube-system -spec: - replicas: 1 - selector: - k8s-app: influx-grafana - addonmanager.kubernetes.io/mode: Reconcile - template: - metadata: - labels: - k8s-app: influx-grafana - addonmanager.kubernetes.io/mode: Reconcile - spec: - containers: - - name: influxdb - image: {{default "k8s.gcr.io" .ImageRepository}}/heapster-influxdb-{{.Arch}}:v1.3.3 - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8083 - - name: api - containerPort: 8086 - volumeMounts: - - mountPath: /data - name: influxdb-storage - - name: grafana - image: {{default "k8s.gcr.io" .ImageRepository}}/heapster-grafana-{{.Arch}}:v4.4.3 - imagePullPolicy: IfNotPresent - env: - - name: INFLUXDB_SERVICE_URL - value: http://localhost:8086 - # The following env variables are required to make Grafana accessible via - # the kubernetes api-server proxy. On production clusters, we recommend - # removing these env variables, setup auth for grafana, and expose the grafana - # service using a LoadBalancer or a public IP. - - name: GF_AUTH_BASIC_ENABLED - value: "false" - - name: GF_AUTH_ANONYMOUS_ENABLED - value: "true" - - name: GF_AUTH_ANONYMOUS_ORG_ROLE - value: Admin - - name: GF_SERVER_ROOT_URL - value: / - ports: - - name: ui - containerPort: 3000 - volumeMounts: - - mountPath: /var - name: grafana-storage - volumes: - - name: influxdb-storage - emptyDir: {} - - name: grafana-storage - emptyDir: {} diff --git a/deploy/addons/heapster/influxdb-svc.yaml.tmpl b/deploy/addons/heapster/influxdb-svc.yaml.tmpl deleted file mode 100644 index 701bfee282..0000000000 --- a/deploy/addons/heapster/influxdb-svc.yaml.tmpl +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2017 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. - -apiVersion: v1 -kind: Service -metadata: - labels: - kubernetes.io/name: monitoring-influxdb - kubernetes.io/minikube-addons: heapster - addonmanager.kubernetes.io/mode: Reconcile - name: monitoring-influxdb - namespace: kube-system -spec: - ports: - - name: http - port: 8083 - targetPort: 8083 - - name: api - port: 8086 - targetPort: 8086 - selector: - addonmanager.kubernetes.io/mode: Reconcile - k8s-app: influx-grafana diff --git a/deploy/addons/helm-tiller/README.md b/deploy/addons/helm-tiller/README.md new file mode 100644 index 0000000000..b5dd8234a2 --- /dev/null +++ b/deploy/addons/helm-tiller/README.md @@ -0,0 +1,23 @@ +## helm-tiller Addon +[Kubernetes Helm](https://helm.sh) - The Kubernetes Package Manager + +### Enabling helm-tiller +To enable this addon, simply run: + +```shell script +minikube addons enable helm-tiller +``` + +In a minute or so tiller will be installed into your cluster. You could run `helm init` each time you create a new minikube instance or you could just enable this addon. +Each time you start a new minikube instance, tiller will be automatically installed. + +### Testing installation + +```shell script +helm ls +``` + +If everything went well you shouldn't get any errors about tiller being installed in your cluster. If you haven't deployed any releases `helm ls` won't return anything. + +### Deprecation of Tiller +When tiller is finally deprecated this addon won't be necessary anymore. If your version of helm doesn't use tiller, you don't need this addon. diff --git a/deploy/addons/helm-tiller/helm-tiller-dp.tmpl b/deploy/addons/helm-tiller/helm-tiller-dp.tmpl new file mode 100644 index 0000000000..c130220e79 --- /dev/null +++ b/deploy/addons/helm-tiller/helm-tiller-dp.tmpl @@ -0,0 +1,83 @@ +# Copyright 2019 The Kubernetes Authors. +# +# 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. + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: helm + name: tiller + addonmanager.kubernetes.io/mode: Reconcile + kubernetes.io/minikube-addons: helm + name: tiller-deploy + namespace: kube-system +spec: + replicas: 1 + selector: + matchLabels: + app: helm + name: tiller + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: helm + name: tiller + spec: + automountServiceAccountToken: true + containers: + - env: + - name: TILLER_NAMESPACE + value: kube-system + - name: TILLER_HISTORY_MAX + value: "0" + image: gcr.io/kubernetes-helm/tiller:v2.14.3 + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /liveness + port: 44135 + scheme: HTTP + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: tiller + ports: + - containerPort: 44134 + name: tiller + protocol: TCP + - containerPort: 44135 + name: http + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /readiness + port: 44135 + scheme: HTTP + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + serviceAccount: tiller + serviceAccountName: tiller \ No newline at end of file diff --git a/deploy/addons/heapster/grafana-svc.yaml.tmpl b/deploy/addons/helm-tiller/helm-tiller-rbac.tmpl similarity index 58% rename from deploy/addons/heapster/grafana-svc.yaml.tmpl rename to deploy/addons/helm-tiller/helm-tiller-rbac.tmpl index 61341604ab..1cc15e26f4 100644 --- a/deploy/addons/heapster/grafana-svc.yaml.tmpl +++ b/deploy/addons/helm-tiller/helm-tiller-rbac.tmpl @@ -1,4 +1,4 @@ -# Copyright 2017 The Kubernetes Authors All rights reserved. +# Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,22 +13,30 @@ # limitations under the License. apiVersion: v1 -kind: Service +kind: ServiceAccount metadata: - labels: - kubernetes.io/name: monitoring-grafana - kubernetes.io/minikube-addons: heapster - kubernetes.io/minikube-addons-endpoint: heapster - addonmanager.kubernetes.io/mode: Reconcile - name: monitoring-grafana + name: tiller namespace: kube-system -spec: - type: NodePort - ports: - - port: 80 - nodePort: 30002 - protocol: TCP - targetPort: ui - selector: + labels: + app: helm + name: tiller addonmanager.kubernetes.io/mode: Reconcile - k8s-app: influx-grafana + kubernetes.io/minikube-addons: helm +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: tiller-clusterrolebinding + labels: + app: helm + name: tiller + addonmanager.kubernetes.io/mode: Reconcile + kubernetes.io/minikube-addons: helm +subjects: + - kind: ServiceAccount + name: tiller + namespace: kube-system +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: "" \ No newline at end of file diff --git a/deploy/addons/heapster/heapster-svc.yaml.tmpl b/deploy/addons/helm-tiller/helm-tiller-svc.tmpl similarity index 69% rename from deploy/addons/heapster/heapster-svc.yaml.tmpl rename to deploy/addons/helm-tiller/helm-tiller-svc.tmpl index 9322781cfb..b115b284fa 100644 --- a/deploy/addons/heapster/heapster-svc.yaml.tmpl +++ b/deploy/addons/helm-tiller/helm-tiller-svc.tmpl @@ -1,4 +1,4 @@ -# Copyright 2017 The Kubernetes Authors All rights reserved. +# Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,16 +16,19 @@ apiVersion: v1 kind: Service metadata: labels: - kubernetes.io/name: heapster - kubernetes.io/minikube-addons: heapster + app: helm + name: tiller addonmanager.kubernetes.io/mode: Reconcile - kubernetes.io/minikube-addons-endpoint: heapster - name: heapster + kubernetes.io/minikube-addons: helm + name: tiller-deploy namespace: kube-system spec: + type: ClusterIP ports: - - port: 80 - targetPort: 8082 + - name: tiller + port: 44134 + protocol: TCP + targetPort: tiller selector: - addonmanager.kubernetes.io/mode: Reconcile - k8s-app: heapster + app: helm + name: tiller \ No newline at end of file diff --git a/deploy/addons/ingress-dns/README.md b/deploy/addons/ingress-dns/README.md new file mode 100644 index 0000000000..f1a6f919aa --- /dev/null +++ b/deploy/addons/ingress-dns/README.md @@ -0,0 +1,175 @@ +# Minikube Ingress DNS +![Build Status](https://gitlab.com/cryptexlabs/public/development/minikube-ingress-dns/badges/master/pipeline.svg) + +DNS service for ingress controllers running on your minikube server + +## Overview + +### Problem +When running minikube locally you are highly likely to want to run your services on an ingress controller so that you +don't have to use minikube tunnel or NodePorts to access your services. While NodePort might be ok in a lot of +circumstances in order to test some features an ingress is necessary. Ingress controllers are great because you can +define your entire architecture in something like a helm chart and all your services will be available. There is only +1 problem. That is that your ingress controller works basically off of dns and while running minikube that means that +your local dns names like `myservice.test` will have to resolve to `$(minikube ip)` not really a big deal except the +only real way to do this is to add an entry for every service in your `/etc/hosts` file. This gets messy for obvious +reasons. If you have a lot of services running that each have their own dns entry then you have to set those up +manually. Even if you automate it you then need to rely on the host operating system storing configurations instead of +storing them in your cluster. To make it worse it has to be constantly maintained and updated as services are added, +remove, and renamed. I call it the `/ets/hosts` pollution problem. + +### Solution +What if you could just access your local services magically without having to edit your `/etc/hosts` file? Well now you +can. This addon acts as a DNS service that runs inside your kubernetes cluster. All you have to do is install the +service and add the `$(minikube ip)` as a DNS server on your host machine. Each time the dns service is queried an +API call is made to the kubernetes master service for a list of all the ingresses. If a match is found for the name a +response is given with an IP address as the `$(minikube ip)`. So for example lets say my minikube ip address is +`192.168.99.106` and I have an ingress controller with the name of `myservice.test` then I would get a result like so: + +```text +#bash:~$ nslookup myservice.test $(minikube ip) +Server: 192.168.99.169 +Address: 192.168.99.169#53 + +Non-authoritative answer: +Name: myservice.test $(minikube ip) +Address: 192.168.99.169 +``` + +## Installation + +### Start minikube +``` +minikube start +``` + +### Install the kubernetes resources +```bash +minikube addons enable ingress-dns +``` + +### Add the minikube ip as a dns server + +#### Mac OS +Create a file in `/etc/resolver/minikube-profilename-test` +``` +domain test +nameserver 192.168.99.169 +search_order 1 +timeout 5 +``` +Replace `192.168.99.169` with your minikube ip and `profilename` is the name of the minikube profile for the +corresponding ip address + +If you have multiple minikube ips you must configure multiple files + +See https://www.unix.com/man-page/opendarwin/5/resolver/ +Note that even though the `port` feature is documented. It does not actually work. + +#### Linux +Update the file `/etc/resolvconf/resolv.conf.d/base` to have the following contents +``` +search test +nameserver 192.168.99.169 +timeout 5 +``` +Replace `192.168.99.169` with your minikube ip + +If your linux OS uses `systemctl` run the following commands +```bash +sudo resolvconf -u +systemctl disable --now resolvconf.service +``` + +If your linux does not use `systemctl` run the following commands + +TODO add supporting docs for Linux OS that do not use `systemctl` + +See https://linux.die.net/man/5/resolver + +#### Windows + +TODO + +## Testing + +### Add the test ingress +```bash +kubectl apply -f https://github.com/kubernetes/minikube/blob/master/deploy/addons/ingress-dns/example/example.yaml +``` +Note: Minimum Kubernetes version for example ingress is 1.14.7 + +### Validate DNS queries are returning A records +```bash +nslookup hello-john.test $(minikube ip) +nslookup hello-jane.test $(minikube ip) +``` + +### Validate domain names are resolving on host OS +```bash +ping hello-john.test +``` +Expected results: +```text +PING hello-john.test (192.168.99.169): 56 data bytes +64 bytes from 192.168.99.169: icmp_seq=0 ttl=64 time=0.361 ms +``` +```bash +ping hello-jane.test +``` +```text +PING hello-jane.test (192.168.99.169): 56 data bytes +64 bytes from 192.168.99.169: icmp_seq=0 ttl=64 time=0.262 ms +``` + +### Curl the example server +```bash +curl http://hello-john.test +``` +Expected result: +```text +Hello, world! +Version: 1.0.0 +Hostname: hello-world-app-557ff7dbd8-64mtv +``` +```bash +curl http://hello-jane.test +``` +Expected result: +```text +Hello, world! +Version: 1.0.0 +Hostname: hello-world-app-557ff7dbd8-64mtv +``` + +## Known issues + +### .localhost domains will not resolve on chromium +.localhost domains will not correctly resolve on chromium since it is used as a loopback address. Instead use .test, .example, or .invalid + +### .local is a reserved TLD +Do not use .local as this is a reserved TLD for mDNS and bind9 DNS servers + +### Mac OS + +#### mDNS reloading +Each time a file is created or a change is made to a file in `/etc/resolver` you may need to run the following to reload Mac OS mDNS resolver. +```bash +sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist +sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist +``` + +## TODO +- Add a service that runs on the host OS which will update the files in `/etc/resolver` automatically +- Start this service when running `minikube addons enable ingress-dns` and stop the service when running +`minikube addons disable ingress-dns` + +## Contributors +- [Josh Woodcock](https://github.com/woodcockjosh) + +## Images used in this plugin + +| Image | Source | Owner | +| :--- | :--- | :--- | +| [nginx-ingress](https://hub.docker.com/r/nginx/nginx-ingress) | [ingress-nginx](https://github.com/kubernetes/ingress-nginx) | Nginx +| [minikube-ingress-dns](https://hub.docker.com/r/cryptexlabs/minikube-ingress-dns) | [minikube-ingress-dns](https://gitlab.com/cryptexlabs/public/development/minikube-ingress-dns) | Cryptex Labs \ No newline at end of file diff --git a/deploy/addons/ingress-dns/example/example.yaml b/deploy/addons/ingress-dns/example/example.yaml new file mode 100644 index 0000000000..59a7ed8b9b --- /dev/null +++ b/deploy/addons/ingress-dns/example/example.yaml @@ -0,0 +1,82 @@ +# Copyright 2016 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. + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hello-world-app + namespace: default +spec: + selector: + matchLabels: + app: hello-world-app + template: + metadata: + labels: + app: hello-world-app + spec: + containers: + - name: hello-world-app + image: gcr.io/google-samples/hello-app:1.0 + ports: + - containerPort: 8080 +--- +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: example-ingress + namespace: kube-system + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$1 +spec: + rules: + - host: hello-john.test + http: + paths: + - path: /|/(.+) + backend: + serviceName: hello-world-app + servicePort: 80 + - host: hello-jane.test + http: + paths: + - path: /|/(.+) + backend: + serviceName: hello-world-app + servicePort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: hello-world-app + namespace: kube-system +spec: + type: ExternalName + externalName: hello-world-app.default.svc.cluster.local +--- +apiVersion: v1 +kind: Service +metadata: + name: hello-world-app + namespace: default +spec: + ports: + - name: http + port: 80 + targetPort: 8080 + protocol: TCP + type: NodePort + selector: + app: hello-world-app \ No newline at end of file diff --git a/deploy/addons/ingress-dns/ingress-dns-pod.yaml b/deploy/addons/ingress-dns/ingress-dns-pod.yaml new file mode 100644 index 0000000000..5c16e12c01 --- /dev/null +++ b/deploy/addons/ingress-dns/ingress-dns-pod.yaml @@ -0,0 +1,93 @@ +# Copyright 2016 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. + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: minikube-ingress-dns + namespace: kube-system + labels: + app: minikube-ingress-dns + kubernetes.io/bootstrapping: rbac-defaults + app.kubernetes.io/part-of: kube-system + addonmanager.kubernetes.io/mode: Reconcile +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: minikube-ingress-dns + namespace: kube-system + labels: + app: minikube-ingress-dns + kubernetes.io/bootstrapping: rbac-defaults + app.kubernetes.io/part-of: kube-system + addonmanager.kubernetes.io/mode: Reconcile +rules: + - apiGroups: + - "" + - "extensions" + - "networking.k8s.io" + resources: + - ingresses + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: minikube-ingress-dns + namespace: kube-system + labels: + app: minikube-ingress-dns + kubernetes.io/bootstrapping: rbac-defaults + app.kubernetes.io/part-of: kube-system + addonmanager.kubernetes.io/mode: Reconcile +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: minikube-ingress-dns +subjects: + - kind: ServiceAccount + name: minikube-ingress-dns + namespace: kube-system +--- +apiVersion: v1 +kind: Pod +metadata: + name: kube-ingress-dns-minikube + namespace: kube-system + labels: + app: minikube-ingress-dns + app.kubernetes.io/part-of: kube-system + addonmanager.kubernetes.io/mode: Reconcile +spec: + serviceAccountName: minikube-ingress-dns + hostNetwork: true + containers: + - name: minikube-ingress-dns + image: "cryptexlabs/minikube-ingress-dns:0.2.0" + imagePullPolicy: IfNotPresent + ports: + - containerPort: 53 + protocol: UDP + env: + - name: DNS_PORT + value: "53" + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP \ No newline at end of file diff --git a/deploy/addons/ingress/ingress-dp.yaml.tmpl b/deploy/addons/ingress/ingress-dp.yaml.tmpl index 12bd6bb794..12f2e72051 100644 --- a/deploy/addons/ingress/ingress-dp.yaml.tmpl +++ b/deploy/addons/ingress/ingress-dp.yaml.tmpl @@ -24,6 +24,12 @@ metadata: addonmanager.kubernetes.io/mode: Reconcile spec: replicas: 1 + strategy: + type: RollingUpdate + rollingUpdate: + # maxUnavailable needs to be 1 so that port conflicts between the old and new pod doesn't happen when using hostPort + maxUnavailable: 1 + maxSurge: 1 selector: matchLabels: app.kubernetes.io/name: nginx-ingress-controller @@ -42,7 +48,7 @@ spec: serviceAccountName: nginx-ingress terminationGracePeriodSeconds: 60 containers: - - image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller{{.ExoticArch}}:0.25.1 + - image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller{{.ExoticArch}}:0.26.1 name: nginx-ingress-controller imagePullPolicy: IfNotPresent readinessProbe: diff --git a/deploy/addons/layouts/gvisor/single.html b/deploy/addons/layouts/gvisor/single.html new file mode 100644 index 0000000000..620f9d82b6 --- /dev/null +++ b/deploy/addons/layouts/gvisor/single.html @@ -0,0 +1,5 @@ +{{ define "main" }} +
+ {{ .Render "content" }} +
+{{ end }} \ No newline at end of file diff --git a/deploy/addons/layouts/helm-tiller/single.html b/deploy/addons/layouts/helm-tiller/single.html new file mode 100644 index 0000000000..620f9d82b6 --- /dev/null +++ b/deploy/addons/layouts/helm-tiller/single.html @@ -0,0 +1,5 @@ +{{ define "main" }} +
+ {{ .Render "content" }} +
+{{ end }} \ No newline at end of file diff --git a/deploy/addons/layouts/ingress-dns/single.html b/deploy/addons/layouts/ingress-dns/single.html new file mode 100644 index 0000000000..27fcd101d0 --- /dev/null +++ b/deploy/addons/layouts/ingress-dns/single.html @@ -0,0 +1,5 @@ +{{ define "main" }} +
+ {{ .Render "content" }} +
+{{ end }} \ No newline at end of file diff --git a/deploy/addons/layouts/storage-provisioner-gluster/single.html b/deploy/addons/layouts/storage-provisioner-gluster/single.html new file mode 100644 index 0000000000..620f9d82b6 --- /dev/null +++ b/deploy/addons/layouts/storage-provisioner-gluster/single.html @@ -0,0 +1,5 @@ +{{ define "main" }} +
+ {{ .Render "content" }} +
+{{ end }} \ No newline at end of file diff --git a/deploy/addons/registry-creds/registry-creds-rc.yaml.tmpl b/deploy/addons/registry-creds/registry-creds-rc.yaml.tmpl index 2dbc0b4e82..8d3c3f1fd4 100644 --- a/deploy/addons/registry-creds/registry-creds-rc.yaml.tmpl +++ b/deploy/addons/registry-creds/registry-creds-rc.yaml.tmpl @@ -1,27 +1,24 @@ -apiVersion: v1 -kind: ReplicationController +apiVersion: apps/v1 +kind: Deployment metadata: name: registry-creds namespace: kube-system labels: - version: v1.9 addonmanager.kubernetes.io/mode: Reconcile kubernetes.io/minikube-addons: registry-creds spec: replicas: 1 selector: - name: registry-creds - version: v1.9 - addonmanager.kubernetes.io/mode: Reconcile + matchLabels: + name: registry-creds template: metadata: labels: name: registry-creds - version: v1.9 addonmanager.kubernetes.io/mode: Reconcile spec: containers: - - image: registry.hub.docker.com/upmcenterprises/registry-creds:1.9 + - image: upmcenterprises/registry-creds:1.9 name: registry-creds imagePullPolicy: Always env: diff --git a/deploy/gvisor/Dockerfile b/deploy/gvisor/Dockerfile index 9dacfa5466..9d62239fc3 100644 --- a/deploy/gvisor/Dockerfile +++ b/deploy/gvisor/Dockerfile @@ -12,9 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM ubuntu:18.04 -RUN apt-get update && \ - apt-get install -y kmod gcc wget xz-utils libc6-dev bc libelf-dev bison flex openssl libssl-dev libidn2-0 sudo libcap2 && \ - rm -rf /var/lib/apt/lists/* +# Need an image with chroot +FROM alpine:3 COPY out/gvisor-addon /gvisor-addon CMD ["/gvisor-addon"] diff --git a/deploy/iso/minikube-iso/board/coreos/minikube/isolinux.cfg b/deploy/iso/minikube-iso/board/coreos/minikube/isolinux.cfg index ecb2f1d708..9857e0bb2a 100644 --- a/deploy/iso/minikube-iso/board/coreos/minikube/isolinux.cfg +++ b/deploy/iso/minikube-iso/board/coreos/minikube/isolinux.cfg @@ -2,4 +2,4 @@ default 1 label 1 kernel /boot/bzImage initrd /boot/initrd - append root=/dev/sr0 loglevel=3 console=ttyS0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes + append root=/dev/sr0 loglevel=3 console=ttyS0 noembed nomodeset norestore waitusb=10 random.trust_cpu=on hw_rng_model=virtio systemd.legacy_systemd_cgroup_controller=yes diff --git a/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/usr/bin/toolbox b/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/usr/bin/toolbox index bdfad1d3e9..2f597cd80b 100644 --- a/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/usr/bin/toolbox +++ b/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/usr/bin/toolbox @@ -8,11 +8,11 @@ machine=$(uname -m) case ${machine} in aarch64 ) TOOLBOX_DOCKER_IMAGE=arm64v8/fedora - TOOLBOX_DOCKER_TAG=29 + TOOLBOX_DOCKER_TAG=latest ;; x86_64 ) TOOLBOX_DOCKER_IMAGE=fedora - TOOLBOX_DOCKER_TAG=29 + TOOLBOX_DOCKER_TAG=latest ;; * ) echo "Warning: Unknown machine type ${machine}" >&2 @@ -69,10 +69,9 @@ if [ "x${1-}" == x-c ]; then set /bin/sh "$@" fi -sudo systemd-nspawn \ +sudo SYSTEMD_NSPAWN_SHARE_SYSTEM=1 systemd-nspawn \ --directory="${machinepath}" \ --capability=all \ - --share-system \ ${TOOLBOX_BIND} \ ${TOOLBOX_ENV} \ --user="${TOOLBOX_USER}" "$@" diff --git a/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/var/run/crio.sock b/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/var/run/crio.sock deleted file mode 120000 index 2c31ff8a45..0000000000 --- a/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/var/run/crio.sock +++ /dev/null @@ -1 +0,0 @@ -crio/crio.sock \ No newline at end of file diff --git a/deploy/iso/minikube-iso/board/coreos/minikube/users b/deploy/iso/minikube-iso/board/coreos/minikube/users index 3797cd023a..8e967e11f1 100644 --- a/deploy/iso/minikube-iso/board/coreos/minikube/users +++ b/deploy/iso/minikube-iso/board/coreos/minikube/users @@ -1 +1 @@ -docker -1 docker -1 =tcuser /home/docker /bin/bash wheel,vboxsf - +docker 1000 docker 1000 =tcuser /home/docker /bin/bash wheel,vboxsf - diff --git a/deploy/iso/minikube-iso/configs/minikube_defconfig b/deploy/iso/minikube-iso/configs/minikube_defconfig index 01010e85da..abcb1c8491 100644 --- a/deploy/iso/minikube-iso/configs/minikube_defconfig +++ b/deploy/iso/minikube-iso/configs/minikube_defconfig @@ -3,7 +3,7 @@ BR2_CCACHE=y BR2_OPTIMIZE_2=y BR2_TOOLCHAIN_BUILDROOT_VENDOR="minikube" BR2_TOOLCHAIN_BUILDROOT_GLIBC=y -BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_15=y +BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_19=y BR2_BINUTILS_VERSION_2_30_X=y BR2_GCC_VERSION_7_X=y BR2_TOOLCHAIN_BUILDROOT_CXX=y @@ -18,8 +18,6 @@ BR2_SYSTEM_BIN_SH_BASH=y BR2_ROOTFS_USERS_TABLES="$(BR2_EXTERNAL_MINIKUBE_PATH)/board/coreos/minikube/users" BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_MINIKUBE_PATH)/board/coreos/minikube/rootfs-overlay" BR2_LINUX_KERNEL=y -BR2_LINUX_KERNEL_CUSTOM_VERSION=y -BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="4.15" BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL_MINIKUBE_PATH)/board/coreos/minikube/linux_defconfig" BR2_LINUX_KERNEL_NEEDS_HOST_LIBELF=y diff --git a/deploy/iso/minikube-iso/package/automount/automount.mk b/deploy/iso/minikube-iso/package/automount/automount.mk index b504d45f36..2eaa14f131 100644 --- a/deploy/iso/minikube-iso/package/automount/automount.mk +++ b/deploy/iso/minikube-iso/package/automount/automount.mk @@ -6,7 +6,7 @@ define AUTOMOUNT_INSTALL_INIT_SYSTEMD $(INSTALL) -D -m 644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/automount/minikube-automount.service \ + $(AUTOMOUNT_PKGDIR)/minikube-automount.service \ $(TARGET_DIR)/usr/lib/systemd/system/minikube-automount.service ln -fs /usr/lib/systemd/system/minikube-automount.service \ @@ -15,7 +15,7 @@ endef define AUTOMOUNT_INSTALL_TARGET_CMDS $(INSTALL) -Dm755 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/automount/minikube-automount \ + $(AUTOMOUNT_PKGDIR)/minikube-automount \ $(TARGET_DIR)/usr/sbin/minikube-automount endef 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 a2ca19e75b..85ec682257 100644 --- a/deploy/iso/minikube-iso/package/containerd-bin/containerd-bin.mk +++ b/deploy/iso/minikube-iso/package/containerd-bin/containerd-bin.mk @@ -11,6 +11,7 @@ CONTAINERD_BIN_DEPENDENCIES = host-go libgpgme CONTAINERD_BIN_GOPATH = $(@D)/_output CONTAINERD_BIN_ENV = \ CGO_ENABLED=1 \ + GO111MODULE=off \ GOPATH="$(CONTAINERD_BIN_GOPATH)" \ GOBIN="$(CONTAINERD_BIN_GOPATH)/bin" \ PATH=$(CONTAINERD_BIN_GOPATH)/bin:$(BR_PATH) @@ -42,13 +43,13 @@ define CONTAINERD_BIN_INSTALL_TARGET_CMDS $(@D)/bin/ctr \ $(TARGET_DIR)/usr/bin $(INSTALL) -Dm644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/containerd-bin/config.toml \ + $(CONTAINERD_BIN_PKGDIR)/config.toml \ $(TARGET_DIR)/etc/containerd/config.toml endef define CONTAINERD_BIN_INSTALL_INIT_SYSTEMD $(INSTALL) -Dm755 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/containerd-bin/containerd.service \ + $(CONTAINERD_BIN_PKGDIR)/containerd.service \ $(TARGET_DIR)/usr/lib/systemd/system/containerd.service $(call link-service,containerd.service) $(call link-service,containerd-shutdown.service) 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 fd33d4dd6d..9791804d38 100644 --- a/deploy/iso/minikube-iso/package/crio-bin/crio-bin.mk +++ b/deploy/iso/minikube-iso/package/crio-bin/crio-bin.mk @@ -26,7 +26,7 @@ define CRIO_BIN_CONFIGURE_CMDS mkdir -p $(CRIO_BIN_GOPATH)/src/github.com/cri-o ln -sf $(@D) $(CRIO_BIN_GOPATH)/src/github.com/cri-o/cri-o # Copy pre-generated conmon/config.h - see - cp $(BR2_EXTERNAL_MINIKUBE_PATH)/package/crio-bin/conmon-config.h $(@D)/conmon/config.h + cp $(CRIO_BIN_PKGDIR)/conmon-config.h $(@D)/conmon/config.h endef define CRIO_BIN_BUILD_CMDS @@ -48,13 +48,13 @@ define CRIO_BIN_INSTALL_TARGET_CMDS $(@D)/bin/pause \ $(TARGET_DIR)/usr/libexec/crio/pause $(INSTALL) -Dm644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/crio-bin/crio.conf \ + $(CRIO_BIN_PKGDIR)/crio.conf \ $(TARGET_DIR)/etc/crio/crio.conf $(INSTALL) -Dm644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/crio-bin/policy.json \ + $(CRIO_BIN_PKGDIR)/policy.json \ $(TARGET_DIR)/etc/containers/policy.json $(INSTALL) -Dm644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/crio-bin/registries.conf \ + $(CRIO_BIN_PKGDIR)/registries.conf \ $(TARGET_DIR)/etc/containers/registries.conf mkdir -p $(TARGET_DIR)/etc/sysconfig @@ -64,7 +64,7 @@ endef define CRIO_BIN_INSTALL_INIT_SYSTEMD $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) install.systemd DESTDIR=$(TARGET_DIR) PREFIX=$(TARGET_DIR)/usr $(INSTALL) -Dm644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/crio-bin/crio.service \ + $(CRIO_BIN_PKGDIR)/crio.service \ $(TARGET_DIR)/usr/lib/systemd/system/crio.service $(call link-service,crio.service) $(call link-service,crio-shutdown.service) diff --git a/deploy/iso/minikube-iso/package/docker-bin/docker-bin.mk b/deploy/iso/minikube-iso/package/docker-bin/docker-bin.mk index 25e12dc7c7..c212f07b85 100644 --- a/deploy/iso/minikube-iso/package/docker-bin/docker-bin.mk +++ b/deploy/iso/minikube-iso/package/docker-bin/docker-bin.mk @@ -27,6 +27,10 @@ define DOCKER_BIN_INSTALL_TARGET_CMDS $(@D)/dockerd \ $(TARGET_DIR)/bin/dockerd + $(INSTALL) -D -m 0755 \ + $(@D)/docker-init \ + $(TARGET_DIR)/bin/docker-init + $(INSTALL) -D -m 0755 \ $(@D)/docker-proxy \ $(TARGET_DIR)/bin/docker-proxy @@ -38,7 +42,7 @@ define DOCKER_BIN_INSTALL_INIT_SYSTEMD $(TARGET_DIR)/usr/lib/systemd/system/docker.socket $(INSTALL) -D -m 644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/docker-bin/forward.conf \ + $(DOCKER_BIN_PKGDIR)/forward.conf \ $(TARGET_DIR)/etc/sysctl.d/forward.conf endef diff --git a/deploy/iso/minikube-iso/package/gluster/gluster.mk b/deploy/iso/minikube-iso/package/gluster/gluster.mk index aedf078124..35a761afd1 100644 --- a/deploy/iso/minikube-iso/package/gluster/gluster.mk +++ b/deploy/iso/minikube-iso/package/gluster/gluster.mk @@ -5,7 +5,10 @@ ################################################################################ GLUSTER_VERSION = 4.1.5 -GLUSTER_SITE = https://download.gluster.org/pub/gluster/glusterfs/4.1/$(GLUSTER_VERSION) +# Official gluster site has SSL problems +# https://bugzilla.redhat.com/show_bug.cgi?id=1572944 +# GLUSTER_SITE = https://download.gluster.org/pub/gluster/glusterfs/4.1/$(GLUSTER_VERSION) +GLUSTER_SITE = http://download.openpkg.org/components/cache/glusterfs GLUSTER_SOURCE = glusterfs-$(GLUSTER_VERSION).tar.gz GLUSTER_CONF_OPTS = --disable-tiering --disable-ec-dynamic --disable-xmltest --disable-crypt-xlator --disable-georeplication --disable-ibverbs --disable-glupy --disable-gnfs --disable-cmocka --without-server GLUSTER_INSTALL_TARGET_OPTS = DESTDIR=$(TARGET_DIR) install diff --git a/deploy/iso/minikube-iso/package/hyperv-daemons/hyperv-daemons.mk b/deploy/iso/minikube-iso/package/hyperv-daemons/hyperv-daemons.mk index d0fadf5b1c..c6f87675be 100644 --- a/deploy/iso/minikube-iso/package/hyperv-daemons/hyperv-daemons.mk +++ b/deploy/iso/minikube-iso/package/hyperv-daemons/hyperv-daemons.mk @@ -4,8 +4,8 @@ # ################################################################################ -HYPERV_DAEMONS_VERSION = 4.15.1 -HYPERV_DAEMONS_SITE = https://www.kernel.org/pub/linux/kernel/v${HYPERV_DAEMONS_VERSION%%.*}.x +HYPERV_DAEMONS_VERSION = 4.19.76 +HYPERV_DAEMONS_SITE = https://www.kernel.org/pub/linux/kernel/v4.x HYPERV_DAEMONS_SOURCE = linux-$(HYPERV_DAEMONS_VERSION).tar.xz define HYPERV_DAEMONS_BUILD_CMDS @@ -37,23 +37,23 @@ endef define HYPERV_DAEMONS_INSTALL_INIT_SYSTEMD $(INSTALL) -D -m 644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/hyperv-daemons/70-hv_fcopy.rules \ + $(HYPERV_DAEMONS_PKGDIR)/70-hv_fcopy.rules \ $(TARGET_DIR)/etc/udev/rules.d/70-hv_fcopy.rules $(INSTALL) -D -m 644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/hyperv-daemons/70-hv_kvp.rules \ + $(HYPERV_DAEMONS_PKGDIR)/70-hv_kvp.rules \ $(TARGET_DIR)/etc/udev/rules.d/70-hv_kvp.rules $(INSTALL) -D -m 644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/hyperv-daemons/70-hv_vss.rules \ + $(HYPERV_DAEMONS_PKGDIR)/70-hv_vss.rules \ $(TARGET_DIR)/etc/udev/rules.d/70-hv_vss.rules $(INSTALL) -D -m 644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/hyperv-daemons/hv_fcopy_daemon.service \ + $(HYPERV_DAEMONS_PKGDIR)/hv_fcopy_daemon.service \ $(TARGET_DIR)/usr/lib/systemd/system/hv_fcopy_daemon.service $(INSTALL) -D -m 644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/hyperv-daemons/hv_kvp_daemon.service \ + $(HYPERV_DAEMONS_PKGDIR)/hv_kvp_daemon.service \ $(TARGET_DIR)/usr/lib/systemd/system/hv_kvp_daemon.service $(INSTALL) -D -m 644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/hyperv-daemons/hv_vss_daemon.service \ + $(HYPERV_DAEMONS_PKGDIR)/hv_vss_daemon.service \ $(TARGET_DIR)/usr/lib/systemd/system/hv_vss_daemon.service ln -fs /usr/lib/systemd/system/hv_fcopy_daemon.service \ diff --git a/deploy/iso/minikube-iso/package/vbox-guest/vbox-guest.hash b/deploy/iso/minikube-iso/package/vbox-guest/vbox-guest.hash index 191a7c833d..f8dd11fe31 100644 --- a/deploy/iso/minikube-iso/package/vbox-guest/vbox-guest.hash +++ b/deploy/iso/minikube-iso/package/vbox-guest/vbox-guest.hash @@ -1,3 +1,6 @@ # From http://download.virtualbox.org/virtualbox/5.1.30/SHA256SUMS sha256 96cab2296fb014ce0a16b7b9603b52208b9403c10c1524b44201d3c274e8a821 VirtualBox-5.1.38.tar.bz2 sha256 0e7ee2c78ebf7cd0d3a933d51148bef04a64f64fb27ccf70d59cddf9ca1e517a VBoxGuestAdditions_5.1.38.iso +# From http://download.virtualbox.org/virtualbox/5.2.32/SHA256SUMS +sha256 ff6390e50cb03718cd3f5779627910999c12279b465e340c80d7175778a33958 VirtualBox-5.2.32.tar.bz2 +sha256 4311c7408a3410e6a33264a9062347d9eec04f58339a49f0a60488c0cabc8996 VBoxGuestAdditions_5.2.32.iso diff --git a/deploy/iso/minikube-iso/package/vbox-guest/vbox-guest.mk b/deploy/iso/minikube-iso/package/vbox-guest/vbox-guest.mk index d813259f09..7b092b8695 100644 --- a/deploy/iso/minikube-iso/package/vbox-guest/vbox-guest.mk +++ b/deploy/iso/minikube-iso/package/vbox-guest/vbox-guest.mk @@ -4,7 +4,7 @@ # ################################################################################ -VBOX_GUEST_VERSION = 5.1.38 +VBOX_GUEST_VERSION = 5.2.32 VBOX_GUEST_SITE = http://download.virtualbox.org/virtualbox/$(VBOX_GUEST_VERSION) VBOX_GUEST_LICENSE = GPLv2 VBOX_GUEST_LICENSE_FILES = COPYING @@ -12,7 +12,7 @@ VBOX_GUEST_SOURCE = VirtualBox-$(VBOX_GUEST_VERSION).tar.bz2 VBOX_GUEST_EXTRA_DOWNLOADS = http://download.virtualbox.org/virtualbox/${VBOX_GUEST_VERSION}/VBoxGuestAdditions_${VBOX_GUEST_VERSION}.iso define VBOX_GUEST_EXPORT_MODULES - ( cd $(@D)/src/VBox/Additions/linux; ./export_modules modules.tar.gz ) + ( cd $(@D)/src/VBox/Additions/linux; ./export_modules.sh modules.tar.gz ) mkdir -p $(@D)/vbox-modules tar -C $(@D)/vbox-modules -xzf $(@D)/src/VBox/Additions/linux/modules.tar.gz endef @@ -20,7 +20,7 @@ endef VBOX_GUEST_POST_EXTRACT_HOOKS += VBOX_GUEST_EXPORT_MODULES VBOX_GUEST_MODULE_SUBDIRS = vbox-modules/ -VBOX_GUEST_MODULE_MAKE_OPTS = KVERSION=$(LINUX_VERSION_PROBED) +VBOX_GUEST_MODULE_MAKE_OPTS = KVERSION=$(LINUX_VERSION_PROBED) KERN_DIR=$(LINUX_DIR) define VBOX_GUEST_USERS - -1 vboxsf -1 - - - - - @@ -28,7 +28,7 @@ endef define VBOX_GUEST_INSTALL_INIT_SYSTEMD $(INSTALL) -D -m 644 \ - $(BR2_EXTERNAL_MINIKUBE_PATH)/package/vbox-guest/vboxservice.service \ + $(VBOX_GUEST_PKGDIR)/vboxservice.service \ $(TARGET_DIR)/usr/lib/systemd/system/vboxservice.service ln -fs /usr/lib/systemd/system/vboxservice.service \ diff --git a/deploy/minikube/release_sanity_test.go b/deploy/minikube/release_sanity_test.go index 53e8d46a68..22bb29d9c7 100644 --- a/deploy/minikube/release_sanity_test.go +++ b/deploy/minikube/release_sanity_test.go @@ -48,7 +48,7 @@ func getSHAFromURL(url string) (string, error) { } func TestReleasesJson(t *testing.T) { - releases, err := notify.GetAllVersionsFromURL(constants.GithubMinikubeReleasesURL) + releases, err := notify.GetAllVersionsFromURL(notify.GithubMinikubeReleasesURL) if err != nil { t.Fatalf("Error getting releases.json: %v", err) } diff --git a/deploy/minikube/releases.json b/deploy/minikube/releases.json index 33573e46b1..18716cfd74 100644 --- a/deploy/minikube/releases.json +++ b/deploy/minikube/releases.json @@ -1,4 +1,28 @@ [ + { + "name": "v1.5.2", + "checksums": { + "darwin": "734306019f837a6aee9cb7a0245839f98ea7688ee2cde387099334cb9356c2c4", + "linux": "1972a9a96de85e480012f6d2c9b8a88fd29217b99b1a973ed5e199386659f7e9", + "windows": "9f012922fd8d701070ef3951b0df77b720805a204d4d0dfa15d11899fda8a2d0" + } + }, + { + "name": "v1.5.1", + "checksums": { + "darwin": "7ba345034e176566930d873acd0f38366dd14fdafd038febe600ea38c24c4208", + "linux": "5aed23a876770c92d0162fcf7862d855dc306516614be78ac6fbc47b5cba55e6", + "windows": "5a7bd914b0ae57e0853d72a06b7fb72e645417f2f3cd86d0f1bc4f636a04d160" + } + }, + { + "name": "v1.5.0", + "checksums": { + "darwin": "eb716c176f404bb555966ff3947d5d9c5fb63eb902d11c83839fda492ff4b1fc", + "linux": "ca50dcc7c83d4dde484d650a5a1934ea1bef692340af3aa831d34c6e847b2770", + "windows": "bdd61e446f49570428848ad15337264edfecc55d1dd4aed4499d559f9c8383b9" + } + }, { "name": "v1.4.0", "checksums": { diff --git a/enhancements/README.md b/enhancements/README.md new file mode 100644 index 0000000000..5c6ef799a7 --- /dev/null +++ b/enhancements/README.md @@ -0,0 +1,13 @@ +# Enhancements + +The *minikube enhancement process (MEP)* is a way to propose, communicate, and coordinate on new efforts for the minikube project. You can read the full details in the [MEP proposal](implemented/20190925-minikube-enhancement-process/README.md) + +MEP is based on a simplification of the [Kubernetes Enhancement Process](https://github.com/kubernetes/enhancements/blob/master/keps/0001-kubernetes-enhancement-proposal-process.md). + +## Workflow + +1. Copy `template.md` to `proposed/-title/README.md`. Include supporting documents in the `proposed/-title/` directory. +1. Send PR out for review, titled: `MEP: ` +1. Proposal will be discussed at the bi-weekly minikube office hours +1. After a 2-week review window, the proposal can be merged once there are 3 approving maintainers or reviewers. To keep proposals neutral, each reviewer must be independent and/or represent a different company. +1. In your PR that implements the enhancement, move the proposal to the `implemented/` folder. diff --git a/enhancements/implemented/20190925-minikube-enhancement-process/README.md b/enhancements/implemented/20190925-minikube-enhancement-process/README.md new file mode 100644 index 0000000000..1ecbefbb27 --- /dev/null +++ b/enhancements/implemented/20190925-minikube-enhancement-process/README.md @@ -0,0 +1,78 @@ +# minikube enhancement process + +First proposed: 2019-09-25 +Authors: tstromberg + +## Reviewer Priorities + +Please review this proposal with the following priorities: + +* Does this fit with minikube's principles? +* Are there other approaches to consider? +* Could the implementation be made simpler? + +Please leave the above text in your proposal as instructions to the reader. + +## Summary + +A design review process for non-trivial enhancements to minikube. + +## Goals + +* Facilitate communication about the "how" and "why" of an enhancement before code is written +* Lightweight enough to not deter casual contributions +* A process applicable to any roadmap-worthy enhancement + +## Non-Goals + +* Coverage for smaller enhancements that would not be represented within the minikube roadmap. +* Reduced development velocity + +## Design Details + +The *minikube enhancement process (MEP)* is a way to propose, communicate, and coordinate on new efforts for the minikube project. MEP is based on a simplification of the [Kubernetes Enhancement Process](https://github.com/kubernetes/enhancements/blob/master/keps/0001-kubernetes-enhancement-proposal-process.md). + +### Proposal Workflow + +1. Copy `template.md` to `proposed/<date>-title.md` +1. Send PR out for review, titled: `Proposal: <title>` +1. Proposal will be discussed at the bi-weekly minikube office hours +1. After a 2-week review window, the proposal can be merged once there are 3 approving maintainers or reviewers. To keep proposals neutral, each reviewer must be independent and/or represent a different company. + +### Implementation Workflow + +1. In your PR that implements the enhancement, move the proposal to the `implemented/` folder. + +## Alternatives Considered + +### Kubernetes Enhancement Process + +KEP's are a well-understood, but lengthier process geared toward making changes where multiple Kubernetes SIG's are affected. + +#### Pro's + +* Easily facilitate input from multiple SIG's +* Clear, well understood process within Kubernetes, shared by multiple projects + +#### Con's + +* Invisible to casual contributors to a project, as these proposals do not show up within the GitHub project page +* Lengthy template (1870+ words) that prompts for information that is not relevant to minikube +* Time commitment deters casual contribution + +### Google Docs Proposal Template + +Rather than maintaining Markdown documents in the minikube repository, we could use a Google Docs template, and then a Google Sheet to track proposal status. + +### Pro's + +* Easier editing for trivial proposals + +### Con's + +* Authors may waste unnecessary time styling output +* Styling may be inconsistent between proposals +* Invisible to casual contributors to a project, as these proposals do not show up within the GitHub project page + + + diff --git a/enhancements/template.md b/enhancements/template.md new file mode 100644 index 0000000000..b3f3eecff4 --- /dev/null +++ b/enhancements/template.md @@ -0,0 +1,41 @@ +# Your inspiring proposal title + +* First proposed: <date> +* Authors: $full_name (@github-handle), $full_name2 (@github_handle2) + +## Reviewer Priorities + +Please review this proposal with the following priorities: + +* Does this fit with minikube's [principles](https://minikube.sigs.k8s.io/docs/concepts/principles/)? +* Are there other approaches to consider? +* Could the implementation be made simpler? +* Are there usability, reliability, or technical debt concerns? + +Please leave the above text in your proposal as instructions to the reader. + +## Summary + +_(1 paragraph) What are you proposing, and why is it important to users and/or developers?_ + +## Goals + +* _A bulleted list of specific goals for this proposal_ +* _How will we know that this proposal has succeeded?_ + +## Non-Goals + +* _A bulleted list of what is out of scope for this proposal_ +* _Listing non-goals helps to focus the discussion_ + +## Design Details + +_(2+ paragraphs) A short overview of your implementation idea, containing only as much detail as required to convey your idea._ + +_If you have multiple ideas, list them concisely._ + +_Include a testing plan to ensure that your enhancement is not broken by future changes._ + +## Alternatives Considered + +_Alternative ideas that you are leaning against._ diff --git a/go.mod b/go.mod index 80bd0d2055..bee76d17ed 100644 --- a/go.mod +++ b/go.mod @@ -21,14 +21,16 @@ require ( github.com/docker/machine v0.7.1-0.20190718054102-a555e4f7a8f5 // version is 0.7.1 to pin to a555e4f7a8f5 github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect + github.com/ghodss/yaml v1.0.0 // indirect github.com/go-ole/go-ole v1.2.4 // indirect github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/google/btree v1.0.0 // indirect github.com/google/go-cmp v0.3.0 - github.com/google/go-github/v25 v25.0.2 github.com/gorilla/mux v1.7.1 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.5.0 // indirect github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce // indirect - github.com/hashicorp/go-getter v1.3.0 + github.com/hashicorp/go-getter v1.4.0 github.com/hashicorp/go-multierror v0.0.0-20160811015721-8c5f0ad93604 // indirect github.com/hashicorp/go-retryablehttp v0.5.4 github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214 // indirect @@ -44,12 +46,14 @@ require ( github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 // indirect github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d // indirect github.com/juju/version v0.0.0-20180108022336-b64dbd566305 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/libvirt/libvirt-go v3.4.0+incompatible github.com/machine-drivers/docker-machine-driver-vmware v0.1.1 github.com/mattn/go-isatty v0.0.8 github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936 github.com/moby/hyperkit v0.0.0-20171020124204-a12cd7250bcd github.com/olekukonko/tablewriter v0.0.0-20160923125401-bdcc175572fd + github.com/otiai10/copy v1.0.2 github.com/pborman/uuid v1.2.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/pkg/browser v0.0.0-20160118053552-9302be274faa @@ -69,13 +73,13 @@ require ( github.com/zchee/go-vmnet v0.0.0-20161021174912-97ebf9174097 golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 - golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a golang.org/x/sync v0.0.0-20190423024810-112230192c58 golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb golang.org/x/text v0.3.2 gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect + gotest.tools v2.2.0+incompatible k8s.io/api v0.0.0 k8s.io/apimachinery v0.0.0 k8s.io/client-go v0.0.0 @@ -86,6 +90,7 @@ require ( replace ( git.apache.org/thrift.git => github.com/apache/thrift v0.0.0-20180902110319-2566ecd5d999 github.com/docker/machine => github.com/machine-drivers/machine v0.7.1-0.20190910053320-21bd2f51b8ea + github.com/hashicorp/go-getter => github.com/afbjorklund/go-getter v1.4.1-0.20190910175809-eb9f6c26742c k8s.io/api => k8s.io/kubernetes/staging/src/k8s.io/api v0.0.0-20190623232353-8c3b7d7679cc k8s.io/apiextensions-apiserver => k8s.io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20190623232353-8c3b7d7679cc k8s.io/apimachinery => k8s.io/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20190623232353-8c3b7d7679cc diff --git a/go.sum b/go.sum index cae562ed96..cef3a35ca6 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,13 @@ cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8= cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1 h1:lRi0CHyU+ytlvylOlFKKq0af6JncuyoRh1J+QJBqQx0= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= @@ -35,6 +42,8 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUW github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= +github.com/afbjorklund/go-getter v1.4.1-0.20190910175809-eb9f6c26742c h1:18gEt7qzn7CW7qMkfPTFyyotlPbvPQo9o4IDV8jZqP4= +github.com/afbjorklund/go-getter v1.4.1-0.20190910175809-eb9f6c26742c/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apache/thrift v0.0.0-20180902110319-2566ecd5d999/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -179,13 +188,18 @@ github.com/golang/mock v0.0.0-20160127222235-bd3c8e81be01/go.mod h1:oTYuIxOrZwtP github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/cadvisor v0.33.2-0.20190411163913-9db8c7dee20a/go.mod h1:1nql6U13uTHaLYB8rLS5x9IJc2qT6Xd/Tr1sTX6NE48= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -195,8 +209,6 @@ github.com/google/go-containerregistry v0.0.0-20180731221751-697ee0b3d46e h1:Hm0 github.com/google/go-containerregistry v0.0.0-20180731221751-697ee0b3d46e/go.mod h1:yZAFP63pRshzrEYLXLGPmUt0Ay+2zdjmMN1loCnRLUk= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-github/v25 v25.0.2 h1:MqXE7nOlIF91NJ/PXAcvS2dC+XXCDbY7RvJzjyEPAoU= -github.com/google/go-github/v25 v25.0.2/go.mod h1:6z5pC69qHtrPJ0sXPsj4BLnd82b+r6sLB7qcBoRZqpw= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= @@ -205,6 +217,7 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -212,6 +225,9 @@ github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mqJt0Cc= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= @@ -233,6 +249,7 @@ github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6K github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-getter v1.3.0 h1:pFMSFlI9l5NaeuzkpE3L7BYk9qQ9juTAgXW/H0cqxcU= github.com/hashicorp/go-getter v1.3.0/go.mod h1:/O1k/AizTN0QmfEKknCYGvICeyKUDqCYA8vvWtGWDeQ= +github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= github.com/hashicorp/go-multierror v0.0.0-20160811015721-8c5f0ad93604 h1:VIq8E7fMiC4h3agg0ya56L0jHn7QisZZcWZXVKJb9jQ= github.com/hashicorp/go-multierror v0.0.0-20160811015721-8c5f0ad93604/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE= @@ -243,6 +260,8 @@ github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PF github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v0.0.0-20160711231752-d8c773c4cba1/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= @@ -296,6 +315,8 @@ github.com/juju/version v0.0.0-20180108022336-b64dbd566305 h1:lQxPJ1URr2fjsKnJRt github.com/juju/version v0.0.0-20180108022336-b64dbd566305/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U= github.com/kardianos/osext v0.0.0-20150410034420-8fef92e41e22/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -383,6 +404,12 @@ github.com/opencontainers/runc v0.0.0-20181113202123-f000fe11ece1/go.mod h1:qT5X github.com/opencontainers/runtime-spec v1.0.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v0.0.0-20170621221121-4a2974bf1ee9/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc= +github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95 h1:+OLn68pqasWca0z5ryit9KGfp3sUsW4Lqg32iRMJyzs= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -513,6 +540,9 @@ github.com/zchee/go-vmnet v0.0.0-20161021174912-97ebf9174097 h1:Ucx5I1l1+TWXvqFm github.com/zchee/go-vmnet v0.0.0-20161021174912-97ebf9174097/go.mod h1:lFZSWRIpCfE/pt91hHBBpV6+x87YlCjsp+aIR2qCPPU= go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -525,15 +555,21 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -545,15 +581,24 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -572,6 +617,10 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -584,13 +633,23 @@ golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20170824195420-5d2fd3ccab98/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= @@ -599,12 +658,18 @@ google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+ google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20170731182057-09f6ed296fc6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -612,11 +677,22 @@ google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8= google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= @@ -648,7 +724,9 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM= k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= @@ -693,6 +771,7 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/sig-storage-lib-external-provisioner v4.0.0+incompatible h1:qV3eFdgCp7Cp/ORjkJI9VBBEOntT+z385jLqdBtmgHA= sigs.k8s.io/sig-storage-lib-external-provisioner v4.0.0+incompatible/go.mod h1:qhqLyNwJC49PoUalmtzYb4s9fT8HOMBTLbTY1QoVOqI= diff --git a/hack/.gitignore b/hack/.gitignore new file mode 100644 index 0000000000..6168f032c4 --- /dev/null +++ b/hack/.gitignore @@ -0,0 +1 @@ +release-notes diff --git a/hack/boilerplate/boilerplate.go b/hack/boilerplate/boilerplate.go new file mode 100644 index 0000000000..1318711f71 --- /dev/null +++ b/hack/boilerplate/boilerplate.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "bytes" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "regexp" + "strings" +) + +var ( + boilerplatedir = flag.String("boilerplate-dir", ".", "Boilerplate directory for boilerplate files") + rootdir = flag.String("rootdir", "../../", "Root directory to examine") + verbose = flag.Bool("v", false, "Verbose") + skippedPaths = regexp.MustCompile(`Godeps|third_party|_gopath|_output|\.git|cluster/env.sh|vendor|test/e2e/generated/bindata.go|site/themes/docsy`) + windowdNewLine = regexp.MustCompile(`\r`) + txtExtension = regexp.MustCompile(`\.txt`) + goBuildTag = regexp.MustCompile(`(?m)^(// \+build.*\n)+\n`) + shebang = regexp.MustCompile(`(?m)^(#!.*\n)\n*`) + copyright = regexp.MustCompile(`Copyright YEAR`) + copyrightReal = regexp.MustCompile(`Copyright \d{4}`) +) + +func main() { + flag.Parse() + refs, err := extensionToBoilerplate(*boilerplatedir) + if err != nil { + log.Fatal(err) + } + if len(refs) == 0 { + log.Fatal("no references in ", *boilerplatedir) + } + files, err := filesToCheck(*rootdir, refs) + if err != nil { + log.Fatal(err) + } + for _, file := range files { + pass, err := filePasses(file, refs[filepath.Ext(file)]) + if err != nil { + log.Println(err) + } + if !pass { + path, err := filepath.Abs(file) + if err != nil { + log.Println(err) + } + fmt.Println(path) + } + } + +} + +// extensionToBoilerplate returns a map of file extension to required boilerplate text. +func extensionToBoilerplate(dir string) (map[string][]byte, error) { + refs := make(map[string][]byte) + files, _ := filepath.Glob(dir + "/*.txt") + for _, filename := range files { + extension := strings.ToLower(filepath.Ext(txtExtension.ReplaceAllString(filename, ""))) + data, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + refs[extension] = windowdNewLine.ReplaceAll(data, nil) + } + if *verbose { + dir, err := filepath.Abs(dir) + if err != nil { + return refs, err + } + fmt.Printf("Found %v boilerplates in %v for the following extensions:", len(refs), dir) + for ext := range refs { + fmt.Printf(" %v", ext) + } + fmt.Println() + } + return refs, nil +} + +// filePasses checks whether the processed file is valid. Returning false means that the file does not the proper boilerplate template. +func filePasses(filename string, expectedBoilerplate []byte) (bool, error) { + data, err := ioutil.ReadFile(filename) + if err != nil { + return false, err + } + data = windowdNewLine.ReplaceAll(data, nil) + + extension := filepath.Ext(filename) + + // remove build tags from the top of Go files + if extension == ".go" { + data = goBuildTag.ReplaceAll(data, nil) + } + + // remove shebang from the top of shell files + if extension == ".sh" { + data = shebang.ReplaceAll(data, nil) + } + + // if our test file is smaller than the reference it surely fails! + if len(data) < len(expectedBoilerplate) { + return false, nil + } + + data = data[:len(expectedBoilerplate)] + + // Search for "Copyright YEAR" which exists in the boilerplate, but shouldn't in the real thing + if copyright.Match(data) { + return false, nil + } + + // Replace all occurrences of the regex "Copyright \d{4}" with "Copyright YEAR" + data = copyrightReal.ReplaceAll(data, []byte(`Copyright YEAR`)) + + return bytes.Equal(data, expectedBoilerplate), nil +} + +// filesToCheck returns the list of the filers that will be checked for the boilerplate. +func filesToCheck(rootDir string, extensions map[string][]byte) ([]string, error) { + var outFiles []string + err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { + // remove current workdir from the beginig of the path in case it matches the skipped path + cwd, _ := os.Getwd() + // replace "\" with "\\" for windows style path + re := regexp.MustCompile(`\\`) + re = regexp.MustCompile(`^` + re.ReplaceAllString(cwd, `\\`)) + if !info.IsDir() && !skippedPaths.MatchString(re.ReplaceAllString(filepath.Dir(path), "")) { + if extensions[strings.ToLower(filepath.Ext(path))] != nil { + outFiles = append(outFiles, path) + } + } + return nil + }) + if err != nil { + return nil, err + } + if *verbose { + rootDir, err = filepath.Abs(rootDir) + if err != nil { + return outFiles, err + } + fmt.Printf("Found %v files to check in %v\n\n", len(outFiles), rootDir) + } + return outFiles, nil +} diff --git a/hack/boilerplate/boilerplate.py b/hack/boilerplate/boilerplate.py deleted file mode 100755 index 35fc425706..0000000000 --- a/hack/boilerplate/boilerplate.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2015 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. - -from __future__ import print_function - -import argparse -import glob -import json -import mmap -import os -import re -import sys - -parser = argparse.ArgumentParser() -parser.add_argument("filenames", help="list of files to check, all files if unspecified", nargs='*') - -rootdir = os.path.dirname(__file__) + "/../../" -rootdir = os.path.abspath(rootdir) -parser.add_argument("--rootdir", default=rootdir, help="root directory to examine") - -default_boilerplate_dir = os.path.join(rootdir, "hack/boilerplate") -parser.add_argument("--boilerplate-dir", default=default_boilerplate_dir) -args = parser.parse_args() - - -def get_refs(): - refs = {} - - for path in glob.glob(os.path.join(args.boilerplate_dir, "boilerplate.*.txt")): - extension = os.path.basename(path).split(".")[1] - - ref_file = open(path, 'r') - ref = ref_file.read().splitlines() - ref_file.close() - refs[extension] = ref - - return refs - -def file_passes(filename, refs, regexs): - try: - f = open(filename, 'r') - except: - return False - - data = f.read() - f.close() - - basename = os.path.basename(filename) - extension = file_extension(filename) - if extension != "": - ref = refs[extension] - else: - ref = refs[basename] - - # remove build tags from the top of Go files - if extension == "go": - p = regexs["go_build_constraints"] - (data, found) = p.subn("", data, 1) - - # remove shebang from the top of shell files - if extension == "sh": - p = regexs["shebang"] - (data, found) = p.subn("", data, 1) - - data = data.splitlines() - - # if our test file is smaller than the reference it surely fails! - if len(ref) > len(data): - return False - - # trim our file to the same number of lines as the reference file - data = data[:len(ref)] - - p = regexs["year"] - for d in data: - if p.search(d): - return False - - # Replace all occurrences of the regex "2018|2017|2016|2015|2014" with "YEAR" - p = regexs["date"] - for i, d in enumerate(data): - (data[i], found) = p.subn('YEAR', d) - if found != 0: - break - - # if we don't match the reference at this point, fail - if ref != data: - return False - - return True - -def file_extension(filename): - return os.path.splitext(filename)[1].split(".")[-1].lower() - -skipped_dirs = ['Godeps', 'third_party', '_gopath', '_output', '.git', 'cluster/env.sh', "vendor", "test/e2e/generated/bindata.go"] - -def normalize_files(files): - newfiles = [] - for pathname in files: - if any(x in pathname for x in skipped_dirs): - continue - newfiles.append(pathname) - for i, pathname in enumerate(newfiles): - if not os.path.isabs(pathname): - newfiles[i] = os.path.join(rootdir, pathname) - return newfiles - -def get_files(extensions): - files = [] - if len(args.filenames) > 0: - files = args.filenames - else: - for root, dirs, walkfiles in os.walk(args.rootdir): - # don't visit certain dirs. This is just a performance improvement - # as we would prune these later in normalize_files(). But doing it - # cuts down the amount of filesystem walking we do and cuts down - # the size of the file list - for d in skipped_dirs: - if d in dirs: - dirs.remove(d) - - for name in walkfiles: - pathname = os.path.join(root, name) - files.append(pathname) - - files = normalize_files(files) - outfiles = [] - for pathname in files: - basename = os.path.basename(pathname) - extension = file_extension(pathname) - if extension in extensions or basename in extensions: - outfiles.append(pathname) - return outfiles - -def get_regexs(): - regexs = {} - # Search for "YEAR" which exists in the boilerplate, but shouldn't in the real thing - regexs["year"] = re.compile( 'YEAR' ) - # dates can be 2010 to 2039 - regexs["date"] = re.compile( '(20[123]\d)' ) - # strip // +build \n\n build constraints - regexs["go_build_constraints"] = re.compile(r"^(// \+build.*\n)+\n", re.MULTILINE) - # strip #!.* from shell scripts - regexs["shebang"] = re.compile(r"^(#!.*\n)\n*", re.MULTILINE) - return regexs - -def main(): - regexs = get_regexs() - refs = get_refs() - filenames = get_files(refs.keys()) - - for filename in filenames: - if not file_passes(filename, refs, regexs): - print(filename, file=sys.stdout) - -if __name__ == "__main__": - sys.exit(main()) diff --git a/hack/boilerplate/fix.sh b/hack/boilerplate/fix.sh index 4d01cb312c..b2b4c98b84 100755 --- a/hack/boilerplate/fix.sh +++ b/hack/boilerplate/fix.sh @@ -21,7 +21,9 @@ function prepend() { local pattern=$1 local ref=$2 local headers=$3 - local files=$(hack/boilerplate/boilerplate.py --rootdir ${ROOT_DIR} | grep -v "$ignore" | grep "$pattern") + pushd hack/boilerplate > /dev/null + local files=$(go run boilerplate.go -rootdir ${ROOT_DIR} -boilerplate-dir ${ROOT_DIR}/hack/boilerplate | grep -v "$ignore" | grep "$pattern") + popd > /dev/null for f in ${files}; do echo ${f}; local copyright="$(cat hack/boilerplate/boilerplate.${ref}.txt | sed s/YEAR/$(date +%Y)/g)" diff --git a/hack/jenkins/common.sh b/hack/jenkins/common.sh index 25c12d8b40..61d9dfe90c 100755 --- a/hack/jenkins/common.sh +++ b/hack/jenkins/common.sh @@ -23,8 +23,6 @@ # EXTRA_START_ARGS: additional flags to pass into minikube start # EXTRA_ARGS: additional flags to pass into minikube # JOB_NAME: the name of the logfile and check name to update on github -# PARALLEL_COUNT: number of tests to run in parallel - readonly TEST_ROOT="${HOME}/minikube-integration" readonly TEST_HOME="${TEST_ROOT}/${OS_ARCH}-${VM_DRIVER}-${MINIKUBE_LOCATION}-$$-${COMMIT}" @@ -37,6 +35,7 @@ echo "job: ${JOB_NAME}" echo "test home: ${TEST_HOME}" echo "sudo: ${SUDO_PREFIX}" echo "kernel: $(uname -v)" +echo "uptime: $(uptime)" # Setting KUBECONFIG prevents the version ceck from erroring out due to permission issues echo "kubectl: $(env KUBECONFIG=${TEST_HOME} kubectl version --client --short=true)" echo "docker: $(docker version --format '{{ .Client.Version }}')" @@ -82,6 +81,7 @@ gsutil -qm cp "gs://minikube-builds/${MINIKUBE_LOCATION}/gvisor-addon" testdata/ export MINIKUBE_BIN="out/minikube-${OS_ARCH}" export E2E_BIN="out/e2e-${OS_ARCH}" chmod +x "${MINIKUBE_BIN}" "${E2E_BIN}" out/docker-machine-driver-* +"${MINIKUBE_BIN}" version procs=$(pgrep "minikube-${OS_ARCH}|e2e-${OS_ARCH}" || true) if [[ "${procs}" != "" ]]; then @@ -91,28 +91,36 @@ if [[ "${procs}" != "" ]]; then kill -9 ${procs} || true fi +# Quickly notice misconfigured test roots +mkdir -p "${TEST_ROOT}" + # Cleanup stale test outputs. echo "" echo ">> Cleaning up after previous test runs ..." +for entry in $(ls ${TEST_ROOT}); do + test_path="${TEST_ROOT}/${entry}" + ls -lad "${test_path}" || continue -for stale_dir in ${TEST_ROOT}/*; do - echo "* Cleaning stale test root: ${stale_dir}" - - for tunnel in $(find ${stale_dir} -name tunnels.json -type f); do + echo "* Cleaning stale test path: ${test_path}" + for tunnel in $(find ${test_path} -name tunnels.json -type f); do env MINIKUBE_HOME="$(dirname ${tunnel})" ${MINIKUBE_BIN} tunnel --cleanup || true done - for home in $(find ${stale_dir} -name .minikube -type d); do - env MINIKUBE_HOME="$(dirname ${home})" ${MINIKUBE_BIN} delete || true - sudo rm -Rf "${home}" + for home in $(find ${test_path} -name .minikube -type d); do + env MINIKUBE_HOME="$(dirname ${home})" ${MINIKUBE_BIN} delete --all || true + sudo rm -Rf "${home}" done - for kconfig in $(find ${stale_dir} -name kubeconfig -type f); do + for kconfig in $(find ${test_path} -name kubeconfig -type f); do sudo rm -f "${kconfig}" done - rm -f "${stale_dir}/*" || true - rmdir "${stale_dir}" || ls "${stale_dir}" + # Be very specific to avoid accidentally deleting other items, like wildcards or devices + if [[ -d "${test_path}" ]]; then + rm -Rf "${test_path}" || true + elif [[ -f "${test_path}" ]]; then + rm -f "${test_path}" || true + fi done # sometimes tests left over zombie procs that won't exit @@ -134,14 +142,25 @@ if type -P virsh; then fi if type -P vboxmanage; then - vboxmanage list vms || true - vboxmanage list vms \ - | egrep -o '{.*?}' \ - | xargs -I {} sh -c "vboxmanage startvm {} --type emergencystop; vboxmanage unregistervm {} --delete" \ - || true + killall VBoxHeadless || true + sleep 1 + killall -9 VBoxHeadless || true + + for guid in $(vboxmanage list vms | grep -Eo '\{[a-zA-Z0-9-]+\}'); do + echo "- Removing stale VirtualBox VM: $guid" + vboxmanage startvm "${guid}" --type emergencystop || true + vboxmanage unregistervm "${guid}" || true + done + + ifaces=$(vboxmanage list hostonlyifs | grep -E "^Name:" | awk '{ print $2 }') + for if in $ifaces; do + vboxmanage hostonlyif remove "${if}" || true + done echo ">> VirtualBox VM list after clean up (should be empty):" vboxmanage list vms || true + echo ">> VirtualBox interface list after clean up (should be empty):" + vboxmanage list hostonlyifs || true fi @@ -213,22 +232,42 @@ mkdir -p "${TEST_HOME}" export MINIKUBE_HOME="${TEST_HOME}/.minikube" export KUBECONFIG="${TEST_HOME}/kubeconfig" -# Build the gvisor image. This will be copied into minikube and loaded by ctr. -# Used by TestContainerd for Gvisor Test. -# TODO: move this to integration test setup. + +# Build the gvisor image so that we can integration test changes to pkg/gvisor chmod +x ./testdata/gvisor-addon # skipping gvisor mac because ofg https://github.com/kubernetes/minikube/issues/5137 if [ "$(uname)" != "Darwin" ]; then - docker build -t gcr.io/k8s-minikube/gvisor-addon:latest -f testdata/gvisor-addon-Dockerfile ./testdata + # Should match GVISOR_IMAGE_VERSION in Makefile + docker build -t gcr.io/k8s-minikube/gvisor-addon:2 -f testdata/gvisor-addon-Dockerfile ./testdata +fi + +readonly LOAD=$(uptime | egrep -o "load average.*: [0-9]+" | cut -d" " -f3) +if [[ "${LOAD}" -gt 2 ]]; then + echo "" + echo "********************** LOAD WARNING ********************************" + echo "Load average is very high (${LOAD}), which may cause failures. Top:" + if [[ "$(uname)" == "Darwin" ]]; then + # Two samples, macOS does not calculate CPU usage on the first one + top -l 2 -o cpu -n 5 | tail -n 15 + else + top -b -n1 | head -n 15 + fi + echo "********************** LOAD WARNING ********************************" + echo "Sleeping 30s to see if load goes down ...." + sleep 30 + uptime fi echo "" echo ">> Starting ${E2E_BIN} at $(date)" +set -x ${SUDO_PREFIX}${E2E_BIN} \ -minikube-start-args="--vm-driver=${VM_DRIVER} ${EXTRA_START_ARGS}" \ - -test.timeout=60m \ - -test.parallel=${PARALLEL_COUNT} \ + -expected-default-driver="${EXPECTED_DEFAULT_DRIVER}" \ + -test.timeout=70m \ + ${EXTRA_TEST_ARGS} \ -binary="${MINIKUBE_BIN}" && result=$? || result=$? +set +x echo ">> ${E2E_BIN} exited with ${result} at $(date)" echo "" diff --git a/hack/jenkins/osx_cleanup_and_reboot.sh b/hack/jenkins/cron/cleanup_and_reboot_Darwin.sh similarity index 53% rename from hack/jenkins/osx_cleanup_and_reboot.sh rename to hack/jenkins/cron/cleanup_and_reboot_Darwin.sh index 503796c212..993d6c82f6 100755 --- a/hack/jenkins/osx_cleanup_and_reboot.sh +++ b/hack/jenkins/cron/cleanup_and_reboot_Darwin.sh @@ -19,21 +19,29 @@ set -uf -o pipefail PATH=/usr/local/bin:/sbin:/usr/local/sbin:$PATH -exit_if_jenkins() { - jenkins=$(pgrep java) - if [[ "$jenkins" -- "" ]]; then - echo "no java, no jenkins" - return 0 +# cleanup shared between Linux and macOS +function check_jenkins() { + jenkins_pid="$(pidof java)" + if [[ "${jenkins_pid}" = "" ]]; then + return fi - pstree $jenkins | grep -v java && echo "jenkins is running..." && exit 1 + pstree "${jenkins_pid}" \ + | egrep -i 'bash|integration|e2e|minikube' \ + && echo "tests are is running on pid ${jenkins_pid} ..." \ + && exit 1 } -exit_if_jenkins -echo "waiting to see if any jobs are coming in..." -sleep 15 -exit_if_jenkins -echo "doing it" +check_jenkins +logger "cleanup_and_reboot running - may shutdown in 60 seconds" +echo "cleanup_and_reboot running - may shutdown in 60 seconds" | wall +sleep 10 +check_jenkins +logger "cleanup_and_reboot is happening!" + +# kill jenkins to avoid an incoming request killall java -sudo rm -Rf ~jenkins/.minikube || echo "could not delete minikube" -sudo rm -Rf ~/jenkins/minikube-integration/* || true + +# macOS specific cleanup +sudo rm /var/db/dhcpd_leases || echo "could not clear dhcpd leases" +sudo softwareupdate -i -a -R sudo reboot diff --git a/hack/jenkins/cron/cleanup_and_reboot_Linux.sh b/hack/jenkins/cron/cleanup_and_reboot_Linux.sh new file mode 100755 index 0000000000..b2e067c58e --- /dev/null +++ b/hack/jenkins/cron/cleanup_and_reboot_Linux.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright 2019 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. + +# cleanup shared between Linux and macOS +function check_jenkins() { + jenkins_pid="$(pidof java)" + if [[ "${jenkins_pid}" = "" ]]; then + return + fi + pstree "${jenkins_pid}" \ + | egrep -i 'bash|integration|e2e|minikube' \ + && echo "tests are is running on pid ${jenkins_pid} ..." \ + && exit 1 +} + +check_jenkins +logger "cleanup_and_reboot running - may shutdown in 60 seconds" +echo "cleanup_and_reboot running - may shutdown in 60 seconds" | wall +sleep 10 +check_jenkins +logger "cleanup_and_reboot is happening!" + +# kill jenkins to avoid an incoming request +killall java + +# Linux-specific cleanup + +# disable localkube, kubelet +systemctl list-unit-files --state=enabled \ + | grep kube \ + | awk '{ print $1 }' \ + | xargs systemctl disable + +# update and reboot +apt update -y && apt upgrade -y && reboot diff --git a/hack/jenkins/installers/check_install_golang.sh b/hack/jenkins/installers/check_install_golang.sh index 72abf97250..a16392daaf 100755 --- a/hack/jenkins/installers/check_install_golang.sh +++ b/hack/jenkins/installers/check_install_golang.sh @@ -28,7 +28,7 @@ INSTALL_PATH=${2} # installs or updates golang if right version doesn't exists function check_and_install_golang() { if ! go version &>/dev/null; then - echo "WARNING: No golang installation found in your enviroment." + echo "WARNING: No golang installation found in your environment." install_golang "$VERSION_TO_INSTALL" "$INSTALL_PATH" return fi diff --git a/hack/jenkins/linux_integration_tests_kvm.sh b/hack/jenkins/linux_integration_tests_kvm.sh index 5af0e48b30..8beef2f7b8 100755 --- a/hack/jenkins/linux_integration_tests_kvm.sh +++ b/hack/jenkins/linux_integration_tests_kvm.sh @@ -28,7 +28,13 @@ set -e OS_ARCH="linux-amd64" VM_DRIVER="kvm2" JOB_NAME="KVM_Linux" -PARALLEL_COUNT=4 +EXPECTED_DEFAULT_DRIVER="kvm2" + +# We pick kvm as our gvisor testbed because it is fast & reliable +EXTRA_TEST_ARGS="-gvisor" + +mkdir -p cron && gsutil -qm rsync "gs://minikube-builds/${MINIKUBE_LOCATION}/cron" cron || echo "FAILED TO GET CRON FILES" +sudo install cron/cleanup_and_reboot_Linux.sh /etc/cron.hourly/cleanup_and_reboot || echo "FAILED TO INSTALL CLEANUP" # Download files and set permissions source ./common.sh diff --git a/hack/jenkins/linux_integration_tests_none.sh b/hack/jenkins/linux_integration_tests_none.sh index 6721d15e24..5cb2c778f8 100755 --- a/hack/jenkins/linux_integration_tests_none.sh +++ b/hack/jenkins/linux_integration_tests_none.sh @@ -30,15 +30,16 @@ OS_ARCH="linux-amd64" VM_DRIVER="none" JOB_NAME="none_Linux" EXTRA_ARGS="--bootstrapper=kubeadm" -PARALLEL_COUNT=1 +EXPECTED_DEFAULT_DRIVER="kvm2" SUDO_PREFIX="sudo -E " export KUBECONFIG="/root/.kube/config" # "none" driver specific cleanup from previous runs. +sudo kubeadm reset -f || true +# kubeadm reset may not stop pods immediately +docker rm -f $(docker ps -aq) >/dev/null 2>&1 || true -# Try without -f first, primarily because older kubeadm versions (v1.10) don't support it anyways. -sudo kubeadm reset || sudo kubeadm reset -f || true # Cleanup data directory sudo rm -rf /data/* # Cleanup old Kubernetes configs @@ -50,5 +51,8 @@ systemctl is-active --quiet kubelet \ && echo "stopping kubelet" \ && sudo systemctl stop kubelet +mkdir -p cron && gsutil -m rsync "gs://minikube-builds/${MINIKUBE_LOCATION}/cron" cron || echo "FAILED TO GET CRON FILES" +sudo install cron/cleanup_and_reboot_Linux.sh /etc/cron.hourly/cleanup_and_reboot || echo "FAILED TO INSTALL CLEANUP" + # Download files and set permissions source ./common.sh diff --git a/hack/jenkins/linux_integration_tests_virtualbox.sh b/hack/jenkins/linux_integration_tests_virtualbox.sh index bd413c1586..0917cf4cea 100755 --- a/hack/jenkins/linux_integration_tests_virtualbox.sh +++ b/hack/jenkins/linux_integration_tests_virtualbox.sh @@ -28,7 +28,11 @@ set -e OS_ARCH="linux-amd64" VM_DRIVER="virtualbox" JOB_NAME="VirtualBox_Linux" -PARALLEL_COUNT=4 +EXPECTED_DEFAULT_DRIVER="kvm2" + +mkdir -p cron && gsutil -qm rsync "gs://minikube-builds/${MINIKUBE_LOCATION}/cron" cron || echo "FAILED TO GET CRON FILES" +sudo install cron/cleanup_and_reboot_Linux.sh /etc/cron.hourly/cleanup_and_reboot || echo "FAILED TO INSTALL CLEANUP" + # Download files and set permissions source ./common.sh diff --git a/hack/jenkins/minikube_cross_build_and_upload.sh b/hack/jenkins/minikube_cross_build_and_upload.sh index 2e95fea227..382adacea7 100755 --- a/hack/jenkins/minikube_cross_build_and_upload.sh +++ b/hack/jenkins/minikube_cross_build_and_upload.sh @@ -41,6 +41,8 @@ docker kill $(docker ps -q) || true docker rm $(docker ps -aq) || true make -j 16 all && failed=$? || failed=$? +"out/minikube-$(go env GOOS)-$(go env GOARCH)" version + gsutil cp "gs://${bucket}/logs/index.html" \ "gs://${bucket}/logs/${ghprbPullId}/index.html" @@ -58,9 +60,16 @@ if [[ "${rebuild}" -eq 1 ]]; then make release-iso fi + cp -r test/integration/testdata out/ # Don't upload the buildroot artifacts if they exist rm -r out/buildroot || true -gsutil -m cp -r out/* "gs://${bucket}/${ghprbPullId}/" +# At this point, the out directory contains the jenkins scripts (populated by jenkins), +# testdata, and our build output. Push the changes to GCS so that worker nodes can re-use them. + +# -d: delete remote files that don't exist (removed test files, for instance) +# -J: gzip compression +# -R: recursive. strangely, this is not the default for sync. +gsutil -m rsync -dJR out "gs://${bucket}/${ghprbPullId}" diff --git a/hack/jenkins/osx_integration_tests_hyperkit.sh b/hack/jenkins/osx_integration_tests_hyperkit.sh index 7091d6512b..8805e8a2eb 100755 --- a/hack/jenkins/osx_integration_tests_hyperkit.sh +++ b/hack/jenkins/osx_integration_tests_hyperkit.sh @@ -31,7 +31,13 @@ VM_DRIVER="hyperkit" JOB_NAME="HyperKit_macOS" EXTRA_ARGS="--bootstrapper=kubeadm" EXTRA_START_ARGS="" -PARALLEL_COUNT=3 +EXPECTED_DEFAULT_DRIVER="hyperkit" + + +mkdir -p cron && gsutil -qm rsync "gs://minikube-builds/${MINIKUBE_LOCATION}/cron" cron || echo "FAILED TO GET CRON FILES" +install cron/cleanup_and_reboot_Darwin.sh $HOME/cleanup_and_reboot.sh || echo "FAILED TO INSTALL CLEANUP" +echo "*/30 * * * * $HOME/cleanup_and_reboot.sh" | crontab +crontab -l # Download files and set permissions source common.sh diff --git a/hack/jenkins/osx_integration_tests_virtualbox.sh b/hack/jenkins/osx_integration_tests_virtualbox.sh index cb48a389ed..49dcba37e7 100755 --- a/hack/jenkins/osx_integration_tests_virtualbox.sh +++ b/hack/jenkins/osx_integration_tests_virtualbox.sh @@ -29,7 +29,15 @@ OS_ARCH="darwin-amd64" VM_DRIVER="virtualbox" JOB_NAME="VirtualBox_macOS" EXTRA_ARGS="--bootstrapper=kubeadm" -PARALLEL_COUNT=3 +# hyperkit behaves better, so it has higher precedence. +# Assumes that hyperkit is also installed on the VirtualBox CI host. +EXPECTED_DEFAULT_DRIVER="hyperkit" + + +mkdir -p cron && gsutil -qm rsync "gs://minikube-builds/${MINIKUBE_LOCATION}/cron" cron || echo "FAILED TO GET CRON FILES" +install cron/cleanup_and_reboot_Darwin.sh $HOME/cleanup_and_reboot.sh || echo "FAILED TO GET INSTALL CLEANUP" +echo "*/30 * * * * $HOME/cleanup_and_reboot.sh" | crontab +crontab -l # Download files and set permissions source common.sh diff --git a/hack/jenkins/release_update_brew.sh b/hack/jenkins/release_update_brew.sh index 4ae92239b5..bfa1508a51 100755 --- a/hack/jenkins/release_update_brew.sh +++ b/hack/jenkins/release_update_brew.sh @@ -39,6 +39,12 @@ if [ -z "${NEW_SHA256}" ]; then exit 1 fi +echo "***********************************************************************" +echo "Sorry, this script has not yet been updated to support non-cask updates" +echo "See https://github.com/kubernetes/minikube/issues/5779" +echo "***********************************************************************" +exit 99 + git config --global user.name "${GITHUB_USER}" git config --global user.email "${GITHUB_USER}@google.com" diff --git a/hack/jenkins/windows_integration_test_hyperv.ps1 b/hack/jenkins/windows_integration_test_hyperv.ps1 index fb42370c49..995b17506c 100644 --- a/hack/jenkins/windows_integration_test_hyperv.ps1 +++ b/hack/jenkins/windows_integration_test_hyperv.ps1 @@ -19,7 +19,7 @@ gsutil.cmd -m cp -r gs://minikube-builds/$env:MINIKUBE_LOCATION/testdata . ./out/minikube-windows-amd64.exe delete -out/e2e-windows-amd64.exe -minikube-start-args="--vm-driver=hyperv --hyperv-virtual-switch=primary-virtual-switch" -binary=out/minikube-windows-amd64.exe -test.v -test.timeout=65m +out/e2e-windows-amd64.exe --expected-default-driver=hyperv -minikube-start-args="--vm-driver=hyperv --hyperv-virtual-switch=primary-virtual-switch" -binary=out/minikube-windows-amd64.exe -test.v -test.timeout=65m $env:result=$lastexitcode # If the last exit code was 0->success, x>0->error If($env:result -eq 0){$env:status="success"} diff --git a/hack/jenkins/windows_integration_test_virtualbox.ps1 b/hack/jenkins/windows_integration_test_virtualbox.ps1 index 559a6e220e..86dfde120a 100644 --- a/hack/jenkins/windows_integration_test_virtualbox.ps1 +++ b/hack/jenkins/windows_integration_test_virtualbox.ps1 @@ -19,7 +19,7 @@ gsutil.cmd -m cp -r gs://minikube-builds/$env:MINIKUBE_LOCATION/testdata . ./out/minikube-windows-amd64.exe delete -out/e2e-windows-amd64.exe -minikube-start-args="--vm-driver=virtualbox" -binary=out/minikube-windows-amd64.exe -test.v -test.timeout=30m +out/e2e-windows-amd64.exe -minikube-start-args="--vm-driver=virtualbox" -expected-default-driver=hyperv -binary=out/minikube-windows-amd64.exe -test.v -test.timeout=30m $env:result=$lastexitcode # If the last exit code was 0->success, x>0->error If($env:result -eq 0){$env:status="success"} diff --git a/hack/release_notes.sh b/hack/release_notes.sh index 0cc0733243..ca0acc9fc4 100755 --- a/hack/release_notes.sh +++ b/hack/release_notes.sh @@ -18,7 +18,23 @@ set -eux -o pipefail DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -go run "${DIR}/release_notes/listpullreqs.go" +install_release_notes_helper() { + release_notes_workdir="$(mktemp -d)" + trap 'rm -rf -- ${release_notes_workdir}' RETURN + + # See https://stackoverflow.com/questions/56842385/using-go-get-to-download-binaries-without-adding-them-to-go-mod for this workaround + cd "${release_notes_workdir}" + go mod init release-notes + GOBIN="$DIR" go get github.com/corneliusweig/release-notes + cd - +} + +if ! [[ -x "${DIR}/release-notes" ]]; then + echo >&2 'Installing release-notes' + install_release_notes_helper +fi + +"${DIR}/release-notes" kubernetes minikube echo "Huge thank you for this release towards our contributors: " git log "$(git describe --abbrev=0)".. --format="%aN" --reverse | sort | uniq | awk '{printf "- %s\n", $0 }' diff --git a/hack/release_notes/listpullreqs.go b/hack/release_notes/listpullreqs.go deleted file mode 100644 index 64ea8fc3d9..0000000000 --- a/hack/release_notes/listpullreqs.go +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright 2018 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. -*/ - -// listpullreqs.go lists pull requests since the last release. -package main - -import ( - "context" - "fmt" - - "github.com/golang/glog" - "github.com/google/go-github/v25/github" - "github.com/spf13/cobra" - "golang.org/x/oauth2" -) - -var ( - token string - fromTag string - toTag string -) - -var rootCmd = &cobra.Command{ - Use: "listpullreqs fromTag toTag", - Short: "Lists pull requests between two versions in our changelog markdown format", - ArgAliases: []string{"fromTag", "toTag"}, - Run: func(cmd *cobra.Command, args []string) { - printPullRequests() - }, -} - -const org = "kubernetes" -const repo = "minikube" - -func main() { - rootCmd.Flags().StringVar(&token, "token", "", "Specify personal Github Token if you are hitting a rate limit anonymously. https://github.com/settings/tokens") - rootCmd.Flags().StringVar(&fromTag, "fromTag", "", "comparison of commits is based on this tag (defaults to the latest tag in the repo)") - rootCmd.Flags().StringVar(&toTag, "toTag", "master", "this is the commit that is compared with fromTag") - if err := rootCmd.Execute(); err != nil { - glog.Fatalf("Failed to execute: %v", err) - } -} - -func printPullRequests() { - client := getClient() - - releases, _, err := client.Repositories.ListReleases(context.Background(), org, repo, &github.ListOptions{}) - if err != nil { - glog.Fatalf("Failed to list releases: %v", err) - } - lastReleaseTime := *releases[0].PublishedAt - fmt.Println(fmt.Sprintf("Collecting pull request that were merged since the last release: %s (%s)", *releases[0].TagName, lastReleaseTime)) - - listSize := 1 - for page := 1; listSize > 0; page++ { - pullRequests, _, err := client.PullRequests.List(context.Background(), org, repo, &github.PullRequestListOptions{ - State: "closed", - Sort: "updated", - Direction: "desc", - ListOptions: github.ListOptions{ - PerPage: 100, - Page: page, - }, - }) - if err != nil { - glog.Fatalf("Failed to list pull requests: %v", err) - } - - seen := 0 - for idx := range pullRequests { - pr := pullRequests[idx] - if pr.MergedAt != nil { - if pr.GetMergedAt().After(lastReleaseTime.Time) { - fmt.Printf("* %s [#%d](https://github.com/%s/%s/pull/%d)\n", pr.GetTitle(), *pr.Number, org, repo, *pr.Number) - seen++ - } - } - } - if seen == 0 { - break - } - listSize = len(pullRequests) - } -} - -func getClient() *github.Client { - if len(token) == 0 { - return github.NewClient(nil) - } - ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - tc := oauth2.NewClient(ctx, ts) - return github.NewClient(tc) -} diff --git a/installers/linux/deb/minikube_deb_template/DEBIAN/control b/installers/linux/deb/minikube_deb_template/DEBIAN/control index 98a93a25b8..ad9d368ba0 100644 --- a/installers/linux/deb/minikube_deb_template/DEBIAN/control +++ b/installers/linux/deb/minikube_deb_template/DEBIAN/control @@ -2,7 +2,7 @@ Package: minikube Version: --VERSION-- Section: base Priority: optional -Architecture: amd64 +Architecture: --ARCH-- Recommends: virtualbox Maintainer: Thomas Strömberg <t+minikube@stromberg.org> Description: Minikube diff --git a/installers/linux/rpm/minikube_rpm_template/minikube.spec b/installers/linux/rpm/minikube_rpm_template/minikube.spec index 16adf43729..01868349ed 100644 --- a/installers/linux/rpm/minikube_rpm_template/minikube.spec +++ b/installers/linux/rpm/minikube_rpm_template/minikube.spec @@ -18,12 +18,12 @@ day-to-day. %prep mkdir -p %{name}-%{version} cd %{name}-%{version} -cp --OUT--/minikube-linux-amd64 . +cp --OUT--/minikube-linux-%{_arch} minikube %install cd %{name}-%{version} mkdir -p %{buildroot}%{_bindir} -install -m 755 minikube-linux-amd64 %{buildroot}%{_bindir}/%{name} +install -m 755 minikube %{buildroot}%{_bindir}/%{name} %files %{_bindir}/%{name} diff --git a/netlify.toml b/netlify.toml index 6084cd18c0..d335d812be 100644 --- a/netlify.toml +++ b/netlify.toml @@ -4,7 +4,7 @@ publish = "site/public/" command = "pwd && cd themes/docsy && git submodule update -f --init && cd ../.. && hugo" [build.environment] -HUGO_VERSION = "0.55.6" +HUGO_VERSION = "0.59.0" [context.production.environment] HUGO_ENV = "production" diff --git a/pkg/drivers/common.go b/pkg/drivers/common.go new file mode 100644 index 0000000000..2fde2efde8 --- /dev/null +++ b/pkg/drivers/common.go @@ -0,0 +1,141 @@ +/* +Copyright 2016 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 drivers + +import ( + "io" + "io/ioutil" + "os" + "path/filepath" + "syscall" + + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/mcnflag" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/ssh" + "github.com/golang/glog" + "github.com/pkg/errors" +) + +// This file is for common code shared among internal machine drivers +// Code here should not be called from within minikube + +// GetDiskPath returns the path of the machine disk image +func GetDiskPath(d *drivers.BaseDriver) string { + return filepath.Join(d.ResolveStorePath("."), d.GetMachineName()+".rawdisk") +} + +// CommonDriver is the common driver base class +type CommonDriver struct{} + +// GetCreateFlags is not implemented yet +func (d *CommonDriver) GetCreateFlags() []mcnflag.Flag { + return nil +} + +// SetConfigFromFlags is not implemented yet +func (d *CommonDriver) SetConfigFromFlags(flags drivers.DriverOptions) error { + return nil +} + +func createRawDiskImage(sshKeyPath, diskPath string, diskSizeMb int) error { + tarBuf, err := mcnutils.MakeDiskImage(sshKeyPath) + if err != nil { + return errors.Wrap(err, "make disk image") + } + + file, err := os.OpenFile(diskPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) + if err != nil { + return errors.Wrap(err, "open") + } + defer file.Close() + if _, err := file.Seek(0, io.SeekStart); err != nil { + return errors.Wrap(err, "seek") + } + + if _, err := file.Write(tarBuf.Bytes()); err != nil { + return errors.Wrap(err, "write tar") + } + if err := file.Close(); err != nil { + return errors.Wrapf(err, "closing file %s", diskPath) + } + + if err := os.Truncate(diskPath, int64(diskSizeMb*1000000)); err != nil { + return errors.Wrap(err, "truncate") + } + return nil +} + +func publicSSHKeyPath(d *drivers.BaseDriver) string { + return d.GetSSHKeyPath() + ".pub" +} + +// Restart a host. This may just call Stop(); Start() if the provider does not +// have any special restart behaviour. +func Restart(d drivers.Driver) error { + if err := d.Stop(); err != nil { + return err + } + + return d.Start() +} + +// MakeDiskImage makes a boot2docker VM disk image. +func MakeDiskImage(d *drivers.BaseDriver, boot2dockerURL string, diskSize int) error { + glog.Infof("Making disk image using store path: %s", d.StorePath) + b2 := mcnutils.NewB2dUtils(d.StorePath) + if err := b2.CopyIsoToMachineDir(boot2dockerURL, d.MachineName); err != nil { + return errors.Wrap(err, "copy iso to machine dir") + } + + keyPath := d.GetSSHKeyPath() + glog.Infof("Creating ssh key: %s...", keyPath) + if err := ssh.GenerateSSHKey(keyPath); err != nil { + return errors.Wrap(err, "generate ssh key") + } + + diskPath := GetDiskPath(d) + glog.Infof("Creating raw disk image: %s...", diskPath) + if _, err := os.Stat(diskPath); os.IsNotExist(err) { + if err := createRawDiskImage(publicSSHKeyPath(d), diskPath, diskSize); err != nil { + return errors.Wrapf(err, "createRawDiskImage(%s)", diskPath) + } + machPath := d.ResolveStorePath(".") + if err := fixMachinePermissions(machPath); err != nil { + return errors.Wrapf(err, "fixing permissions on %s", machPath) + } + } + return nil +} + +func fixMachinePermissions(path string) error { + glog.Infof("Fixing permissions on %s ...", path) + if err := os.Chown(path, syscall.Getuid(), syscall.Getegid()); err != nil { + return errors.Wrap(err, "chown dir") + } + files, err := ioutil.ReadDir(path) + if err != nil { + return errors.Wrap(err, "read dir") + } + for _, f := range files { + fp := filepath.Join(path, f.Name()) + if err := os.Chown(fp, syscall.Getuid(), syscall.Getegid()); err != nil { + return errors.Wrap(err, "chown file") + } + } + return nil +} diff --git a/pkg/drivers/drivers_test.go b/pkg/drivers/common_test.go similarity index 71% rename from pkg/drivers/drivers_test.go rename to pkg/drivers/common_test.go index 10e42e3e44..98982b9b36 100644 --- a/pkg/drivers/drivers_test.go +++ b/pkg/drivers/common_test.go @@ -48,27 +48,3 @@ func Test_createDiskImage(t *testing.T) { t.Errorf("Disk size is %v, want %v", fi.Size(), sizeInBytes) } } - -func TestExtractVMDriverVersion(t *testing.T) { - v := extractVMDriverVersion("") - if len(v) != 0 { - t.Error("Expected empty string") - } - - v = extractVMDriverVersion("random text") - if len(v) != 0 { - t.Error("Expected empty string") - } - - expectedVersion := "1.2.3" - - v = extractVMDriverVersion("version: v1.2.3") - if expectedVersion != v { - t.Errorf("Expected version: %s, got: %s", expectedVersion, v) - } - - v = extractVMDriverVersion("version: 1.2.3") - if expectedVersion != v { - t.Errorf("Expected version: %s, got: %s", expectedVersion, v) - } -} diff --git a/pkg/drivers/drivers.go b/pkg/drivers/drivers.go deleted file mode 100644 index da7edaf2d8..0000000000 --- a/pkg/drivers/drivers.go +++ /dev/null @@ -1,283 +0,0 @@ -/* -Copyright 2016 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 drivers - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "regexp" - "strings" - "syscall" - - "github.com/blang/semver" - "github.com/docker/machine/libmachine/drivers" - "github.com/docker/machine/libmachine/mcnflag" - "github.com/docker/machine/libmachine/mcnutils" - "github.com/docker/machine/libmachine/ssh" - "github.com/golang/glog" - "github.com/hashicorp/go-getter" - "github.com/pkg/errors" - "k8s.io/minikube/pkg/version" - - "k8s.io/minikube/pkg/minikube/constants" - "k8s.io/minikube/pkg/minikube/out" - "k8s.io/minikube/pkg/util" -) - -// GetDiskPath returns the path of the machine disk image -func GetDiskPath(d *drivers.BaseDriver) string { - return filepath.Join(d.ResolveStorePath("."), d.GetMachineName()+".rawdisk") -} - -// CommonDriver is the common driver base class -type CommonDriver struct{} - -// GetCreateFlags is not implemented yet -func (d *CommonDriver) GetCreateFlags() []mcnflag.Flag { - return nil -} - -// SetConfigFromFlags is not implemented yet -func (d *CommonDriver) SetConfigFromFlags(flags drivers.DriverOptions) error { - return nil -} - -func createRawDiskImage(sshKeyPath, diskPath string, diskSizeMb int) error { - tarBuf, err := mcnutils.MakeDiskImage(sshKeyPath) - if err != nil { - return errors.Wrap(err, "make disk image") - } - - file, err := os.OpenFile(diskPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) - if err != nil { - return errors.Wrap(err, "open") - } - defer file.Close() - if _, err := file.Seek(0, io.SeekStart); err != nil { - return errors.Wrap(err, "seek") - } - - if _, err := file.Write(tarBuf.Bytes()); err != nil { - return errors.Wrap(err, "write tar") - } - if err := file.Close(); err != nil { - return errors.Wrapf(err, "closing file %s", diskPath) - } - - if err := os.Truncate(diskPath, int64(diskSizeMb*1000000)); err != nil { - return errors.Wrap(err, "truncate") - } - return nil -} - -func publicSSHKeyPath(d *drivers.BaseDriver) string { - return d.GetSSHKeyPath() + ".pub" -} - -// Restart a host. This may just call Stop(); Start() if the provider does not -// have any special restart behaviour. -func Restart(d drivers.Driver) error { - if err := d.Stop(); err != nil { - return err - } - - return d.Start() -} - -// MakeDiskImage makes a boot2docker VM disk image. -func MakeDiskImage(d *drivers.BaseDriver, boot2dockerURL string, diskSize int) error { - glog.Infof("Making disk image using store path: %s", d.StorePath) - b2 := mcnutils.NewB2dUtils(d.StorePath) - if err := b2.CopyIsoToMachineDir(boot2dockerURL, d.MachineName); err != nil { - return errors.Wrap(err, "copy iso to machine dir") - } - - keyPath := d.GetSSHKeyPath() - glog.Infof("Creating ssh key: %s...", keyPath) - if err := ssh.GenerateSSHKey(keyPath); err != nil { - return errors.Wrap(err, "generate ssh key") - } - - diskPath := GetDiskPath(d) - glog.Infof("Creating raw disk image: %s...", diskPath) - if _, err := os.Stat(diskPath); os.IsNotExist(err) { - if err := createRawDiskImage(publicSSHKeyPath(d), diskPath, diskSize); err != nil { - return errors.Wrapf(err, "createRawDiskImage(%s)", diskPath) - } - machPath := d.ResolveStorePath(".") - if err := fixMachinePermissions(machPath); err != nil { - return errors.Wrapf(err, "fixing permissions on %s", machPath) - } - } - return nil -} - -func fixMachinePermissions(path string) error { - glog.Infof("Fixing permissions on %s ...", path) - if err := os.Chown(path, syscall.Getuid(), syscall.Getegid()); err != nil { - return errors.Wrap(err, "chown dir") - } - files, err := ioutil.ReadDir(path) - if err != nil { - return errors.Wrap(err, "read dir") - } - for _, f := range files { - fp := filepath.Join(path, f.Name()) - if err := os.Chown(fp, syscall.Getuid(), syscall.Getegid()); err != nil { - return errors.Wrap(err, "chown file") - } - } - return nil -} - -// InstallOrUpdate downloads driver if it is not present, or updates it if there's a newer version -func InstallOrUpdate(driver string, directory string, v semver.Version, interactive bool) error { - if driver != constants.DriverKvm2 && driver != constants.DriverHyperkit { - return nil - } - - executable := fmt.Sprintf("docker-machine-driver-%s", driver) - path, err := validateDriver(executable, v) - if err != nil { - glog.Warningf("%s: %v", executable, err) - path = filepath.Join(directory, executable) - derr := download(executable, path, v) - if derr != nil { - return derr - } - } - return fixDriverPermissions(driver, path, interactive) -} - -// fixDriverPermissions fixes the permissions on a driver -func fixDriverPermissions(driver string, path string, interactive bool) error { - // This method only supports hyperkit so far (because it's complicated) - if driver != constants.DriverHyperkit { - return nil - } - - // Using the find command for hyperkit is far easier than cross-platform uid checks in Go. - stdout, err := exec.Command("find", path, "-uid", "0", "-perm", "4755").Output() - glog.Infof("stdout: %s", stdout) - if err == nil && strings.TrimSpace(string(stdout)) == path { - glog.Infof("%s looks good", path) - return nil - } - - cmds := []*exec.Cmd{ - exec.Command("sudo", "chown", "root:wheel", path), - exec.Command("sudo", "chmod", "u+s", path), - } - - var example strings.Builder - for _, c := range cmds { - example.WriteString(fmt.Sprintf(" $ %s \n", strings.Join(c.Args, " "))) - } - - out.T(out.Permissions, "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\n\n{{ .example }}\n", out.V{"driver": driver, "example": example.String()}) - for _, c := range cmds { - testArgs := append([]string{"-n"}, c.Args[1:]...) - test := exec.Command("sudo", testArgs...) - glog.Infof("testing: %v", test.Args) - if err := test.Run(); err != nil { - glog.Infof("%v may require a password: %v", c.Args, err) - if !interactive { - return fmt.Errorf("%v requires a password, and --interactive=false", c.Args) - } - } - glog.Infof("running: %v", c.Args) - err := c.Run() - if err != nil { - return errors.Wrapf(err, "%v", c.Args) - } - } - return nil -} - -// validateDriver validates if a driver appears to be up-to-date and installed properly -func validateDriver(driver string, v semver.Version) (string, error) { - glog.Infof("Validating %s, PATH=%s", driver, os.Getenv("PATH")) - path, err := exec.LookPath(driver) - if err != nil { - return path, err - } - - output, err := exec.Command(path, "version").Output() - if err != nil { - return path, err - } - - ev := extractVMDriverVersion(string(output)) - if len(ev) == 0 { - return path, fmt.Errorf("%s: unable to extract version from %q", driver, output) - } - - vmDriverVersion, err := semver.Make(ev) - if err != nil { - return path, errors.Wrap(err, "can't parse driver version") - } - if vmDriverVersion.LT(v) { - return path, fmt.Errorf("%s is version %s, want %s", driver, vmDriverVersion, v) - } - return path, nil -} - -func driverWithChecksumURL(driver string, v semver.Version) string { - base := fmt.Sprintf("https://github.com/kubernetes/minikube/releases/download/v%s/%s", v, driver) - return fmt.Sprintf("%s?checksum=file:%s.sha256", base, base) -} - -// download an arbitrary driver -func download(driver string, destination string, v semver.Version) error { - out.T(out.FileDownload, "Downloading driver {{.driver}}:", out.V{"driver": driver}) - os.Remove(destination) - url := driverWithChecksumURL(driver, v) - client := &getter.Client{ - Src: url, - Dst: destination, - Mode: getter.ClientModeFile, - Options: []getter.ClientOption{getter.WithProgress(util.DefaultProgressBar)}, - } - - glog.Infof("Downloading: %+v", client) - if err := client.Get(); err != nil { - return errors.Wrapf(err, "download failed: %s", url) - } - // Give downloaded drivers a baseline decent file permission - return os.Chmod(destination, 0755) -} - -// extractVMDriverVersion extracts the driver version. -// KVM and Hyperkit drivers support the 'version' command, that display the information as: -// version: vX.X.X -// commit: XXXX -// This method returns the version 'vX.X.X' or empty if the version isn't found. -func extractVMDriverVersion(s string) string { - versionRegex := regexp.MustCompile(`version:(.*)`) - matches := versionRegex.FindStringSubmatch(s) - - if len(matches) != 2 { - return "" - } - - v := strings.TrimSpace(matches[1]) - return strings.TrimPrefix(v, version.VersionPrefix) -} diff --git a/pkg/drivers/hyperkit/driver.go b/pkg/drivers/hyperkit/driver.go index e783da55da..bf775240e6 100644 --- a/pkg/drivers/hyperkit/driver.go +++ b/pkg/drivers/hyperkit/driver.go @@ -198,19 +198,11 @@ func (d *Driver) Restart() error { return pkgdrivers.Restart(d) } -// Start a host -func (d *Driver) Start() error { - if err := d.verifyRootPermissions(); err != nil { - return err - } - +func (d *Driver) createHost() (*hyperkit.HyperKit, error) { stateDir := filepath.Join(d.StorePath, "machines", d.MachineName) - if err := d.recoverFromUncleanShutdown(); err != nil { - return err - } h, err := hyperkit.New("", d.VpnKitSock, stateDir) if err != nil { - return errors.Wrap(err, "new-ing Hyperkit") + return nil, errors.Wrap(err, "new-ing Hyperkit") } // TODO: handle the rest of our settings. @@ -227,12 +219,38 @@ func (d *Driver) Start() error { h.SetLogger(logger) if vsockPorts, err := d.extractVSockPorts(); err != nil { - return err + return nil, err } else if len(vsockPorts) >= 1 { h.VSock = true h.VSockPorts = vsockPorts } + h.Disks = []hyperkit.DiskConfig{ + { + Path: pkgdrivers.GetDiskPath(d.BaseDriver), + Size: d.DiskSize, + Driver: "virtio-blk", + }, + } + + return h, nil +} + +// Start a host +func (d *Driver) Start() error { + if err := d.verifyRootPermissions(); err != nil { + return err + } + + if err := d.recoverFromUncleanShutdown(); err != nil { + return err + } + + h, err := d.createHost() + if err != nil { + return err + } + log.Debugf("Using UUID %s", h.UUID) mac, err := GetMACAddressFromUUID(h.UUID) if err != nil { @@ -242,18 +260,23 @@ func (d *Driver) Start() error { // Need to strip 0's mac = trimMacAddress(mac) log.Debugf("Generated MAC %s", mac) - h.Disks = []hyperkit.DiskConfig{ - { - Path: pkgdrivers.GetDiskPath(d.BaseDriver), - Size: d.DiskSize, - Driver: "virtio-blk", - }, - } + log.Debugf("Starting with cmdline: %s", d.Cmdline) if err := h.Start(d.Cmdline); err != nil { return errors.Wrapf(err, "starting with cmd line: %s", d.Cmdline) } + if err := d.setupIP(mac); err != nil { + return err + } + + if err := d.setupNFSMounts(); err != nil { + return err + } + return nil +} + +func (d *Driver) setupIP(mac string) error { getIP := func() error { st, err := d.GetState() if err != nil { @@ -270,6 +293,8 @@ func (d *Driver) Start() error { return nil } + var err error + // Implement a retry loop without calling any minikube code for i := 0; i < 30; i++ { log.Debugf("Attempt %d", i) @@ -288,6 +313,12 @@ func (d *Driver) Start() error { } log.Debugf("IP: %s", d.IPAddress) + return nil +} + +func (d *Driver) setupNFSMounts() error { + var err error + if len(d.NFSShares) > 0 { log.Info("Setting up NFS mounts") // takes some time here for ssh / nfsd to work properly diff --git a/pkg/drivers/kvm/network_test.go b/pkg/drivers/kvm/network_test.go index 985ac7f26e..905816165a 100644 --- a/pkg/drivers/kvm/network_test.go +++ b/pkg/drivers/kvm/network_test.go @@ -46,7 +46,7 @@ var ( ]`) ) -func Test_parseStatusAndReturnIp(t *testing.T) { +func TestParseStatusAndReturnIp(t *testing.T) { type args struct { mac string statuses []byte diff --git a/pkg/drivers/none/none.go b/pkg/drivers/none/none.go index 5731d22841..4d99fd8b8e 100644 --- a/pkg/drivers/none/none.go +++ b/pkg/drivers/none/none.go @@ -17,8 +17,8 @@ limitations under the License. package none import ( - "bytes" "fmt" + "os/exec" "strings" "time" @@ -29,17 +29,15 @@ import ( "k8s.io/apimachinery/pkg/util/net" pkgdrivers "k8s.io/minikube/pkg/drivers" "k8s.io/minikube/pkg/minikube/command" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/cruntime" + "k8s.io/minikube/pkg/minikube/vmpath" "k8s.io/minikube/pkg/util/retry" ) -const driverName = constants.DriverNone - // cleanupPaths are paths to be removed by cleanup, and are used by both kubeadm and minikube. var cleanupPaths = []string{ - constants.GuestEphemeralDir, - constants.GuestManifestsDir, + vmpath.GuestEphemeralDir, + vmpath.GuestManifestsDir, "/var/lib/minikube", } @@ -91,7 +89,7 @@ func (d *Driver) Create() error { // DriverName returns the name of the driver func (d *Driver) DriverName() string { - return driverName + return "none" } // GetIP returns an IP or hostname that this host is available at @@ -170,8 +168,8 @@ func (d *Driver) Remove() error { return errors.Wrap(err, "kill") } glog.Infof("Removing: %s", cleanupPaths) - cmd := fmt.Sprintf("sudo rm -rf %s", strings.Join(cleanupPaths, " ")) - if err := d.exec.Run(cmd); err != nil { + args := append([]string{"rm", "-rf"}, cleanupPaths...) + if _, err := d.exec.RunCmd(exec.Command("sudo", args...)); err != nil { glog.Errorf("cleanup incomplete: %v", err) } return nil @@ -219,22 +217,20 @@ func (d *Driver) RunSSHCommandFromDriver() error { } // stopKubelet idempotently stops the kubelet -func stopKubelet(exec command.Runner) error { +func stopKubelet(cr command.Runner) error { glog.Infof("stopping kubelet.service ...") stop := func() error { - cmdStop := "sudo systemctl stop kubelet.service" - cmdCheck := "sudo systemctl show -p SubState kubelet" - err := exec.Run(cmdStop) + cmd := exec.Command("sudo", "systemctl", "stop", "kubelet.service") + if rr, err := cr.RunCmd(cmd); err != nil { + glog.Errorf("temporary error for %q : %v", rr.Command(), err) + } + cmd = exec.Command("sudo", "systemctl", "show", "-p", "SubState", "kubelet") + rr, err := cr.RunCmd(cmd) if err != nil { - glog.Errorf("temporary error for %q : %v", cmdStop, err) + glog.Errorf("temporary error: for %q : %v", rr.Command(), err) } - var out bytes.Buffer - errStatus := exec.CombinedOutputTo(cmdCheck, &out) - if errStatus != nil { - glog.Errorf("temporary error: for %q : %v", cmdCheck, errStatus) - } - if !strings.Contains(out.String(), "dead") && !strings.Contains(out.String(), "failed") { - return fmt.Errorf("unexpected kubelet state: %q", out) + if !strings.Contains(rr.Stdout.String(), "dead") && !strings.Contains(rr.Stdout.String(), "failed") { + return fmt.Errorf("unexpected kubelet state: %q", rr.Stdout.String()) } return nil } @@ -247,13 +243,21 @@ func stopKubelet(exec command.Runner) error { } // restartKubelet restarts the kubelet -func restartKubelet(exec command.Runner) error { +func restartKubelet(cr command.Runner) error { glog.Infof("restarting kubelet.service ...") - return exec.Run("sudo systemctl restart kubelet.service") + c := exec.Command("sudo", "systemctl", "restart", "kubelet.service") + if _, err := cr.RunCmd(c); err != nil { + return err + } + return nil } // checkKubelet returns an error if the kubelet is not running. -func checkKubelet(exec command.Runner) error { +func checkKubelet(cr command.Runner) error { glog.Infof("checking for running kubelet ...") - return exec.Run("systemctl is-active --quiet service kubelet") + c := exec.Command("systemctl", "is-active", "--quiet", "service", "kubelet") + if _, err := cr.RunCmd(c); err != nil { + return errors.Wrap(err, "check kubelet") + } + return nil } diff --git a/pkg/gvisor/disable.go b/pkg/gvisor/disable.go index 4c00bbaea0..b55fadbec3 100644 --- a/pkg/gvisor/disable.go +++ b/pkg/gvisor/disable.go @@ -23,17 +23,16 @@ import ( "github.com/docker/machine/libmachine/mcnutils" "github.com/pkg/errors" - "k8s.io/minikube/pkg/minikube/constants" ) // Disable reverts containerd config files and restarts containerd func Disable() error { log.Print("Disabling gvisor...") - if err := os.Remove(filepath.Join(nodeDir, constants.ContainerdConfigTomlPath)); err != nil { - return errors.Wrapf(err, "removing %s", constants.ContainerdConfigTomlPath) + if err := os.Remove(filepath.Join(nodeDir, containerdConfigTomlPath)); err != nil { + return errors.Wrapf(err, "removing %s", containerdConfigTomlPath) } - log.Printf("Restoring default config.toml at %s", constants.ContainerdConfigTomlPath) - if err := mcnutils.CopyFile(filepath.Join(nodeDir, constants.StoredContainerdConfigTomlPath), filepath.Join(nodeDir, constants.ContainerdConfigTomlPath)); err != nil { + log.Printf("Restoring default config.toml at %s", containerdConfigTomlPath) + if err := mcnutils.CopyFile(filepath.Join(nodeDir, storedContainerdConfigTomlPath), filepath.Join(nodeDir, containerdConfigTomlPath)); err != nil { return errors.Wrap(err, "reverting back to default config.toml") } // restart containerd diff --git a/pkg/gvisor/enable.go b/pkg/gvisor/enable.go index 403ab0d4d8..b2905574b8 100644 --- a/pkg/gvisor/enable.go +++ b/pkg/gvisor/enable.go @@ -35,7 +35,11 @@ import ( ) const ( - nodeDir = "/node" + nodeDir = "/node" + containerdConfigTomlPath = "/etc/containerd/config.toml" + storedContainerdConfigTomlPath = "/tmp/config.toml" + gvisorContainerdShimURL = "https://github.com/google/gvisor-containerd-shim/releases/download/v0.0.3/containerd-shim-runsc-v1.linux-amd64" + gvisorURL = "https://storage.googleapis.com/gvisor/releases/nightly/2019-01-14/runsc" ) // Enable follows these steps for enabling gvisor in minikube: @@ -102,13 +106,13 @@ func downloadBinaries() error { // downloads the gvisor-containerd-shim func gvisorContainerdShim() error { dest := filepath.Join(nodeDir, "usr/bin/containerd-shim-runsc-v1") - return downloadFileToDest(constants.GvisorContainerdShimURL, dest) + return downloadFileToDest(gvisorContainerdShimURL, dest) } // downloads the runsc binary and returns a path to the binary func runsc() error { dest := filepath.Join(nodeDir, "usr/bin/runsc") - return downloadFileToDest(constants.GvisorURL, dest) + return downloadFileToDest(gvisorURL, dest) } // downloadFileToDest downloads the given file to the dest @@ -149,12 +153,12 @@ func downloadFileToDest(url, dest string) error { // 2. gvisor containerd config.toml // and save the default version of config.toml func copyConfigFiles() error { - log.Printf("Storing default config.toml at %s", constants.StoredContainerdConfigTomlPath) - if err := mcnutils.CopyFile(filepath.Join(nodeDir, constants.ContainerdConfigTomlPath), filepath.Join(nodeDir, constants.StoredContainerdConfigTomlPath)); err != nil { + log.Printf("Storing default config.toml at %s", storedContainerdConfigTomlPath) + if err := mcnutils.CopyFile(filepath.Join(nodeDir, containerdConfigTomlPath), filepath.Join(nodeDir, storedContainerdConfigTomlPath)); err != nil { return errors.Wrap(err, "copying default config.toml") } - log.Print("Copying containerd config.toml with gvisor...") - if err := copyAssetToDest(constants.GvisorConfigTomlTargetName, filepath.Join(nodeDir, constants.ContainerdConfigTomlPath)); err != nil { + log.Printf("Copying %s asset to %s", constants.GvisorConfigTomlTargetName, filepath.Join(nodeDir, containerdConfigTomlPath)) + if err := copyAssetToDest(constants.GvisorConfigTomlTargetName, filepath.Join(nodeDir, containerdConfigTomlPath)); err != nil { return errors.Wrap(err, "copying gvisor version of config.toml") } return nil @@ -167,8 +171,13 @@ func copyAssetToDest(targetName, dest string) error { asset = a } } + if asset == nil { + return fmt.Errorf("no asset matching target %s among %+v", targetName, assets.Addons["gvisor"]) + } + // Now, copy the data from this asset to dest src := filepath.Join(constants.GvisorFilesPath, asset.GetTargetName()) + log.Printf("%s asset path: %s", targetName, src) contents, err := ioutil.ReadFile(src) if err != nil { return errors.Wrapf(err, "getting contents of %s", asset.GetAssetName()) @@ -178,6 +187,8 @@ func copyAssetToDest(targetName, dest string) error { return errors.Wrapf(err, "removing %s", dest) } } + + log.Printf("creating %s", dest) f, err := os.Create(dest) if err != nil { return errors.Wrapf(err, "creating %s", dest) @@ -189,28 +200,24 @@ func copyAssetToDest(targetName, dest string) error { } func restartContainerd() error { - dir := filepath.Join(nodeDir, "usr/libexec/sudo") - if err := os.Setenv("LD_LIBRARY_PATH", dir); err != nil { - return errors.Wrap(err, dir) - } + log.Print("restartContainerd black magic happening") log.Print("Stopping rpc-statd.service...") - // first, stop rpc-statd.service - cmd := exec.Command("sudo", "-E", "systemctl", "stop", "rpc-statd.service") + cmd := exec.Command("/usr/sbin/chroot", "/node", "sudo", "systemctl", "stop", "rpc-statd.service") if out, err := cmd.CombinedOutput(); err != nil { fmt.Println(string(out)) return errors.Wrap(err, "stopping rpc-statd.service") } - // restart containerd + log.Print("Restarting containerd...") - cmd = exec.Command("sudo", "-E", "systemctl", "restart", "containerd") + cmd = exec.Command("/usr/sbin/chroot", "/node", "sudo", "systemctl", "restart", "containerd") if out, err := cmd.CombinedOutput(); err != nil { log.Print(string(out)) return errors.Wrap(err, "restarting containerd") } - // start rpc-statd.service + log.Print("Starting rpc-statd...") - cmd = exec.Command("sudo", "-E", "systemctl", "start", "rpc-statd.service") + cmd = exec.Command("/usr/sbin/chroot", "/node", "sudo", "systemctl", "start", "rpc-statd.service") if out, err := cmd.CombinedOutput(); err != nil { log.Print(string(out)) return errors.Wrap(err, "restarting rpc-statd.service") diff --git a/pkg/minikube/assets/addons.go b/pkg/minikube/assets/addons.go index 5d1a5cdc16..9184ec1d6b 100644 --- a/pkg/minikube/assets/addons.go +++ b/pkg/minikube/assets/addons.go @@ -28,6 +28,7 @@ import ( "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/minikube/vmpath" "k8s.io/minikube/pkg/util" ) @@ -72,27 +73,27 @@ var Addons = map[string]*Addon{ "addon-manager": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/addon-manager.yaml.tmpl", - constants.GuestManifestsDir, + vmpath.GuestManifestsDir, "addon-manager.yaml.tmpl", "0640", true), }, true, "addon-manager"), "dashboard": NewAddon([]*BinAsset{ - MustBinAsset("deploy/addons/dashboard/dashboard-clusterrole.yaml", constants.GuestAddonsDir, "dashboard-clusterrole.yaml", "0640", false), - MustBinAsset("deploy/addons/dashboard/dashboard-clusterrolebinding.yaml", constants.GuestAddonsDir, "dashboard-clusterrolebinding.yaml", "0640", false), - MustBinAsset("deploy/addons/dashboard/dashboard-configmap.yaml", constants.GuestAddonsDir, "dashboard-configmap.yaml", "0640", false), - MustBinAsset("deploy/addons/dashboard/dashboard-dp.yaml", constants.GuestAddonsDir, "dashboard-dp.yaml", "0640", false), - MustBinAsset("deploy/addons/dashboard/dashboard-ns.yaml", constants.GuestAddonsDir, "dashboard-ns.yaml", "0640", false), - MustBinAsset("deploy/addons/dashboard/dashboard-role.yaml", constants.GuestAddonsDir, "dashboard-role.yaml", "0640", false), - MustBinAsset("deploy/addons/dashboard/dashboard-rolebinding.yaml", constants.GuestAddonsDir, "dashboard-rolebinding.yaml", "0640", false), - MustBinAsset("deploy/addons/dashboard/dashboard-sa.yaml", constants.GuestAddonsDir, "dashboard-sa.yaml", "0640", false), - MustBinAsset("deploy/addons/dashboard/dashboard-secret.yaml", constants.GuestAddonsDir, "dashboard-secret.yaml", "0640", false), - MustBinAsset("deploy/addons/dashboard/dashboard-svc.yaml", constants.GuestAddonsDir, "dashboard-svc.yaml", "0640", false), + MustBinAsset("deploy/addons/dashboard/dashboard-clusterrole.yaml", vmpath.GuestAddonsDir, "dashboard-clusterrole.yaml", "0640", false), + MustBinAsset("deploy/addons/dashboard/dashboard-clusterrolebinding.yaml", vmpath.GuestAddonsDir, "dashboard-clusterrolebinding.yaml", "0640", false), + MustBinAsset("deploy/addons/dashboard/dashboard-configmap.yaml", vmpath.GuestAddonsDir, "dashboard-configmap.yaml", "0640", false), + MustBinAsset("deploy/addons/dashboard/dashboard-dp.yaml", vmpath.GuestAddonsDir, "dashboard-dp.yaml", "0640", false), + MustBinAsset("deploy/addons/dashboard/dashboard-ns.yaml", vmpath.GuestAddonsDir, "dashboard-ns.yaml", "0640", false), + MustBinAsset("deploy/addons/dashboard/dashboard-role.yaml", vmpath.GuestAddonsDir, "dashboard-role.yaml", "0640", false), + MustBinAsset("deploy/addons/dashboard/dashboard-rolebinding.yaml", vmpath.GuestAddonsDir, "dashboard-rolebinding.yaml", "0640", false), + MustBinAsset("deploy/addons/dashboard/dashboard-sa.yaml", vmpath.GuestAddonsDir, "dashboard-sa.yaml", "0640", false), + MustBinAsset("deploy/addons/dashboard/dashboard-secret.yaml", vmpath.GuestAddonsDir, "dashboard-secret.yaml", "0640", false), + MustBinAsset("deploy/addons/dashboard/dashboard-svc.yaml", vmpath.GuestAddonsDir, "dashboard-svc.yaml", "0640", false), }, false, "dashboard"), "default-storageclass": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/storageclass/storageclass.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "storageclass.yaml", "0640", false), @@ -100,7 +101,7 @@ var Addons = map[string]*Addon{ "storage-provisioner": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/storage-provisioner/storage-provisioner.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "storage-provisioner.yaml", "0640", true), @@ -108,95 +109,63 @@ var Addons = map[string]*Addon{ "storage-provisioner-gluster": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/storage-provisioner-gluster/storage-gluster-ns.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "storage-gluster-ns.yaml", "0640", false), MustBinAsset( "deploy/addons/storage-provisioner-gluster/glusterfs-daemonset.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "glusterfs-daemonset.yaml", "0640", false), MustBinAsset( "deploy/addons/storage-provisioner-gluster/heketi-deployment.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "heketi-deployment.yaml", "0640", false), MustBinAsset( "deploy/addons/storage-provisioner-gluster/storage-provisioner-glusterfile.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "storage-privisioner-glusterfile.yaml", "0640", false), }, false, "storage-provisioner-gluster"), - "heapster": NewAddon([]*BinAsset{ - MustBinAsset( - "deploy/addons/heapster/influx-grafana-rc.yaml.tmpl", - constants.GuestAddonsDir, - "influxGrafana-rc.yaml", - "0640", - true), - MustBinAsset( - "deploy/addons/heapster/grafana-svc.yaml.tmpl", - constants.GuestAddonsDir, - "grafana-svc.yaml", - "0640", - false), - MustBinAsset( - "deploy/addons/heapster/influxdb-svc.yaml.tmpl", - constants.GuestAddonsDir, - "influxdb-svc.yaml", - "0640", - false), - MustBinAsset( - "deploy/addons/heapster/heapster-rc.yaml.tmpl", - constants.GuestAddonsDir, - "heapster-rc.yaml", - "0640", - true), - MustBinAsset( - "deploy/addons/heapster/heapster-svc.yaml.tmpl", - constants.GuestAddonsDir, - "heapster-svc.yaml", - "0640", - false), - }, false, "heapster"), "efk": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/efk/elasticsearch-rc.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "elasticsearch-rc.yaml", "0640", true), MustBinAsset( "deploy/addons/efk/elasticsearch-svc.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "elasticsearch-svc.yaml", "0640", false), MustBinAsset( "deploy/addons/efk/fluentd-es-rc.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "fluentd-es-rc.yaml", "0640", true), MustBinAsset( "deploy/addons/efk/fluentd-es-configmap.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "fluentd-es-configmap.yaml", "0640", false), MustBinAsset( "deploy/addons/efk/kibana-rc.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "kibana-rc.yaml", "0640", false), MustBinAsset( "deploy/addons/efk/kibana-svc.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "kibana-svc.yaml", "0640", false), @@ -204,19 +173,19 @@ var Addons = map[string]*Addon{ "ingress": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/ingress/ingress-configmap.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "ingress-configmap.yaml", "0640", false), MustBinAsset( "deploy/addons/ingress/ingress-rbac.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "ingress-rbac.yaml", "0640", false), MustBinAsset( "deploy/addons/ingress/ingress-dp.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "ingress-dp.yaml", "0640", true), @@ -224,19 +193,19 @@ var Addons = map[string]*Addon{ "metrics-server": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/metrics-server/metrics-apiservice.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "metrics-apiservice.yaml", "0640", false), MustBinAsset( "deploy/addons/metrics-server/metrics-server-deployment.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "metrics-server-deployment.yaml", "0640", true), MustBinAsset( "deploy/addons/metrics-server/metrics-server-service.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "metrics-server-service.yaml", "0640", false), @@ -244,19 +213,19 @@ var Addons = map[string]*Addon{ "registry": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/registry/registry-rc.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "registry-rc.yaml", "0640", false), MustBinAsset( "deploy/addons/registry/registry-svc.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "registry-svc.yaml", "0640", false), MustBinAsset( "deploy/addons/registry/registry-proxy.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "registry-proxy.yaml", "0640", false), @@ -264,7 +233,7 @@ var Addons = map[string]*Addon{ "registry-creds": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/registry-creds/registry-creds-rc.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "registry-creds-rc.yaml", "0640", false), @@ -272,7 +241,7 @@ var Addons = map[string]*Addon{ "freshpod": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/freshpod/freshpod-rc.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "freshpod-rc.yaml", "0640", true), @@ -280,7 +249,7 @@ var Addons = map[string]*Addon{ "nvidia-driver-installer": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/gpu/nvidia-driver-installer.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "nvidia-driver-installer.yaml", "0640", true), @@ -288,7 +257,7 @@ var Addons = map[string]*Addon{ "nvidia-gpu-device-plugin": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/gpu/nvidia-gpu-device-plugin.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "nvidia-gpu-device-plugin.yaml", "0640", true), @@ -296,13 +265,13 @@ var Addons = map[string]*Addon{ "logviewer": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/logviewer/logviewer-dp-and-svc.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "logviewer-dp-and-svc.yaml", "0640", false), MustBinAsset( "deploy/addons/logviewer/logviewer-rbac.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "logviewer-rbac.yaml", "0640", false), @@ -310,13 +279,13 @@ var Addons = map[string]*Addon{ "gvisor": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/gvisor/gvisor-pod.yaml.tmpl", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "gvisor-pod.yaml", "0640", true), MustBinAsset( "deploy/addons/gvisor/gvisor-runtimeclass.yaml", - constants.GuestAddonsDir, + vmpath.GuestAddonsDir, "gvisor-runtimeclass.yaml", "0640", false), @@ -327,12 +296,40 @@ var Addons = map[string]*Addon{ "0640", true), }, false, "gvisor"), + "helm-tiller": NewAddon([]*BinAsset{ + MustBinAsset( + "deploy/addons/helm-tiller/helm-tiller-dp.tmpl", + vmpath.GuestAddonsDir, + "helm-tiller-dp.yaml", + "0640", + true), + MustBinAsset( + "deploy/addons/helm-tiller/helm-tiller-rbac.tmpl", + vmpath.GuestAddonsDir, + "helm-tiller-rbac.yaml", + "0640", + true), + MustBinAsset( + "deploy/addons/helm-tiller/helm-tiller-svc.tmpl", + vmpath.GuestAddonsDir, + "helm-tiller-svc.yaml", + "0640", + true), + }, false, "helm-tiller"), + "ingress-dns": NewAddon([]*BinAsset{ + MustBinAsset( + "deploy/addons/ingress-dns/ingress-dns-pod.yaml", + vmpath.GuestAddonsDir, + "ingress-dns-pod.yaml", + "0640", + false), + }, false, "ingress-dns"), } // AddMinikubeDirAssets adds all addons and files to the list // of files to be copied to the vm. func AddMinikubeDirAssets(assets *[]CopyableFile) error { - if err := addMinikubeDirToAssets(localpath.MakeMiniPath("addons"), constants.GuestAddonsDir, assets); err != nil { + if err := addMinikubeDirToAssets(localpath.MakeMiniPath("addons"), vmpath.GuestAddonsDir, assets); err != nil { return errors.Wrap(err, "adding addons folder to assets") } if err := addMinikubeDirToAssets(localpath.MakeMiniPath("files"), "", assets); err != nil { @@ -391,7 +388,7 @@ func GenerateTemplateData(cfg config.KubernetesConfig) interface{} { // for less common architectures blank suffix for amd64 ea := "" if runtime.GOARCH != "amd64" { - ea = runtime.GOARCH + ea = "-" + runtime.GOARCH } opts := struct { Arch string diff --git a/pkg/minikube/assets/addons_test.go b/pkg/minikube/assets/addons_test.go index 1446d81f01..9e7e745f06 100644 --- a/pkg/minikube/assets/addons_test.go +++ b/pkg/minikube/assets/addons_test.go @@ -23,8 +23,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/minikube/vmpath" ) func setupTestDir() (string, error) { @@ -57,18 +57,18 @@ func TestAddMinikubeDirAssets(t *testing.T) { }{ { relativePath: "/dir1/file1.txt", - expectedPath: constants.GuestAddonsDir, + expectedPath: vmpath.GuestAddonsDir, }, { relativePath: "/dir1/file2.txt", - expectedPath: constants.GuestAddonsDir, + expectedPath: vmpath.GuestAddonsDir, }, { relativePath: "/dir2/file1.txt", - expectedPath: constants.GuestAddonsDir, + expectedPath: vmpath.GuestAddonsDir, }, }, - vmPath: constants.GuestAddonsDir, + vmPath: vmpath.GuestAddonsDir, }, { description: "absolute path assets", diff --git a/pkg/minikube/bootstrapper/bootstrapper.go b/pkg/minikube/bootstrapper/bootstrapper.go index 88c7e0ee00..f0b244f3e7 100644 --- a/pkg/minikube/bootstrapper/bootstrapper.go +++ b/pkg/minikube/bootstrapper/bootstrapper.go @@ -41,7 +41,7 @@ type Bootstrapper interface { UpdateCluster(config.KubernetesConfig) error RestartCluster(config.KubernetesConfig) error DeleteCluster(config.KubernetesConfig) error - WaitCluster(config.KubernetesConfig, time.Duration) error + WaitForPods(config.KubernetesConfig, time.Duration, []string) error // LogCommands returns a map of log type to a command which will display that log. LogCommands(LogOptions) map[string]string SetupCerts(cfg config.KubernetesConfig) error @@ -68,7 +68,7 @@ func GetCachedBinaryList(bootstrapper string) []string { func GetCachedImageList(imageRepository string, version string, bootstrapper string) []string { switch bootstrapper { case BootstrapperTypeKubeadm: - _, images := images.CachedImages(imageRepository, version) + images := images.CachedImages(imageRepository, version) return images default: return []string{} diff --git a/pkg/minikube/bootstrapper/certs.go b/pkg/minikube/bootstrapper/certs.go index 20a06999d4..6702a6b122 100644 --- a/pkg/minikube/bootstrapper/certs.go +++ b/pkg/minikube/bootstrapper/certs.go @@ -22,6 +22,7 @@ import ( "io/ioutil" "net" "os" + "os/exec" "path" "path/filepath" "strings" @@ -35,9 +36,9 @@ import ( "k8s.io/minikube/pkg/minikube/assets" "k8s.io/minikube/pkg/minikube/command" "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/kubeconfig" "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/minikube/vmpath" "k8s.io/minikube/pkg/util" "github.com/juju/clock" @@ -45,8 +46,10 @@ import ( ) const ( + // CACertificatesDir contains CA certificates CACertificatesDir = "/usr/share/ca-certificates" - SSLCertStoreDir = "/etc/ssl/certs" + // SSLCertStoreDir contains SSL certificates + SSLCertStoreDir = "/etc/ssl/certs" ) var ( @@ -89,7 +92,7 @@ func SetupCerts(cmd command.Runner, k8s config.KubernetesConfig) error { if strings.HasSuffix(cert, ".key") { perms = "0600" } - certFile, err := assets.NewFileAsset(p, constants.GuestCertsDir, cert, perms) + certFile, err := assets.NewFileAsset(p, vmpath.GuestCertsDir, cert, perms) if err != nil { return err } @@ -112,9 +115,9 @@ func SetupCerts(cmd command.Runner, k8s config.KubernetesConfig) error { kcs := &kubeconfig.Settings{ ClusterName: k8s.NodeName, ClusterServerAddress: fmt.Sprintf("https://localhost:%d", k8s.NodePort), - ClientCertificate: path.Join(constants.GuestCertsDir, "apiserver.crt"), - ClientKey: path.Join(constants.GuestCertsDir, "apiserver.key"), - CertificateAuthority: path.Join(constants.GuestCertsDir, "ca.crt"), + ClientCertificate: path.Join(vmpath.GuestCertsDir, "apiserver.crt"), + ClientKey: path.Join(vmpath.GuestCertsDir, "apiserver.key"), + CertificateAuthority: path.Join(vmpath.GuestCertsDir, "ca.crt"), KeepContext: false, } @@ -128,7 +131,7 @@ func SetupCerts(cmd command.Runner, k8s config.KubernetesConfig) error { return errors.Wrap(err, "encoding kubeconfig") } - kubeCfgFile := assets.NewMemoryAsset(data, constants.GuestPersistentDir, "kubeconfig", "0644") + kubeCfgFile := assets.NewMemoryAsset(data, vmpath.GuestPersistentDir, "kubeconfig", "0644") copyableFiles = append(copyableFiles, kubeCfgFile) for _, f := range copyableFiles { @@ -139,7 +142,7 @@ func SetupCerts(cmd command.Runner, k8s config.KubernetesConfig) error { // configure CA certificates if err := configureCACerts(cmd, caCerts); err != nil { - return errors.Wrapf(err, "error configuring CA certificates during provisioning %v", err) + return errors.Wrapf(err, "Configuring CA certs") } return nil } @@ -316,21 +319,21 @@ func collectCACerts() (map[string]string, error) { } // getSubjectHash calculates Certificate Subject Hash for creating certificate symlinks -func getSubjectHash(cmd command.Runner, filePath string) (string, error) { - out, err := cmd.CombinedOutput(fmt.Sprintf("openssl x509 -hash -noout -in '%s'", filePath)) +func getSubjectHash(cr command.Runner, filePath string) (string, error) { + rr, err := cr.RunCmd(exec.Command("openssl", "x509", "-hash", "-noout", "-in", filePath)) if err != nil { - return "", err + return "", errors.Wrapf(err, rr.Command()) } - - stringHash := strings.TrimSpace(out) + stringHash := strings.TrimSpace(rr.Stdout.String()) return stringHash, nil } // configureCACerts looks up and installs all uploaded PEM certificates in /usr/share/ca-certificates to system-wide certificate store (/etc/ssl/certs). // OpenSSL binary required in minikube ISO -func configureCACerts(cmd command.Runner, caCerts map[string]string) error { +func configureCACerts(cr command.Runner, caCerts map[string]string) error { hasSSLBinary := true - if err := cmd.Run("which openssl"); err != nil { + _, err := cr.RunCmd(exec.Command("openssl", "version")) + if err != nil { hasSSLBinary = false } @@ -341,24 +344,25 @@ func configureCACerts(cmd command.Runner, caCerts map[string]string) error { for _, caCertFile := range caCerts { dstFilename := path.Base(caCertFile) certStorePath := path.Join(SSLCertStoreDir, dstFilename) - if err := cmd.Run(fmt.Sprintf("sudo test -f '%s'", certStorePath)); err != nil { - if err := cmd.Run(fmt.Sprintf("sudo ln -s '%s' '%s'", caCertFile, certStorePath)); err != nil { - return errors.Wrapf(err, "error making symbol link for certificate %s", caCertFile) + _, err := cr.RunCmd(exec.Command("sudo", "test", "-f", certStorePath)) + if err != nil { + if _, err := cr.RunCmd(exec.Command("sudo", "ln", "-s", caCertFile, certStorePath)); err != nil { + return errors.Wrapf(err, "create symlink for %s", caCertFile) } } if hasSSLBinary { - subjectHash, err := getSubjectHash(cmd, caCertFile) + subjectHash, err := getSubjectHash(cr, caCertFile) if err != nil { - return errors.Wrapf(err, "error calculating subject hash for certificate %s", caCertFile) + return errors.Wrapf(err, "calculate hash for cacert %s", caCertFile) } subjectHashLink := path.Join(SSLCertStoreDir, fmt.Sprintf("%s.0", subjectHash)) - if err := cmd.Run(fmt.Sprintf("sudo test -f '%s'", subjectHashLink)); err != nil { - if err := cmd.Run(fmt.Sprintf("sudo ln -s '%s' '%s'", certStorePath, subjectHashLink)); err != nil { - return errors.Wrapf(err, "error making subject hash symbol link for certificate %s", caCertFile) + _, err = cr.RunCmd(exec.Command("sudo", "test", "-f", subjectHashLink)) + if err != nil { + if _, err := cr.RunCmd(exec.Command("sudo", "ln", "-s", certStorePath, subjectHashLink)); err != nil { + return errors.Wrapf(err, "linking caCertFile %s", caCertFile) } } } } - return nil } diff --git a/pkg/minikube/bootstrapper/certs_test.go b/pkg/minikube/bootstrapper/certs_test.go index e6a25f484e..7d65e6c3af 100644 --- a/pkg/minikube/bootstrapper/certs_test.go +++ b/pkg/minikube/bootstrapper/certs_test.go @@ -62,9 +62,9 @@ func TestSetupCerts(t *testing.T) { certStorePath := path.Join(SSLCertStoreDir, dst) certNameHash := "abcdef" remoteCertHashLink := path.Join(SSLCertStoreDir, fmt.Sprintf("%s.0", certNameHash)) - cmdMap[fmt.Sprintf("sudo ln -s '%s' '%s'", certFile, certStorePath)] = "1" - cmdMap[fmt.Sprintf("openssl x509 -hash -noout -in '%s'", certFile)] = certNameHash - cmdMap[fmt.Sprintf("sudo ln -s '%s' '%s'", certStorePath, remoteCertHashLink)] = "1" + cmdMap[fmt.Sprintf("sudo ln -s %s %s", certFile, certStorePath)] = "1" + cmdMap[fmt.Sprintf("openssl x509 -hash -noout -in %s", certFile)] = certNameHash + cmdMap[fmt.Sprintf("sudo ln -s %s %s", certStorePath, remoteCertHashLink)] = "1" } f := command.NewFakeCommandRunner() f.SetCommandToOutput(cmdMap) diff --git a/pkg/minikube/bootstrapper/images/images.go b/pkg/minikube/bootstrapper/images/images.go index 92f67844f7..b69d1160bd 100644 --- a/pkg/minikube/bootstrapper/images/images.go +++ b/pkg/minikube/bootstrapper/images/images.go @@ -25,19 +25,43 @@ import ( minikubeVersion "k8s.io/minikube/pkg/version" ) -// CachedImages gets the images to cache for kubeadm for a version -func CachedImages(imageRepository string, kubernetesVersionStr string) (string, []string) { - minikubeRepository := imageRepository +const ( + // DefaultImageRepo is the default repository for images + DefaultImageRepo = "k8s.gcr.io" + // DefaultMinikubeRepo is the default repository for minikube + DefaultMinikubeRepo = "gcr.io/k8s-minikube" +) + +// getImageRepositories returns either the k8s image registry on GCR or a mirror if specified +func getImageRepository(imageRepository string) string { if imageRepository == "" { - imageRepository = "k8s.gcr.io" - minikubeRepository = "gcr.io/k8s-minikube" + imageRepository = DefaultImageRepo } if !strings.HasSuffix(imageRepository, "/") { imageRepository += "/" } + + return imageRepository +} + +// getMinikubeRepository returns either the minikube image registry on GCR or a mirror if specified +func getMinikubeRepository(imageRepository string) string { + minikubeRepository := imageRepository + if minikubeRepository == "" { + minikubeRepository = DefaultMinikubeRepo + } if !strings.HasSuffix(minikubeRepository, "/") { minikubeRepository += "/" } + + return minikubeRepository +} + +// CachedImages gets the images to cache for kubeadm for a version +func CachedImages(imageRepositoryStr string, kubernetesVersionStr string) []string { + imageRepository := getImageRepository(imageRepositoryStr) + minikubeRepository := getMinikubeRepository(imageRepositoryStr) + v1_16plus := semver.MustParseRange(">=1.16.0") v1_14plus := semver.MustParseRange(">=1.14.0 <1.16.0") v1_13 := semver.MustParseRange(">=1.13.0 <1.14.0") @@ -67,9 +91,8 @@ func CachedImages(imageRepository string, kubernetesVersionStr string) (string, }...) } - var podInfraContainerImage string + podInfraContainerImage := PauseImage(imageRepository, kubernetesVersionStr) if v1_16plus(kubernetesVersion) { - podInfraContainerImage = imageRepository + "pause:3.1" images = append(images, []string{ podInfraContainerImage, imageRepository + "k8s-dns-kube-dns" + ArchTag(true) + "1.14.13", @@ -80,7 +103,6 @@ func CachedImages(imageRepository string, kubernetesVersionStr string) (string, }...) } else if v1_14plus(kubernetesVersion) { - podInfraContainerImage = imageRepository + "pause:3.1" images = append(images, []string{ podInfraContainerImage, imageRepository + "k8s-dns-kube-dns" + ArchTag(true) + "1.14.13", @@ -91,7 +113,6 @@ func CachedImages(imageRepository string, kubernetesVersionStr string) (string, }...) } else if v1_13(kubernetesVersion) { - podInfraContainerImage = imageRepository + "pause" + ArchTag(false) + "3.1" images = append(images, []string{ podInfraContainerImage, imageRepository + "k8s-dns-kube-dns" + ArchTag(true) + "1.14.8", @@ -102,7 +123,6 @@ func CachedImages(imageRepository string, kubernetesVersionStr string) (string, }...) } else if v1_12(kubernetesVersion) { - podInfraContainerImage = imageRepository + "pause:3.1" images = append(images, []string{ podInfraContainerImage, imageRepository + "k8s-dns-kube-dns" + ArchTag(true) + "1.14.8", @@ -113,7 +133,6 @@ func CachedImages(imageRepository string, kubernetesVersionStr string) (string, }...) } else if v1_11(kubernetesVersion) { - podInfraContainerImage = imageRepository + "pause" + ArchTag(false) + "3.1" images = append(images, []string{ podInfraContainerImage, imageRepository + "k8s-dns-kube-dns" + ArchTag(true) + "1.14.8", @@ -122,8 +141,6 @@ func CachedImages(imageRepository string, kubernetesVersionStr string) (string, imageRepository + "etcd" + ArchTag(true) + "3.2.18", imageRepository + "coredns:1.1.3", }...) - } else { - podInfraContainerImage = imageRepository + "pause" + ArchTag(false) + "3.0" } images = append(images, []string{ @@ -132,7 +149,46 @@ func CachedImages(imageRepository string, kubernetesVersionStr string) (string, minikubeRepository + "storage-provisioner" + ArchTag(false) + "v1.8.1", }...) - return podInfraContainerImage, images + return images +} + +// PauseImage returns the image name for pause image (for pod infra) +func PauseImage(imageRepositoryStr string, kubernetesVersionStr string) string { + imageRepository := getImageRepository(imageRepositoryStr) + + v1_16plus := semver.MustParseRange(">=1.16.0") + v1_14plus := semver.MustParseRange(">=1.14.0 <1.16.0") + v1_13 := semver.MustParseRange(">=1.13.0 <1.14.0") + v1_12 := semver.MustParseRange(">=1.12.0 <1.13.0") + v1_11 := semver.MustParseRange(">=1.11.0 <1.12.0") + + kubernetesVersion, err := semver.Make(strings.TrimPrefix(kubernetesVersionStr, minikubeVersion.VersionPrefix)) + if err != nil { + glog.Errorln("Error parsing version semver: ", err) + } + + var podInfraContainerImage string + switch { + case v1_16plus(kubernetesVersion): + podInfraContainerImage = imageRepository + "pause:3.1" + + case v1_14plus(kubernetesVersion): + podInfraContainerImage = imageRepository + "pause:3.1" + + case v1_13(kubernetesVersion): + podInfraContainerImage = imageRepository + "pause" + ArchTag(false) + "3.1" + + case v1_12(kubernetesVersion): + podInfraContainerImage = imageRepository + "pause:3.1" + + case v1_11(kubernetesVersion): + podInfraContainerImage = imageRepository + "pause" + ArchTag(false) + "3.1" + + default: + podInfraContainerImage = imageRepository + "pause" + ArchTag(false) + "3.0" + } + + return podInfraContainerImage } // ArchTag returns the archtag for images diff --git a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go index af46b3b24c..366a750795 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go @@ -19,6 +19,7 @@ package kubeadm import ( "bytes" "crypto/tls" + "os/exec" "fmt" "net" @@ -36,6 +37,7 @@ import ( "github.com/golang/glog" "github.com/pkg/errors" "golang.org/x/sync/errgroup" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" @@ -50,6 +52,7 @@ import ( "k8s.io/minikube/pkg/minikube/cruntime" "k8s.io/minikube/pkg/minikube/machine" "k8s.io/minikube/pkg/minikube/out" + "k8s.io/minikube/pkg/minikube/vmpath" "k8s.io/minikube/pkg/util" "k8s.io/minikube/pkg/util/retry" ) @@ -57,8 +60,17 @@ import ( // enum to differentiate kubeadm command line parameters from kubeadm config file parameters (see the // KubeadmExtraArgsWhitelist variable below for more info) const ( - KubeadmCmdParam = iota - KubeadmConfigParam = iota + KubeadmCmdParam = iota + KubeadmConfigParam = iota + defaultCNIConfigPath = "/etc/cni/net.d/k8s.conf" + kubeletServiceFile = "/lib/systemd/system/kubelet.service" + kubeletSystemdConfFile = "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf" + AllPods = "ALL_PODS" +) + +const ( + // Container runtimes + remoteContainerRuntime = "remote" ) // KubeadmExtraArgsWhitelist is a whitelist of supported kubeadm params that can be supplied to kubeadm through @@ -100,7 +112,7 @@ var PodsByLayer = []pod{ } // yamlConfigPath is the path to the kubeadm configuration -var yamlConfigPath = path.Join(constants.GuestEphemeralDir, "kubeadm.yaml") +var yamlConfigPath = path.Join(vmpath.GuestEphemeralDir, "kubeadm.yaml") // SkipAdditionalPreflights are additional preflights we skip depending on the runtime in use. var SkipAdditionalPreflights = map[string][]string{} @@ -127,12 +139,11 @@ func NewKubeadmBootstrapper(api libmachine.API) (*Bootstrapper, error) { // GetKubeletStatus returns the kubelet status func (k *Bootstrapper) GetKubeletStatus() (string, error) { - statusCmd := `sudo systemctl is-active kubelet` - status, err := k.c.CombinedOutput(statusCmd) + rr, err := k.c.RunCmd(exec.Command("sudo", "systemctl", "is-active", "kubelet")) if err != nil { - return "", errors.Wrap(err, "getting status") + return "", errors.Wrapf(err, "getting kublet status. command: %q", rr.Command()) } - s := strings.TrimSpace(status) + s := strings.TrimSpace(rr.Stdout.String()) switch s { case "active": return state.Running.String(), nil @@ -168,7 +179,7 @@ func (k *Bootstrapper) GetAPIServerStatus(ip net.IP, apiserverPort int) (string, // LogCommands returns a map of log type to a command which will display that log. func (k *Bootstrapper) LogCommands(o bootstrapper.LogOptions) map[string]string { var kubelet strings.Builder - kubelet.WriteString("journalctl -u kubelet") + kubelet.WriteString("sudo journalctl -u kubelet") if o.Lines > 0 { kubelet.WriteString(fmt.Sprintf(" -n %d", o.Lines)) } @@ -190,7 +201,7 @@ func (k *Bootstrapper) LogCommands(o bootstrapper.LogOptions) map[string]string } } -// createFlagsFromExtraArgs converts kubeadm extra args into flags to be supplied from the commad linne +// createFlagsFromExtraArgs converts kubeadm extra args into flags to be supplied from the command linne func createFlagsFromExtraArgs(extraOptions config.ExtraOptionSlice) string { kubeadmExtraOpts := extraOptions.AsMap().Get(Kubeadm) @@ -207,22 +218,22 @@ func createFlagsFromExtraArgs(extraOptions config.ExtraOptionSlice) string { // etcdDataDir is where etcd data is stored. func etcdDataDir() string { - return path.Join(constants.GuestPersistentDir, "etcd") + return path.Join(vmpath.GuestPersistentDir, "etcd") } // createCompatSymlinks creates compatibility symlinks to transition running services to new directory structures func (k *Bootstrapper) createCompatSymlinks() error { legacyEtcd := "/data/minikube" - if err := k.c.Run(fmt.Sprintf("sudo test -d %s", legacyEtcd)); err != nil { - glog.Infof("%s check failed, skipping compat symlinks: %v", legacyEtcd, err) + + if _, err := k.c.RunCmd(exec.Command("sudo", "test", "-d", legacyEtcd)); err != nil { + glog.Infof("%s skipping compat symlinks: %v", legacyEtcd, err) return nil } - glog.Infof("Found %s, creating compatibility symlinks ...", legacyEtcd) - cmd := fmt.Sprintf("sudo ln -s %s %s", legacyEtcd, etcdDataDir()) - out, err := k.c.CombinedOutput(cmd) - if err != nil { - return errors.Wrapf(err, "cmd failed: %s\n%s\n", cmd, out) + + c := exec.Command("sudo", "ln", "-s", legacyEtcd, etcdDataDir()) + if rr, err := k.c.RunCmd(c); err != nil { + return errors.Wrapf(err, "create symlink failed: %s", rr.Command()) } return nil } @@ -247,8 +258,8 @@ func (k *Bootstrapper) StartCluster(k8s config.KubernetesConfig) error { } ignore := []string{ - fmt.Sprintf("DirAvailable-%s", strings.Replace(constants.GuestManifestsDir, "/", "-", -1)), - fmt.Sprintf("DirAvailable-%s", strings.Replace(constants.GuestPersistentDir, "/", "-", -1)), + fmt.Sprintf("DirAvailable-%s", strings.Replace(vmpath.GuestManifestsDir, "/", "-", -1)), + fmt.Sprintf("DirAvailable-%s", strings.Replace(vmpath.GuestPersistentDir, "/", "-", -1)), "FileAvailable--etc-kubernetes-manifests-kube-scheduler.yaml", "FileAvailable--etc-kubernetes-manifests-kube-apiserver.yaml", "FileAvailable--etc-kubernetes-manifests-kube-controller-manager.yaml", @@ -264,11 +275,9 @@ func (k *Bootstrapper) StartCluster(k8s config.KubernetesConfig) error { ignore = append(ignore, "SystemVerification") } - cmd := fmt.Sprintf("%s init --config %s %s --ignore-preflight-errors=%s", - invokeKubeadm(k8s.KubernetesVersion), yamlConfigPath, extraFlags, strings.Join(ignore, ",")) - out, err := k.c.CombinedOutput(cmd) - if err != nil { - return errors.Wrapf(err, "cmd failed: %s\n%s\n", cmd, out) + c := exec.Command("/bin/bash", "-c", fmt.Sprintf("%s init --config %s %s --ignore-preflight-errors=%s", invokeKubeadm(k8s.KubernetesVersion), yamlConfigPath, extraFlags, strings.Join(ignore, ","))) + if rr, err := k.c.RunCmd(c); err != nil { + return errors.Wrapf(err, "init failed. cmd: %q", rr.Command()) } glog.Infof("Configuring cluster permissions ...") @@ -293,22 +302,23 @@ func (k *Bootstrapper) StartCluster(k8s config.KubernetesConfig) error { // adjustResourceLimits makes fine adjustments to pod resources that aren't possible via kubeadm config. func (k *Bootstrapper) adjustResourceLimits() error { - score, err := k.c.CombinedOutput("cat /proc/$(pgrep kube-apiserver)/oom_adj") + rr, err := k.c.RunCmd(exec.Command("/bin/bash", "-c", "cat /proc/$(pgrep kube-apiserver)/oom_adj")) if err != nil { - return errors.Wrap(err, "oom_adj check") + return errors.Wrapf(err, "oom_adj check cmd %s. ", rr.Command()) } - glog.Infof("apiserver oom_adj: %s", score) + glog.Infof("apiserver oom_adj: %s", rr.Stdout.String()) // oom_adj is already a negative number - if strings.HasPrefix(score, "-") { + if strings.HasPrefix(rr.Stdout.String(), "-") { return nil } glog.Infof("adjusting apiserver oom_adj to -10") // Prevent the apiserver from OOM'ing before other pods, as it is our gateway into the cluster. // It'd be preferable to do this via Kubernetes, but kubeadm doesn't have a way to set pod QoS. - if err := k.c.Run("echo -10 | sudo tee /proc/$(pgrep kube-apiserver)/oom_adj"); err != nil { - return errors.Wrap(err, "oom_adj adjust") + if _, err = k.c.RunCmd(exec.Command("/bin/bash", "-c", "echo -10 | sudo tee /proc/$(pgrep kube-apiserver)/oom_adj")); err != nil { + return errors.Wrap(err, fmt.Sprintf("oom_adj adjust")) } + return nil } @@ -343,7 +353,7 @@ func addAddons(files *[]assets.CopyableFile, data interface{}) error { // client returns a Kubernetes client to use to speak to a kubeadm launched apiserver func (k *Bootstrapper) client(k8s config.KubernetesConfig) (*kubernetes.Clientset, error) { - // Catch case if WaitCluster was called with a stale ~/.kube/config + // Catch case if WaitForPods was called with a stale ~/.kube/config config, err := kapi.ClientConfig(k.contextName) if err != nil { return nil, errors.Wrap(err, "client config") @@ -358,8 +368,8 @@ func (k *Bootstrapper) client(k8s config.KubernetesConfig) (*kubernetes.Clientse return kubernetes.NewForConfig(config) } -// WaitCluster blocks until Kubernetes appears to be healthy. -func (k *Bootstrapper) WaitCluster(k8s config.KubernetesConfig, timeout time.Duration) error { +// WaitForPods blocks until pods specified in podsToWaitFor appear to be healthy. +func (k *Bootstrapper) WaitForPods(k8s config.KubernetesConfig, timeout time.Duration, podsToWaitFor []string) error { // Do not wait for "k8s-app" pods in the case of CNI, as they are managed // by a CNI plugin which is usually started after minikube has been brought // up. Otherwise, minikube won't start, as "k8s-app" pods are not ready. @@ -368,9 +378,12 @@ func (k *Bootstrapper) WaitCluster(k8s config.KubernetesConfig, timeout time.Dur // Wait until the apiserver can answer queries properly. We don't care if the apiserver // pod shows up as registered, but need the webserver for all subsequent queries. - out.String(" apiserver") - if err := k.waitForAPIServer(k8s); err != nil { - return errors.Wrap(err, "waiting for apiserver") + + if shouldWaitForPod("apiserver", podsToWaitFor) { + out.String(" apiserver") + if err := k.waitForAPIServer(k8s); err != nil { + return errors.Wrap(err, "waiting for apiserver") + } } client, err := k.client(k8s) @@ -382,6 +395,9 @@ func (k *Bootstrapper) WaitCluster(k8s config.KubernetesConfig, timeout time.Dur if componentsOnly && p.key != "component" { // skip component check if network plugin is cni continue } + if !shouldWaitForPod(p.name, podsToWaitFor) { + continue + } out.String(" %s", p.name) selector := labels.SelectorFromSet(labels.Set(map[string]string{p.key: p.value})) if err := kapi.WaitForPodsWithLabelRunning(client, "kube-system", selector, timeout); err != nil { @@ -392,6 +408,29 @@ func (k *Bootstrapper) WaitCluster(k8s config.KubernetesConfig, timeout time.Dur return nil } +// shouldWaitForPod returns true if: +// 1. podsToWaitFor is nil +// 2. name is in podsToWaitFor +// 3. ALL_PODS is in podsToWaitFor +// else, return false +func shouldWaitForPod(name string, podsToWaitFor []string) bool { + if podsToWaitFor == nil { + return true + } + if len(podsToWaitFor) == 0 { + return false + } + for _, p := range podsToWaitFor { + if p == AllPods { + return true + } + if p == name { + return true + } + } + return false +} + // RestartCluster restarts the Kubernetes cluster configured by kubeadm func (k *Bootstrapper) RestartCluster(k8s config.KubernetesConfig) error { glog.Infof("RestartCluster start") @@ -425,18 +464,20 @@ func (k *Bootstrapper) RestartCluster(k8s config.KubernetesConfig) error { } // Run commands one at a time so that it is easier to root cause failures. - for _, cmd := range cmds { - if err := k.c.Run(cmd); err != nil { - return errors.Wrapf(err, "running cmd: %s", cmd) + for _, c := range cmds { + rr, err := k.c.RunCmd(exec.Command("/bin/bash", "-c", c)) + if err != nil { + return errors.Wrapf(err, "running cmd: %s", rr.Command()) } } if err := k.waitForAPIServer(k8s); err != nil { return errors.Wrap(err, "waiting for apiserver") } + // restart the proxy and coredns - if err := k.c.Run(fmt.Sprintf("%s phase addon all --config %s", baseCmd, yamlConfigPath)); err != nil { - return errors.Wrapf(err, "addon phase") + if rr, err := k.c.RunCmd(exec.Command("/bin/bash", "-c", fmt.Sprintf("%s phase addon all --config %s", baseCmd, yamlConfigPath))); err != nil { + return errors.Wrapf(err, fmt.Sprintf("addon phase cmd:%q", rr.Command())) } if err := k.adjustResourceLimits(); err != nil { @@ -456,9 +497,9 @@ func (k *Bootstrapper) waitForAPIServer(k8s config.KubernetesConfig) error { // To give a better error message, first check for process existence via ssh // Needs minutes in case the image isn't cached (such as with v1.10.x) err := wait.PollImmediate(time.Millisecond*300, time.Minute*3, func() (bool, error) { - ierr := k.c.Run(`sudo pgrep kube-apiserver`) + rr, ierr := k.c.RunCmd(exec.Command("sudo", "pgrep", "kube-apiserver")) if ierr != nil { - glog.Warningf("pgrep apiserver: %v", ierr) + glog.Warningf("pgrep apiserver: %v cmd: %s", ierr, rr.Command()) return false, nil } return true, nil @@ -468,6 +509,7 @@ func (k *Bootstrapper) waitForAPIServer(k8s config.KubernetesConfig) error { } glog.Infof("Waiting for apiserver to port healthy status ...") + var client *kubernetes.Clientset f := func() (bool, error) { status, err := k.GetAPIServerStatus(net.ParseIP(k8s.NodeIP), k8s.NodePort) glog.Infof("apiserver status: %s, err: %v", status, err) @@ -478,11 +520,24 @@ func (k *Bootstrapper) waitForAPIServer(k8s config.KubernetesConfig) error { if status != "Running" { return false, nil } - return true, nil + // Make sure apiserver pod is retrievable + if client == nil { + // We only want to get the clientset once, because this line takes ~1 second to complete + client, err = k.client(k8s) + if err != nil { + glog.Warningf("get kubernetes client: %v", err) + return false, nil + } + } + _, err = client.CoreV1().Pods("kube-system").Get("kube-apiserver-minikube", metav1.GetOptions{}) + if err != nil { + return false, nil + } + + return true, nil // TODO: Check apiserver/kubelet logs for fatal errors so that users don't // need to wait minutes to find out their flag didn't work. - } err = wait.PollImmediate(kconst.APICallRetryInterval, 2*kconst.DefaultControlPlaneTimeout, f) return err @@ -499,9 +554,9 @@ func (k *Bootstrapper) DeleteCluster(k8s config.KubernetesConfig) error { if version.LT(semver.MustParse("1.11.0")) { cmd = fmt.Sprintf("%s reset", invokeKubeadm(k8s.KubernetesVersion)) } - out, err := k.c.CombinedOutput(cmd) - if err != nil { - return errors.Wrapf(err, "kubeadm reset: %s\n%s\n", cmd, out) + + if rr, err := k.c.RunCmd(exec.Command("/bin/bash", "-c", cmd)); err != nil { + return errors.Wrapf(err, "kubeadm reset: cmd: %q", rr.Command()) } return nil @@ -517,9 +572,9 @@ func (k *Bootstrapper) PullImages(k8s config.KubernetesConfig) error { return fmt.Errorf("pull command is not supported by kubeadm v%s", version) } - cmd := fmt.Sprintf("%s config images pull --config %s", invokeKubeadm(k8s.KubernetesVersion), yamlConfigPath) - if err := k.c.Run(cmd); err != nil { - return errors.Wrapf(err, "running cmd: %s", cmd) + rr, err := k.c.RunCmd(exec.Command("/bin/bash", "-c", fmt.Sprintf("%s config images pull --config %s", invokeKubeadm(k8s.KubernetesVersion), yamlConfigPath))) + if err != nil { + return errors.Wrapf(err, "running cmd: %q", rr.Command()) } return nil } @@ -548,10 +603,13 @@ func NewKubeletConfig(k8s config.KubernetesConfig, r cruntime.Manager) ([]byte, if k8s.NetworkPlugin != "" { extraOpts["network-plugin"] = k8s.NetworkPlugin } + if _, ok := extraOpts["node-ip"]; !ok { + extraOpts["node-ip"] = k8s.NodeIP + } - podInfraContainerImage, _ := images.CachedImages(k8s.ImageRepository, k8s.KubernetesVersion) - if _, ok := extraOpts["pod-infra-container-image"]; !ok && k8s.ImageRepository != "" && podInfraContainerImage != "" { - extraOpts["pod-infra-container-image"] = podInfraContainerImage + pauseImage := images.PauseImage(k8s.ImageRepository, k8s.KubernetesVersion) + if _, ok := extraOpts["pod-infra-container-image"]; !ok && k8s.ImageRepository != "" && pauseImage != "" && k8s.ContainerRuntime != remoteContainerRuntime { + extraOpts["pod-infra-container-image"] = pauseImage } // parses a map of the feature gates for kubelet @@ -583,7 +641,7 @@ func NewKubeletConfig(k8s config.KubernetesConfig, r cruntime.Manager) ([]byte, // UpdateCluster updates the cluster func (k *Bootstrapper) UpdateCluster(cfg config.KubernetesConfig) error { - _, images := images.CachedImages(cfg.ImageRepository, cfg.KubernetesVersion) + images := images.CachedImages(cfg.ImageRepository, cfg.KubernetesVersion) if cfg.ShouldLoadCachedImages { if err := machine.LoadImages(k.c, images, constants.ImageCacheDir); err != nil { out.FailureT("Unable to load cached images: {{.error}}", out.V{"error": err}) @@ -610,11 +668,12 @@ func (k *Bootstrapper) UpdateCluster(cfg config.KubernetesConfig) error { glog.Infof("kubelet %s config:\n%s", cfg.KubernetesVersion, kubeletCfg) + stopCmd := exec.Command("/bin/bash", "-c", "pgrep kubelet && sudo systemctl stop kubelet") // stop kubelet to avoid "Text File Busy" error - err = k.c.Run(`pgrep kubelet && sudo systemctl stop kubelet`) - if err != nil { - glog.Warningf("unable to stop kubelet: %s", err) + if rr, err := k.c.RunCmd(stopCmd); err != nil { + glog.Warningf("unable to stop kubelet: %s command: %q output: %q", err, rr.Command(), rr.Output()) } + if err := transferBinaries(cfg, k.c); err != nil { return errors.Wrap(err, "downloading binaries") } @@ -628,7 +687,7 @@ func (k *Bootstrapper) UpdateCluster(cfg config.KubernetesConfig) error { } } - if err := k.c.Run(`sudo systemctl daemon-reload && sudo systemctl start kubelet`); err != nil { + if _, err := k.c.RunCmd(exec.Command("/bin/bash", "-c", "sudo systemctl daemon-reload && sudo systemctl start kubelet")); err != nil { return errors.Wrap(err, "starting kubelet") } return nil @@ -686,13 +745,14 @@ func generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) ([]byte, er KubernetesVersion string EtcdDataDir string NodeName string + DNSDomain string CRISocket string ImageRepository string ExtraArgs []ComponentExtraArgs FeatureArgs map[string]bool NoTaintMaster bool }{ - CertDir: constants.GuestCertsDir, + CertDir: vmpath.GuestCertsDir, ServiceCIDR: util.DefaultServiceCIDR, PodSubnet: k8s.ExtraOptions.Get("pod-network-cidr", Kubeadm), AdvertiseAddress: k8s.NodeIP, @@ -705,6 +765,7 @@ func generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) ([]byte, er ExtraArgs: extraComponentConfig, FeatureArgs: kubeadmFeatureArgs, NoTaintMaster: false, // That does not work with k8s 1.12+ + DNSDomain: k8s.DNSDomain, } if k8s.ServiceCIDR != "" { @@ -742,21 +803,21 @@ func NewKubeletService(cfg config.KubernetesConfig) ([]byte, error) { func configFiles(cfg config.KubernetesConfig, kubeadm []byte, kubelet []byte, kubeletSvc []byte) []assets.CopyableFile { fs := []assets.CopyableFile{ assets.NewMemoryAssetTarget(kubeadm, yamlConfigPath, "0640"), - assets.NewMemoryAssetTarget(kubelet, constants.KubeletSystemdConfFile, "0640"), - assets.NewMemoryAssetTarget(kubeletSvc, constants.KubeletServiceFile, "0640"), + assets.NewMemoryAssetTarget(kubelet, kubeletSystemdConfFile, "0644"), + assets.NewMemoryAssetTarget(kubeletSvc, kubeletServiceFile, "0644"), } // Copy the default CNI config (k8s.conf), so that kubelet can successfully // start a Pod in the case a user hasn't manually installed any CNI plugin // and minikube was started with "--extra-config=kubelet.network-plugin=cni". if cfg.EnableDefaultCNI { - fs = append(fs, assets.NewMemoryAssetTarget([]byte(defaultCNIConfig), constants.DefaultCNIConfigPath, "0644")) + fs = append(fs, assets.NewMemoryAssetTarget([]byte(defaultCNIConfig), defaultCNIConfigPath, "0644")) } return fs } // binDir returns the persistent path binaries are stored in func binRoot(version string) string { - return path.Join(constants.GuestPersistentDir, "binaries", version) + return path.Join(vmpath.GuestPersistentDir, "binaries", version) } // invokeKubeadm returns the invocation command for Kubeadm diff --git a/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go index 7822f053ed..fefd6a75e3 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go @@ -48,7 +48,7 @@ Wants=docker.socket [Service] ExecStart= -ExecStart=/var/lib/minikube/binaries/v1.11.10/kubelet --allow-privileged=true --authorization-mode=Webhook --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --cadvisor-port=0 --cgroup-driver=cgroupfs --client-ca-file=/var/lib/minikube/certs/ca.crt --cluster-dns=10.96.0.10 --cluster-domain=cluster.local --container-runtime=docker --fail-swap-on=false --hostname-override=minikube --kubeconfig=/etc/kubernetes/kubelet.conf --pod-manifest-path=/etc/kubernetes/manifests +ExecStart=/var/lib/minikube/binaries/v1.11.10/kubelet --allow-privileged=true --authorization-mode=Webhook --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --cadvisor-port=0 --cgroup-driver=cgroupfs --client-ca-file=/var/lib/minikube/certs/ca.crt --cluster-dns=10.96.0.10 --cluster-domain=cluster.local --config=/var/lib/kubelet/config.yaml --container-runtime=docker --fail-swap-on=false --hostname-override=minikube --kubeconfig=/etc/kubernetes/kubelet.conf --node-ip=192.168.1.100 --pod-manifest-path=/etc/kubernetes/manifests [Install] `, @@ -66,7 +66,7 @@ Wants=crio.service [Service] ExecStart= -ExecStart=/var/lib/minikube/binaries/v1.16.0/kubelet --authorization-mode=Webhook --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --cgroup-driver=cgroupfs --client-ca-file=/var/lib/minikube/certs/ca.crt --cluster-dns=10.96.0.10 --cluster-domain=cluster.local --container-runtime=remote --container-runtime-endpoint=/var/run/crio/crio.sock --fail-swap-on=false --hostname-override=minikube --image-service-endpoint=/var/run/crio/crio.sock --kubeconfig=/etc/kubernetes/kubelet.conf --pod-manifest-path=/etc/kubernetes/manifests --runtime-request-timeout=15m +ExecStart=/var/lib/minikube/binaries/v1.16.2/kubelet --authorization-mode=Webhook --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --cgroup-driver=cgroupfs --client-ca-file=/var/lib/minikube/certs/ca.crt --cluster-dns=10.96.0.10 --cluster-domain=cluster.local --config=/var/lib/kubelet/config.yaml --container-runtime=remote --container-runtime-endpoint=/var/run/crio/crio.sock --fail-swap-on=false --hostname-override=minikube --image-service-endpoint=/var/run/crio/crio.sock --kubeconfig=/etc/kubernetes/kubelet.conf --node-ip=192.168.1.100 --pod-manifest-path=/etc/kubernetes/manifests --runtime-request-timeout=15m [Install] `, @@ -84,7 +84,32 @@ Wants=containerd.service [Service] ExecStart= -ExecStart=/var/lib/minikube/binaries/v1.16.0/kubelet --authorization-mode=Webhook --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --cgroup-driver=cgroupfs --client-ca-file=/var/lib/minikube/certs/ca.crt --cluster-dns=10.96.0.10 --cluster-domain=cluster.local --container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --fail-swap-on=false --hostname-override=minikube --image-service-endpoint=unix:///run/containerd/containerd.sock --kubeconfig=/etc/kubernetes/kubelet.conf --pod-manifest-path=/etc/kubernetes/manifests --runtime-request-timeout=15m +ExecStart=/var/lib/minikube/binaries/v1.16.2/kubelet --authorization-mode=Webhook --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --cgroup-driver=cgroupfs --client-ca-file=/var/lib/minikube/certs/ca.crt --cluster-dns=10.96.0.10 --cluster-domain=cluster.local --config=/var/lib/kubelet/config.yaml --container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --fail-swap-on=false --hostname-override=minikube --image-service-endpoint=unix:///run/containerd/containerd.sock --kubeconfig=/etc/kubernetes/kubelet.conf --node-ip=192.168.1.100 --pod-manifest-path=/etc/kubernetes/manifests --runtime-request-timeout=15m + +[Install] +`, + }, + { + description: "default containerd runtime", + cfg: config.KubernetesConfig{ + NodeIP: "192.168.1.100", + KubernetesVersion: constants.DefaultKubernetesVersion, + NodeName: "minikube", + ContainerRuntime: "containerd", + ExtraOptions: config.ExtraOptionSlice{ + config.ExtraOption{ + Component: Kubelet, + Key: "node-ip", + Value: "192.168.1.200", + }, + }, + }, + expected: `[Unit] +Wants=containerd.service + +[Service] +ExecStart= +ExecStart=/var/lib/minikube/binaries/v1.16.2/kubelet --authorization-mode=Webhook --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --cgroup-driver=cgroupfs --client-ca-file=/var/lib/minikube/certs/ca.crt --cluster-dns=10.96.0.10 --cluster-domain=cluster.local --config=/var/lib/kubelet/config.yaml --container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --fail-swap-on=false --hostname-override=minikube --image-service-endpoint=unix:///run/containerd/containerd.sock --kubeconfig=/etc/kubernetes/kubelet.conf --node-ip=192.168.1.200 --pod-manifest-path=/etc/kubernetes/manifests --runtime-request-timeout=15m [Install] `, @@ -103,7 +128,7 @@ Wants=docker.socket [Service] ExecStart= -ExecStart=/var/lib/minikube/binaries/v1.16.0/kubelet --authorization-mode=Webhook --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --cgroup-driver=cgroupfs --client-ca-file=/var/lib/minikube/certs/ca.crt --cluster-dns=10.96.0.10 --cluster-domain=cluster.local --container-runtime=docker --fail-swap-on=false --hostname-override=minikube --kubeconfig=/etc/kubernetes/kubelet.conf --pod-infra-container-image=docker-proxy-image.io/google_containers/pause:3.1 --pod-manifest-path=/etc/kubernetes/manifests +ExecStart=/var/lib/minikube/binaries/v1.16.2/kubelet --authorization-mode=Webhook --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --cgroup-driver=cgroupfs --client-ca-file=/var/lib/minikube/certs/ca.crt --cluster-dns=10.96.0.10 --cluster-domain=cluster.local --config=/var/lib/kubelet/config.yaml --container-runtime=docker --fail-swap-on=false --hostname-override=minikube --kubeconfig=/etc/kubernetes/kubelet.conf --node-ip=192.168.1.100 --pod-infra-container-image=docker-proxy-image.io/google_containers/pause:3.1 --pod-manifest-path=/etc/kubernetes/manifests [Install] `, @@ -210,6 +235,67 @@ func recentReleases() ([]string, error) { return versions, nil } +/** +Need a separate test function to test the DNS server IP +as v1.11 yaml file is very different compared to v1.12+. +This test case has only 1 thing to test and that is the +nnetworking/dnsDomain value +*/ +func TestGenerateConfigDNS(t *testing.T) { + versions := []string{"v1.16", "v1.15", "v1.14", "v1.13", "v1.12"} + tests := []struct { + name string + runtime string + shouldErr bool + cfg config.KubernetesConfig + }{ + {"dns", "docker", false, config.KubernetesConfig{DNSDomain: "1.1.1.1"}}, + } + for _, version := range versions { + for _, tc := range tests { + runtime, err := cruntime.New(cruntime.Config{Type: tc.runtime}) + if err != nil { + t.Fatalf("runtime: %v", err) + } + tname := tc.name + "_" + version + t.Run(tname, func(t *testing.T) { + cfg := tc.cfg + cfg.NodeIP = "1.1.1.1" + cfg.NodeName = "mk" + cfg.KubernetesVersion = version + ".0" + + got, err := generateConfig(cfg, runtime) + if err != nil && !tc.shouldErr { + t.Fatalf("got unexpected error generating config: %v", err) + } + if err == nil && tc.shouldErr { + t.Fatalf("expected error but got none, config: %s", got) + } + if tc.shouldErr { + return + } + expected, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s/%s.yaml", version, tc.name)) + if err != nil { + t.Fatalf("unable to read testdata: %v", err) + } + diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(string(expected)), + B: difflib.SplitLines(string(got)), + FromFile: "Expected", + ToFile: "Got", + Context: 1, + }) + if err != nil { + t.Fatalf("diff error: %v", err) + } + if diff != "" { + t.Errorf("unexpected diff:\n%s\n===== [RAW OUTPUT] =====\n%s", diff, got) + } + }) + } + } +} + func TestGenerateConfig(t *testing.T) { extraOpts := getExtraOpts() extraOptsPodCidr := getExtraOptsPodCidr() @@ -277,3 +363,45 @@ func TestGenerateConfig(t *testing.T) { } } } + +func TestShouldWaitForPod(t *testing.T) { + tests := []struct { + description string + pod string + podsToWaitFor []string + expected bool + }{ + { + description: "pods to wait for is nil", + pod: "apiserver", + expected: true, + }, { + description: "pods to wait for is empty", + pod: "apiserver", + podsToWaitFor: []string{}, + }, { + description: "pod is in podsToWaitFor", + pod: "apiserver", + podsToWaitFor: []string{"etcd", "apiserver"}, + expected: true, + }, { + description: "pod is not in podsToWaitFor", + pod: "apiserver", + podsToWaitFor: []string{"etcd", "gvisor"}, + }, { + description: "wait for all pods", + pod: "apiserver", + podsToWaitFor: []string{"ALL_PODS"}, + expected: true, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + actual := shouldWaitForPod(test.pod, test.podsToWaitFor) + if actual != test.expected { + t.Fatalf("unexpected diff: got %t, expected %t", actual, test.expected) + } + }) + } +} diff --git a/pkg/minikube/bootstrapper/kubeadm/templates.go b/pkg/minikube/bootstrapper/kubeadm/templates.go index dd1bfe6590..5f5325c152 100644 --- a/pkg/minikube/bootstrapper/kubeadm/templates.go +++ b/pkg/minikube/bootstrapper/kubeadm/templates.go @@ -84,7 +84,7 @@ etcd: dataDir: {{.EtcdDataDir}} kubernetesVersion: {{.KubernetesVersion}} networking: - dnsDomain: cluster.local + dnsDomain: {{if .DNSDomain}}{{.DNSDomain}}{{else}}cluster.local{{end}} podSubnet: {{if .PodSubnet}}{{.PodSubnet}}{{else}}""{{end}} serviceSubnet: {{.ServiceCIDR}} --- @@ -138,7 +138,7 @@ etcd: dataDir: {{.EtcdDataDir}} kubernetesVersion: {{.KubernetesVersion}} networking: - dnsDomain: cluster.local + dnsDomain: {{if .DNSDomain}}{{.DNSDomain}}{{else}}cluster.local{{end}} podSubnet: "" serviceSubnet: {{.ServiceCIDR}} --- @@ -151,7 +151,7 @@ evictionHard: imagefs.available: "0%" `)) -// kubeletSystemdTemplate hosts the override kubelet flags, written to constants.KubeletSystemdConfFile +// kubeletSystemdTemplate hosts the override kubelet flags, written to kubeletSystemdConfFile var kubeletSystemdTemplate = template.Must(template.New("kubeletSystemdTemplate").Parse(`[Unit] {{if or (eq .ContainerRuntime "cri-o") (eq .ContainerRuntime "cri")}}Wants=crio.service{{else if eq .ContainerRuntime "containerd"}}Wants=containerd.service{{else}}Wants=docker.socket{{end}} @@ -162,7 +162,7 @@ ExecStart={{.KubeletPath}}{{if .ExtraOptions}} {{.ExtraOptions}}{{end}} [Install] `)) -// kubeletServiceTemplate is the base kubelet systemd template, written to constanst.KubeletServiceFile +// kubeletServiceTemplate is the base kubelet systemd template, written to kubeletServiceFile var kubeletServiceTemplate = template.Must(template.New("kubeletServiceTemplate").Parse(`[Unit] Description=kubelet: The Kubernetes Node Agent Documentation=http://kubernetes.io/docs/ diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/v1.12/dns.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/v1.12/dns.yaml new file mode 100644 index 0000000000..a4a813404c --- /dev/null +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/v1.12/dns.yaml @@ -0,0 +1,39 @@ +apiVersion: kubeadm.k8s.io/v1alpha3 +kind: InitConfiguration +apiEndpoint: + advertiseAddress: 1.1.1.1 + bindPort: 8443 +bootstrapTokens: + - groups: + - system:bootstrappers:kubeadm:default-node-token + ttl: 24h0m0s + usages: + - signing + - authentication +nodeRegistration: + criSocket: /var/run/dockershim.sock + name: mk + taints: [] +--- +apiVersion: kubeadm.k8s.io/v1alpha3 +kind: ClusterConfiguration +apiServerExtraArgs: + enable-admission-plugins: "Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" +certificatesDir: /var/lib/minikube/certs +clusterName: kubernetes +controlPlaneEndpoint: localhost:8443 +etcd: + local: + dataDir: /var/lib/minikube/etcd +kubernetesVersion: v1.12.0 +networking: + dnsDomain: 1.1.1.1 + podSubnet: "" + serviceSubnet: 10.96.0.0/12 +--- +apiVersion: kubelet.config.k8s.io/v1beta1 +kind: KubeletConfiguration +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/v1.13/dns.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/v1.13/dns.yaml new file mode 100644 index 0000000000..cdf2a0405f --- /dev/null +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/v1.13/dns.yaml @@ -0,0 +1,39 @@ +apiVersion: kubeadm.k8s.io/v1alpha3 +kind: InitConfiguration +apiEndpoint: + advertiseAddress: 1.1.1.1 + bindPort: 8443 +bootstrapTokens: + - groups: + - system:bootstrappers:kubeadm:default-node-token + ttl: 24h0m0s + usages: + - signing + - authentication +nodeRegistration: + criSocket: /var/run/dockershim.sock + name: mk + taints: [] +--- +apiVersion: kubeadm.k8s.io/v1alpha3 +kind: ClusterConfiguration +apiServerExtraArgs: + enable-admission-plugins: "Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" +certificatesDir: /var/lib/minikube/certs +clusterName: kubernetes +controlPlaneEndpoint: localhost:8443 +etcd: + local: + dataDir: /var/lib/minikube/etcd +kubernetesVersion: v1.13.0 +networking: + dnsDomain: 1.1.1.1 + podSubnet: "" + serviceSubnet: 10.96.0.0/12 +--- +apiVersion: kubelet.config.k8s.io/v1beta1 +kind: KubeletConfiguration +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/v1.14/dns.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/v1.14/dns.yaml new file mode 100644 index 0000000000..93ce010c2a --- /dev/null +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/v1.14/dns.yaml @@ -0,0 +1,43 @@ +apiVersion: kubeadm.k8s.io/v1beta1 +kind: InitConfiguration +localAPIEndpoint: + advertiseAddress: 1.1.1.1 + bindPort: 8443 +bootstrapTokens: + - groups: + - system:bootstrappers:kubeadm:default-node-token + ttl: 24h0m0s + usages: + - signing + - authentication +nodeRegistration: + criSocket: /var/run/dockershim.sock + name: mk + taints: [] +--- +apiVersion: kubeadm.k8s.io/v1beta1 +kind: ClusterConfiguration +apiServer: + extraArgs: + enable-admission-plugins: "NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" +certificatesDir: /var/lib/minikube/certs +clusterName: kubernetes +controlPlaneEndpoint: localhost:8443 +dns: + type: CoreDNS +etcd: + local: + dataDir: /var/lib/minikube/etcd +kubernetesVersion: v1.14.0 +networking: + dnsDomain: 1.1.1.1 + podSubnet: "" + serviceSubnet: 10.96.0.0/12 +--- +apiVersion: kubelet.config.k8s.io/v1beta1 +kind: KubeletConfiguration +imageGCHighThresholdPercent: 100 +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/v1.15/dns.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/v1.15/dns.yaml new file mode 100644 index 0000000000..d6474d3bee --- /dev/null +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/v1.15/dns.yaml @@ -0,0 +1,43 @@ +apiVersion: kubeadm.k8s.io/v1beta1 +kind: InitConfiguration +localAPIEndpoint: + advertiseAddress: 1.1.1.1 + bindPort: 8443 +bootstrapTokens: + - groups: + - system:bootstrappers:kubeadm:default-node-token + ttl: 24h0m0s + usages: + - signing + - authentication +nodeRegistration: + criSocket: /var/run/dockershim.sock + name: mk + taints: [] +--- +apiVersion: kubeadm.k8s.io/v1beta1 +kind: ClusterConfiguration +apiServer: + extraArgs: + enable-admission-plugins: "NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" +certificatesDir: /var/lib/minikube/certs +clusterName: kubernetes +controlPlaneEndpoint: localhost:8443 +dns: + type: CoreDNS +etcd: + local: + dataDir: /var/lib/minikube/etcd +kubernetesVersion: v1.15.0 +networking: + dnsDomain: 1.1.1.1 + podSubnet: "" + serviceSubnet: 10.96.0.0/12 +--- +apiVersion: kubelet.config.k8s.io/v1beta1 +kind: KubeletConfiguration +imageGCHighThresholdPercent: 100 +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/v1.16/dns.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/v1.16/dns.yaml new file mode 100644 index 0000000000..1fd0562a55 --- /dev/null +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/v1.16/dns.yaml @@ -0,0 +1,43 @@ +apiVersion: kubeadm.k8s.io/v1beta1 +kind: InitConfiguration +localAPIEndpoint: + advertiseAddress: 1.1.1.1 + bindPort: 8443 +bootstrapTokens: + - groups: + - system:bootstrappers:kubeadm:default-node-token + ttl: 24h0m0s + usages: + - signing + - authentication +nodeRegistration: + criSocket: /var/run/dockershim.sock + name: mk + taints: [] +--- +apiVersion: kubeadm.k8s.io/v1beta1 +kind: ClusterConfiguration +apiServer: + extraArgs: + enable-admission-plugins: "NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" +certificatesDir: /var/lib/minikube/certs +clusterName: kubernetes +controlPlaneEndpoint: localhost:8443 +dns: + type: CoreDNS +etcd: + local: + dataDir: /var/lib/minikube/etcd +kubernetesVersion: v1.16.0 +networking: + dnsDomain: 1.1.1.1 + podSubnet: "" + serviceSubnet: 10.96.0.0/12 +--- +apiVersion: kubelet.config.k8s.io/v1beta1 +kind: KubeletConfiguration +imageGCHighThresholdPercent: 100 +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%" diff --git a/pkg/minikube/bootstrapper/kubeadm/versions.go b/pkg/minikube/bootstrapper/kubeadm/versions.go index 65621dd79d..82e157fd79 100644 --- a/pkg/minikube/bootstrapper/kubeadm/versions.go +++ b/pkg/minikube/bootstrapper/kubeadm/versions.go @@ -29,6 +29,7 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/features" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/vmpath" "k8s.io/minikube/pkg/util" ) @@ -206,7 +207,7 @@ var versionSpecificOpts = []config.VersionedExtraOption{ config.NewUnversionedOption(Kubelet, "hostname-override", constants.DefaultNodeName), // System pods args - config.NewUnversionedOption(Kubelet, "pod-manifest-path", constants.GuestManifestsDir), + config.NewUnversionedOption(Kubelet, "pod-manifest-path", vmpath.GuestManifestsDir), { Option: config.ExtraOption{ Component: Kubelet, @@ -216,13 +217,16 @@ var versionSpecificOpts = []config.VersionedExtraOption{ LessThanOrEqual: semver.MustParse("1.15.0-alpha.3"), }, + // Kubelet config file + config.NewUnversionedOption(Kubelet, "config", "/var/lib/kubelet/config.yaml"), + // Network args config.NewUnversionedOption(Kubelet, "cluster-dns", "10.96.0.10"), config.NewUnversionedOption(Kubelet, "cluster-domain", "cluster.local"), // Auth args config.NewUnversionedOption(Kubelet, "authorization-mode", "Webhook"), - config.NewUnversionedOption(Kubelet, "client-ca-file", path.Join(constants.GuestCertsDir, "ca.crt")), + config.NewUnversionedOption(Kubelet, "client-ca-file", path.Join(vmpath.GuestCertsDir, "ca.crt")), // Cgroup args config.NewUnversionedOption(Kubelet, "cgroup-driver", "cgroupfs"), diff --git a/pkg/minikube/bootstrapper/kubeadm/versions_test.go b/pkg/minikube/bootstrapper/kubeadm/versions_test.go index 40d46c077e..9b76841b94 100644 --- a/pkg/minikube/bootstrapper/kubeadm/versions_test.go +++ b/pkg/minikube/bootstrapper/kubeadm/versions_test.go @@ -46,7 +46,7 @@ func TestVersionIsBetween(t *testing.T) { expected: false, }, { - description: "greather than max version", + description: "greater than max version", ver: semver.MustParse("2.8.0"), gte: semver.MustParse("1.7.0"), lte: semver.MustParse("1.9.0"), diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index 0d3c6c298b..3e1a4683f0 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -43,8 +43,10 @@ import ( "github.com/shirou/gopsutil/disk" "github.com/shirou/gopsutil/mem" "github.com/spf13/viper" + cfg "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/out" @@ -78,7 +80,7 @@ func init() { // CacheISO downloads and caches ISO. func CacheISO(config cfg.MachineConfig) error { - if localDriver(config.VMDriver) { + if driver.BareMetal(config.VMDriver) { return nil } return config.Downloader.CacheMinikubeISOFromURL(config.MinikubeISO) @@ -136,14 +138,6 @@ func StartHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error) return h, nil } -// localDriver returns whether or not the driver should be considered local -func localDriver(name string) bool { - if name == constants.DriverNone || name == constants.DriverMock { - return true - } - return false -} - // configureHost handles any post-powerup configuration required func configureHost(h *host.Host, e *engine.Options) error { start := time.Now() @@ -165,7 +159,7 @@ func configureHost(h *host.Host, e *engine.Options) error { } } - if localDriver(h.Driver.DriverName()) { + if driver.BareMetal(h.Driver.DriverName()) { glog.Infof("%s is a local driver, skipping auth/time setup", h.Driver.DriverName()) return nil } @@ -251,7 +245,7 @@ func StopHost(api libmachine.API) error { } out.T(out.Stopping, `Stopping "{{.profile_name}}" in {{.driver_name}} ...`, out.V{"profile_name": cfg.GetMachineName(), "driver_name": host.DriverName}) - if host.DriverName == constants.DriverHyperv { + if host.DriverName == driver.HyperV { glog.Infof("As there are issues with stopping Hyper-V VMs using API, trying to shut down using SSH") if err := trySSHPowerOff(host); err != nil { return errors.Wrap(err, "ssh power off") @@ -274,11 +268,24 @@ func DeleteHost(api libmachine.API) error { if err != nil { return errors.Wrap(err, "load") } + + // Get the status of the host. Ensure that it exists before proceeding ahead. + status, err := GetHostStatus(api) + if err != nil { + // Warn, but proceed + out.WarningT("Unable to get the status of the {{.name}} cluster.", out.V{"name": cfg.GetMachineName()}) + } + + if status == state.None.String() { + return mcnerror.ErrHostDoesNotExist{Name: host.Name} + } + // This is slow if SSH is not responding, but HyperV hangs otherwise, See issue #2914 - if host.Driver.DriverName() == constants.DriverHyperv { + if host.Driver.DriverName() == driver.HyperV { if err := trySSHPowerOff(host); err != nil { glog.Infof("Unable to power off minikube because the host was not found.") } + out.T(out.DeletingHost, "Successfully powered off Hyper-V. minikube driver -- {{.driver}}", out.V{"driver": host.Driver.DriverName()}) } out.T(out.DeletingHost, `Deleting "{{.profile_name}}" in {{.driver_name}} ...`, out.V{"profile_name": cfg.GetMachineName(), "driver_name": host.DriverName}) @@ -411,13 +418,13 @@ func showRemoteOsRelease(driver drivers.Driver) { } func createHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error) { - if config.VMDriver == constants.DriverVmwareFusion && viper.GetBool(cfg.ShowDriverDeprecationNotification) { + if config.VMDriver == driver.VMwareFusion && viper.GetBool(cfg.ShowDriverDeprecationNotification) { out.WarningT(`The vmwarefusion driver is deprecated and support for it will be removed in a future release. Please consider switching to the new vmware unified driver, which is intended to replace the vmwarefusion driver. See https://minikube.sigs.k8s.io/docs/reference/drivers/vmware/ for more information. To disable this message, run [minikube config set ShowDriverDeprecationNotification false]`) } - if !localDriver(config.VMDriver) { + if !driver.BareMetal(config.VMDriver) { out.T(out.StartingVM, "Creating {{.driver_name}} VM (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...", out.V{"driver_name": config.VMDriver, "number_of_cpus": config.CPUs, "memory_size": config.Memory, "disk_size": config.DiskSize}) } else { info, err := getHostInfo() @@ -426,16 +433,12 @@ func createHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error } } - def, err := registry.Driver(config.VMDriver) - if err != nil { - if err == registry.ErrDriverNotFound { - return nil, fmt.Errorf("unsupported driver: %s", config.VMDriver) - } - return nil, errors.Wrap(err, "error getting driver") + def := registry.Driver(config.VMDriver) + if def.Empty() { + return nil, fmt.Errorf("unsupported/missing driver: %s", config.VMDriver) } - - driver := def.ConfigCreator(config) - data, err := json.Marshal(driver) + dd := def.Config(config) + data, err := json.Marshal(dd) if err != nil { return nil, errors.Wrap(err, "marshal") } @@ -455,7 +458,7 @@ func createHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error return nil, errors.Wrap(err, "create") } - if !localDriver(config.VMDriver) { + if !driver.BareMetal(config.VMDriver) { showRemoteOsRelease(h.Driver) // Ensure that even new VM's have proper time synchronization up front // It's 2019, and I can't believe I am still dealing with time desync as a problem. @@ -497,9 +500,9 @@ func GetHostDockerEnv(api libmachine.API) (map[string]string, error) { // GetVMHostIP gets the ip address to be used for mapping host -> VM and VM -> host func GetVMHostIP(host *host.Host) (net.IP, error) { switch host.DriverName { - case constants.DriverKvm2: + case driver.KVM2: return net.ParseIP("192.168.39.1"), nil - case constants.DriverHyperv: + case driver.HyperV: re := regexp.MustCompile(`"VSwitch": "(.*?)",`) // TODO(aprindle) Change this to deserialize the driver instead hypervVirtualSwitch := re.FindStringSubmatch(string(host.RawDriver))[1] @@ -508,8 +511,8 @@ func GetVMHostIP(host *host.Host) (net.IP, error) { return []byte{}, errors.Wrap(err, fmt.Sprintf("ip for interface (%s)", hypervVirtualSwitch)) } return ip, nil - case constants.DriverVirtualbox: - out, err := exec.Command(detectVBoxManageCmd(), "showvminfo", host.Name, "--machinereadable").Output() + case driver.VirtualBox: + out, err := exec.Command(driver.VBoxManagePath(), "showvminfo", host.Name, "--machinereadable").Output() if err != nil { return []byte{}, errors.Wrap(err, "vboxmanage") } @@ -520,9 +523,9 @@ func GetVMHostIP(host *host.Host) (net.IP, error) { return []byte{}, errors.Wrap(err, "Error getting VM/Host IP address") } return ip, nil - case constants.DriverHyperkit: + case driver.HyperKit: return net.ParseIP("192.168.64.1"), nil - case constants.DriverVmware: + case driver.VMware: vmIPString, err := host.Driver.GetIP() if err != nil { return []byte{}, errors.Wrap(err, "Error getting VM IP address") @@ -592,14 +595,15 @@ func CreateSSHShell(api libmachine.API, args []string) error { return client.Shell(args...) } -// EnsureMinikubeRunningOrExit checks that minikube has a status available and that -// the status is `Running`, otherwise it will exit -func EnsureMinikubeRunningOrExit(api libmachine.API, exitStatus int) { +// IsMinikubeRunning checks that minikube has a status available and that +// the status is `Running` +func IsMinikubeRunning(api libmachine.API) bool { s, err := GetHostStatus(api) if err != nil { - exit.WithError("Error getting machine status", err) + return false } if s != state.Running.String() { - exit.WithCodeT(exit.Unavailable, "minikube is not running, so the service cannot be accessed") + return false } + return true } diff --git a/pkg/minikube/cluster/cluster_test.go b/pkg/minikube/cluster/cluster_test.go index f71dcce266..ae2ee8c5b8 100644 --- a/pkg/minikube/cluster/cluster_test.go +++ b/pkg/minikube/cluster/cluster_test.go @@ -22,12 +22,16 @@ import ( "testing" "time" + // Driver used by testdata + _ "k8s.io/minikube/pkg/minikube/registry/drvs/virtualbox" + "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/provision" "github.com/docker/machine/libmachine/state" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/registry" "k8s.io/minikube/pkg/minikube/tests" ) @@ -43,18 +47,13 @@ func createMockDriverHost(c config.MachineConfig) interface{} { func RegisterMockDriver(t *testing.T) { t.Helper() - _, err := registry.Driver(constants.DriverMock) - // Already registered - if err == nil { + if !registry.Driver(driver.Mock).Empty() { return } - err = registry.Register(registry.DriverDef{ - Name: constants.DriverMock, - Builtin: true, - ConfigCreator: createMockDriverHost, - DriverCreator: func() drivers.Driver { - return &tests.MockDriver{T: t} - }, + err := registry.Register(registry.DriverDef{ + Name: driver.Mock, + Config: createMockDriverHost, + Init: func() drivers.Driver { return &tests.MockDriver{T: t} }, }) if err != nil { t.Fatalf("register failed: %v", err) @@ -62,7 +61,7 @@ func RegisterMockDriver(t *testing.T) { } var defaultMachineConfig = config.MachineConfig{ - VMDriver: constants.DriverMock, + VMDriver: driver.Mock, MinikubeISO: constants.DefaultISOURL, Downloader: MockDownloader{}, DockerEnv: []string{"MOCK_MAKE_IT_PROVISION=true"}, @@ -99,7 +98,7 @@ func TestCreateHost(t *testing.T) { } found := false - for _, def := range registry.ListDrivers() { + for _, def := range registry.List() { if h.DriverName == def.Name { found = true break @@ -107,7 +106,7 @@ func TestCreateHost(t *testing.T) { } if !found { - t.Fatalf("Wrong driver name: %v. It should be among drivers %v", h.DriverName, registry.ListDrivers()) + t.Fatalf("Wrong driver name: %v. It should be among drivers %v", h.DriverName, registry.List()) } } @@ -216,7 +215,7 @@ func TestStartHostConfig(t *testing.T) { provision.SetDetector(md) config := config.MachineConfig{ - VMDriver: constants.DriverMock, + VMDriver: driver.Mock, DockerEnv: []string{"FOO=BAR"}, DockerOpt: []string{"param=value"}, Downloader: MockDownloader{}, diff --git a/pkg/minikube/cluster/machine.go b/pkg/minikube/cluster/machine.go new file mode 100644 index 0000000000..90f7979cba --- /dev/null +++ b/pkg/minikube/cluster/machine.go @@ -0,0 +1,130 @@ +/* +Copyright 2019 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 cluster + +import ( + "io/ioutil" + "path/filepath" + + "github.com/docker/machine/libmachine/host" + "github.com/golang/glog" + "github.com/pkg/errors" + "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/minikube/machine" +) + +// Machine contains information about a machine +type Machine struct { + *host.Host +} + +// IsValid checks if the machine has the essential info needed for a machine +func (h *Machine) IsValid() bool { + if h == nil { + return false + } + + if h.Host == nil { + return false + } + + if h.Host.Name == "" { + return false + } + + if h.Host.Driver == nil { + return false + } + + if h.Host.HostOptions == nil { + return false + } + + if h.Host.RawDriver == nil { + return false + } + return true +} + +// ListMachines return all valid and invalid machines +// If a machine is valid or invalid is determined by the cluster.IsValid function +func ListMachines(miniHome ...string) (validMachines []*Machine, inValidMachines []*Machine, err error) { + pDirs, err := machineDirs(miniHome...) + if err != nil { + return nil, nil, err + } + for _, n := range pDirs { + p, err := LoadMachine(n) + if err != nil { + glog.Infof("%s not valid: %v", n, err) + inValidMachines = append(inValidMachines, p) + continue + } + if !p.IsValid() { + inValidMachines = append(inValidMachines, p) + continue + } + validMachines = append(validMachines, p) + } + return validMachines, inValidMachines, nil +} + +// LoadMachine loads a machine or throws an error if the machine could not be loadedG +func LoadMachine(name string) (*Machine, error) { + api, err := machine.NewAPIClient() + if err != nil { + return nil, err + } + + h, err := CheckIfHostExistsAndLoad(api, name) + if err != nil { + return nil, err + } + + var mm Machine + if h != nil { + mm.Host = h + } else { + return nil, errors.New("host is nil") + } + + return &mm, nil +} + +func machineDirs(miniHome ...string) (dirs []string, err error) { + miniPath := localpath.MiniPath() + if len(miniHome) > 0 { + miniPath = miniHome[0] + } + mRootDir := filepath.Join(miniPath, "machines") + items, err := ioutil.ReadDir(mRootDir) + for _, f := range items { + if f.IsDir() { + dirs = append(dirs, f.Name()) + } + } + return dirs, err +} + +// MachinePath returns the Minikube machine path of a machine +func MachinePath(machine string, miniHome ...string) string { + miniPath := localpath.MiniPath() + if len(miniHome) > 0 { + miniPath = miniHome[0] + } + return filepath.Join(miniPath, "machines", machine) +} diff --git a/pkg/minikube/cluster/machine_test.go b/pkg/minikube/cluster/machine_test.go new file mode 100644 index 0000000000..a04f40aa8b --- /dev/null +++ b/pkg/minikube/cluster/machine_test.go @@ -0,0 +1,75 @@ +/* +Copyright 2019 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 cluster + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/spf13/viper" + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/localpath" +) + +func TestListMachines(t *testing.T) { + const ( + numberOfValidMachines = 2 + numberOfInValidMachines = 3 + totalNumberOfMachines = numberOfValidMachines + numberOfInValidMachines + ) + + viper.Set(config.MachineProfile, "") + + testMinikubeDir := "./testdata/list-machines/.minikube" + miniDir, err := filepath.Abs(testMinikubeDir) + + if err != nil { + t.Errorf("error getting dir path for %s : %v", testMinikubeDir, err) + } + + err = os.Setenv(localpath.MinikubeHome, miniDir) + if err != nil { + t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome) + } + + files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) + numberOfMachineDirs := len(files) + + validMachines, inValidMachines, err := ListMachines() + + if err != nil { + t.Error(err) + } + + if numberOfValidMachines != len(validMachines) { + t.Errorf("expected %d valid machines, got %d", numberOfValidMachines, len(validMachines)) + } + + if numberOfInValidMachines != len(inValidMachines) { + t.Errorf("expected %d invalid machines, got %d", numberOfInValidMachines, len(inValidMachines)) + } + + if totalNumberOfMachines != len(validMachines)+len(inValidMachines) { + t.Errorf("expected %d total machines, got %d", totalNumberOfMachines, len(validMachines)+len(inValidMachines)) + } + + if numberOfMachineDirs != len(validMachines)+len(inValidMachines) { + t.Error("expected number of machine directories to be equal to the number of total machines") + } +} diff --git a/pkg/minikube/cluster/mount.go b/pkg/minikube/cluster/mount.go index 59e3af1055..eeba0a7452 100644 --- a/pkg/minikube/cluster/mount.go +++ b/pkg/minikube/cluster/mount.go @@ -19,12 +19,14 @@ package cluster import ( "fmt" "os" + "os/exec" "sort" "strconv" "strings" "github.com/golang/glog" "github.com/pkg/errors" + "k8s.io/minikube/pkg/minikube/command" ) // MountConfig defines the options available to the Mount command @@ -49,7 +51,7 @@ type MountConfig struct { // mountRunner is the subset of CommandRunner used for mounting type mountRunner interface { - CombinedOutput(string) (string, error) + RunCmd(*exec.Cmd) (*command.RunResult, error) } // Mount runs the mount command from the 9p client on the VM to the 9p server on the host @@ -58,14 +60,16 @@ func Mount(r mountRunner, source string, target string, c *MountConfig) error { return errors.Wrap(err, "umount") } - cmd := fmt.Sprintf("sudo mkdir -m %o -p %s && %s", c.Mode, target, mntCmd(source, target, c)) - glog.Infof("Will run: %s", cmd) - out, err := r.CombinedOutput(cmd) - if err != nil { - glog.Infof("%s failed: err=%s, output: %q", cmd, err, out) - return errors.Wrap(err, out) + if _, err := r.RunCmd(exec.Command("/bin/bash", "-c", fmt.Sprintf("sudo mkdir -m %o -p %s && %s", c.Mode, target, mntCmd(source, target, c)))); err != nil { + return errors.Wrap(err, "create folder pre-mount") } - glog.Infof("%s output: %q", cmd, out) + + rr, err := r.RunCmd(exec.Command("/bin/bash", "-c", mntCmd(source, target, c))) + if err != nil { + return errors.Wrapf(err, "mount with cmd %s ", rr.Command()) + } + + glog.Infof("mount successful: %q", rr.Output()) return nil } @@ -131,20 +135,13 @@ func mntCmd(source string, target string, c *MountConfig) string { return fmt.Sprintf("sudo mount -t %s -o %s %s %s", c.Type, strings.Join(opts, ","), source, target) } -// umountCmd returns a command for unmounting -func umountCmd(target string) string { - // grep because findmnt will also display the parent! - return fmt.Sprintf("[ \"x$(findmnt -T %s | grep %s)\" != \"x\" ] && sudo umount -f %s || echo ", target, target, target) -} - // Unmount unmounts a path func Unmount(r mountRunner, target string) error { - cmd := umountCmd(target) - glog.Infof("Will run: %s", cmd) - out, err := r.CombinedOutput(cmd) - glog.Infof("unmount force err=%v, out=%s", err, out) - if err != nil { - return errors.Wrap(err, out) + // grep because findmnt will also display the parent! + c := exec.Command("/bin/bash", "-c", fmt.Sprintf("[ \"x$(findmnt -T %s | grep %s)\" != \"x\" ] && sudo umount -f %s || echo ", target, target, target)) + if _, err := r.RunCmd(c); err != nil { + return errors.Wrap(err, "unmount") } + glog.Infof("unmount for %s ran successfully", target) return nil } diff --git a/pkg/minikube/cluster/mount_test.go b/pkg/minikube/cluster/mount_test.go index db56f96faa..bb1890069c 100644 --- a/pkg/minikube/cluster/mount_test.go +++ b/pkg/minikube/cluster/mount_test.go @@ -23,50 +23,27 @@ import ( "github.com/google/go-cmp/cmp" ) -type mockMountRunner struct { - cmds []string - T *testing.T -} - -func newMockMountRunner(t *testing.T) *mockMountRunner { - return &mockMountRunner{ - T: t, - cmds: []string{}, - } -} - -func (m *mockMountRunner) CombinedOutput(cmd string) (string, error) { - m.cmds = append(m.cmds, cmd) - return "", nil -} - -func TestMount(t *testing.T) { +func TestMntCmd(t *testing.T) { var tests = []struct { name string source string target string cfg *MountConfig - want []string + want string }{ { name: "simple", source: "src", target: "target", cfg: &MountConfig{Type: "9p", Mode: os.FileMode(0700)}, - want: []string{ - "[ \"x$(findmnt -T target | grep target)\" != \"x\" ] && sudo umount -f target || echo ", - "sudo mkdir -m 700 -p target && sudo mount -t 9p -o dfltgid=0,dfltuid=0 src target", - }, + want: "sudo mount -t 9p -o dfltgid=0,dfltuid=0 src target", }, { name: "named uid", source: "src", target: "target", cfg: &MountConfig{Type: "9p", Mode: os.FileMode(0700), UID: "docker", GID: "docker"}, - want: []string{ - "[ \"x$(findmnt -T target | grep target)\" != \"x\" ] && sudo umount -f target || echo ", - "sudo mkdir -m 700 -p target && sudo mount -t 9p -o dfltgid=$(grep ^docker: /etc/group | cut -d: -f3),dfltuid=$(id -u docker) src target", - }, + want: "sudo mount -t 9p -o dfltgid=$(grep ^docker: /etc/group | cut -d: -f3),dfltuid=$(id -u docker) src target", }, { name: "everything", @@ -76,10 +53,7 @@ func TestMount(t *testing.T) { "noextend": "", "cache": "fscache", }}, - want: []string{ - "[ \"x$(findmnt -T /target | grep /target)\" != \"x\" ] && sudo umount -f /target || echo ", - "sudo mkdir -m 777 -p /target && sudo mount -t 9p -o cache=fscache,dfltgid=72,dfltuid=82,noextend,version=9p2000.u 10.0.0.1 /target", - }, + want: "sudo mount -t 9p -o cache=fscache,dfltgid=72,dfltuid=82,noextend,version=9p2000.u 10.0.0.1 /target", }, { name: "version-conflict", @@ -88,35 +62,17 @@ func TestMount(t *testing.T) { cfg: &MountConfig{Type: "9p", Mode: os.FileMode(0700), Version: "9p2000.u", Options: map[string]string{ "version": "9p2000.L", }}, - want: []string{ - "[ \"x$(findmnt -T tgt | grep tgt)\" != \"x\" ] && sudo umount -f tgt || echo ", - "sudo mkdir -m 700 -p tgt && sudo mount -t 9p -o dfltgid=0,dfltuid=0,version=9p2000.L src tgt", - }, + want: "sudo mount -t 9p -o dfltgid=0,dfltuid=0,version=9p2000.L src tgt", }, } + for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - r := newMockMountRunner(t) - err := Mount(r, tc.source, tc.target, tc.cfg) - if err != nil { - t.Fatalf("Mount(%s, %s, %+v): %v", tc.source, tc.target, tc.cfg, err) - } - if diff := cmp.Diff(r.cmds, tc.want); diff != "" { + got := mntCmd(tc.source, tc.target, tc.cfg) + want := tc.want + if diff := cmp.Diff(got, want); diff != "" { t.Errorf("command diff (-want +got): %s", diff) } }) } } - -func TestUnmount(t *testing.T) { - r := newMockMountRunner(t) - err := Unmount(r, "/mnt") - if err != nil { - t.Fatalf("Unmount(/mnt): %v", err) - } - - want := []string{"[ \"x$(findmnt -T /mnt | grep /mnt)\" != \"x\" ] && sudo umount -f /mnt || echo "} - if diff := cmp.Diff(r.cmds, want); diff != "" { - t.Errorf("command diff (-want +got): %s", diff) - } -} diff --git a/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p1/config.json b/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p1/config.json new file mode 100644 index 0000000000..83a27c78b2 --- /dev/null +++ b/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p1/config.json @@ -0,0 +1,80 @@ +{ + "ConfigVersion": 3, + "Driver": { + "IPAddress": "192.168.64.50", + "MachineName": "p1", + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p1/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "Boot2DockerURL": "file:///Users/someuser/.minikube/cache/iso/minikube-v1.2.0.iso", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p1", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "5e6787b6-aecb-11e9-81cf-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p1/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p1/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "DriverName": "virtualbox", + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertPath": "/Users/someuser/.minikube/machines/server.pem", + "ServerKeyPath": "/Users/someuser/.minikube/machines/server-key.pem", + "ClientKeyPath": "/Users/someuser/.minikube/certs/key.pem", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, + "Name": "p1" +} diff --git a/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p2/config.json b/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p2/config.json new file mode 100644 index 0000000000..4bb1814211 --- /dev/null +++ b/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p2/config.json @@ -0,0 +1,80 @@ +{ + "ConfigVersion": 3, + "Driver": { + "IPAddress": "192.168.64.50", + "MachineName": "p2", + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p2/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "Boot2DockerURL": "file:///Users/someuser/.minikube/cache/iso/minikube-v1.2.0.iso", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p2", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "5e6787b6-aecb-11e9-81cf-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p2/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p2/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "DriverName": "virtualbox", + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertPath": "/Users/someuser/.minikube/machines/server.pem", + "ServerKeyPath": "/Users/someuser/.minikube/machines/server-key.pem", + "ClientKeyPath": "/Users/someuser/.minikube/certs/key.pem", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, + "Name": "p2" +} diff --git a/pkg/minikube/config/testdata/.minikube/profiles/p3_empty/config.json b/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p3_empty_config/config.json similarity index 100% rename from pkg/minikube/config/testdata/.minikube/profiles/p3_empty/config.json rename to pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p3_empty_config/config.json diff --git a/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p4_invalid_machine_config/config.json b/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p4_invalid_machine_config/config.json new file mode 100644 index 0000000000..581f9e648f --- /dev/null +++ b/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p4_invalid_machine_config/config.json @@ -0,0 +1 @@ +invalid json file :) diff --git a/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p5_partial_config/config.json b/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p5_partial_config/config.json new file mode 100644 index 0000000000..d7b7092761 --- /dev/null +++ b/pkg/minikube/cluster/testdata/list-machines/.minikube/machines/p5_partial_config/config.json @@ -0,0 +1,72 @@ +{ + "ConfigVersion": 3, + "Driver": { + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p5_partial_config/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p5_partial_config", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "4f16872c-aee8-11e9-8815-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p5_partial_config/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p5_partial_config/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, +} diff --git a/pkg/minikube/command/command_runner.go b/pkg/minikube/command/command_runner.go index 57f250b0ca..309c8f6133 100644 --- a/pkg/minikube/command/command_runner.go +++ b/pkg/minikube/command/command_runner.go @@ -17,34 +17,28 @@ limitations under the License. package command import ( + "bytes" "fmt" - "io" + "os/exec" "path" + "strings" "k8s.io/minikube/pkg/minikube/assets" ) +// RunResult holds the results of a Runner +type RunResult struct { + Stdout bytes.Buffer + Stderr bytes.Buffer + ExitCode int + Args []string // the args that was passed to Runner +} + // Runner represents an interface to run commands. type Runner interface { - // Run starts the specified command and waits for it to complete. - Run(cmd string) error - - // CombinedOutputTo runs the command and stores both command - // output and error to out. A typical usage is: - // - // var b bytes.Buffer - // CombinedOutput(cmd, &b) - // fmt.Println(b.Bytes()) - // - // Or, you can set out to os.Stdout, the command output and - // error would show on your terminal immediately before you - // cmd exit. This is useful for a long run command such as - // continuously print running logs. - CombinedOutputTo(cmd string, out io.Writer) error - - // CombinedOutput runs the command and returns its combined standard - // output and standard error. - CombinedOutput(cmd string) (string, error) + // RunCmd runs a cmd of exec.Cmd type. allowing user to set cmd.Stdin, cmd.Stdout,... + // not all implementors are guaranteed to handle all the properties of cmd. + RunCmd(cmd *exec.Cmd) (*RunResult, error) // Copy is a convenience method that runs a command to copy a file Copy(assets.CopyableFile) error @@ -56,3 +50,29 @@ type Runner interface { func getDeleteFileCommand(f assets.CopyableFile) string { return fmt.Sprintf("sudo rm %s", path.Join(f.GetTargetDir(), f.GetTargetName())) } + +// Command returns a human readable command string that does not induce eye fatigue +func (rr RunResult) Command() string { + var sb strings.Builder + sb.WriteString(rr.Args[0]) + for _, a := range rr.Args[1:] { + if strings.Contains(a, " ") { + sb.WriteString(fmt.Sprintf(` "%s"`, a)) + continue + } + sb.WriteString(fmt.Sprintf(" %s", a)) + } + return sb.String() +} + +// Output returns human-readable output for an execution result +func (rr RunResult) Output() string { + var sb strings.Builder + if rr.Stdout.Len() > 0 { + sb.WriteString(fmt.Sprintf("-- stdout --\n%s\n-- /stdout --", rr.Stdout.Bytes())) + } + if rr.Stderr.Len() > 0 { + sb.WriteString(fmt.Sprintf("\n** stderr ** \n%s\n** /stderr **", rr.Stderr.Bytes())) + } + return sb.String() +} diff --git a/pkg/minikube/command/exec_runner.go b/pkg/minikube/command/exec_runner.go index 84891597e7..a10023b3a8 100644 --- a/pkg/minikube/command/exec_runner.go +++ b/pkg/minikube/command/exec_runner.go @@ -24,6 +24,7 @@ import ( "path" "path/filepath" "strconv" + "time" "github.com/golang/glog" "github.com/pkg/errors" @@ -35,41 +36,45 @@ import ( // It implements the CommandRunner interface. type ExecRunner struct{} -// Run starts the specified command in a bash shell and waits for it to complete. -func (*ExecRunner) Run(cmd string) error { - glog.Infoln("Run:", cmd) - c := exec.Command("/bin/bash", "-c", cmd) - if err := c.Run(); err != nil { - return errors.Wrapf(err, "running command: %s", cmd) - } - return nil -} +// RunCmd implements the Command Runner interface to run a exec.Cmd object +func (*ExecRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) { + rr := &RunResult{Args: cmd.Args} + glog.Infof("(ExecRunner) Run: %v", rr.Command()) -// CombinedOutputTo runs the command and stores both command -// output and error to out. -func (*ExecRunner) CombinedOutputTo(cmd string, out io.Writer) error { - glog.Infoln("Run with output:", cmd) - c := exec.Command("/bin/bash", "-c", cmd) - c.Stdout = out - c.Stderr = out - err := c.Run() - if err != nil { - return errors.Wrapf(err, "running command: %s\n.", cmd) + var outb, errb io.Writer + if cmd.Stdout == nil { + var so bytes.Buffer + outb = io.MultiWriter(&so, &rr.Stdout) + } else { + outb = io.MultiWriter(cmd.Stdout, &rr.Stdout) } - return nil -} - -// CombinedOutput runs the command in a bash shell and returns its -// combined standard output and standard error. -func (e *ExecRunner) CombinedOutput(cmd string) (string, error) { - var b bytes.Buffer - err := e.CombinedOutputTo(cmd, &b) - if err != nil { - return "", errors.Wrapf(err, "running command: %s\n output: %s", cmd, b.Bytes()) + if cmd.Stderr == nil { + var se bytes.Buffer + errb = io.MultiWriter(&se, &rr.Stderr) + } else { + errb = io.MultiWriter(cmd.Stderr, &rr.Stderr) } - return b.String(), nil + cmd.Stdout = outb + cmd.Stderr = errb + + start := time.Now() + err := cmd.Run() + elapsed := time.Since(start) + if err == nil { + // Reduce log spam + if elapsed > (1 * time.Second) { + glog.Infof("(ExecRunner) Done: %v: (%s)", rr.Command(), elapsed) + } + } else { + if exitError, ok := err.(*exec.ExitError); ok { + rr.ExitCode = exitError.ExitCode() + } + glog.Infof("(ExecRunner) Non-zero exit: %v: %v (%s)\n%s", rr.Command(), err, elapsed, rr.Output()) + err = errors.Wrapf(err, "command failed: %s\nstdout: %s\nstderr: %s", rr.Command(), rr.Stdout.String(), rr.Stderr.String()) + } + return rr, err } // Copy copies a file and its permissions diff --git a/pkg/minikube/command/fake_runner.go b/pkg/minikube/command/fake_runner.go index 029000cdc8..fbdd40ca40 100644 --- a/pkg/minikube/command/fake_runner.go +++ b/pkg/minikube/command/fake_runner.go @@ -20,9 +20,13 @@ import ( "bytes" "fmt" "io" + "os/exec" + "strings" + "time" "golang.org/x/sync/syncmap" + "github.com/golang/glog" "github.com/pkg/errors" "k8s.io/minikube/pkg/minikube/assets" @@ -43,34 +47,38 @@ func NewFakeCommandRunner() *FakeCommandRunner { return &FakeCommandRunner{} } -// Run returns nil if output has been set for the given command text. -func (f *FakeCommandRunner) Run(cmd string) error { - _, err := f.CombinedOutput(cmd) - return err -} +// RunCmd implements the Command Runner interface to run a exec.Cmd object +func (f *FakeCommandRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) { + rr := &RunResult{Args: cmd.Args} + glog.Infof("(FakeCommandRunner) Run: %v", rr.Command()) -// CombinedOutputTo runs the command and stores both command -// output and error to out. -func (f *FakeCommandRunner) CombinedOutputTo(cmd string, out io.Writer) error { - value, ok := f.cmdMap.Load(cmd) - if !ok { - return fmt.Errorf("unavailable command: %s", cmd) + start := time.Now() + + out, ok := f.cmdMap.Load(strings.Join(rr.Args, " ")) + var buf bytes.Buffer + outStr := "" + if out != nil { + outStr = out.(string) } - _, err := fmt.Fprint(out, value) + _, err := buf.WriteString(outStr) if err != nil { - return err + return rr, errors.Wrap(err, "Writing outStr to FakeCommandRunner's buffer") } + rr.Stdout = buf + rr.Stderr = buf - return nil -} + elapsed := time.Since(start) -// CombinedOutput returns the set output for a given command text. -func (f *FakeCommandRunner) CombinedOutput(cmd string) (string, error) { - out, ok := f.cmdMap.Load(cmd) - if !ok { - return "", fmt.Errorf("unavailable command: %s", cmd) + if ok { + // Reduce log spam + if elapsed > (1 * time.Second) { + glog.Infof("(FakeCommandRunner) Done: %v: (%s)", rr.Command(), elapsed) + } + } else { + glog.Infof("(FakeCommandRunner) Non-zero exit: %v: (%s)\n%s", rr.Command(), elapsed, out) + return rr, fmt.Errorf("unavailable command: %s", rr.Command()) } - return out.(string), nil + return rr, nil } // Copy adds the filename, file contents key value pair to the stored map. diff --git a/pkg/minikube/command/ssh_runner.go b/pkg/minikube/command/ssh_runner.go index 01fd780e97..a341afb049 100644 --- a/pkg/minikube/command/ssh_runner.go +++ b/pkg/minikube/command/ssh_runner.go @@ -17,13 +17,17 @@ limitations under the License. package command import ( + "bufio" "bytes" "fmt" "io" + "os/exec" "path" "sync" + "time" "github.com/golang/glog" + "github.com/kballard/go-shellquote" "github.com/pkg/errors" "golang.org/x/crypto/ssh" "golang.org/x/sync/errgroup" @@ -55,17 +59,6 @@ func (s *SSHRunner) Remove(f assets.CopyableFile) error { return sess.Run(cmd) } -type singleWriter struct { - b bytes.Buffer - mu sync.Mutex -} - -func (w *singleWriter) Write(p []byte) (int, error) { - w.mu.Lock() - defer w.mu.Unlock() - return w.b.Write(p) -} - // teeSSH runs an SSH command, streaming stdout, stderr to logs func teeSSH(s *ssh.Session, cmd string, outB io.Writer, errB io.Writer) error { outPipe, err := s.StdoutPipe() @@ -81,13 +74,13 @@ func teeSSH(s *ssh.Session, cmd string, outB io.Writer, errB io.Writer) error { wg.Add(2) go func() { - if err := util.TeePrefix(util.ErrPrefix, errPipe, errB, glog.V(8).Infof); err != nil { + if err := teePrefix(util.ErrPrefix, errPipe, errB, glog.V(8).Infof); err != nil { glog.Errorf("tee stderr: %v", err) } wg.Done() }() go func() { - if err := util.TeePrefix(util.OutPrefix, outPipe, outB, glog.V(8).Infof); err != nil { + if err := teePrefix(util.OutPrefix, outPipe, outB, glog.V(8).Infof); err != nil { glog.Errorf("tee stdout: %v", err) } wg.Done() @@ -97,12 +90,31 @@ func teeSSH(s *ssh.Session, cmd string, outB io.Writer, errB io.Writer) error { return err } -// Run starts a command on the remote and waits for it to return. -func (s *SSHRunner) Run(cmd string) error { - glog.Infof("SSH: %s", cmd) +// RunCmd implements the Command Runner interface to run a exec.Cmd object +func (s *SSHRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) { + rr := &RunResult{Args: cmd.Args} + glog.Infof("(SSHRunner) Run: %v", rr.Command()) + + var outb, errb io.Writer + start := time.Now() + + if cmd.Stdout == nil { + var so bytes.Buffer + outb = io.MultiWriter(&so, &rr.Stdout) + } else { + outb = io.MultiWriter(cmd.Stdout, &rr.Stdout) + } + + if cmd.Stderr == nil { + var se bytes.Buffer + errb = io.MultiWriter(&se, &rr.Stderr) + } else { + errb = io.MultiWriter(cmd.Stderr, &rr.Stderr) + } + sess, err := s.c.NewSession() if err != nil { - return errors.Wrap(err, "NewSession") + return rr, errors.Wrap(err, "NewSession") } defer func() { @@ -112,43 +124,21 @@ func (s *SSHRunner) Run(cmd string) error { } } }() - var outB bytes.Buffer - var errB bytes.Buffer - err = teeSSH(sess, cmd, &outB, &errB) - if err != nil { - return errors.Wrapf(err, "command failed: %s\nstdout: %s\nstderr: %s", cmd, outB.String(), errB.String()) - } - return nil -} -// CombinedOutputTo runs the command and stores both command -// output and error to out. -func (s *SSHRunner) CombinedOutputTo(cmd string, w io.Writer) error { - out, err := s.CombinedOutput(cmd) - if err != nil { - return err + elapsed := time.Since(start) + err = teeSSH(sess, shellquote.Join(cmd.Args...), outb, errb) + if err == nil { + // Reduce log spam + if elapsed > (1 * time.Second) { + glog.Infof("(SSHRunner) Done: %v: (%s)", rr.Command(), elapsed) + } + } else { + if exitError, ok := err.(*exec.ExitError); ok { + rr.ExitCode = exitError.ExitCode() + } + glog.Infof("(SSHRunner) Non-zero exit: %v: %v (%s)\n%s", rr.Command(), err, elapsed, rr.Output()) } - _, err = w.Write([]byte(out)) - return err -} - -// CombinedOutput runs the command on the remote and returns its combined -// standard output and standard error. -func (s *SSHRunner) CombinedOutput(cmd string) (string, error) { - glog.Infoln("Run with output:", cmd) - sess, err := s.c.NewSession() - if err != nil { - return "", errors.Wrap(err, "NewSession") - } - defer sess.Close() - - var combined singleWriter - err = teeSSH(sess, cmd, &combined, &combined) - out := combined.b.String() - if err != nil { - return out, err - } - return out, nil + return rr, err } // Copy copies a file to the remote over SSH. @@ -198,3 +188,30 @@ func (s *SSHRunner) Copy(f assets.CopyableFile) error { } return g.Wait() } + +// teePrefix copies bytes from a reader to writer, logging each new line. +func teePrefix(prefix string, r io.Reader, w io.Writer, logger func(format string, args ...interface{})) error { + scanner := bufio.NewScanner(r) + scanner.Split(bufio.ScanBytes) + var line bytes.Buffer + + for scanner.Scan() { + b := scanner.Bytes() + if _, err := w.Write(b); err != nil { + return err + } + if bytes.IndexAny(b, "\r\n") == 0 { + if line.Len() > 0 { + logger("%s%s", prefix, line.String()) + line.Reset() + } + continue + } + line.Write(b) + } + // Catch trailing output in case stream does not end with a newline + if line.Len() > 0 { + logger("%s%s", prefix, line.String()) + } + return nil +} diff --git a/pkg/minikube/command/ssh_runner_test.go b/pkg/minikube/command/ssh_runner_test.go new file mode 100644 index 0000000000..df9e7d509e --- /dev/null +++ b/pkg/minikube/command/ssh_runner_test.go @@ -0,0 +1,63 @@ +/* +Copyright 2019 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 command + +import ( + "bytes" + "fmt" + "strings" + "sync" + "testing" +) + +func TestTeePrefix(t *testing.T) { + var in bytes.Buffer + var out bytes.Buffer + var logged strings.Builder + + logSink := func(format string, args ...interface{}) { + logged.WriteString("(" + fmt.Sprintf(format, args...) + ")") + } + + // Simulate the primary use case: tee in the background. This also helps avoid I/O races. + var wg sync.WaitGroup + wg.Add(1) + go func() { + if err := teePrefix(":", &in, &out, logSink); err != nil { + t.Errorf("teePrefix: %v", err) + } + wg.Done() + }() + + in.Write([]byte("goo")) + in.Write([]byte("\n")) + in.Write([]byte("g\r\n\r\n")) + in.Write([]byte("le")) + wg.Wait() + + gotBytes := out.Bytes() + wantBytes := []byte("goo\ng\r\n\r\nle") + if !bytes.Equal(gotBytes, wantBytes) { + t.Errorf("output=%q, want: %q", gotBytes, wantBytes) + } + + gotLog := logged.String() + wantLog := "(:goo)(:g)(:le)" + if gotLog != wantLog { + t.Errorf("log=%q, want: %q", gotLog, wantLog) + } +} diff --git a/pkg/minikube/config/config.go b/pkg/minikube/config/config.go index 18d568fc0c..0ceab72ecf 100644 --- a/pkg/minikube/config/config.go +++ b/pkg/minikube/config/config.go @@ -26,6 +26,7 @@ import ( "github.com/spf13/viper" "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/localpath" ) const ( @@ -59,7 +60,7 @@ type MinikubeConfig map[string]interface{} // Get gets a named value from config func Get(name string) (string, error) { - m, err := ReadConfig(constants.ConfigFile) + m, err := ReadConfig(localpath.ConfigFile) if err != nil { return "", err } @@ -94,13 +95,13 @@ func ReadConfig(configFile string) (MinikubeConfig, error) { if os.IsNotExist(err) { return make(map[string]interface{}), nil } - return nil, fmt.Errorf("open %s: %v", constants.ConfigFile, err) + return nil, fmt.Errorf("open %s: %v", localpath.ConfigFile, err) } defer f.Close() m, err := decode(f) if err != nil { - return nil, fmt.Errorf("decode %s: %v", constants.ConfigFile, err) + return nil, fmt.Errorf("decode %s: %v", localpath.ConfigFile, err) } return m, nil diff --git a/pkg/minikube/config/config_test.go b/pkg/minikube/config/config_test.go index d1c7254c1a..19671ef617 100644 --- a/pkg/minikube/config/config_test.go +++ b/pkg/minikube/config/config_test.go @@ -22,8 +22,6 @@ import ( "os" "reflect" "testing" - - "k8s.io/minikube/pkg/minikube/constants" ) type configTestCase struct { @@ -48,10 +46,10 @@ var configTestCases = []configTestCase{ "log_dir": "/etc/hosts", "show-libmachine-logs": true, "v": 5, - "vm-driver": "kvm2" + "vm-driver": "test-driver" }`, config: map[string]interface{}{ - "vm-driver": constants.DriverKvm2, + "vm-driver": "test-driver", "cpus": 4, "disk-size": "20g", "v": 5, @@ -108,7 +106,7 @@ func TestReadConfig(t *testing.T) { // non existing file mkConfig, err := ReadConfig("non_existing_file") if err != nil { - t.Fatalf("Error not exepected but got %v", err) + t.Fatalf("Error not expected but got %v", err) } if len(mkConfig) != 0 { @@ -132,7 +130,7 @@ func TestReadConfig(t *testing.T) { } expectedConfig := map[string]interface{}{ - "vm-driver": constants.DriverKvm2, + "vm-driver": "test-driver", "cpus": 4, "disk-size": "20g", "show-libmachine-logs": true, @@ -151,7 +149,7 @@ func TestWriteConfig(t *testing.T) { } cfg := map[string]interface{}{ - "vm-driver": constants.DriverKvm2, + "vm-driver": "test-driver", "cpus": 4, "disk-size": "20g", "show-libmachine-logs": true, @@ -174,7 +172,7 @@ func TestWriteConfig(t *testing.T) { } } -func Test_encode(t *testing.T) { +func TestEncode(t *testing.T) { var b bytes.Buffer for _, tt := range configTestCases { err := encode(&b, tt.config) diff --git a/pkg/minikube/config/extra_options.go b/pkg/minikube/config/extra_options.go index 0448750f39..d2ced45963 100644 --- a/pkg/minikube/config/extra_options.go +++ b/pkg/minikube/config/extra_options.go @@ -109,7 +109,7 @@ func (cm ComponentExtraOptionMap) Get(component string) map[string]string { } // ContainsParam checks if a given slice of strings contains the provided string. -// If a modifier func is provided, it is called with the slice item before the comparation. +// If a modifier func is provided, it is called with the slice item before the comparison. func ContainsParam(slice []string, s string) bool { for _, item := range slice { if item == s { diff --git a/pkg/minikube/config/profile.go b/pkg/minikube/config/profile.go index e3c2872bdf..f07aad446b 100644 --- a/pkg/minikube/config/profile.go +++ b/pkg/minikube/config/profile.go @@ -21,14 +21,20 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "github.com/golang/glog" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/util/lock" ) -// isValid checks if the profile has the essential info needed for a profile -func (p *Profile) isValid() bool { +var keywords = []string{"start", "stop", "status", "delete", "config", "open", "profile", "addons", "cache", "logs"} + +// IsValid checks if the profile has the essential info needed for a profile +func (p *Profile) IsValid() bool { + if p.Config == nil { + return false + } if p.Config.MachineConfig.VMDriver == "" { return false } @@ -38,6 +44,16 @@ func (p *Profile) isValid() bool { return true } +// ProfileNameInReservedKeywords checks if the profile is an internal keywords +func ProfileNameInReservedKeywords(name string) bool { + for _, v := range keywords { + if strings.EqualFold(v, name) { + return true + } + } + return false +} + // ProfileExists returns true if there is a profile config (regardless of being valid) func ProfileExists(name string, miniHome ...string) bool { miniPath := localpath.MiniPath() @@ -50,7 +66,7 @@ func ProfileExists(name string, miniHome ...string) bool { return err == nil } -// CreateProfile creates an empty profile stores in $MINIKUBE_HOME/profiles/<profilename>/config.json +// CreateEmptyProfile creates an empty profile stores in $MINIKUBE_HOME/profiles/<profilename>/config.json func CreateEmptyProfile(name string, miniHome ...string) error { cfg := &Config{} return CreateProfile(name, cfg, miniHome...) @@ -100,12 +116,13 @@ func CreateProfile(name string, cfg *Config, miniHome ...string) error { return nil } +// DeleteProfile deletes a profile and removes the profile dir func DeleteProfile(profile string, miniHome ...string) error { miniPath := localpath.MiniPath() if len(miniHome) > 0 { miniPath = miniHome[0] } - return os.RemoveAll(profileFolderPath(profile, miniPath)) + return os.RemoveAll(ProfileFolderPath(profile, miniPath)) } // ListProfiles returns all valid and invalid (if any) minikube profiles @@ -117,12 +134,12 @@ func ListProfiles(miniHome ...string) (validPs []*Profile, inValidPs []*Profile, return nil, nil, err } for _, n := range pDirs { - p, err := loadProfile(n, miniHome...) + p, err := LoadProfile(n, miniHome...) if err != nil { inValidPs = append(inValidPs, p) continue } - if !p.isValid() { + if !p.IsValid() { inValidPs = append(inValidPs, p) continue } @@ -131,8 +148,8 @@ func ListProfiles(miniHome ...string) (validPs []*Profile, inValidPs []*Profile, return validPs, inValidPs, nil } -// loadProfile loads type Profile based on its name -func loadProfile(name string, miniHome ...string) (*Profile, error) { +// LoadProfile loads type Profile based on its name +func LoadProfile(name string, miniHome ...string) (*Profile, error) { cfg, err := DefaultLoader.LoadConfigFromFile(name, miniHome...) p := &Profile{ Name: name, @@ -167,8 +184,8 @@ func profileFilePath(profile string, miniHome ...string) string { return filepath.Join(miniPath, "profiles", profile, "config.json") } -// profileFolderPath returns path of profile folder -func profileFolderPath(profile string, miniHome ...string) string { +// ProfileFolderPath returns path of profile folder +func ProfileFolderPath(profile string, miniHome ...string) string { miniPath := localpath.MiniPath() if len(miniHome) > 0 { miniPath = miniHome[0] diff --git a/pkg/minikube/config/profile_test.go b/pkg/minikube/config/profile_test.go index 2ef38378ac..8852ea9b6f 100644 --- a/pkg/minikube/config/profile_test.go +++ b/pkg/minikube/config/profile_test.go @@ -23,7 +23,7 @@ import ( // TestListProfiles uses a different uses different MINIKUBE_HOME with rest of tests since it relies on file list index func TestListProfiles(t *testing.T) { - miniDir, err := filepath.Abs("./testdata/.minikube") + miniDir, err := filepath.Abs("./testdata/profile/.minikube") if err != nil { t.Errorf("error getting dir path for ./testdata/.minikube : %v", err) } @@ -72,6 +72,32 @@ func TestListProfiles(t *testing.T) { } } +func TestProfileNameInReservedKeywords(t *testing.T) { + var testCases = []struct { + name string + expected bool + }{ + {"start", true}, + {"stop", true}, + {"status", true}, + {"delete", true}, + {"config", true}, + {"open", true}, + {"profile", true}, + {"addons", true}, + {"cache", true}, + {"logs", true}, + {"myprofile", false}, + {"log", false}, + } + for _, tt := range testCases { + got := ProfileNameInReservedKeywords(tt.name) + if got != tt.expected { + t.Errorf("expected ProfileNameInReservedKeywords(%s)=%t but got %t ", tt.name, tt.expected, got) + } + } +} + func TestProfileExists(t *testing.T) { miniDir, err := filepath.Abs("./testdata/.minikube2") if err != nil { diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/machines/p1/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p1/config.json new file mode 100644 index 0000000000..90a026a01c --- /dev/null +++ b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p1/config.json @@ -0,0 +1,80 @@ +{ + "ConfigVersion": 3, + "Driver": { + "IPAddress": "192.168.64.50", + "MachineName": "p1", + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p1/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "Boot2DockerURL": "file:///Users/someuser/.minikube/cache/iso/minikube-v1.2.0.iso", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p1", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "5e6787b6-aecb-11e9-81cf-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p1/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p1/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "DriverName": "hyperkit", + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertPath": "/Users/someuser/.minikube/machines/server.pem", + "ServerKeyPath": "/Users/someuser/.minikube/machines/server-key.pem", + "ClientKeyPath": "/Users/someuser/.minikube/certs/key.pem", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, + "Name": "p1" +} diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/machines/p2_empty_profile_config/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p2_empty_profile_config/config.json new file mode 100644 index 0000000000..a9e6592c7a --- /dev/null +++ b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p2_empty_profile_config/config.json @@ -0,0 +1,80 @@ +{ + "ConfigVersion": 3, + "Driver": { + "IPAddress": "192.168.64.52", + "MachineName": "p2_empty_profile_config", + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p2_empty_profile_config/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "Boot2DockerURL": "file:///Users/someuser/.minikube/cache/iso/minikube-v1.2.0.iso", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p2_empty_profile_config", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "ddd37c1c-aedf-11e9-afa7-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p2_empty_profile_config/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p2_empty_profile_config/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "DriverName": "hyperkit", + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertPath": "/Users/someuser/.minikube/machines/server.pem", + "ServerKeyPath": "/Users/someuser/.minikube/machines/server-key.pem", + "ClientKeyPath": "/Users/someuser/.minikube/certs/key.pem", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, + "Name": "p2_empty_profile_config" +} diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/machines/p3_invalid_profile_config/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p3_invalid_profile_config/config.json new file mode 100644 index 0000000000..8d23c98dc6 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p3_invalid_profile_config/config.json @@ -0,0 +1,80 @@ +{ + "ConfigVersion": 3, + "Driver": { + "IPAddress": "192.168.64.53", + "MachineName": "p3_invalid_profile_config", + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p3_invalid_profile_config/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "Boot2DockerURL": "file:///Users/someuser/.minikube/cache/iso/minikube-v1.2.0.iso", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p3_invalid_profile_config", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "759044c8-aee4-11e9-bd0a-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p3_invalid_profile_config/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p3_invalid_profile_config/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "DriverName": "hyperkit", + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertPath": "/Users/someuser/.minikube/machines/server.pem", + "ServerKeyPath": "/Users/someuser/.minikube/machines/server-key.pem", + "ClientKeyPath": "/Users/someuser/.minikube/certs/key.pem", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, + "Name": "p3_invalid_profile_config" +} diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/machines/p4_partial_profile_config/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p4_partial_profile_config/config.json new file mode 100644 index 0000000000..14b416d3c2 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p4_partial_profile_config/config.json @@ -0,0 +1,80 @@ +{ + "ConfigVersion": 3, + "Driver": { + "IPAddress": "192.168.64.54", + "MachineName": "p4_partial_profile_config", + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p4_partial_profile_config/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "Boot2DockerURL": "file:///Users/someuser/.minikube/cache/iso/minikube-v1.2.0.iso", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p4_partial_profile_config", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "faf8face-aee4-11e9-9ba1-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p4_partial_profile_config/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p4_partial_profile_config/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "DriverName": "hyperkit", + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertPath": "/Users/someuser/.minikube/machines/server.pem", + "ServerKeyPath": "/Users/someuser/.minikube/machines/server-key.pem", + "ClientKeyPath": "/Users/someuser/.minikube/certs/key.pem", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, + "Name": "p4_partial_profile_config" +} diff --git a/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/var/run/crio/.empty b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p6_empty_machine_config/config.json similarity index 100% rename from deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/var/run/crio/.empty rename to pkg/minikube/config/testdata/delete-all/.minikube/machines/p6_empty_machine_config/config.json diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/machines/p7_invalid_machine_config/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p7_invalid_machine_config/config.json new file mode 100644 index 0000000000..581f9e648f --- /dev/null +++ b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p7_invalid_machine_config/config.json @@ -0,0 +1 @@ +invalid json file :) diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/machines/p8_partial_machine_config/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p8_partial_machine_config/config.json new file mode 100644 index 0000000000..d4525a7861 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-all/.minikube/machines/p8_partial_machine_config/config.json @@ -0,0 +1,72 @@ +{ + "ConfigVersion": 3, + "Driver": { + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p8_partial_machine_config/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p8_partial_machine_config", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "4f16872c-aee8-11e9-8815-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p8_partial_machine_config/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p8_partial_machine_config/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, +} diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p1/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p1/config.json new file mode 100644 index 0000000000..00655266e6 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p1/config.json @@ -0,0 +1,49 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "VMDriver": "hyperkit", + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "KubernetesVersion": "v1.15.0", + "NodeIP": "192.168.64.50", + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} \ No newline at end of file diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p2_empty_profile_config/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p2_empty_profile_config/config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pkg/minikube/config/testdata/.minikube/profiles/p4_invalid_file/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p3_invalid_profile_config/config.json similarity index 100% rename from pkg/minikube/config/testdata/.minikube/profiles/p4_invalid_file/config.json rename to pkg/minikube/config/testdata/delete-all/.minikube/profiles/p3_invalid_profile_config/config.json diff --git a/pkg/minikube/config/testdata/.minikube/profiles/p5_partial_config/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p4_partial_profile_config/config.json similarity index 100% rename from pkg/minikube/config/testdata/.minikube/profiles/p5_partial_config/config.json rename to pkg/minikube/config/testdata/delete-all/.minikube/profiles/p4_partial_profile_config/config.json diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p5_missing_machine_config/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p5_missing_machine_config/config.json new file mode 100644 index 0000000000..1e0980752a --- /dev/null +++ b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p5_missing_machine_config/config.json @@ -0,0 +1,49 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "VMDriver": "hyperkit", + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "KubernetesVersion": "v1.15.0", + "NodeIP": "192.168.64.55", + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} \ No newline at end of file diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p6_empty_machine_config/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p6_empty_machine_config/config.json new file mode 100644 index 0000000000..a1557f6df1 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p6_empty_machine_config/config.json @@ -0,0 +1,49 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "VMDriver": "hyperkit", + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "KubernetesVersion": "v1.15.0", + "NodeIP": "192.168.64.57", + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} \ No newline at end of file diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p7_invalid_machine_config/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p7_invalid_machine_config/config.json new file mode 100644 index 0000000000..ea47981d29 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p7_invalid_machine_config/config.json @@ -0,0 +1,49 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "VMDriver": "hyperkit", + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "KubernetesVersion": "v1.15.0", + "NodeIP": "192.168.64.59", + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} \ No newline at end of file diff --git a/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p8_partial_machine_config/config.json b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p8_partial_machine_config/config.json new file mode 100644 index 0000000000..789b9b5557 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-all/.minikube/profiles/p8_partial_machine_config/config.json @@ -0,0 +1,49 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "VMDriver": "hyperkit", + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "KubernetesVersion": "v1.15.0", + "NodeIP": "192.168.64.60", + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/machines/p1/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p1/config.json new file mode 100644 index 0000000000..90a026a01c --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p1/config.json @@ -0,0 +1,80 @@ +{ + "ConfigVersion": 3, + "Driver": { + "IPAddress": "192.168.64.50", + "MachineName": "p1", + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p1/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "Boot2DockerURL": "file:///Users/someuser/.minikube/cache/iso/minikube-v1.2.0.iso", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p1", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "5e6787b6-aecb-11e9-81cf-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p1/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p1/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "DriverName": "hyperkit", + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertPath": "/Users/someuser/.minikube/machines/server.pem", + "ServerKeyPath": "/Users/someuser/.minikube/machines/server-key.pem", + "ClientKeyPath": "/Users/someuser/.minikube/certs/key.pem", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, + "Name": "p1" +} diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/machines/p2_empty_profile_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p2_empty_profile_config/config.json new file mode 100644 index 0000000000..a9e6592c7a --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p2_empty_profile_config/config.json @@ -0,0 +1,80 @@ +{ + "ConfigVersion": 3, + "Driver": { + "IPAddress": "192.168.64.52", + "MachineName": "p2_empty_profile_config", + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p2_empty_profile_config/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "Boot2DockerURL": "file:///Users/someuser/.minikube/cache/iso/minikube-v1.2.0.iso", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p2_empty_profile_config", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "ddd37c1c-aedf-11e9-afa7-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p2_empty_profile_config/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p2_empty_profile_config/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "DriverName": "hyperkit", + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertPath": "/Users/someuser/.minikube/machines/server.pem", + "ServerKeyPath": "/Users/someuser/.minikube/machines/server-key.pem", + "ClientKeyPath": "/Users/someuser/.minikube/certs/key.pem", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, + "Name": "p2_empty_profile_config" +} diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/machines/p3_invalid_profile_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p3_invalid_profile_config/config.json new file mode 100644 index 0000000000..8d23c98dc6 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p3_invalid_profile_config/config.json @@ -0,0 +1,80 @@ +{ + "ConfigVersion": 3, + "Driver": { + "IPAddress": "192.168.64.53", + "MachineName": "p3_invalid_profile_config", + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p3_invalid_profile_config/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "Boot2DockerURL": "file:///Users/someuser/.minikube/cache/iso/minikube-v1.2.0.iso", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p3_invalid_profile_config", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "759044c8-aee4-11e9-bd0a-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p3_invalid_profile_config/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p3_invalid_profile_config/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "DriverName": "hyperkit", + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertPath": "/Users/someuser/.minikube/machines/server.pem", + "ServerKeyPath": "/Users/someuser/.minikube/machines/server-key.pem", + "ClientKeyPath": "/Users/someuser/.minikube/certs/key.pem", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, + "Name": "p3_invalid_profile_config" +} diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/machines/p4_partial_profile_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p4_partial_profile_config/config.json new file mode 100644 index 0000000000..14b416d3c2 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p4_partial_profile_config/config.json @@ -0,0 +1,80 @@ +{ + "ConfigVersion": 3, + "Driver": { + "IPAddress": "192.168.64.54", + "MachineName": "p4_partial_profile_config", + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p4_partial_profile_config/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "Boot2DockerURL": "file:///Users/someuser/.minikube/cache/iso/minikube-v1.2.0.iso", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p4_partial_profile_config", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "faf8face-aee4-11e9-9ba1-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p4_partial_profile_config/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p4_partial_profile_config/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "DriverName": "hyperkit", + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertPath": "/Users/someuser/.minikube/machines/server.pem", + "ServerKeyPath": "/Users/someuser/.minikube/machines/server-key.pem", + "ClientKeyPath": "/Users/someuser/.minikube/certs/key.pem", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, + "Name": "p4_partial_profile_config" +} diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/machines/p6_empty_machine_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p6_empty_machine_config/config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/machines/p7_invalid_machine_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p7_invalid_machine_config/config.json new file mode 100644 index 0000000000..581f9e648f --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p7_invalid_machine_config/config.json @@ -0,0 +1 @@ +invalid json file :) diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/machines/p8_partial_machine_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p8_partial_machine_config/config.json new file mode 100644 index 0000000000..d4525a7861 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/machines/p8_partial_machine_config/config.json @@ -0,0 +1,72 @@ +{ + "ConfigVersion": 3, + "Driver": { + "SSHUser": "docker", + "SSHPort": 22, + "SSHKeyPath": "/Users/someuser/.minikube/machines/p8_partial_machine_config/id_rsa", + "StorePath": "/Users/someuser/.minikube", + "SwarmMaster": false, + "SwarmHost": "", + "SwarmDiscovery": "", + "DiskSize": 20000, + "CPU": 2, + "Memory": 2000, + "Cmdline": "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=p8_partial_machine_config", + "NFSShares": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "4f16872c-aee8-11e9-8815-8c8590c3b8b4", + "BootKernel": "/Users/someuser/.minikube/machines/p8_partial_machine_config/b2d-image/boot/bzImage", + "BootInitrd": "/Users/someuser/.minikube/machines/p8_partial_machine_config/b2d-image/boot/initrd", + "Initrd": "initrd", + "Vmlinuz": "bzImage" + }, + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": null, + "Dns": null, + "GraphDir": "", + "Env": null, + "Ipv6": false, + "InsecureRegistry": [ + "10.96.0.0/12" + ], + "Labels": null, + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsVerify": false, + "RegistryMirror": null, + "InstallURL": "" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Agent": false, + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "ArbitraryFlags": null, + "ArbitraryJoinFlags": null, + "Env": null, + "IsExperimental": false + }, + "AuthOptions": { + "CertDir": "/Users/someuser/.minikube", + "CaCertPath": "/Users/someuser/.minikube/certs/ca.pem", + "CaPrivateKeyPath": "/Users/someuser/.minikube/certs/ca-key.pem", + "CaCertRemotePath": "", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "ClientCertPath": "/Users/someuser/.minikube/certs/cert.pem", + "ServerCertSANs": null, + "StorePath": "/Users/someuser/.minikube" + } + }, +} diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p1/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p1/config.json new file mode 100644 index 0000000000..00655266e6 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p1/config.json @@ -0,0 +1,49 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "VMDriver": "hyperkit", + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "KubernetesVersion": "v1.15.0", + "NodeIP": "192.168.64.50", + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} \ No newline at end of file diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p2_empty_profile_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p2_empty_profile_config/config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p3_invalid_profile_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p3_invalid_profile_config/config.json new file mode 100644 index 0000000000..9e2e347718 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p3_invalid_profile_config/config.json @@ -0,0 +1 @@ +invalid json file :) \ No newline at end of file diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p4_partial_profile_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p4_partial_profile_config/config.json new file mode 100644 index 0000000000..29f62c0149 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p4_partial_profile_config/config.json @@ -0,0 +1,47 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "XhyveDiskDriver": "ahci-hd", + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} \ No newline at end of file diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p5_missing_machine_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p5_missing_machine_config/config.json new file mode 100644 index 0000000000..1e0980752a --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p5_missing_machine_config/config.json @@ -0,0 +1,49 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "VMDriver": "hyperkit", + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "KubernetesVersion": "v1.15.0", + "NodeIP": "192.168.64.55", + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} \ No newline at end of file diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p6_empty_machine_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p6_empty_machine_config/config.json new file mode 100644 index 0000000000..a1557f6df1 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p6_empty_machine_config/config.json @@ -0,0 +1,49 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "VMDriver": "hyperkit", + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "KubernetesVersion": "v1.15.0", + "NodeIP": "192.168.64.57", + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} \ No newline at end of file diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p7_invalid_machine_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p7_invalid_machine_config/config.json new file mode 100644 index 0000000000..ea47981d29 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p7_invalid_machine_config/config.json @@ -0,0 +1,49 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "VMDriver": "hyperkit", + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "KubernetesVersion": "v1.15.0", + "NodeIP": "192.168.64.59", + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} \ No newline at end of file diff --git a/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p8_partial_machine_config/config.json b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p8_partial_machine_config/config.json new file mode 100644 index 0000000000..789b9b5557 --- /dev/null +++ b/pkg/minikube/config/testdata/delete-single/.minikube/profiles/p8_partial_machine_config/config.json @@ -0,0 +1,49 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "VMDriver": "hyperkit", + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "KubernetesVersion": "v1.15.0", + "NodeIP": "192.168.64.60", + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} diff --git a/pkg/minikube/config/testdata/.minikube/profiles/p1/config.json b/pkg/minikube/config/testdata/profile/.minikube/profiles/p1/config.json similarity index 100% rename from pkg/minikube/config/testdata/.minikube/profiles/p1/config.json rename to pkg/minikube/config/testdata/profile/.minikube/profiles/p1/config.json diff --git a/pkg/minikube/config/testdata/.minikube/profiles/p2/config.json b/pkg/minikube/config/testdata/profile/.minikube/profiles/p2/config.json similarity index 100% rename from pkg/minikube/config/testdata/.minikube/profiles/p2/config.json rename to pkg/minikube/config/testdata/profile/.minikube/profiles/p2/config.json diff --git a/pkg/minikube/config/testdata/profile/.minikube/profiles/p3_empty/config.json b/pkg/minikube/config/testdata/profile/.minikube/profiles/p3_empty/config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pkg/minikube/config/testdata/profile/.minikube/profiles/p4_invalid_file/config.json b/pkg/minikube/config/testdata/profile/.minikube/profiles/p4_invalid_file/config.json new file mode 100644 index 0000000000..9e2e347718 --- /dev/null +++ b/pkg/minikube/config/testdata/profile/.minikube/profiles/p4_invalid_file/config.json @@ -0,0 +1 @@ +invalid json file :) \ No newline at end of file diff --git a/pkg/minikube/config/testdata/profile/.minikube/profiles/p5_partial_config/config.json b/pkg/minikube/config/testdata/profile/.minikube/profiles/p5_partial_config/config.json new file mode 100644 index 0000000000..29f62c0149 --- /dev/null +++ b/pkg/minikube/config/testdata/profile/.minikube/profiles/p5_partial_config/config.json @@ -0,0 +1,47 @@ +{ + "MachineConfig": { + "KeepContext": false, + "MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso", + "Memory": 2000, + "CPUs": 2, + "DiskSize": 20000, + "ContainerRuntime": "docker", + "HyperkitVpnKitSock": "", + "HyperkitVSockPorts": [], + "XhyveDiskDriver": "ahci-hd", + "DockerEnv": null, + "InsecureRegistry": null, + "RegistryMirror": null, + "HostOnlyCIDR": "192.168.99.1/24", + "HypervVirtualSwitch": "", + "KVMNetwork": "default", + "KVMQemuURI": "qemu:///system", + "KVMGPU": false, + "KVMHidden": false, + "DockerOpt": null, + "DisableDriverMounts": false, + "NFSShare": [], + "NFSSharesRoot": "/nfsshares", + "UUID": "", + "NoVTXCheck": false, + "DNSProxy": false, + "HostDNSResolver": true + }, + "KubernetesConfig": { + "NodePort": 8443, + "NodeName": "minikube", + "APIServerName": "minikubeCA", + "APIServerNames": null, + "APIServerIPs": null, + "DNSDomain": "cluster.local", + "ContainerRuntime": "docker", + "CRISocket": "", + "NetworkPlugin": "", + "FeatureGates": "", + "ServiceCIDR": "10.96.0.0/12", + "ImageRepository": "", + "ExtraOptions": null, + "ShouldLoadCachedImages": true, + "EnableDefaultCNI": false + } +} \ No newline at end of file diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index 4d3a2bac4b..ba0aeacf10 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -19,7 +19,6 @@ package constants import ( "fmt" "path/filepath" - "time" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" @@ -28,38 +27,14 @@ import ( ) const ( - APIServerPort = 8443 - APIServerName = "minikubeCA" + // APIServerPort is the default API server port + APIServerPort = 8443 + // APIServerName is the default API server name + APIServerName = "minikubeCA" + // ClusterDNSDomain is the default DNS domain ClusterDNSDomain = "cluster.local" ) -// DriverMock is a mock driver. -const DriverMock = "mock-driver" - -// DriverNone is the none driver. -const DriverNone = "none" - -// DriverKvm2 is the kvm2 driver option name for in linux -const DriverKvm2 = "kvm2" - -// DriverVirtualbox is the virtualbox driver option name -const DriverVirtualbox = "virtualbox" - -// DriverHyperkit is the hyperkit driver option name for mac os -const DriverHyperkit = "hyperkit" - -// DriverVmware is the vmware driver option name -const DriverVmware = "vmware" - -// DriverVmwareFusion is the vmware fusion driver option -const DriverVmwareFusion = "vmwarefusion" - -// DriverHyperv is the hyperv driver option for windows -const DriverHyperv = "hyperv" - -// DriverParallels is the parallels driver option name -const DriverParallels = "parallels" - // DefaultMinipath is the default Minikube path (under the home directory) var DefaultMinipath = filepath.Join(homedir.HomeDir(), ".minikube") @@ -69,70 +44,18 @@ var KubeconfigPath = clientcmd.RecommendedHomeFile // KubeconfigEnvVar is the env var to check for the Kubernetes client config var KubeconfigEnvVar = clientcmd.RecommendedConfigPathEnvVar -// MinikubeContext is the kubeconfig context name used for minikube -const MinikubeContext = "minikube" - -// MinikubeEnvPrefix is the prefix for the environmental variables -const MinikubeEnvPrefix = "MINIKUBE" - // DefaultMachineName is the default name for the VM const DefaultMachineName = "minikube" // DefaultNodeName is the default name for the kubeadm node within the VM const DefaultNodeName = "minikube" -// DefaultStorageClassProvisioner is the name of the default storage class provisioner -const DefaultStorageClassProvisioner = "standard" - -// Cache is used to modify the cache field in the config file -const Cache = "cache" - // MountProcessFileName is the filename of the mount process var MountProcessFileName = ".mount-process" const ( - // DefaultEmbedCerts is if the certs should be embedded in the kubeconfig file - DefaultEmbedCerts = false - // DefaultKeepContext is if we should keep context by default - DefaultKeepContext = false // SHASuffix is the suffix of a SHA-256 checksum file SHASuffix = ".sha256" - // DefaultMemorySize is the default memory which will be allocated to minikube, in megabytes - DefaultMemorySize = "2000mb" - // MinimumMemorySize is the minimum memory size, in megabytes - MinimumMemorySize = "1024mb" - // DefaultCPUS is the default number of cpus of a host - DefaultCPUS = 2 - // MinimumCPUS is the minimum number of cpus of a host - MinimumCPUS = 2 - // DefaultDiskSize is the default disk image size, in megabytes - DefaultDiskSize = "20000mb" - // MinimumDiskSize is the minimum disk image size, in megabytes - MinimumDiskSize = "2000mb" - // DefaultVMDriver is the default virtual machine driver name - DefaultVMDriver = DriverVirtualbox - // DefaultStatusFormat is the default format of a host - DefaultStatusFormat = `host: {{.Host}} -kubelet: {{.Kubelet}} -apiserver: {{.APIServer}} -kubectl: {{.Kubeconfig}} -` - // DefaultAddonListFormat is the default format of addon list - DefaultAddonListFormat = "- {{.AddonName}}: {{.AddonStatus}}\n" - // DefaultConfigViewFormat is the default format of config view - DefaultConfigViewFormat = "- {{.ConfigKey}}: {{.ConfigValue}}\n" - // DefaultCacheListFormat is the default format of cache list - DefaultCacheListFormat = "{{.CacheImage}}\n" - // GithubMinikubeReleasesURL is the URL of the minikube github releases JSON file - GithubMinikubeReleasesURL = "https://storage.googleapis.com/minikube/releases.json" - // DefaultWait is the default wait time, in seconds - DefaultWait = 20 - // DefaultInterval is the default interval, in seconds - DefaultInterval = 6 - // DefaultK8sClientTimeout is the default kubernetes client timeout - DefaultK8sClientTimeout = 60 * time.Second - // DefaultClusterBootstrapper is the default cluster bootstrapper - DefaultClusterBootstrapper = "kubeadm" ) // DefaultISOURL is the default location of the minikube.iso file @@ -142,50 +65,17 @@ var DefaultISOURL = fmt.Sprintf("https://storage.googleapis.com/%s/minikube-%s.i var DefaultISOSHAURL = DefaultISOURL + SHASuffix // DefaultKubernetesVersion is the default kubernetes version -var DefaultKubernetesVersion = "v1.16.0" +var DefaultKubernetesVersion = "v1.16.2" // NewestKubernetesVersion is the newest Kubernetes version to test against -var NewestKubernetesVersion = "v1.16.0" +var NewestKubernetesVersion = "v1.16.2" // OldestKubernetesVersion is the oldest Kubernetes version to test against var OldestKubernetesVersion = "v1.11.10" -// ConfigFile is the path of the config file -var ConfigFile = localpath.MakeMiniPath("config", "config.json") - const ( - // KubeletServiceFile is the path to the kubelet systemd service - KubeletServiceFile = "/lib/systemd/system/kubelet.service" - // KubeletSystemdConfFile is the path to the kubelet systemd configuration - KubeletSystemdConfFile = "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf" - // DefaultCNIConfigPath is the path to the CNI configuration - DefaultCNIConfigPath = "/etc/cni/net.d/k8s.conf" - - // GuestAddonsDir is the default path of the addons configuration - GuestAddonsDir = "/etc/kubernetes/addons" - // GuestManifestsDir is where the kubelet should look for static Pod manifests - GuestManifestsDir = "/etc/kubernetes/manifests" - // GuestEphemeralDir is the path where ephemeral data should be stored within the VM - GuestEphemeralDir = "/var/tmp/minikube" - // PersistentDir is the path where persistent data should be stored within the VM (not tmpfs) - GuestPersistentDir = "/var/lib/minikube" - // GuestCertsDir are where Kubernetes certificates are kept on the guest - GuestCertsDir = GuestPersistentDir + "/certs" - // DefaultUfsPort is the default port of UFS - DefaultUfsPort = "5640" - // DefaultUfsDebugLvl is the default debug level of UFS - DefaultUfsDebugLvl = 0 - // DefaultMountEndpoint is the default mount endpoint - DefaultMountEndpoint = "/minikube-host" - // DefaultMsize is the default number of bytes to use for 9p packet payload - DefaultMsize = 262144 - // DefaultMountVersion is the default 9p version to use for mount - DefaultMountVersion = "9p2000.L" - // IsMinikubeChildProcess is the name of "is minikube child process" variable IsMinikubeChildProcess = "IS_MINIKUBE_CHILD_PROCESS" - // FileScheme is the file scheme - FileScheme = "file" ) // ImageRepositories contains all known image repositories @@ -203,16 +93,7 @@ var ImageCacheDir = localpath.MakeMiniPath("cache", "images") const ( // GvisorFilesPath is the path to the gvisor files saved by go-bindata GvisorFilesPath = "/tmp/gvisor" - // ContainerdConfigTomlPath is the path to the containerd config.toml - ContainerdConfigTomlPath = "/etc/containerd/config.toml" - // StoredContainerdConfigTomlPath is the path where the default config.toml will be stored - StoredContainerdConfigTomlPath = "/tmp/config.toml" // GvisorConfigTomlTargetName is the go-bindata target name for the gvisor config.toml GvisorConfigTomlTargetName = "gvisor-config.toml" - - // GvisorContainerdShimURL is the url to download gvisor-containerd-shim - GvisorContainerdShimURL = "https://github.com/google/gvisor-containerd-shim/releases/download/v0.0.3/containerd-shim-runsc-v1.linux-amd64" - // GvisorURL is the url to download gvisor - GvisorURL = "https://storage.googleapis.com/gvisor/releases/nightly/2019-01-14/runsc" ) diff --git a/pkg/minikube/constants/constants_darwin.go b/pkg/minikube/constants/constants_darwin.go index 707f5f9925..a10fe2be02 100644 --- a/pkg/minikube/constants/constants_darwin.go +++ b/pkg/minikube/constants/constants_darwin.go @@ -18,13 +18,5 @@ limitations under the License. package constants +// DefaultMountDir is the default mounting directory for Darwin var DefaultMountDir = "/Users" - -// SupportedVMDrivers is a list of supported drivers on Darwin. -var SupportedVMDrivers = [...]string{ - DriverVirtualbox, - DriverParallels, - DriverVmwareFusion, - DriverHyperkit, - DriverVmware, -} diff --git a/pkg/minikube/constants/constants_gendocs.go b/pkg/minikube/constants/constants_gendocs.go index 19eceaf26b..fae58648eb 100644 --- a/pkg/minikube/constants/constants_gendocs.go +++ b/pkg/minikube/constants/constants_gendocs.go @@ -19,15 +19,3 @@ limitations under the License. package constants var DefaultMountDir = "$HOME" - -// SupportedVMDrivers is a list of supported drivers on all platforms. -var SupportedVMDrivers = [...]string{ - DriverVirtualbox, - DriverParallels, - DriverVmwareFusion, - DriverHyperv, - DriverHyperkit, - DriverKvm2, - DriverVmware, - DriverNone, -} diff --git a/pkg/minikube/constants/constants_linux.go b/pkg/minikube/constants/constants_linux.go index 0fa300617f..77a47a765f 100644 --- a/pkg/minikube/constants/constants_linux.go +++ b/pkg/minikube/constants/constants_linux.go @@ -24,13 +24,3 @@ import ( // DefaultMountDir is the default mount dir var DefaultMountDir = homedir.HomeDir() - -// SupportedVMDrivers is a list of supported drivers on Linux. -var SupportedVMDrivers = [...]string{ - DriverVirtualbox, - DriverParallels, - DriverVmwareFusion, - DriverKvm2, - DriverVmware, - DriverNone, -} diff --git a/pkg/minikube/constants/constants_windows.go b/pkg/minikube/constants/constants_windows.go index fb0139964a..da58387eb9 100644 --- a/pkg/minikube/constants/constants_windows.go +++ b/pkg/minikube/constants/constants_windows.go @@ -23,11 +23,3 @@ import ( ) var DefaultMountDir = homedir.HomeDir() - -// SupportedVMDrivers is a list of supported drivers on Windows. -var SupportedVMDrivers = [...]string{ - DriverVirtualbox, - DriverVmwareFusion, - DriverHyperv, - DriverVmware, -} diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index 88d9e17606..934f65c416 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -17,17 +17,101 @@ limitations under the License. package cruntime import ( + "bytes" + "encoding/base64" "fmt" + "os/exec" + "path" "strings" + "text/template" "github.com/golang/glog" + "github.com/pkg/errors" + "k8s.io/minikube/pkg/minikube/bootstrapper/images" "k8s.io/minikube/pkg/minikube/out" ) +const ( + // ContainerdConfFile is the path to the containerd configuration + containerdConfigFile = "/etc/containerd/config.toml" + containerdConfigTemplate = `root = "/var/lib/containerd" +state = "/run/containerd" +oom_score = 0 + +[grpc] + address = "/run/containerd/containerd.sock" + uid = 0 + gid = 0 + max_recv_message_size = 16777216 + max_send_message_size = 16777216 + +[debug] + address = "" + uid = 0 + gid = 0 + level = "" + +[metrics] + address = "" + grpc_histogram = false + +[cgroup] + path = "" + +[plugins] + [plugins.cgroups] + no_prometheus = false + [plugins.cri] + stream_server_address = "" + stream_server_port = "10010" + enable_selinux = false + sandbox_image = "{{ .PodInfraContainerImage }}" + stats_collect_period = 10 + systemd_cgroup = false + enable_tls_streaming = false + max_container_log_line_size = 16384 + [plugins.cri.containerd] + snapshotter = "overlayfs" + no_pivot = true + [plugins.cri.containerd.default_runtime] + runtime_type = "io.containerd.runtime.v1.linux" + runtime_engine = "" + runtime_root = "" + [plugins.cri.containerd.untrusted_workload_runtime] + runtime_type = "" + runtime_engine = "" + runtime_root = "" + [plugins.cri.cni] + bin_dir = "/opt/cni/bin" + conf_dir = "/etc/cni/net.d" + conf_template = "" + [plugins.cri.registry] + [plugins.cri.registry.mirrors] + [plugins.cri.registry.mirrors."docker.io"] + endpoint = ["https://registry-1.docker.io"] + [plugins.diff-service] + default = ["walking"] + [plugins.linux] + shim = "containerd-shim" + runtime = "runc" + runtime_root = "" + no_shim = false + shim_debug = false + [plugins.scheduler] + pause_threshold = 0.02 + deletion_threshold = 0 + mutation_threshold = 100 + schedule_delay = "0s" + startup_delay = "100ms" +` +) + // Containerd contains containerd runtime state type Containerd struct { - Socket string - Runner CommandRunner + Socket string + Runner CommandRunner + ImageRepository string + KubernetesVersion string } // Name is a human readable name for containerd @@ -42,17 +126,17 @@ func (r *Containerd) Style() out.StyleEnum { // Version retrieves the current version of this runtime func (r *Containerd) Version() (string, error) { - ver, err := r.Runner.CombinedOutput("containerd --version") + c := exec.Command("containerd", "--version") + rr, err := r.Runner.RunCmd(c) if err != nil { - return "", err + return "", errors.Wrapf(err, "containerd check version.") } - // containerd github.com/containerd/containerd v1.2.0 c4446665cb9c30056f4998ed953e6d4ff22c7c39 - words := strings.Split(ver, " ") + words := strings.Split(rr.Stdout.String(), " ") if len(words) >= 4 && words[0] == "containerd" { return strings.Replace(words[2], "v", "", 1), nil } - return "", fmt.Errorf("unknown version: %q", ver) + return "", fmt.Errorf("unknown version: %q", rr.Stdout.String()) } // SocketPath returns the path to the socket file for containerd @@ -70,13 +154,38 @@ func (r *Containerd) DefaultCNI() bool { // Active returns if containerd is active on the host func (r *Containerd) Active() bool { - err := r.Runner.Run("systemctl is-active --quiet service containerd") + c := exec.Command("systemctl", "is-active", "--quiet", "service", "containerd") + _, err := r.Runner.RunCmd(c) return err == nil } // Available returns an error if it is not possible to use this runtime on a host func (r *Containerd) Available() error { - return r.Runner.Run("command -v containerd") + c := exec.Command("which", "containerd") + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "check containerd availability.") + } + return nil +} + +// generateContainerdConfig sets up /etc/containerd/config.toml +func generateContainerdConfig(cr CommandRunner, imageRepository string, k8sVersion string) error { + cPath := containerdConfigFile + t, err := template.New("containerd.config.toml").Parse(containerdConfigTemplate) + if err != nil { + return err + } + pauseImage := images.PauseImage(imageRepository, k8sVersion) + opts := struct{ PodInfraContainerImage string }{PodInfraContainerImage: pauseImage} + var b bytes.Buffer + if err := t.Execute(&b, opts); err != nil { + return err + } + c := exec.Command("/bin/bash", "-c", fmt.Sprintf("sudo mkdir -p %s && printf %%s \"%s\" | base64 -d | sudo tee %s", path.Dir(cPath), base64.StdEncoding.EncodeToString(b.Bytes()), cPath)) + if _, err := cr.RunCmd(c); err != nil { + return errors.Wrap(err, "generate containerd cfg.") + } + return nil } // Enable idempotently enables containerd on a host @@ -89,22 +198,37 @@ func (r *Containerd) Enable(disOthers bool) error { if err := populateCRIConfig(r.Runner, r.SocketPath()); err != nil { return err } + if err := generateContainerdConfig(r.Runner, r.ImageRepository, r.KubernetesVersion); err != nil { + return err + } if err := enableIPForwarding(r.Runner); err != nil { return err } // Otherwise, containerd will fail API requests with 'Unimplemented' - return r.Runner.Run("sudo systemctl restart containerd") + c := exec.Command("sudo", "systemctl", "restart", "containerd") + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "restart containerd") + } + return nil } // Disable idempotently disables containerd on a host func (r *Containerd) Disable() error { - return r.Runner.Run("sudo systemctl stop containerd") + c := exec.Command("sudo", "systemctl", "stop", "containerd") + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrapf(err, "stop containerd") + } + return nil } // LoadImage loads an image into this runtime func (r *Containerd) LoadImage(path string) error { glog.Infof("Loading image: %s", path) - return r.Runner.Run(fmt.Sprintf("sudo ctr -n=k8s.io images import %s", path)) + c := exec.Command("sudo", "ctr", "-n=k8s.io", "images", "import", path) + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrapf(err, "ctr images import") + } + return nil } // KubeletOptions returns kubelet options for a containerd diff --git a/pkg/minikube/cruntime/cri.go b/pkg/minikube/cruntime/cri.go index 34308ada0a..173e652301 100644 --- a/pkg/minikube/cruntime/cri.go +++ b/pkg/minikube/cruntime/cri.go @@ -18,29 +18,335 @@ package cruntime import ( "bytes" + "encoding/base64" "fmt" "html/template" + "os/exec" "path" "strings" "github.com/golang/glog" + "github.com/pkg/errors" + "k8s.io/minikube/pkg/minikube/bootstrapper/images" + "k8s.io/minikube/pkg/minikube/command" +) + +const ( + // CRIOConfFile is the path to the CRI-O configuration + crioConfigFile = "/etc/crio/crio.conf" + crioConfigTemplate = `# The CRI-O configuration file specifies all of the available configuration +# options and command-line flags for the crio(8) OCI Kubernetes Container Runtime +# daemon, but in a TOML format that can be more easily modified and versioned. +# +# Please refer to crio.conf(5) for details of all configuration options. + +# CRI-O supports partial configuration reload during runtime, which can be +# done by sending SIGHUP to the running process. Currently supported options +# are explicitly mentioned with: 'This option supports live configuration +# reload'. + +# CRI-O reads its storage defaults from the containers-storage.conf(5) file +# located at /etc/containers/storage.conf. Modify this storage configuration if +# you want to change the system's defaults. If you want to modify storage just +# for CRI-O, you can change the storage configuration options here. +[crio] + +# Path to the "root directory". CRI-O stores all of its data, including +# containers images, in this directory. +root = "/var/lib/containers/storage" + +# Path to the "run directory". CRI-O stores all of its state in this directory. +runroot = "/var/run/containers/storage" + +# Storage driver used to manage the storage of images and containers. Please +# refer to containers-storage.conf(5) to see all available storage drivers. +storage_driver = "overlay" + +# List to pass options to the storage driver. Please refer to +# containers-storage.conf(5) to see all available storage options. +#storage_option = [ +#] + +# If set to false, in-memory locking will be used instead of file-based locking. +# **Deprecated** this option will be removed in the future. +file_locking = false + +# Path to the lock file. +# **Deprecated** this option will be removed in the future. +file_locking_path = "/run/crio.lock" + + +# The crio.api table contains settings for the kubelet/gRPC interface. +[crio.api] + +# Path to AF_LOCAL socket on which CRI-O will listen. +listen = "/var/run/crio/crio.sock" + +# IP address on which the stream server will listen. +stream_address = "127.0.0.1" + +# The port on which the stream server will listen. +stream_port = "0" + +# Enable encrypted TLS transport of the stream server. +stream_enable_tls = false + +# Path to the x509 certificate file used to serve the encrypted stream. This +# file can change, and CRI-O will automatically pick up the changes within 5 +# minutes. +stream_tls_cert = "" + +# Path to the key file used to serve the encrypted stream. This file can +# change, and CRI-O will automatically pick up the changes within 5 minutes. +stream_tls_key = "" + +# Path to the x509 CA(s) file used to verify and authenticate client +# communication with the encrypted stream. This file can change, and CRI-O will +# automatically pick up the changes within 5 minutes. +stream_tls_ca = "" + +# Maximum grpc send message size in bytes. If not set or <=0, then CRI-O will default to 16 * 1024 * 1024. +grpc_max_send_msg_size = 16777216 + +# Maximum grpc receive message size. If not set or <= 0, then CRI-O will default to 16 * 1024 * 1024. +grpc_max_recv_msg_size = 16777216 + +# The crio.runtime table contains settings pertaining to the OCI runtime used +# and options for how to set up and manage the OCI runtime. +[crio.runtime] + +# A list of ulimits to be set in containers by default, specified as +# "<ulimit name>=<soft limit>:<hard limit>", for example: +# "nofile=1024:2048" +# If nothing is set here, settings will be inherited from the CRI-O daemon +#default_ulimits = [ +#] + +# default_runtime is the _name_ of the OCI runtime to be used as the default. +# The name is matched against the runtimes map below. +default_runtime = "runc" + +# If true, the runtime will not use pivot_root, but instead use MS_MOVE. +no_pivot = true + +# Path to the conmon binary, used for monitoring the OCI runtime. +conmon = "/usr/libexec/crio/conmon" + +# Cgroup setting for conmon +conmon_cgroup = "pod" + +# Environment variable list for the conmon process, used for passing necessary +# environment variables to conmon or the runtime. +conmon_env = [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", +] + +# If true, SELinux will be used for pod separation on the host. +selinux = false + +# Path to the seccomp.json profile which is used as the default seccomp profile +# for the runtime. If not specified, then the internal default seccomp profile +# will be used. +seccomp_profile = "" + +# Used to change the name of the default AppArmor profile of CRI-O. The default +# profile name is "crio-default-" followed by the version string of CRI-O. +apparmor_profile = "crio-default" + +# Cgroup management implementation used for the runtime. +cgroup_manager = "cgroupfs" + +# List of default capabilities for containers. If it is empty or commented out, +# only the capabilities defined in the containers json file by the user/kube +# will be added. +default_capabilities = [ + "CHOWN", + "DAC_OVERRIDE", + "FSETID", + "FOWNER", + "NET_RAW", + "SETGID", + "SETUID", + "SETPCAP", + "NET_BIND_SERVICE", + "SYS_CHROOT", + "KILL", +] + +# List of default sysctls. If it is empty or commented out, only the sysctls +# defined in the container json file by the user/kube will be added. +default_sysctls = [ +] + +# List of additional devices. specified as +# "<device-on-host>:<device-on-container>:<permissions>", for example: "--device=/dev/sdc:/dev/xvdc:rwm". +#If it is empty or commented out, only the devices +# defined in the container json file by the user/kube will be added. +additional_devices = [ +] + +# Path to OCI hooks directories for automatically executed hooks. +hooks_dir = [ +] + +# List of default mounts for each container. **Deprecated:** this option will +# be removed in future versions in favor of default_mounts_file. +default_mounts = [ +] + +# Path to the file specifying the defaults mounts for each container. The +# format of the config is /SRC:/DST, one mount per line. Notice that CRI-O reads +# its default mounts from the following two files: +# +# 1) /etc/containers/mounts.conf (i.e., default_mounts_file): This is the +# override file, where users can either add in their own default mounts, or +# override the default mounts shipped with the package. +# +# 2) /usr/share/containers/mounts.conf: This is the default file read for +# mounts. If you want CRI-O to read from a different, specific mounts file, +# you can change the default_mounts_file. Note, if this is done, CRI-O will +# only add mounts it finds in this file. +# +#default_mounts_file = "" + +# Maximum number of processes allowed in a container. +pids_limit = 1024 + +# Maximum sized allowed for the container log file. Negative numbers indicate +# that no size limit is imposed. If it is positive, it must be >= 8192 to +# match/exceed conmon's read buffer. The file is truncated and re-opened so the +# limit is never exceeded. +log_size_max = -1 + +# Whether container output should be logged to journald in addition to the kuberentes log file +log_to_journald = false + +# Path to directory in which container exit files are written to by conmon. +container_exits_dir = "/var/run/crio/exits" + +# Path to directory for container attach sockets. +container_attach_socket_dir = "/var/run/crio" + +# If set to true, all containers will run in read-only mode. +read_only = false + +# Changes the verbosity of the logs based on the level it is set to. Options +# are fatal, panic, error, warn, info, and debug. This option supports live +# configuration reload. +log_level = "error" + +# The default log directory where all logs will go unless directly specified by the kubelet +log_dir = "/var/log/crio/pods" + +# The UID mappings for the user namespace of each container. A range is +# specified in the form containerUID:HostUID:Size. Multiple ranges must be +# separated by comma. +uid_mappings = "" + +# The GID mappings for the user namespace of each container. A range is +# specified in the form containerGID:HostGID:Size. Multiple ranges must be +# separated by comma. +gid_mappings = "" + +# The minimal amount of time in seconds to wait before issuing a timeout +# regarding the proper termination of the container. +ctr_stop_timeout = 0 + +# ManageNetworkNSLifecycle determines whether we pin and remove network namespace +# and manage its lifecycle. +manage_network_ns_lifecycle = false + +# The "crio.runtime.runtimes" table defines a list of OCI compatible runtimes. +# The runtime to use is picked based on the runtime_handler provided by the CRI. +# If no runtime_handler is provided, the runtime will be picked based on the level +# of trust of the workload. + +[crio.runtime.runtimes.runc] +runtime_path = "/usr/bin/runc" +runtime_type = "oci" +runtime_root = "/run/runc" + + +# The crio.image table contains settings pertaining to the management of OCI images. +# +# CRI-O reads its configured registries defaults from the system wide +# containers-registries.conf(5) located in /etc/containers/registries.conf. If +# you want to modify just CRI-O, you can change the registries configuration in +# this file. Otherwise, leave insecure_registries and registries commented out to +# use the system's defaults from /etc/containers/registries.conf. +[crio.image] + +# Default transport for pulling images from a remote container storage. +default_transport = "docker://" + +# The path to a file containing credentials necessary for pulling images from +# secure registries. The file is similar to that of /var/lib/kubelet/config.json +global_auth_file = "" + +# The image used to instantiate infra containers. +# This option supports live configuration reload. +pause_image = "{{ .PodInfraContainerImage }}" + +# The path to a file containing credentials specific for pulling the pause_image from +# above. The file is similar to that of /var/lib/kubelet/config.json +# This option supports live configuration reload. +pause_image_auth_file = "" + +# The command to run to have a container stay in the paused state. +# This option supports live configuration reload. +pause_command = "/pause" + +# Path to the file which decides what sort of policy we use when deciding +# whether or not to trust an image that we've pulled. It is not recommended that +# this option be used, as the default behavior of using the system-wide default +# policy (i.e., /etc/containers/policy.json) is most often preferred. Please +# refer to containers-policy.json(5) for more details. +signature_policy = "" + +# Controls how image volumes are handled. The valid values are mkdir, bind and +# ignore; the latter will ignore volumes entirely. +image_volumes = "mkdir" + +# List of registries to be used when pulling an unqualified image (e.g., +# "alpine:latest"). By default, registries is set to "docker.io" for +# compatibility reasons. Depending on your workload and usecase you may add more +# registries (e.g., "quay.io", "registry.fedoraproject.org", +# "registry.opensuse.org", etc.). +registries = [ + "docker.io" +] + + +# The crio.network table containers settings pertaining to the management of +# CNI plugins. +[crio.network] + +# Path to the directory where CNI configuration files are located. +network_dir = "/etc/cni/net.d/" + +# Paths to directories where CNI plugin binaries are located. +plugin_dirs = [ + "/opt/cni/bin/", +] +` ) // listCRIContainers returns a list of containers using crictl func listCRIContainers(cr CommandRunner, filter string) ([]string, error) { - var content string var err error + var rr *command.RunResult state := "Running" if filter != "" { - content, err = cr.CombinedOutput(fmt.Sprintf(`sudo crictl ps -a --name=%s --state=%s --quiet`, filter, state)) + c := exec.Command("sudo", "crictl", "ps", "-a", fmt.Sprintf("--name=%s", filter), fmt.Sprintf("--state=%s", state), "--quiet") + rr, err = cr.RunCmd(c) } else { - content, err = cr.CombinedOutput(fmt.Sprintf(`sudo crictl ps -a --state=%s --quiet`, state)) + rr, err = cr.RunCmd(exec.Command("sudo", "crictl", "ps", "-a", fmt.Sprintf("--state=%s", state), "--quiet")) } if err != nil { return nil, err } var ids []string - for _, line := range strings.Split(content, "\n") { + for _, line := range strings.Split(rr.Stderr.String(), "\n") { if line != "" { ids = append(ids, line) } @@ -54,7 +360,13 @@ func killCRIContainers(cr CommandRunner, ids []string) error { return nil } glog.Infof("Killing containers: %s", ids) - return cr.Run(fmt.Sprintf("sudo crictl rm %s", strings.Join(ids, " "))) + + args := append([]string{"crictl", "rm"}, ids...) + c := exec.Command("sudo", args...) + if _, err := cr.RunCmd(c); err != nil { + return errors.Wrap(err, "kill cri containers.") + } + return nil } // stopCRIContainers stops containers using crictl @@ -63,7 +375,13 @@ func stopCRIContainers(cr CommandRunner, ids []string) error { return nil } glog.Infof("Stopping containers: %s", ids) - return cr.Run(fmt.Sprintf("sudo crictl stop %s", strings.Join(ids, " "))) + args := append([]string{"crictl", "rm"}, ids...) + c := exec.Command("sudo", args...) + if _, err := cr.RunCmd(c); err != nil { + return errors.Wrap(err, "stop cri containers") + } + return nil + } // populateCRIConfig sets up /etc/crictl.yaml @@ -81,7 +399,32 @@ image-endpoint: unix://{{.Socket}} if err := t.Execute(&b, opts); err != nil { return err } - return cr.Run(fmt.Sprintf("sudo mkdir -p %s && printf %%s \"%s\" | sudo tee %s", path.Dir(cPath), b.String(), cPath)) + c := exec.Command("/bin/bash", "-c", fmt.Sprintf("sudo mkdir -p %s && printf %%s \"%s\" | sudo tee %s", path.Dir(cPath), b.String(), cPath)) + if rr, err := cr.RunCmd(c); err != nil { + return errors.Wrapf(err, "Run: %q", rr.Command()) + } + return nil +} + +// generateCRIOConfig sets up /etc/crio/crio.conf +func generateCRIOConfig(cr CommandRunner, imageRepository string, k8sVersion string) error { + cPath := crioConfigFile + t, err := template.New("crio.conf").Parse(crioConfigTemplate) + if err != nil { + return err + } + pauseImage := images.PauseImage(imageRepository, k8sVersion) + opts := struct{ PodInfraContainerImage string }{PodInfraContainerImage: pauseImage} + var b bytes.Buffer + if err := t.Execute(&b, opts); err != nil { + return err + } + + c := exec.Command("/bin/bash", "-c", fmt.Sprintf("sudo mkdir -p %s && printf %%s \"%s\" | base64 -d | sudo tee %s", path.Dir(cPath), base64.StdEncoding.EncodeToString(b.Bytes()), cPath)) + if _, err := cr.RunCmd(c); err != nil { + return errors.Wrap(err, "generateCRIOConfig.") + } + return nil } // criContainerLogCmd returns the command to retrieve the log for a container based on ID diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index 4c9a8bb192..763e52ac73 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -18,16 +18,20 @@ package cruntime import ( "fmt" + "os/exec" "strings" "github.com/golang/glog" + "github.com/pkg/errors" "k8s.io/minikube/pkg/minikube/out" ) // CRIO contains CRIO runtime state type CRIO struct { - Socket string - Runner CommandRunner + Socket string + Runner CommandRunner + ImageRepository string + KubernetesVersion string } // Name is a human readable name for CRIO @@ -42,14 +46,15 @@ func (r *CRIO) Style() out.StyleEnum { // Version retrieves the current version of this runtime func (r *CRIO) Version() (string, error) { - ver, err := r.Runner.CombinedOutput("crio --version") + c := exec.Command("crio", "--version") + rr, err := r.Runner.RunCmd(c) if err != nil { - return "", err + return "", errors.Wrap(err, "crio version.") } // crio version 1.13.0 // commit: "" - line := strings.Split(ver, "\n")[0] + line := strings.Split(rr.Stdout.String(), "\n")[0] return strings.Replace(line, "crio version ", "", 1), nil } @@ -68,12 +73,18 @@ func (r *CRIO) DefaultCNI() bool { // Available returns an error if it is not possible to use this runtime on a host func (r *CRIO) Available() error { - return r.Runner.Run("command -v crio") + c := exec.Command("which", "crio") + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrapf(err, "check crio available.") + } + return nil + } // Active returns if CRIO is active on the host func (r *CRIO) Active() bool { - err := r.Runner.Run("systemctl is-active --quiet service crio") + c := exec.Command("systemctl", "is-active", "--quiet", "service", "crio") + _, err := r.Runner.RunCmd(c) return err == nil } @@ -87,21 +98,35 @@ func (r *CRIO) Enable(disOthers bool) error { if err := populateCRIConfig(r.Runner, r.SocketPath()); err != nil { return err } + if err := generateCRIOConfig(r.Runner, r.ImageRepository, r.KubernetesVersion); err != nil { + return err + } if err := enableIPForwarding(r.Runner); err != nil { return err } - return r.Runner.Run("sudo systemctl restart crio") + + if _, err := r.Runner.RunCmd(exec.Command("sudo", "systemctl", "restart", "crio")); err != nil { + return errors.Wrapf(err, "enable crio.") + } + return nil } // Disable idempotently disables CRIO on a host func (r *CRIO) Disable() error { - return r.Runner.Run("sudo systemctl stop crio") + if _, err := r.Runner.RunCmd(exec.Command("sudo", "systemctl", "stop", "crio")); err != nil { + return errors.Wrapf(err, "disable crio.") + } + return nil } // LoadImage loads an image into this runtime func (r *CRIO) LoadImage(path string) error { glog.Infof("Loading image: %s", path) - return r.Runner.Run(fmt.Sprintf("sudo podman load -i %s", path)) + c := exec.Command("sudo", "podman", "load", "-i", path) + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "crio load image") + } + return nil } // KubeletOptions returns kubelet options for a runtime. diff --git a/pkg/minikube/cruntime/cruntime.go b/pkg/minikube/cruntime/cruntime.go index b7b74e1310..6566a8d8cc 100644 --- a/pkg/minikube/cruntime/cruntime.go +++ b/pkg/minikube/cruntime/cruntime.go @@ -19,16 +19,17 @@ package cruntime import ( "fmt" + "os/exec" "github.com/golang/glog" "github.com/pkg/errors" + "k8s.io/minikube/pkg/minikube/command" "k8s.io/minikube/pkg/minikube/out" ) // CommandRunner is the subset of command.Runner this package consumes type CommandRunner interface { - Run(string) error - CombinedOutput(string) (string, error) + RunCmd(cmd *exec.Cmd) (*command.RunResult, error) } // Manager is a common interface for container runtimes @@ -78,6 +79,10 @@ type Config struct { Socket string // Runner is the CommandRunner object to execute commands with Runner CommandRunner + // ImageRepository image repository to download image from + ImageRepository string + // KubernetesVersion Kubernetes version + KubernetesVersion string } // New returns an appropriately configured runtime @@ -86,9 +91,9 @@ func New(c Config) (Manager, error) { case "", "docker": return &Docker{Socket: c.Socket, Runner: c.Runner}, nil case "crio", "cri-o": - return &CRIO{Socket: c.Socket, Runner: c.Runner}, nil + return &CRIO{Socket: c.Socket, Runner: c.Runner, ImageRepository: c.ImageRepository, KubernetesVersion: c.KubernetesVersion}, nil case "containerd": - return &Containerd{Socket: c.Socket, Runner: c.Runner}, nil + return &Containerd{Socket: c.Socket, Runner: c.Runner, ImageRepository: c.ImageRepository, KubernetesVersion: c.KubernetesVersion}, nil default: return nil, fmt.Errorf("unknown runtime type: %q", c.Type) } @@ -126,11 +131,14 @@ func disableOthers(me Manager, cr CommandRunner) error { // enableIPForwarding configures IP forwarding, which is handled normally by Docker // Context: https://github.com/kubernetes/kubeadm/issues/1062 func enableIPForwarding(cr CommandRunner) error { - if err := cr.Run("sudo modprobe br_netfilter"); err != nil { + c := exec.Command("sudo", "modprobe", "br_netfilter") + if _, err := cr.RunCmd(c); err != nil { return errors.Wrap(err, "br_netfilter") } - if err := cr.Run("sudo sh -c \"echo 1 > /proc/sys/net/ipv4/ip_forward\""); err != nil { - return errors.Wrap(err, "ip_forward") + + c = exec.Command("sudo", "sh", "-c", "echo 1 > /proc/sys/net/ipv4/ip_forward") + if _, err := cr.RunCmd(c); err != nil { + return errors.Wrapf(err, "ip_forward") } return nil } diff --git a/pkg/minikube/cruntime/cruntime_test.go b/pkg/minikube/cruntime/cruntime_test.go index a439269121..13ed8bdde6 100644 --- a/pkg/minikube/cruntime/cruntime_test.go +++ b/pkg/minikube/cruntime/cruntime_test.go @@ -17,12 +17,16 @@ limitations under the License. package cruntime import ( + "bytes" "fmt" + "os/exec" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/pkg/errors" + "k8s.io/minikube/pkg/minikube/command" ) func TestName(t *testing.T) { @@ -110,38 +114,47 @@ func NewFakeRunner(t *testing.T) *FakeRunner { } } -// Run a fake command! -func (f *FakeRunner) CombinedOutput(cmd string) (string, error) { - f.cmds = append(f.cmds, cmd) - - root := false - args := strings.Split(cmd, " ") - bin, args := args[0], args[1:] - f.t.Logf("bin=%s args=%v", bin, args) - if bin == "sudo" { - root = true - bin, args = args[0], args[1:] +func buffer(s string, err error) (*command.RunResult, error) { + rr := &command.RunResult{} + if err != nil { + return rr, err } - switch bin { - case "systemctl": - return f.systemctl(args, root) - case "docker": - return f.docker(args, root) - case "crictl": - return f.crictl(args, root) - case "crio": - return f.crio(args, root) - case "containerd": - return f.containerd(args, root) - default: - return "", nil + var buf bytes.Buffer + _, err = buf.WriteString(s) + if err != nil { + return rr, errors.Wrap(err, "Writing outStr to FakeRunner's buffer") } + rr.Stdout = buf + rr.Stderr = buf + return rr, err } // Run a fake command! -func (f *FakeRunner) Run(cmd string) error { - _, err := f.CombinedOutput(cmd) - return err +func (f *FakeRunner) RunCmd(cmd *exec.Cmd) (*command.RunResult, error) { + xargs := cmd.Args + f.cmds = append(f.cmds, xargs...) + root := false + bin, args := xargs[0], xargs[1:] + f.t.Logf("bin=%s args=%v", bin, args) + if bin == "sudo" { + root = true + bin, args = xargs[1], xargs[2:] + } + switch bin { + case "systemctl": + return buffer(f.systemctl(args, root)) + case "docker": + return buffer(f.docker(args, root)) + case "crictl": + return buffer(f.crictl(args, root)) + case "crio": + return buffer(f.crio(args, root)) + case "containerd": + return buffer(f.containerd(args, root)) + default: + rr := &command.RunResult{} + return rr, nil + } } // docker is a fake implementation of docker @@ -150,7 +163,7 @@ func (f *FakeRunner) docker(args []string, _ bool) (string, error) { case "ps": // ps -a --filter="name=apiserver" --format="{{.ID}}" if args[1] == "-a" && strings.HasPrefix(args[2], "--filter") { - filter := strings.Split(args[2], `"`)[1] + filter := strings.Split(args[2], `r=`)[1] fname := strings.Split(filter, "=")[1] ids := []string{} f.t.Logf("fake docker: Looking for containers matching %q", fname) @@ -163,7 +176,8 @@ func (f *FakeRunner) docker(args []string, _ bool) (string, error) { return strings.Join(ids, "\n"), nil } case "stop": - for _, id := range args[1:] { + ids := strings.Split(args[1], " ") + for _, id := range ids { f.t.Logf("fake docker: Stopping id %q", id) if f.containers[id] == "" { return "", fmt.Errorf("no such container") @@ -181,16 +195,16 @@ func (f *FakeRunner) docker(args []string, _ bool) (string, error) { } case "version": + if args[1] == "--format" && args[2] == "'{{.Server.Version}}'" { return "18.06.2-ce", nil } - } return "", nil } // crio is a fake implementation of crio -func (f *FakeRunner) crio(args []string, _ bool) (string, error) { +func (f *FakeRunner) crio(args []string, _ bool) (string, error) { //nolint (result 1 (error) is always nil) if args[0] == "--version" { return "crio version 1.13.0", nil } @@ -202,6 +216,9 @@ func (f *FakeRunner) containerd(args []string, _ bool) (string, error) { if args[0] == "--version" { return "containerd github.com/containerd/containerd v1.2.0 c4446665cb9c30056f4998ed953e6d4ff22c7c39", nil } + if args[0] != "--version" { // doing this to suppress lint "result 1 (error) is always nil" + return "", fmt.Errorf("unknown args[0]") + } return "", nil } @@ -253,7 +270,7 @@ func (f *FakeRunner) crictl(args []string, _ bool) (string, error) { } // systemctl is a fake implementation of systemctl -func (f *FakeRunner) systemctl(args []string, root bool) (string, error) { +func (f *FakeRunner) systemctl(args []string, root bool) (string, error) { // nolint result 0 (string) is always "" action := args[0] svcs := args[1:] out := "" @@ -263,6 +280,7 @@ func (f *FakeRunner) systemctl(args []string, root bool) (string, error) { if arg == "service" { svcs = args[i+1:] } + } for _, svc := range svcs { @@ -314,8 +332,7 @@ func TestVersion(t *testing.T) { } for _, tc := range tests { t.Run(tc.runtime, func(t *testing.T) { - runner := NewFakeRunner(t) - r, err := New(Config{Type: tc.runtime, Runner: runner}) + r, err := New(Config{Type: tc.runtime, Runner: NewFakeRunner(t)}) if err != nil { t.Fatalf("New(%s): %v", tc.runtime, err) } @@ -344,9 +361,9 @@ func TestDisable(t *testing.T) { runtime string want []string }{ - {"docker", []string{"sudo systemctl stop docker docker.socket"}}, - {"crio", []string{"sudo systemctl stop crio"}}, - {"containerd", []string{"sudo systemctl stop containerd"}}, + {"docker", []string{"sudo", "systemctl", "stop", "docker", "docker.socket"}}, + {"crio", []string{"sudo", "systemctl", "stop", "crio"}}, + {"containerd", []string{"sudo", "systemctl", "stop", "containerd"}}, } for _, tc := range tests { t.Run(tc.runtime, func(t *testing.T) { diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index 817049aff1..3d80fca459 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/golang/glog" + "github.com/pkg/errors" "k8s.io/minikube/pkg/minikube/out" ) @@ -47,12 +48,12 @@ func (r *Docker) Style() out.StyleEnum { // Version retrieves the current version of this runtime func (r *Docker) Version() (string, error) { // Note: the server daemon has to be running, for this call to return successfully - ver, err := r.Runner.CombinedOutput("docker version --format '{{.Server.Version}}'") + c := exec.Command("docker", "version", "--format", "'{{.Server.Version}}'") + rr, err := r.Runner.RunCmd(c) if err != nil { return "", err } - - return strings.Split(ver, "\n")[0], nil + return strings.Split(rr.Stdout.String(), "\n")[0], nil } // SocketPath returns the path to the socket file for Docker @@ -73,7 +74,8 @@ func (r *Docker) Available() error { // Active returns if docker is active on the host func (r *Docker) Active() bool { - err := r.Runner.Run("systemctl is-active --quiet service docker") + c := exec.Command("systemctl", "is-active", "--quiet", "service", "docker") + _, err := r.Runner.RunCmd(c) return err == nil } @@ -84,18 +86,31 @@ func (r *Docker) Enable(disOthers bool) error { glog.Warningf("disableOthers: %v", err) } } - return r.Runner.Run("sudo systemctl start docker") + c := exec.Command("sudo", "systemctl", "start", "docker") + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "enable docker.") + } + return nil } // Disable idempotently disables Docker on a host func (r *Docker) Disable() error { - return r.Runner.Run("sudo systemctl stop docker docker.socket") + c := exec.Command("sudo", "systemctl", "stop", "docker", "docker.socket") + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "disable docker") + } + return nil } // LoadImage loads an image into this runtime func (r *Docker) LoadImage(path string) error { glog.Infof("Loading image: %s", path) - return r.Runner.Run(fmt.Sprintf("docker load -i %s", path)) + c := exec.Command("docker", "load", "-i", path) + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "loadimage docker.") + } + return nil + } // KubeletOptions returns kubelet options for a runtime. @@ -108,12 +123,12 @@ func (r *Docker) KubeletOptions() map[string]string { // ListContainers returns a list of containers func (r *Docker) ListContainers(filter string) ([]string, error) { filter = KubernetesContainerPrefix + filter - content, err := r.Runner.CombinedOutput(fmt.Sprintf(`docker ps -a --filter="name=%s" --format="{{.ID}}"`, filter)) + rr, err := r.Runner.RunCmd(exec.Command("docker", "ps", "-a", fmt.Sprintf("--filter=name=%s", filter), "--format=\"{{.ID}}\"")) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "docker ListContainers. ") } var ids []string - for _, line := range strings.Split(content, "\n") { + for _, line := range strings.Split(rr.Stdout.String(), "\n") { if line != "" { ids = append(ids, line) } @@ -127,7 +142,12 @@ func (r *Docker) KillContainers(ids []string) error { return nil } glog.Infof("Killing containers: %s", ids) - return r.Runner.Run(fmt.Sprintf("docker rm -f %s", strings.Join(ids, " "))) + args := append([]string{"rm", "-f"}, ids...) + c := exec.Command("docker", args...) + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "Killing containers docker.") + } + return nil } // StopContainers stops a running container based on ID @@ -136,7 +156,12 @@ func (r *Docker) StopContainers(ids []string) error { return nil } glog.Infof("Stopping containers: %s", ids) - return r.Runner.Run(fmt.Sprintf("docker stop %s", strings.Join(ids, " "))) + args := append([]string{"stop"}, ids...) + c := exec.Command("docker", args...) + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "stopping containers docker.") + } + return nil } // ContainerLogCmd returns the command to retrieve the log for a container based on ID diff --git a/pkg/minikube/driver/driver.go b/pkg/minikube/driver/driver.go new file mode 100644 index 0000000000..44c2a54fae --- /dev/null +++ b/pkg/minikube/driver/driver.go @@ -0,0 +1,142 @@ +/* +Copyright 2019 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 driver + +import ( + "fmt" + "os" + "sort" + + "github.com/golang/glog" + "k8s.io/minikube/pkg/minikube/registry" +) + +const ( + // Mock driver + Mock = "mock" + // None driver + None = "none" + // KVM2 driver + KVM2 = "kvm2" + // VirtualBox driver + VirtualBox = "virtualbox" + // HyperKit driver + HyperKit = "hyperkit" + // VMware driver + VMware = "vmware" + // VMwareFusion driver + VMwareFusion = "vmwarefusion" + // HyperV driver + HyperV = "hyperv" + // Parallels driver + Parallels = "parallels" +) + +var ( + // systemdResolvConf is path to systemd's DNS configuration. https://github.com/kubernetes/minikube/issues/3511 + systemdResolvConf = "/run/systemd/resolve/resolv.conf" +) + +// SupportedDrivers returns a list of supported drivers +func SupportedDrivers() []string { + return supportedDrivers +} + +// Supported returns if the driver is supported on this host. +func Supported(name string) bool { + for _, d := range supportedDrivers { + if name == d { + return true + } + } + return false +} + +// BareMetal returns if this driver is unisolated +func BareMetal(name string) bool { + return name == None || name == Mock +} + +// FlagHints are hints for what default options should be used for this driver +type FlagHints struct { + ExtraOptions string + CacheImages bool +} + +// FlagDefaults returns suggested defaults based on a driver +func FlagDefaults(name string) FlagHints { + if name != None { + return FlagHints{CacheImages: true} + } + + extraOpts := "" + if _, err := os.Stat(systemdResolvConf); err == nil { + extraOpts = fmt.Sprintf("kubelet.resolv-conf=%s", systemdResolvConf) + } + return FlagHints{ + ExtraOptions: extraOpts, + CacheImages: false, + } +} + +// Choices returns a list of drivers which are possible on this system +func Choices() []registry.DriverState { + options := []registry.DriverState{} + for _, ds := range registry.Installed() { + if !ds.State.Healthy { + glog.Warningf("%q is installed, but unhealthy: %v", ds.Name, ds.State.Error) + continue + } + options = append(options, ds) + } + + // Descending priority for predictability and appearance + sort.Slice(options, func(i, j int) bool { + return options[i].Priority > options[j].Priority + }) + return options +} + +// Choose returns a suggested driver from a set of options +func Choose(options []registry.DriverState) (registry.DriverState, []registry.DriverState) { + pick := registry.DriverState{} + for _, ds := range options { + if ds.Priority <= registry.Discouraged { + glog.Infof("not recommending %q due to priority: %d", ds.Name, ds.Priority) + continue + } + if ds.Priority > pick.Priority { + glog.V(1).Infof("%q has a higher priority (%d) than %q (%d)", ds.Name, ds.Priority, pick.Name, pick.Priority) + pick = ds + } + } + + alternates := []registry.DriverState{} + for _, ds := range options { + if ds != pick { + alternates = append(alternates, ds) + } + } + glog.Infof("Picked: %+v", pick) + glog.Infof("Alternatives: %+v", alternates) + return pick, alternates +} + +// Status returns the status of a driver +func Status(name string) registry.State { + return registry.Status(name) +} diff --git a/pkg/minikube/cluster/cluster_darwin.go b/pkg/minikube/driver/driver_darwin.go similarity index 69% rename from pkg/minikube/cluster/cluster_darwin.go rename to pkg/minikube/driver/driver_darwin.go index ba4ff3896e..5ce4e6b448 100644 --- a/pkg/minikube/cluster/cluster_darwin.go +++ b/pkg/minikube/driver/driver_darwin.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +Copyright 2019 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,13 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cluster +package driver -import ( - "os/exec" -) +import "os/exec" -func detectVBoxManageCmd() string { +// supportedDrivers is a list of supported drivers on Darwin. +var supportedDrivers = []string{ + VirtualBox, + Parallels, + VMwareFusion, + HyperKit, + VMware, +} + +func VBoxManagePath() string { cmd := "VBoxManage" if path, err := exec.LookPath(cmd); err == nil { return path diff --git a/pkg/minikube/cluster/cluster_linux.go b/pkg/minikube/driver/driver_linux.go similarity index 72% rename from pkg/minikube/cluster/cluster_linux.go rename to pkg/minikube/driver/driver_linux.go index ba4ff3896e..12cbbd2ef8 100644 --- a/pkg/minikube/cluster/cluster_linux.go +++ b/pkg/minikube/driver/driver_linux.go @@ -14,13 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cluster +package driver import ( "os/exec" ) -func detectVBoxManageCmd() string { +// supportedDrivers is a list of supported drivers on Linux. +var supportedDrivers = []string{ + VirtualBox, + Parallels, + VMwareFusion, + KVM2, + VMware, + None, +} + +// VBoxManagePath returns the path to the VBoxManage command +func VBoxManagePath() string { cmd := "VBoxManage" if path, err := exec.LookPath(cmd); err == nil { return path diff --git a/pkg/minikube/driver/driver_test.go b/pkg/minikube/driver/driver_test.go new file mode 100644 index 0000000000..cd35e8d775 --- /dev/null +++ b/pkg/minikube/driver/driver_test.go @@ -0,0 +1,162 @@ +/* +Copyright 2018 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 driver + +import ( + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/google/go-cmp/cmp" + "k8s.io/minikube/pkg/minikube/registry" +) + +func TestSupportedDrivers(t *testing.T) { + got := SupportedDrivers() + found := false + for _, s := range SupportedDrivers() { + if s == VirtualBox { + found = true + } + } + + if found == false { + t.Errorf("%s not in supported drivers: %v", VirtualBox, got) + } +} + +func TestSupported(t *testing.T) { + if !Supported(VirtualBox) { + t.Errorf("Supported(%s) is false", VirtualBox) + } + if Supported("yabba?") { + t.Errorf("Supported(yabba?) is true") + } +} + +func TestBareMetal(t *testing.T) { + if !BareMetal(None) { + t.Errorf("Supported(%s) is false", None) + } + if BareMetal(VirtualBox) { + t.Errorf("Supported(%s) is true", VirtualBox) + } +} + +func TestFlagDefaults(t *testing.T) { + expected := FlagHints{CacheImages: true} + if diff := cmp.Diff(FlagDefaults(VirtualBox), expected); diff != "" { + t.Errorf("defaults mismatch (-want +got):\n%s", diff) + } + + tf, err := ioutil.TempFile("", "resolv.conf") + if err != nil { + t.Fatalf("tempfile: %v", err) + } + defer os.Remove(tf.Name()) // clean up + + expected = FlagHints{ + CacheImages: false, + ExtraOptions: fmt.Sprintf("kubelet.resolv-conf=%s", tf.Name()), + } + systemdResolvConf = tf.Name() + if diff := cmp.Diff(FlagDefaults(None), expected); diff != "" { + t.Errorf("defaults mismatch (-want +got):\n%s", diff) + } +} + +func TestChoices(t *testing.T) { + + tests := []struct { + def registry.DriverDef + choices []string + pick string + alts []string + }{ + { + def: registry.DriverDef{ + Name: "unhealthy", + Priority: registry.Default, + Status: func() registry.State { return registry.State{Installed: true, Healthy: false} }, + }, + choices: []string{}, + pick: "", + alts: []string{}, + }, + { + def: registry.DriverDef{ + Name: "discouraged", + Priority: registry.Discouraged, + Status: func() registry.State { return registry.State{Installed: true, Healthy: true} }, + }, + choices: []string{"discouraged"}, + pick: "", + alts: []string{"discouraged"}, + }, + { + def: registry.DriverDef{ + Name: "default", + Priority: registry.Default, + Status: func() registry.State { return registry.State{Installed: true, Healthy: true} }, + }, + choices: []string{"default", "discouraged"}, + pick: "default", + alts: []string{"discouraged"}, + }, + { + def: registry.DriverDef{ + Name: "preferred", + Priority: registry.Preferred, + Status: func() registry.State { return registry.State{Installed: true, Healthy: true} }, + }, + choices: []string{"preferred", "default", "discouraged"}, + pick: "preferred", + alts: []string{"default", "discouraged"}, + }, + } + for _, tc := range tests { + t.Run(tc.def.Name, func(t *testing.T) { + if err := registry.Register(tc.def); err != nil { + t.Errorf("register returned error: %v", err) + } + + got := Choices() + gotNames := []string{} + for _, c := range got { + gotNames = append(gotNames, c.Name) + } + + if diff := cmp.Diff(gotNames, tc.choices); diff != "" { + t.Errorf("choices mismatch (-want +got):\n%s", diff) + } + + pick, alts := Choose(got) + if pick.Name != tc.pick { + t.Errorf("pick = %q, expected %q", pick.Name, tc.pick) + } + + gotAlts := []string{} + for _, a := range alts { + gotAlts = append(gotAlts, a.Name) + } + if diff := cmp.Diff(gotAlts, tc.alts); diff != "" { + t.Errorf("alts mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/minikube/cluster/cluster_windows.go b/pkg/minikube/driver/driver_windows.go similarity index 92% rename from pkg/minikube/cluster/cluster_windows.go rename to pkg/minikube/driver/driver_windows.go index 21f2b94dc9..6569eec025 100644 --- a/pkg/minikube/cluster/cluster_windows.go +++ b/pkg/minikube/driver/driver_windows.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cluster +package driver import ( "fmt" @@ -27,7 +27,15 @@ import ( "golang.org/x/sys/windows/registry" ) -func detectVBoxManageCmd() string { +// supportedDrivers is a list of supported drivers on Windows. +var supportedDrivers = []string{ + VirtualBox, + VMwareFusion, + HyperV, + VMware, +} + +func VBoxManagePath() string { cmd := "VBoxManage" if p := os.Getenv("VBOX_INSTALL_PATH"); p != "" { if path, err := exec.LookPath(filepath.Join(p, cmd)); err == nil { @@ -72,7 +80,6 @@ func findVBoxInstallDirInRegistry() (string, error) { installDir, _, err := registryKey.GetStringValue("InstallDir") if err != nil { errorMessage := fmt.Sprintf("Can't find InstallDir registry key within VirtualBox registries entries, is VirtualBox really installed properly? %v", err) - glog.Errorf(errorMessage) return "", errors.New(errorMessage) } diff --git a/pkg/minikube/driver/install.go b/pkg/minikube/driver/install.go new file mode 100644 index 0000000000..1d0b856e39 --- /dev/null +++ b/pkg/minikube/driver/install.go @@ -0,0 +1,173 @@ +/* +Copyright 2019 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 driver + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + + "github.com/blang/semver" + "github.com/golang/glog" + "github.com/hashicorp/go-getter" + "github.com/pkg/errors" + + "k8s.io/minikube/pkg/minikube/out" + "k8s.io/minikube/pkg/util" +) + +// InstallOrUpdate downloads driver if it is not present, or updates it if there's a newer version +func InstallOrUpdate(name string, directory string, v semver.Version, interactive bool, autoUpdate bool) error { + if name != KVM2 && name != HyperKit { + return nil + } + + executable := fmt.Sprintf("docker-machine-driver-%s", name) + exists := driverExists(executable) + path, err := validateDriver(executable, v) + if !exists || (err != nil && autoUpdate) { + glog.Warningf("%s: %v", executable, err) + path = filepath.Join(directory, executable) + derr := download(executable, path, v) + if derr != nil { + return derr + } + } + return fixDriverPermissions(name, path, interactive) +} + +// fixDriverPermissions fixes the permissions on a driver +func fixDriverPermissions(name string, path string, interactive bool) error { + if name != HyperKit { + return nil + } + + // Using the find command for hyperkit is far easier than cross-platform uid checks in Go. + stdout, err := exec.Command("find", path, "-uid", "0", "-perm", "4755").Output() + glog.Infof("stdout: %s", stdout) + if err == nil && strings.TrimSpace(string(stdout)) == path { + glog.Infof("%s looks good", path) + return nil + } + + cmds := []*exec.Cmd{ + exec.Command("sudo", "chown", "root:wheel", path), + exec.Command("sudo", "chmod", "u+s", path), + } + + var example strings.Builder + for _, c := range cmds { + example.WriteString(fmt.Sprintf(" $ %s \n", strings.Join(c.Args, " "))) + } + + out.T(out.Permissions, "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\n\n{{ .example }}\n", out.V{"driver": name, "example": example.String()}) + for _, c := range cmds { + testArgs := append([]string{"-n"}, c.Args[1:]...) + test := exec.Command("sudo", testArgs...) + glog.Infof("testing: %v", test.Args) + if err := test.Run(); err != nil { + glog.Infof("%v may require a password: %v", c.Args, err) + if !interactive { + return fmt.Errorf("%v requires a password, and --interactive=false", c.Args) + } + } + glog.Infof("running: %v", c.Args) + err := c.Run() + if err != nil { + return errors.Wrapf(err, "%v", c.Args) + } + } + return nil +} + +// validateDriver validates if a driver appears to be up-to-date and installed properly +func validateDriver(executable string, v semver.Version) (string, error) { + glog.Infof("Validating %s, PATH=%s", executable, os.Getenv("PATH")) + path, err := exec.LookPath(executable) + if err != nil { + return path, err + } + + output, err := exec.Command(path, "version").Output() + if err != nil { + return path, err + } + + ev := extractVMDriverVersion(string(output)) + if len(ev) == 0 { + return path, fmt.Errorf("%s: unable to extract version from %q", executable, output) + } + + vmDriverVersion, err := semver.Make(ev) + if err != nil { + return path, errors.Wrap(err, "can't parse driver version") + } + if vmDriverVersion.LT(v) { + return path, fmt.Errorf("%s is version %s, want %s", executable, vmDriverVersion, v) + } + return path, nil +} + +func driverWithChecksumURL(name string, v semver.Version) string { + base := fmt.Sprintf("https://github.com/kubernetes/minikube/releases/download/v%s/%s", v, name) + return fmt.Sprintf("%s?checksum=file:%s.sha256", base, base) +} + +// download an arbitrary driver +func download(name string, destination string, v semver.Version) error { + out.T(out.FileDownload, "Downloading driver {{.driver}}:", out.V{"driver": name}) + os.Remove(destination) + url := driverWithChecksumURL(name, v) + client := &getter.Client{ + Src: url, + Dst: destination, + Mode: getter.ClientModeFile, + Options: []getter.ClientOption{getter.WithProgress(util.DefaultProgressBar)}, + } + + glog.Infof("Downloading: %+v", client) + if err := client.Get(); err != nil { + return errors.Wrapf(err, "download failed: %s", url) + } + // Give downloaded drivers a baseline decent file permission + return os.Chmod(destination, 0755) +} + +// extractVMDriverVersion extracts the driver version. +// KVM and Hyperkit drivers support the 'version' command, that display the information as: +// version: vX.X.X +// commit: XXXX +// This method returns the version 'vX.X.X' or empty if the version isn't found. +func extractVMDriverVersion(s string) string { + versionRegex := regexp.MustCompile(`version:(.*)`) + matches := versionRegex.FindStringSubmatch(s) + + if len(matches) != 2 { + return "" + } + + v := strings.TrimSpace(matches[1]) + return strings.TrimPrefix(v, "v") +} + +func driverExists(driver string) bool { + _, err := exec.LookPath(driver) + return err == nil +} diff --git a/pkg/minikube/driver/install_test.go b/pkg/minikube/driver/install_test.go new file mode 100644 index 0000000000..f57e1f541e --- /dev/null +++ b/pkg/minikube/driver/install_test.go @@ -0,0 +1,45 @@ +/* +Copyright 2019 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 driver + +import ( + "testing" +) + +func TestExtractVMDriverVersion(t *testing.T) { + v := extractVMDriverVersion("") + if len(v) != 0 { + t.Error("Expected empty string") + } + + v = extractVMDriverVersion("random text") + if len(v) != 0 { + t.Error("Expected empty string") + } + + expectedVersion := "1.2.3" + + v = extractVMDriverVersion("version: v1.2.3") + if expectedVersion != v { + t.Errorf("Expected version: %s, got: %s", expectedVersion, v) + } + + v = extractVMDriverVersion("version: 1.2.3") + if expectedVersion != v { + t.Errorf("Expected version: %s, got: %s", expectedVersion, v) + } +} diff --git a/pkg/minikube/drivers/kvm2/driver.go b/pkg/minikube/drivers/kvm2/driver.go deleted file mode 100644 index 5a4f6af940..0000000000 --- a/pkg/minikube/drivers/kvm2/driver.go +++ /dev/null @@ -1,79 +0,0 @@ -// +build linux - -/* -Copyright 2018 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 kvm2 - -import ( - "fmt" - "path/filepath" - - "github.com/docker/machine/libmachine/drivers" - cfg "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" - "k8s.io/minikube/pkg/minikube/localpath" - "k8s.io/minikube/pkg/minikube/registry" -) - -func init() { - if err := registry.Register(registry.DriverDef{ - Name: constants.DriverKvm2, - Builtin: false, - ConfigCreator: createKVM2Host, - }); err != nil { - panic(fmt.Sprintf("register failed: %v", err)) - } -} - -// Delete this once the following PR is merged: -// https://github.com/dhiltgen/docker-machine-kvm/pull/68 -type kvmDriver struct { - *drivers.BaseDriver - - Memory int - DiskSize int - CPU int - Network string - PrivateNetwork string - ISO string - Boot2DockerURL string - DiskPath string - GPU bool - Hidden bool - ConnectionURI string -} - -func createKVM2Host(config cfg.MachineConfig) interface{} { - return &kvmDriver{ - BaseDriver: &drivers.BaseDriver{ - MachineName: cfg.GetMachineName(), - StorePath: localpath.MiniPath(), - SSHUser: "docker", - }, - Memory: config.Memory, - CPU: config.CPUs, - Network: config.KVMNetwork, - PrivateNetwork: "minikube-net", - Boot2DockerURL: config.Downloader.GetISOFileURI(config.MinikubeISO), - DiskSize: config.DiskSize, - DiskPath: filepath.Join(localpath.MiniPath(), "machines", cfg.GetMachineName(), fmt.Sprintf("%s.rawdisk", cfg.GetMachineName())), - ISO: filepath.Join(localpath.MiniPath(), "machines", cfg.GetMachineName(), "boot2docker.iso"), - GPU: config.KVMGPU, - Hidden: config.KVMHidden, - ConnectionURI: config.KVMQemuURI, - } -} diff --git a/pkg/minikube/drivers/virtualbox/driver.go b/pkg/minikube/drivers/virtualbox/driver.go deleted file mode 100644 index b520325e09..0000000000 --- a/pkg/minikube/drivers/virtualbox/driver.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright 2018 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 virtualbox - -import ( - "fmt" - - "github.com/docker/machine/drivers/virtualbox" - "github.com/docker/machine/libmachine/drivers" - cfg "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" - "k8s.io/minikube/pkg/minikube/localpath" - "k8s.io/minikube/pkg/minikube/registry" -) - -const defaultVirtualboxNicType = "virtio" - -func init() { - err := registry.Register(registry.DriverDef{ - Name: constants.DriverVirtualbox, - Builtin: true, - ConfigCreator: createVirtualboxHost, - DriverCreator: func() drivers.Driver { - return virtualbox.NewDriver("", "") - }, - }) - if err != nil { - panic(fmt.Sprintf("unable to register: %v", err)) - } -} - -func createVirtualboxHost(config cfg.MachineConfig) interface{} { - d := virtualbox.NewDriver(cfg.GetMachineName(), localpath.MiniPath()) - - d.Boot2DockerURL = config.Downloader.GetISOFileURI(config.MinikubeISO) - d.Memory = config.Memory - d.CPU = config.CPUs - d.DiskSize = config.DiskSize - d.HostOnlyCIDR = config.HostOnlyCIDR - d.NoShare = config.DisableDriverMounts - d.NoVTXCheck = config.NoVTXCheck - d.NatNicType = defaultVirtualboxNicType - d.HostOnlyNicType = defaultVirtualboxNicType - d.DNSProxy = config.DNSProxy - d.HostDNSResolver = config.HostDNSResolver - - return d -} diff --git a/pkg/minikube/extract/extract.go b/pkg/minikube/extract/extract.go index b0dd931e25..8453d53ea5 100644 --- a/pkg/minikube/extract/extract.go +++ b/pkg/minikube/extract/extract.go @@ -328,6 +328,9 @@ func checkString(s string) string { // Parse out quote marks stringToTranslate := s[1 : len(s)-1] + // Trim whitespace + stringToTranslate = strings.TrimSpace(stringToTranslate) + // Don't translate integers if _, err := strconv.Atoi(stringToTranslate); err == nil { return "" @@ -382,7 +385,7 @@ func checkKeyValueExpression(kvp *ast.KeyValueExpr, e *state) { // Ok now this is just a mess if help, ok := kvp.Value.(*ast.BinaryExpr); ok { - s := checkBinaryExpression(help, e) + s := checkBinaryExpression(help) if s != "" { e.translations[s] = "" } @@ -391,7 +394,7 @@ func checkKeyValueExpression(kvp *ast.KeyValueExpr, e *state) { } // checkBinaryExpression checks binary expressions, stuff of the form x + y, for strings and concats them -func checkBinaryExpression(b *ast.BinaryExpr, e *state) string { +func checkBinaryExpression(b *ast.BinaryExpr) string { // Check the left side var s string if l, ok := b.X.(*ast.BasicLit); ok { @@ -407,7 +410,7 @@ func checkBinaryExpression(b *ast.BinaryExpr, e *state) string { } if b1, ok := b.X.(*ast.BinaryExpr); ok { - if x := checkBinaryExpression(b1, e); x != "" { + if x := checkBinaryExpression(b1); x != "" { s += x } } @@ -426,7 +429,7 @@ func checkBinaryExpression(b *ast.BinaryExpr, e *state) string { } if b1, ok := b.Y.(*ast.BinaryExpr); ok { - if x := checkBinaryExpression(b1, e); x != "" { + if x := checkBinaryExpression(b1); x != "" { s += x } } diff --git a/pkg/minikube/kubeconfig/kubeconfig.go b/pkg/minikube/kubeconfig/kubeconfig.go index fbd2ff2527..6742ac4979 100644 --- a/pkg/minikube/kubeconfig/kubeconfig.go +++ b/pkg/minikube/kubeconfig/kubeconfig.go @@ -82,7 +82,7 @@ func Port(clusterName string, configPath ...string) (int, error) { return port, err } -// PathFromEnv() gets the path to the first kubeconfig +// PathFromEnv gets the path to the first kubeconfig func PathFromEnv() string { kubeConfigEnv := os.Getenv(constants.KubeconfigEnvVar) if kubeConfigEnv == "" { diff --git a/pkg/minikube/kubeconfig/kubeconfig_test.go b/pkg/minikube/kubeconfig/kubeconfig_test.go index 5173f935c6..7c58b574d2 100644 --- a/pkg/minikube/kubeconfig/kubeconfig_test.go +++ b/pkg/minikube/kubeconfig/kubeconfig_test.go @@ -571,7 +571,7 @@ func TestGetKubeConfigPath(t *testing.T) { for _, test := range tests { os.Setenv(clientcmd.RecommendedConfigPathEnvVar, test.input) if result := PathFromEnv(); result != os.ExpandEnv(test.want) { - t.Errorf("Expected first splitted chunk, got: %s", result) + t.Errorf("Expected first split chunk, got: %s", result) } } } diff --git a/pkg/minikube/kubeconfig/settings.go b/pkg/minikube/kubeconfig/settings.go index 4d35321a50..28601d0b6e 100644 --- a/pkg/minikube/kubeconfig/settings.go +++ b/pkg/minikube/kubeconfig/settings.go @@ -66,7 +66,7 @@ func (k *Settings) filePath() string { return k.kubeConfigFile.Load().(string) } -// Populate populates an api.Config object with values from *Settings +// PopulateFromSettings populates an api.Config object with values from *Settings func PopulateFromSettings(cfg *Settings, apiCfg *api.Config) error { var err error clusterName := cfg.ClusterName @@ -115,7 +115,7 @@ func PopulateFromSettings(cfg *Settings, apiCfg *api.Config) error { return nil } -// update reads config from disk, adds the minikube settings, and writes it back. +// Update reads config from disk, adds the minikube settings, and writes it back. // activeContext is true when minikube is the CurrentContext // If no CurrentContext is set, the given name will be used. func Update(kcs *Settings) error { diff --git a/pkg/minikube/localpath/localpath.go b/pkg/minikube/localpath/localpath.go index bd070d0670..8b6ae0a16e 100644 --- a/pkg/minikube/localpath/localpath.go +++ b/pkg/minikube/localpath/localpath.go @@ -26,6 +26,9 @@ import ( // MinikubeHome is the name of the minikube home directory variable. const MinikubeHome = "MINIKUBE_HOME" +// ConfigFile is the path of the config file +var ConfigFile = MakeMiniPath("config", "config.json") + // MiniPath returns the path to the user's minikube dir func MiniPath() string { if os.Getenv(MinikubeHome) == "" { diff --git a/pkg/minikube/logs/logs.go b/pkg/minikube/logs/logs.go index 0c6625f8b4..f562302619 100644 --- a/pkg/minikube/logs/logs.go +++ b/pkg/minikube/logs/logs.go @@ -22,11 +22,13 @@ import ( "bytes" "fmt" "os" + "os/exec" "regexp" "sort" "strings" "github.com/golang/glog" + "github.com/pkg/errors" "k8s.io/minikube/pkg/minikube/bootstrapper" "k8s.io/minikube/pkg/minikube/command" "k8s.io/minikube/pkg/minikube/cruntime" @@ -51,18 +53,30 @@ var importantPods = []string{ "kube-controller-manager", } +// logRunner is the subset of CommandRunner used for logging +type logRunner interface { + RunCmd(*exec.Cmd) (*command.RunResult, error) +} + // lookbackwardsCount is how far back to look in a log for problems. This should be large enough to // include usage messages from a failed binary, but small enough to not include irrelevant problems. const lookBackwardsCount = 200 // Follow follows logs from multiple files in tail(1) format -func Follow(r cruntime.Manager, bs bootstrapper.Bootstrapper, runner command.Runner) error { +func Follow(r cruntime.Manager, bs bootstrapper.Bootstrapper, cr logRunner) error { cs := []string{} for _, v := range logCommands(r, bs, 0, true) { cs = append(cs, v+" &") } cs = append(cs, "wait") - return runner.CombinedOutputTo(strings.Join(cs, " "), os.Stdout) + + cmd := exec.Command("/bin/bash", "-c", strings.Join(cs, " ")) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stdout + if _, err := cr.RunCmd(cmd); err != nil { + return errors.Wrapf(err, "log follow") + } + return nil } // IsProblem returns whether this line matches a known problem @@ -71,15 +85,18 @@ func IsProblem(line string) bool { } // FindProblems finds possible root causes among the logs -func FindProblems(r cruntime.Manager, bs bootstrapper.Bootstrapper, runner command.Runner) map[string][]string { +func FindProblems(r cruntime.Manager, bs bootstrapper.Bootstrapper, cr logRunner) map[string][]string { pMap := map[string][]string{} cmds := logCommands(r, bs, lookBackwardsCount, false) - for name, cmd := range cmds { + for name := range cmds { glog.Infof("Gathering logs for %s ...", name) var b bytes.Buffer - err := runner.CombinedOutputTo(cmds[name], &b) - if err != nil { - glog.Warningf("failed %s: %s: %v", name, cmd, err) + c := exec.Command("/bin/bash", "-c", cmds[name]) + c.Stderr = &b + c.Stdout = &b + + if rr, err := cr.RunCmd(c); err != nil { + glog.Warningf("failed %s: command: %s %v output: %s", name, rr.Command(), err, rr.Output()) continue } scanner := bufio.NewScanner(&b) @@ -129,10 +146,11 @@ func Output(r cruntime.Manager, bs bootstrapper.Bootstrapper, runner command.Run } out.T(out.Empty, "==> {{.name}} <==", out.V{"name": name}) var b bytes.Buffer - - err := runner.CombinedOutputTo(cmds[name], &b) - if err != nil { - glog.Errorf("failed: %v", err) + c := exec.Command("/bin/bash", "-c", cmds[name]) + c.Stdout = &b + c.Stderr = &b + if rr, err := runner.RunCmd(c); err != nil { + glog.Errorf("command %s failed with error: %v output: %q", rr.Command(), err, rr.Output()) failed = append(failed, name) continue } diff --git a/pkg/minikube/machine/cache_binaries_test.go b/pkg/minikube/machine/cache_binaries_test.go new file mode 100644 index 0000000000..19bbb1fa2e --- /dev/null +++ b/pkg/minikube/machine/cache_binaries_test.go @@ -0,0 +1,203 @@ +/* +Copyright 2019 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 machine + +import ( + "fmt" + "io/ioutil" + "os" + "runtime" + "testing" + + "k8s.io/minikube/pkg/minikube/assets" + "k8s.io/minikube/pkg/minikube/bootstrapper" + "k8s.io/minikube/pkg/minikube/command" +) + +type copyFailRunner struct { + command.Runner +} + +func (copyFailRunner) Copy(a assets.CopyableFile) error { + return fmt.Errorf("test error during copy file") +} + +func newFakeCommandRunnerCopyFail() command.Runner { + return copyFailRunner{command.NewFakeCommandRunner()} +} + +func TestCopyBinary(t *testing.T) { + var tc = []struct { + lastUpdateCheckFilePath string + src, dst, desc string + err bool + runner command.Runner + }{ + { + desc: "not existing src", + dst: "/tmp/testCopyBinary1", + src: "/tmp/testCopyBinary2", + err: true, + runner: command.NewFakeCommandRunner(), + }, + { + desc: "src /etc/hosts", + dst: "/tmp/testCopyBinary1", + src: "/etc/hosts", + err: false, + runner: command.NewFakeCommandRunner(), + }, + { + desc: "existing src, copy fail", + dst: "/etc/passwd", + src: "/etc/hosts", + err: true, + runner: newFakeCommandRunnerCopyFail(), + }, + } + for _, test := range tc { + t.Run(test.desc, func(t *testing.T) { + err := CopyBinary(test.runner, test.src, test.dst) + if err != nil && !test.err { + t.Fatalf("Error %v expected but not occurred", err) + } + if err == nil && test.err { + t.Fatal("Unexpected error") + } + }) + } +} + +func TestCacheBinariesForBootstrapper(t *testing.T) { + var tc = []struct { + version, clusterBootstrapper string + err bool + }{ + { + version: "v1.16.0", + clusterBootstrapper: bootstrapper.BootstrapperTypeKubeadm, + }, + { + version: "invalid version", + clusterBootstrapper: bootstrapper.BootstrapperTypeKubeadm, + err: true, + }, + } + for _, test := range tc { + t.Run(test.version, func(t *testing.T) { + err := CacheBinariesForBootstrapper(test.version, test.clusterBootstrapper) + if err != nil && !test.err { + t.Fatalf("Got unexpected error %v", err) + } + if err == nil && test.err { + t.Fatalf("Expected error but got %v", err) + } + }) + } +} +func TestCacheBinary(t *testing.T) { + oldMinikubeHome := os.Getenv("MINIKUBE_HOME") + defer os.Setenv("MINIKUBE_HOME", oldMinikubeHome) + + minikubeHome, err := ioutil.TempDir("/tmp", "") + if err != nil { + t.Fatalf("error during creating tmp dir: %v", err) + } + defer os.RemoveAll(minikubeHome) + noWritePermDir, err := ioutil.TempDir("/tmp", "") + if err != nil { + t.Fatalf("error during creating tmp dir: %v", err) + } + defer os.RemoveAll(noWritePermDir) + err = os.Chmod(noWritePermDir, 0000) + if err != nil { + t.Fatalf("error (%v) during changing permissions of dir %v", err, noWritePermDir) + } + + var tc = []struct { + desc, version, osName, archName string + minikubeHome, binary, description string + err bool + }{ + { + desc: "ok kubeadm", + version: "v1.16.0", + osName: "linux", + archName: runtime.GOARCH, + binary: "kubeadm", + err: false, + minikubeHome: minikubeHome, + }, + { + desc: "minikube home in dir without perms and arm runtime", + version: "v1.16.0", + osName: runtime.GOOS, + archName: "arm", + binary: "kubectl", + err: true, + minikubeHome: noWritePermDir, + }, + { + desc: "minikube home in dir without perms", + version: "v1.16.0", + osName: runtime.GOOS, + archName: runtime.GOARCH, + binary: "kubectl", + err: true, + minikubeHome: noWritePermDir, + }, + { + desc: "binary foo", + version: "v1.16.0", + osName: runtime.GOOS, + archName: runtime.GOARCH, + binary: "foo", + err: true, + minikubeHome: minikubeHome, + }, + { + desc: "version 9000", + version: "v9000", + osName: runtime.GOOS, + archName: runtime.GOARCH, + binary: "foo", + err: true, + minikubeHome: minikubeHome, + }, + { + desc: "bad os", + version: "v1.16.0", + osName: "no-such-os", + archName: runtime.GOARCH, + binary: "kubectl", + err: true, + minikubeHome: minikubeHome, + }, + } + for _, test := range tc { + t.Run(test.desc, func(t *testing.T) { + os.Setenv("MINIKUBE_HOME", test.minikubeHome) + _, err := CacheBinary(test.binary, test.version, test.osName, test.archName) + if err != nil && !test.err { + t.Fatalf("Got unexpected error %v", err) + } + if err == nil && test.err { + t.Fatalf("Expected error but got %v", err) + } + }) + } +} diff --git a/pkg/minikube/machine/cache_images.go b/pkg/minikube/machine/cache_images.go index 397460ff93..2c33890c1f 100644 --- a/pkg/minikube/machine/cache_images.go +++ b/pkg/minikube/machine/cache_images.go @@ -42,10 +42,11 @@ import ( "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/cruntime" + "k8s.io/minikube/pkg/minikube/vmpath" ) // loadRoot is where images should be loaded from within the guest VM -var loadRoot = path.Join(constants.GuestPersistentDir, "images") +var loadRoot = path.Join(vmpath.GuestPersistentDir, "images") var getWindowsVolumeName = getWindowsVolumeNameCmd diff --git a/pkg/minikube/machine/client.go b/pkg/minikube/machine/client.go index fdd4f2069f..dd10ee31c9 100644 --- a/pkg/minikube/machine/client.go +++ b/pkg/minikube/machine/client.go @@ -19,6 +19,7 @@ package machine import ( "crypto/tls" "encoding/json" + "fmt" "net" "os" "path/filepath" @@ -41,7 +42,7 @@ import ( "github.com/docker/machine/libmachine/version" "github.com/pkg/errors" "k8s.io/minikube/pkg/minikube/command" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/out" @@ -80,27 +81,25 @@ type LocalClient struct { } // NewHost creates a new Host -func (api *LocalClient) NewHost(driverName string, rawDriver []byte) (*host.Host, error) { - var def registry.DriverDef - var err error - if def, err = registry.Driver(driverName); err != nil { - return nil, err - } else if !def.Builtin || def.DriverCreator == nil { - return api.legacyClient.NewHost(driverName, rawDriver) +func (api *LocalClient) NewHost(drvName string, rawDriver []byte) (*host.Host, error) { + def := registry.Driver(drvName) + if def.Empty() { + return nil, fmt.Errorf("driver %q does not exist", drvName) } - - driver := def.DriverCreator() - - err = json.Unmarshal(rawDriver, driver) + if def.Init == nil { + return api.legacyClient.NewHost(drvName, rawDriver) + } + d := def.Init() + err := json.Unmarshal(rawDriver, d) if err != nil { return nil, errors.Wrapf(err, "Error getting driver %s", string(rawDriver)) } return &host.Host{ ConfigVersion: version.ConfigVersion, - Name: driver.GetMachineName(), - Driver: driver, - DriverName: driver.DriverName(), + Name: d.GetMachineName(), + Driver: d, + DriverName: d.DriverName(), HostOptions: &host.Options{ AuthOptions: &auth.Options{ CertDir: api.certsDir, @@ -127,14 +126,14 @@ func (api *LocalClient) Load(name string) (*host.Host, error) { return nil, errors.Wrapf(err, "filestore %q", name) } - var def registry.DriverDef - if def, err = registry.Driver(h.DriverName); err != nil { - return nil, err - } else if !def.Builtin || def.DriverCreator == nil { + def := registry.Driver(h.DriverName) + if def.Empty() { + return nil, fmt.Errorf("driver %q does not exist", h.DriverName) + } + if def.Init == nil { return api.legacyClient.Load(name) } - - h.Driver = def.DriverCreator() + h.Driver = def.Init() return h, json.Unmarshal(h.RawDriver, h.Driver) } @@ -148,10 +147,10 @@ func (api *LocalClient) Close() error { // CommandRunner returns best available command runner for this host func CommandRunner(h *host.Host) (command.Runner, error) { - if h.DriverName == constants.DriverMock { + if h.DriverName == driver.Mock { return &command.FakeCommandRunner{}, nil } - if h.DriverName == constants.DriverNone { + if driver.BareMetal(h.Driver.DriverName()) { return &command.ExecRunner{}, nil } client, err := sshutil.NewSSHClient(h.Driver) @@ -163,9 +162,11 @@ func CommandRunner(h *host.Host) (command.Runner, error) { // Create creates the host func (api *LocalClient) Create(h *host.Host) error { - if def, err := registry.Driver(h.DriverName); err != nil { - return err - } else if !def.Builtin || def.DriverCreator == nil { + def := registry.Driver(h.DriverName) + if def.Empty() { + return fmt.Errorf("driver %q does not exist", h.DriverName) + } + if def.Init == nil { return api.legacyClient.Create(h) } @@ -194,7 +195,7 @@ func (api *LocalClient) Create(h *host.Host) error { { "waiting", func() error { - if h.Driver.DriverName() == constants.DriverNone { + if driver.BareMetal(h.Driver.DriverName()) { return nil } return mcnutils.WaitFor(drivers.MachineInState(h.Driver, state.Running)) @@ -203,7 +204,7 @@ func (api *LocalClient) Create(h *host.Host) error { { "provisioning", func() error { - if h.Driver.DriverName() == constants.DriverNone { + if driver.BareMetal(h.Driver.DriverName()) { return nil } pv := provision.NewBuildrootProvisioner(h.Driver) @@ -270,13 +271,10 @@ func (cg *CertGenerator) ValidateCertificate(addr string, authOptions *auth.Opti return true, nil } -func registerDriver(driverName string) { - def, err := registry.Driver(driverName) - if err != nil { - if err == registry.ErrDriverNotFound { - exit.UsageT("unsupported driver: {{.name}}", out.V{"name": driverName}) - } - exit.WithError("error getting driver", err) +func registerDriver(drvName string) { + def := registry.Driver(drvName) + if def.Empty() { + exit.UsageT("unsupported or missing driver: {{.name}}", out.V{"name": drvName}) } - plugin.RegisterDriver(def.DriverCreator()) + plugin.RegisterDriver(def.Init()) } diff --git a/pkg/minikube/machine/client_test.go b/pkg/minikube/machine/client_test.go index b59d90a94a..36465c5b08 100644 --- a/pkg/minikube/machine/client_test.go +++ b/pkg/minikube/machine/client_test.go @@ -27,9 +27,10 @@ import ( "testing" "github.com/docker/machine/libmachine/drivers/plugin/localbinary" - "k8s.io/minikube/pkg/minikube/constants" - _ "k8s.io/minikube/pkg/minikube/drivers/virtualbox" + + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/localpath" + _ "k8s.io/minikube/pkg/minikube/registry/drvs/virtualbox" ) const vboxConfig = ` @@ -77,12 +78,12 @@ func TestLocalClientNewHost(t *testing.T) { }{ { description: "host vbox correct", - driver: constants.DriverVirtualbox, + driver: driver.VirtualBox, rawDriver: []byte(vboxConfig), }, { description: "host vbox incorrect", - driver: constants.DriverVirtualbox, + driver: driver.VirtualBox, rawDriver: []byte("?"), err: true, }, @@ -138,7 +139,7 @@ func TestRunDriver(t *testing.T) { defer os.RemoveAll(tempDir) os.Setenv(localbinary.PluginEnvKey, localbinary.PluginEnvVal) - os.Setenv(localbinary.PluginEnvDriverName, constants.DriverVirtualbox) + os.Setenv(localbinary.PluginEnvDriverName, driver.VirtualBox) // Capture stdout and reset it later. old := os.Stdout diff --git a/pkg/minikube/cluster/default_drivers.go b/pkg/minikube/notify/constants.go similarity index 50% rename from pkg/minikube/cluster/default_drivers.go rename to pkg/minikube/notify/constants.go index 99dfe368f7..ed42c17e4b 100644 --- a/pkg/minikube/cluster/default_drivers.go +++ b/pkg/minikube/notify/constants.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +Copyright 2019 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,16 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cluster +package notify -import ( - // Import all the default drivers - _ "k8s.io/minikube/pkg/minikube/drivers/hyperkit" - _ "k8s.io/minikube/pkg/minikube/drivers/hyperv" - _ "k8s.io/minikube/pkg/minikube/drivers/kvm2" - _ "k8s.io/minikube/pkg/minikube/drivers/none" - _ "k8s.io/minikube/pkg/minikube/drivers/parallels" - _ "k8s.io/minikube/pkg/minikube/drivers/virtualbox" - _ "k8s.io/minikube/pkg/minikube/drivers/vmware" - _ "k8s.io/minikube/pkg/minikube/drivers/vmwarefusion" +const ( + // GithubMinikubeReleasesURL is the URL of the minikube github releases JSON file + GithubMinikubeReleasesURL = "https://storage.googleapis.com/minikube/releases.json" ) diff --git a/pkg/minikube/notify/notify.go b/pkg/minikube/notify/notify.go index ee4e1570bd..e7fd0ae2ac 100644 --- a/pkg/minikube/notify/notify.go +++ b/pkg/minikube/notify/notify.go @@ -30,7 +30,6 @@ import ( "github.com/pkg/errors" "github.com/spf13/viper" "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/out" "k8s.io/minikube/pkg/util/lock" @@ -44,7 +43,7 @@ var ( // MaybePrintUpdateTextFromGithub prints update text if needed, from github func MaybePrintUpdateTextFromGithub() bool { - return MaybePrintUpdateText(constants.GithubMinikubeReleasesURL, lastUpdateCheckFilePath) + return MaybePrintUpdateText(GithubMinikubeReleasesURL, lastUpdateCheckFilePath) } // MaybePrintUpdateText prints update text, returns a bool if life is good. diff --git a/pkg/minikube/notify/notify_test.go b/pkg/minikube/notify/notify_test.go index c87c2de372..8f926daea6 100644 --- a/pkg/minikube/notify/notify_test.go +++ b/pkg/minikube/notify/notify_test.go @@ -19,6 +19,7 @@ package notify import ( "encoding/json" "fmt" + "io/ioutil" "net/http" "net/http/httptest" "os" @@ -34,6 +35,12 @@ import ( "k8s.io/minikube/pkg/version" ) +func TestMaybePrintUpdateTextFromGithub(t *testing.T) { + if MaybePrintUpdateTextFromGithub() { + t.Fatal("MaybePrintUpdateTextFromGithub() expected to return false for basic setup, bot got true") + } +} + func TestShouldCheckURL(t *testing.T) { tempDir := tests.MakeTempDir() defer os.RemoveAll(tempDir) @@ -146,39 +153,85 @@ func TestGetLatestVersionFromURLMalformed(t *testing.T) { func TestMaybePrintUpdateText(t *testing.T) { tempDir := tests.MakeTempDir() defer os.RemoveAll(tempDir) - - viper.Set(config.WantUpdateNotification, true) - viper.Set(config.ReminderWaitPeriodInHours, 24) - outputBuffer := tests.NewFakeFile() out.SetErrFile(outputBuffer) - lastUpdateCheckFilePath := filepath.Join(tempDir, "last_update_check") - // test that no update text is printed if the latest version is lower/equal to the current version - latestVersionFromURL := "0.0.0-dev" - handler := &URLHandlerCorrect{ - releases: []Release{{Name: version.VersionPrefix + latestVersionFromURL}}, - } - server := httptest.NewServer(handler) - defer server.Close() - - MaybePrintUpdateText(server.URL, lastUpdateCheckFilePath) - if len(outputBuffer.String()) != 0 { - t.Fatalf("Expected MaybePrintUpdateText to not output text as the current version is %s and version %s was served from URL but output was [%s]", - version.GetVersion(), latestVersionFromURL, outputBuffer.String()) + var tc = []struct { + len int + wantUpdateNotification bool + latestVersionFromURL string + description string + status bool + url string + lastUpdateCheckFilePath string + }{ + { + len: 1, + latestVersionFromURL: "0.0.0-dev", + wantUpdateNotification: true, + description: "latest version lower or equal", + }, + { + len: 0, + latestVersionFromURL: "100.0.0-dev", + wantUpdateNotification: true, + description: "latest version greater", + status: true, + }, + { + len: 1, + latestVersionFromURL: "100.0.0-dev", + wantUpdateNotification: false, + description: "notification unwanted", + }, + { + len: 1, + latestVersionFromURL: "100.0.0-dev", + wantUpdateNotification: true, + description: "bad url", + url: "this is not valid url", + status: true, + }, + { + len: 1, + latestVersionFromURL: "10.0.0-dev", + wantUpdateNotification: true, + description: "bad lastUpdateCheckFilePath", + lastUpdateCheckFilePath: "/etc/passwd", + status: true, + }, } - // test that update text is printed if the latest version is greater than the current version - latestVersionFromURL = "100.0.0-dev" - handler = &URLHandlerCorrect{ - releases: []Release{{Name: version.VersionPrefix + latestVersionFromURL}}, - } - server = httptest.NewServer(handler) - defer server.Close() - - MaybePrintUpdateText(server.URL, lastUpdateCheckFilePath) - if len(outputBuffer.String()) == 0 { - t.Fatalf("Expected MaybePrintUpdateText to output text as the current version is %s and version %s was served from URL but output was [%s]", - version.GetVersion(), latestVersionFromURL, outputBuffer.String()) + viper.Set(config.ReminderWaitPeriodInHours, 24) + for _, test := range tc { + t.Run(test.description, func(t *testing.T) { + viper.Set(config.WantUpdateNotification, test.wantUpdateNotification) + lastUpdateCheckFilePath = filepath.Join(tempDir, "last_update_check") + if test.lastUpdateCheckFilePath != "" { + lastUpdateCheckFilePath = test.lastUpdateCheckFilePath + } + latestVersionFromURL := test.latestVersionFromURL + handler := &URLHandlerCorrect{ + releases: []Release{{Name: version.VersionPrefix + latestVersionFromURL}}, + } + server := httptest.NewServer(handler) + defer server.Close() + if test.url == "" { + test.url = server.URL + } + tmpfile, err := ioutil.TempFile("", "") + if err != nil { + t.Fatalf("Cannot create temp file: %v", err) + } + defer os.Remove(tmpfile.Name()) + status := MaybePrintUpdateText(test.url, tmpfile.Name()) + if test.status != status { + t.Fatalf("MaybePrintUpdateText expected to return %v, but got %v", test.status, status) + } + if len(outputBuffer.String()) == test.len { + t.Fatalf("Expected MaybePrintUpdateText to output text as the current version is %s and version %s was served from URL but output was [%s]", + version.GetVersion(), latestVersionFromURL, outputBuffer.String()) + } + }) } } diff --git a/pkg/minikube/out/out.go b/pkg/minikube/out/out.go index d053a803df..8fd1542c3d 100644 --- a/pkg/minikube/out/out.go +++ b/pkg/minikube/out/out.go @@ -161,9 +161,10 @@ func wantsColor(fd uintptr) bool { } term := os.Getenv("TERM") + colorTerm := os.Getenv("COLORTERM") // Example: term-256color - if !strings.Contains(term, "color") { - glog.Infof("TERM=%s, which probably does not support color", term) + if !strings.Contains(term, "color") && !strings.Contains(colorTerm, "truecolor") && !strings.Contains(colorTerm, "24bit") && !strings.Contains(colorTerm, "yes") { + glog.Infof("TERM=%s,COLORTERM=%s, which probably does not support color", term, colorTerm) return false } diff --git a/pkg/minikube/out/style.go b/pkg/minikube/out/style.go index 4791787413..0af52ed464 100644 --- a/pkg/minikube/out/style.go +++ b/pkg/minikube/out/style.go @@ -81,6 +81,7 @@ var styles = map[StyleEnum]style{ Check: {Prefix: "✅ "}, Celebration: {Prefix: "🎉 "}, Workaround: {Prefix: "👉 ", LowPrefix: lowIndent}, + Sparkle: {Prefix: "✨ "}, // Specialized purpose styles ISODownload: {Prefix: "💿 "}, diff --git a/pkg/minikube/out/style_enum.go b/pkg/minikube/out/style_enum.go index 890c54f6b0..0bc8ac1822 100644 --- a/pkg/minikube/out/style_enum.go +++ b/pkg/minikube/out/style_enum.go @@ -83,4 +83,5 @@ const ( Fileserver Empty Workaround + Sparkle ) diff --git a/pkg/minikube/out/style_test.go b/pkg/minikube/out/style_test.go new file mode 100644 index 0000000000..06e1c42073 --- /dev/null +++ b/pkg/minikube/out/style_test.go @@ -0,0 +1,187 @@ +/* +Copyright 2019 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 out + +import ( + "fmt" + "strings" + "testing" +) + +func TestApplyPrefix(t *testing.T) { + + var tests = []struct { + prefix, format, expected, description string + }{ + { + prefix: "bar", + format: "foo", + expected: "barfoo", + description: "bar prefix", + }, + { + prefix: "", + format: "foo", + expected: "foo", + description: "empty prefix", + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + got := applyPrefix(test.prefix, test.format) + if got != test.expected { + t.Errorf("Expected %v but got %v", test.expected, got) + } + }) + } +} + +func TestLowPrefix(t *testing.T) { + + var tests = []struct { + expected string + description string + style style + }{ + { + expected: lowBullet, + description: "empty prefix", + }, + { + expected: "bar", + style: style{LowPrefix: "bar"}, + description: "lowPrefix", + }, + { + expected: lowBullet, + style: style{Prefix: "foo"}, + description: "prefix without spaces", + }, + { + expected: lowIndent, + style: style{Prefix: " foo"}, + description: "prefix with spaces", + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + got := lowPrefix(test.style) + if got != test.expected { + t.Errorf("Expected %v but got %v", test.expected, got) + } + }) + } +} + +func TestApplyStyle(t *testing.T) { + + var tests = []struct { + expected string + description string + styleEnum StyleEnum + format string + useColor bool + }{ + { + expected: fmt.Sprintf("%sbar", lowBullet), + description: "format bar, empty style, color off", + styleEnum: Empty, + useColor: false, + format: "bar", + }, + { + expected: "bar", + description: "not existing style", + styleEnum: 9999, + useColor: false, + format: "bar", + }, + { + expected: fmt.Sprintf("%sfoo", styles[Ready].Prefix), + description: "format foo, ready style, color on", + styleEnum: Ready, + useColor: true, + format: "foo", + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + rawGot := applyStyle(test.styleEnum, test.useColor, test.format) + got := strings.TrimSpace(rawGot) + if got != test.expected { + t.Errorf("Expected '%v' but got '%v'", test.expected, got) + } + }) + } +} + +func TestApplyTemplateFormating(t *testing.T) { + + var tests = []struct { + expected string + description string + styleEnum StyleEnum + format string + useColor bool + a []V + }{ + { + expected: fmt.Sprintf("%sbar", lowBullet), + description: "format bar, empty style, color off", + styleEnum: Empty, + useColor: false, + format: "bar", + }, + { + expected: "bar", + description: "not existing style", + styleEnum: 9999, + useColor: false, + format: "bar", + }, + { + expected: fmt.Sprintf("%sfoo", styles[Ready].Prefix), + description: "format foo, ready style, color on, a nil", + styleEnum: Ready, + useColor: true, + format: "foo", + }, + { + expected: fmt.Sprintf("%sfoo", styles[Ready].Prefix), + description: "format foo, ready style, color on", + styleEnum: Ready, + useColor: true, + format: "foo", + }, + { + expected: fmt.Sprintf("%s{{ a }}", styles[Ready].Prefix), + description: "bad format", + styleEnum: Ready, + useColor: true, + format: "{{ a }}", + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + rawGot := applyTemplateFormatting(test.styleEnum, test.useColor, test.format, test.a...) + got := strings.TrimSpace(rawGot) + if got != test.expected { + t.Errorf("Expected '%v' but got '%v'", test.expected, got) + } + }) + } +} diff --git a/pkg/minikube/problem/err_map.go b/pkg/minikube/problem/err_map.go index 4d5538cdb9..bad986cdcf 100644 --- a/pkg/minikube/problem/err_map.go +++ b/pkg/minikube/problem/err_map.go @@ -56,7 +56,7 @@ var vmProblems = map[string]match{ Issues: []int{1926, 4206}, }, "HYPERKIT_NOT_FOUND": { - Regexp: re(`Driver "hyperkit" not found. Do you have the plugin binary .* accessible in your PATH?`), + Regexp: re(`Driver "hyperkit" not found.`), Advice: "Please install the minikube hyperkit VM driver, or select an alternative --vm-driver", URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/hyperkit/", HideCreateLink: true, @@ -227,10 +227,16 @@ var vmProblems = map[string]match{ }, "VBOX_VTX_DISABLED": { Regexp: re(`This computer doesn't have VT-X/AMD-v enabled`), - Advice: "Your host does not support virtualization. If you are running minikube within a VM, try '--vm-driver=none'. Otherwise, enable virtualization in your BIOS", + Advice: "Virtualization support is disabled on your computer. If you are running minikube within a VM, try '--vm-driver=none'. Otherwise, consult your systems BIOS manual for how to enable virtualization.", Issues: []int{3900, 4730}, HideCreateLink: true, }, + "VERR_VERR_VMX_DISABLED": { + Regexp: re(`VT-x is disabled.*VERR_VMX_MSR_ALL_VMX_DISABLED`), + Advice: "Virtualization support is disabled on your computer. If you are running minikube within a VM, try '--vm-driver=none'. Otherwise, consult your systems BIOS manual for how to enable virtualization.", + Issues: []int{5282, 5456}, + HideCreateLink: true, + }, "VBOX_VERR_VMX_NO_VMX": { Regexp: re(`VT-x is not available.*VERR_VMX_NO_VMX`), Advice: "Your host does not support virtualization. If you are running minikube within a VM, try '--vm-driver=none'. Otherwise, enable virtualization in your BIOS", diff --git a/pkg/minikube/problem/problem_test.go b/pkg/minikube/problem/problem_test.go index 4bb561d5d7..47add13a70 100644 --- a/pkg/minikube/problem/problem_test.go +++ b/pkg/minikube/problem/problem_test.go @@ -110,7 +110,7 @@ func TestFromError(t *testing.T) { {0, "", "", "this is just a lame error message with no matches."}, {2991, "", "KVM_UNAVAILABLE", "Unable to start VM: create: Error creating machine: Error in driver during machine creation: creating domain: Error defining domain xml:\n\n: virError(Code=8, Domain=44, Message='invalid argument: could not find capabilities for domaintype=kvm ')"}, {3594, "", "HOST_CIDR_CONFLICT", "Error starting host: Error starting stopped host: Error setting up host only network on machine start: host-only cidr conflicts with the network address of a host interface."}, - {3614, "", "VBOX_HOST_ADAPTER", "Error starting host: Error starting stopped host: Error setting up host only network on machine start: The host-only adapter we just created is not visible. This is a well known VirtualBox bug. You might want to uninstall it and reinstall at least version 5.0.12 that is is supposed to fix this issue"}, + {3614, "", "VBOX_HOST_ADAPTER", "Error starting host: Error starting stopped host: Error setting up host only network on machine start: The host-only adapter we just created is not visible. This is a well known VirtualBox bug. You might want to uninstall it and reinstall at least version 5.0.12 that is supposed to fix this issue"}, {3784, "", "VBOX_NOT_FOUND", "create: precreate: VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path"}, {3849, "", "IP_NOT_FOUND", "bootstrapper: Error creating new ssh host from driver: Error getting ssh host name for driver: IP not found"}, {3859, "windows", "VBOX_HARDENING", `Unable to start VM: create: creating: Unable to start the VM: C:\Program Files\Oracle\VirtualBox\VBoxManage.exe startvm minikube --type headless failed: @@ -121,7 +121,7 @@ VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component MachineWrap, {4107, "darwin", "VBOX_BLOCKED", "Result Code: NS_ERROR_FAILURE (0x80004005)"}, {4302, "", "APISERVER_TIMEOUT", "apiserver: timed out waiting for the condition"}, {4252, "", "DOWNLOAD_TLS_OVERSIZED", "Failed to update cluster: downloading binaries: downloading kubeadm: Error downloading kubeadm v1.14.1: failed to download: failed to download to temp file: download failed: 5 error(s) occurred:\n\nTemporary download error: Get https://storage.googleapis.com/kubernetes-release/release/v1.14.1/bin/linux/amd64/kubeadm: proxyconnect tcp: tls: oversized record received with length 20527"}, - {4222, "", "VBOX_HOST_ADAPTER", "Unable to start VM: create: creating: Error setting up host only network on machine start: The host-only adapter we just created is not visible. This is a well known VirtualBox bug. You might want to uninstall it and reinstall at least version 5.0.12 that is is supposed to fix this issue"}, + {4222, "", "VBOX_HOST_ADAPTER", "Unable to start VM: create: creating: Error setting up host only network on machine start: The host-only adapter we just created is not visible. This is a well known VirtualBox bug. You might want to uninstall it and reinstall at least version 5.0.12 that is supposed to fix this issue"}, } for _, tc := range tests { t.Run(tc.want, func(t *testing.T) { diff --git a/pkg/minikube/proxy/proxy_test.go b/pkg/minikube/proxy/proxy_test.go index 17f62e2a86..4e76023f91 100644 --- a/pkg/minikube/proxy/proxy_test.go +++ b/pkg/minikube/proxy/proxy_test.go @@ -241,7 +241,7 @@ func TestUpdateTransport(t *testing.T) { rt := c.WrapTransport(nil) if rt == rc.WrapTransport(transport) { - t.Fatalf("Excpected to reuse existing RoundTripper(%v) but found %v", rt, transport) + t.Fatalf("Expected to reuse existing RoundTripper(%v) but found %v", rt, transport) } }) diff --git a/pkg/minikube/drivers/hyperkit/doc.go b/pkg/minikube/registry/drvs/hyperkit/doc.go similarity index 100% rename from pkg/minikube/drivers/hyperkit/doc.go rename to pkg/minikube/registry/drvs/hyperkit/doc.go diff --git a/pkg/minikube/drivers/hyperkit/driver.go b/pkg/minikube/registry/drvs/hyperkit/hyperkit.go similarity index 58% rename from pkg/minikube/drivers/hyperkit/driver.go rename to pkg/minikube/registry/drvs/hyperkit/hyperkit.go index 0db642f25b..e42b4207da 100644 --- a/pkg/minikube/drivers/hyperkit/driver.go +++ b/pkg/minikube/registry/drvs/hyperkit/hyperkit.go @@ -20,30 +20,38 @@ package hyperkit import ( "fmt" + "os/exec" + "strings" "github.com/docker/machine/libmachine/drivers" "github.com/pborman/uuid" + "k8s.io/minikube/pkg/drivers/hyperkit" cfg "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/registry" ) +const ( + docURL = "https://minikube.sigs.k8s.io/docs/reference/drivers/hyperkit/" +) + func init() { if err := registry.Register(registry.DriverDef{ - Name: constants.DriverHyperkit, - Builtin: false, - ConfigCreator: createHyperkitHost, + Name: driver.HyperKit, + Config: configure, + Status: status, + Priority: registry.Preferred, }); err != nil { panic(fmt.Sprintf("register: %v", err)) } } -func createHyperkitHost(config cfg.MachineConfig) interface{} { - uuID := config.UUID - if uuID == "" { - uuID = uuid.NewUUID().String() +func configure(config cfg.MachineConfig) interface{} { + u := config.UUID + if u == "" { + u = uuid.NewUUID().String() } return &hyperkit.Driver{ @@ -58,9 +66,24 @@ func createHyperkitHost(config cfg.MachineConfig) interface{} { CPU: config.CPUs, NFSShares: config.NFSShare, NFSSharesRoot: config.NFSSharesRoot, - UUID: uuID, + UUID: u, VpnKitSock: config.HyperkitVpnKitSock, VSockPorts: config.HyperkitVSockPorts, - Cmdline: "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=" + cfg.GetMachineName(), + Cmdline: "loglevel=3 console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes random.trust_cpu=on hw_rng_model=virtio base host=" + cfg.GetMachineName(), } } + +func status() registry.State { + path, err := exec.LookPath("hyperkit") + if err != nil { + return registry.State{Error: err, Fix: "Run 'brew install hyperkit'", Doc: docURL} + } + + cmd := exec.Command(path, "-v") + out, err := cmd.CombinedOutput() + if err != nil { + return registry.State{Installed: true, Error: fmt.Errorf("%s failed:\n%s", strings.Join(cmd.Args, " "), out), Fix: "Run 'brew install hyperkit'", Doc: docURL} + } + + return registry.State{Installed: true, Healthy: true} +} diff --git a/pkg/minikube/drivers/hyperv/doc.go b/pkg/minikube/registry/drvs/hyperv/doc.go similarity index 100% rename from pkg/minikube/drivers/hyperv/doc.go rename to pkg/minikube/registry/drvs/hyperv/doc.go diff --git a/pkg/minikube/drivers/hyperv/driver.go b/pkg/minikube/registry/drvs/hyperv/hyperv.go similarity index 52% rename from pkg/minikube/drivers/hyperv/driver.go rename to pkg/minikube/registry/drvs/hyperv/hyperv.go index 00b7543351..e0216f12cf 100644 --- a/pkg/minikube/drivers/hyperv/driver.go +++ b/pkg/minikube/registry/drvs/hyperv/hyperv.go @@ -19,35 +19,57 @@ limitations under the License. package hyperv import ( + "fmt" + "os/exec" + "strings" + "github.com/docker/machine/drivers/hyperv" "github.com/docker/machine/libmachine/drivers" + cfg "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/registry" ) +const ( + docURL = "https://minikube.sigs.k8s.io/docs/reference/drivers/hyperv/" +) + func init() { - registry.Register(registry.DriverDef{ - Name: constants.DriverHyperv, - Builtin: true, - ConfigCreator: createHypervHost, - DriverCreator: func() drivers.Driver { - return hyperv.NewDriver("", "") - }, - }) + if err := registry.Register(registry.DriverDef{ + Name: driver.HyperV, + Init: func() drivers.Driver { return hyperv.NewDriver("", "") }, + Config: configure, + Status: status, + Priority: registry.Preferred, + }); err != nil { + panic(fmt.Sprintf("register: %v", err)) + } } -func createHypervHost(config cfg.MachineConfig) interface{} { +func configure(config cfg.MachineConfig) interface{} { d := hyperv.NewDriver(cfg.GetMachineName(), localpath.MiniPath()) - d.Boot2DockerURL = config.Downloader.GetISOFileURI(config.MinikubeISO) d.VSwitch = config.HypervVirtualSwitch d.MemSize = config.Memory d.CPU = config.CPUs - d.DiskSize = int(config.DiskSize) + d.DiskSize = config.DiskSize d.SSHUser = "docker" d.DisableDynamicMemory = true // default to disable dynamic memory as minikube is unlikely to work properly with dynamic memory - return d } + +func status() registry.State { + path, err := exec.LookPath("powershell") + if err != nil { + return registry.State{Error: err} + } + + cmd := exec.Command(path, "Get-WindowsOptionalFeature", "-FeatureName", "Microsoft-Hyper-V-All", "-Online") + out, err := cmd.CombinedOutput() + if err != nil { + return registry.State{Installed: false, Error: fmt.Errorf("%s failed:\n%s", strings.Join(cmd.Args, " "), out), Fix: "Start PowerShell as Administrator, and run: 'Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All'", Doc: docURL} + } + return registry.State{Installed: true, Healthy: true} +} diff --git a/pkg/minikube/registry/drvs/init.go b/pkg/minikube/registry/drvs/init.go new file mode 100644 index 0000000000..c159e893ee --- /dev/null +++ b/pkg/minikube/registry/drvs/init.go @@ -0,0 +1,29 @@ +/* +Copyright 2019 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 drvs + +import ( + // Register all of the drvs we know of + _ "k8s.io/minikube/pkg/minikube/registry/drvs/hyperkit" + _ "k8s.io/minikube/pkg/minikube/registry/drvs/hyperv" + _ "k8s.io/minikube/pkg/minikube/registry/drvs/kvm2" + _ "k8s.io/minikube/pkg/minikube/registry/drvs/none" + _ "k8s.io/minikube/pkg/minikube/registry/drvs/parallels" + _ "k8s.io/minikube/pkg/minikube/registry/drvs/virtualbox" + _ "k8s.io/minikube/pkg/minikube/registry/drvs/vmware" + _ "k8s.io/minikube/pkg/minikube/registry/drvs/vmwarefusion" +) diff --git a/pkg/minikube/drivers/kvm2/doc.go b/pkg/minikube/registry/drvs/kvm2/doc.go similarity index 100% rename from pkg/minikube/drivers/kvm2/doc.go rename to pkg/minikube/registry/drvs/kvm2/doc.go diff --git a/pkg/minikube/registry/drvs/kvm2/kvm2.go b/pkg/minikube/registry/drvs/kvm2/kvm2.go new file mode 100644 index 0000000000..cdd61f2094 --- /dev/null +++ b/pkg/minikube/registry/drvs/kvm2/kvm2.go @@ -0,0 +1,117 @@ +// +build linux + +/* +Copyright 2018 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 kvm2 + +import ( + "fmt" + "os/exec" + "path/filepath" + "strings" + + "github.com/docker/machine/libmachine/drivers" + + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/driver" + "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/minikube/registry" +) + +const ( + docURL = "https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/" +) + +func init() { + if err := registry.Register(registry.DriverDef{ + Name: driver.KVM2, + Config: configure, + Status: status, + Priority: registry.Preferred, + }); err != nil { + panic(fmt.Sprintf("register failed: %v", err)) + } +} + +// This is duplicate of kvm.Driver. Avoids importing the kvm2 driver, which requires cgo & libvirt. +type kvmDriver struct { + *drivers.BaseDriver + + Memory int + DiskSize int + CPU int + Network string + PrivateNetwork string + ISO string + Boot2DockerURL string + DiskPath string + GPU bool + Hidden bool + ConnectionURI string +} + +func configure(mc config.MachineConfig) interface{} { + name := config.GetMachineName() + return kvmDriver{ + BaseDriver: &drivers.BaseDriver{ + MachineName: name, + StorePath: localpath.MiniPath(), + SSHUser: "docker", + }, + Memory: mc.Memory, + CPU: mc.CPUs, + Network: mc.KVMNetwork, + PrivateNetwork: "minikube-net", + Boot2DockerURL: mc.Downloader.GetISOFileURI(mc.MinikubeISO), + DiskSize: mc.DiskSize, + DiskPath: filepath.Join(localpath.MiniPath(), "machines", name, fmt.Sprintf("%s.rawdisk", name)), + ISO: filepath.Join(localpath.MiniPath(), "machines", name, "boot2docker.iso"), + GPU: mc.KVMGPU, + Hidden: mc.KVMHidden, + ConnectionURI: mc.KVMQemuURI, + } +} + +func status() registry.State { + path, err := exec.LookPath("virsh") + if err != nil { + return registry.State{Error: err, Fix: "Install libvirt", Doc: docURL} + } + + cmd := exec.Command(path, "domcapabilities", "--virttype", "kvm") + out, err := cmd.CombinedOutput() + if err != nil { + return registry.State{ + Installed: true, + Error: fmt.Errorf("%s failed:\n%s", strings.Join(cmd.Args, " "), strings.TrimSpace(string(out))), + Fix: "Follow your Linux distribution instructions for configuring KVM", + Doc: docURL, + } + } + + cmd = exec.Command("virsh", "list") + out, err = cmd.CombinedOutput() + if err != nil { + return registry.State{ + Installed: true, + Error: fmt.Errorf("%s failed:\n%s", strings.Join(cmd.Args, " "), strings.TrimSpace(string(out))), + Fix: "Check that libvirtd is properly installed and that you are a member of the appropriate libvirt group", + Doc: docURL, + } + } + return registry.State{Installed: true, Healthy: true} +} diff --git a/pkg/minikube/drivers/none/doc.go b/pkg/minikube/registry/drvs/none/doc.go similarity index 100% rename from pkg/minikube/drivers/none/doc.go rename to pkg/minikube/registry/drvs/none/doc.go diff --git a/pkg/minikube/drivers/none/driver.go b/pkg/minikube/registry/drvs/none/none.go similarity index 56% rename from pkg/minikube/drivers/none/driver.go rename to pkg/minikube/registry/drvs/none/none.go index 9489bd89b2..fa094f9aea 100644 --- a/pkg/minikube/drivers/none/driver.go +++ b/pkg/minikube/registry/drvs/none/none.go @@ -1,3 +1,5 @@ +// +build linux + /* Copyright 2018 The Kubernetes Authors All rights reserved. @@ -18,44 +20,40 @@ package none import ( "fmt" - "os" + "os/exec" "github.com/docker/machine/libmachine/drivers" "k8s.io/minikube/pkg/drivers/none" - cfg "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/registry" ) func init() { if err := registry.Register(registry.DriverDef{ - Name: constants.DriverNone, - Builtin: true, - ConfigCreator: createNoneHost, - DriverCreator: func() drivers.Driver { - return none.NewDriver(none.Config{}) - }, + Name: driver.None, + Config: configure, + Init: func() drivers.Driver { return none.NewDriver(none.Config{}) }, + Status: status, + Priority: registry.Discouraged, // requires root }); err != nil { panic(fmt.Sprintf("register failed: %v", err)) } } -// createNoneHost creates a none Driver from a MachineConfig -func createNoneHost(config cfg.MachineConfig) interface{} { +func configure(mc config.MachineConfig) interface{} { return none.NewDriver(none.Config{ - MachineName: cfg.GetMachineName(), + MachineName: config.GetMachineName(), StorePath: localpath.MiniPath(), - ContainerRuntime: config.ContainerRuntime, + ContainerRuntime: mc.ContainerRuntime, }) } -// AutoOptions returns suggested extra options based on the current config -func AutoOptions() string { - // for more info see: https://github.com/kubernetes/minikube/issues/3511 - f := "/run/systemd/resolve/resolv.conf" - if _, err := os.Stat(f); err != nil { - return "" +func status() registry.State { + _, err := exec.LookPath("systemctl") + if err != nil { + return registry.State{Error: err, Fix: "Use a systemd based Linux distribution", Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/none/"} } - return fmt.Sprintf("kubelet.resolv-conf=%s", f) + return registry.State{Installed: true, Healthy: true} } diff --git a/pkg/minikube/drivers/parallels/doc.go b/pkg/minikube/registry/drvs/parallels/doc.go similarity index 100% rename from pkg/minikube/drivers/parallels/doc.go rename to pkg/minikube/registry/drvs/parallels/doc.go diff --git a/pkg/minikube/drivers/parallels/driver.go b/pkg/minikube/registry/drvs/parallels/parallels.go similarity index 67% rename from pkg/minikube/drivers/parallels/driver.go rename to pkg/minikube/registry/drvs/parallels/parallels.go index c967740f82..193ea54da9 100644 --- a/pkg/minikube/drivers/parallels/driver.go +++ b/pkg/minikube/registry/drvs/parallels/parallels.go @@ -20,23 +20,23 @@ package parallels import ( "fmt" + "os/exec" parallels "github.com/Parallels/docker-machine-parallels" "github.com/docker/machine/libmachine/drivers" cfg "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/registry" ) func init() { err := registry.Register(registry.DriverDef{ - Name: constants.DriverParallels, - Builtin: true, - ConfigCreator: createParallelsHost, - DriverCreator: func() drivers.Driver { - return parallels.NewDriver("", "") - }, + Name: driver.Parallels, + Config: configure, + Status: status, + Priority: registry.Default, + Init: func() drivers.Driver { return parallels.NewDriver("", "") }, }) if err != nil { panic(fmt.Sprintf("unable to register: %v", err)) @@ -44,7 +44,7 @@ func init() { } -func createParallelsHost(config cfg.MachineConfig) interface{} { +func configure(config cfg.MachineConfig) interface{} { d := parallels.NewDriver(cfg.GetMachineName(), localpath.MiniPath()).(*parallels.Driver) d.Boot2DockerURL = config.Downloader.GetISOFileURI(config.MinikubeISO) d.Memory = config.Memory @@ -52,3 +52,11 @@ func createParallelsHost(config cfg.MachineConfig) interface{} { d.DiskSize = config.DiskSize return d } + +func status() registry.State { + _, err := exec.LookPath("docker-machine-driver-parallels") + if err != nil { + return registry.State{Error: err, Fix: "Install docker-machine-driver-parallels", Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/parallels/"} + } + return registry.State{Installed: true, Healthy: true} +} diff --git a/pkg/minikube/registry/drvs/virtualbox/virtualbox.go b/pkg/minikube/registry/drvs/virtualbox/virtualbox.go new file mode 100644 index 0000000000..fa4483cf0b --- /dev/null +++ b/pkg/minikube/registry/drvs/virtualbox/virtualbox.go @@ -0,0 +1,91 @@ +/* +Copyright 2018 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 virtualbox + +import ( + "fmt" + "os/exec" + "strings" + + "github.com/docker/machine/drivers/virtualbox" + "github.com/docker/machine/libmachine/drivers" + + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/driver" + "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/minikube/registry" +) + +const ( + defaultVirtualboxNicType = "virtio" + docURL = "https://minikube.sigs.k8s.io/docs/reference/drivers/virtualbox/" +) + +func init() { + err := registry.Register(registry.DriverDef{ + Name: driver.VirtualBox, + Config: configure, + Status: status, + Priority: registry.Fallback, + Init: func() drivers.Driver { return virtualbox.NewDriver("", "") }, + }) + if err != nil { + panic(fmt.Sprintf("unable to register: %v", err)) + } +} + +func configure(mc config.MachineConfig) interface{} { + d := virtualbox.NewDriver(config.GetMachineName(), localpath.MiniPath()) + d.Boot2DockerURL = mc.Downloader.GetISOFileURI(mc.MinikubeISO) + d.Memory = mc.Memory + d.CPU = mc.CPUs + d.DiskSize = mc.DiskSize + d.HostOnlyCIDR = mc.HostOnlyCIDR + d.NoShare = mc.DisableDriverMounts + d.NoVTXCheck = mc.NoVTXCheck + d.NatNicType = defaultVirtualboxNicType + d.HostOnlyNicType = defaultVirtualboxNicType + d.DNSProxy = mc.DNSProxy + d.HostDNSResolver = mc.HostDNSResolver + return d +} + +func status() registry.State { + // Re-use this function as it's particularly helpful for Windows + tryPath := driver.VBoxManagePath() + path, err := exec.LookPath(tryPath) + if err != nil { + return registry.State{ + Error: fmt.Errorf("unable to find VBoxManage in $PATH"), + Fix: "Install VirtualBox", + Doc: docURL, + } + } + + cmd := exec.Command(path, "list", "hostinfo") + out, err := cmd.CombinedOutput() + if err != nil { + return registry.State{ + Installed: true, + Error: fmt.Errorf("%s failed:\n%s", strings.Join(cmd.Args, " "), out), + Fix: "Install the latest version of VirtualBox", + Doc: docURL, + } + } + + return registry.State{Installed: true, Healthy: true} +} diff --git a/pkg/minikube/drivers/vmware/doc.go b/pkg/minikube/registry/drvs/vmware/doc.go similarity index 100% rename from pkg/minikube/drivers/vmware/doc.go rename to pkg/minikube/registry/drvs/vmware/doc.go diff --git a/pkg/minikube/drivers/vmware/driver.go b/pkg/minikube/registry/drvs/vmware/vmware.go similarity index 53% rename from pkg/minikube/drivers/vmware/driver.go rename to pkg/minikube/registry/drvs/vmware/vmware.go index 6a1d16a496..82b1126d18 100644 --- a/pkg/minikube/drivers/vmware/driver.go +++ b/pkg/minikube/registry/drvs/vmware/vmware.go @@ -18,34 +18,48 @@ package vmware import ( "fmt" + "os/exec" vmwcfg "github.com/machine-drivers/docker-machine-driver-vmware/pkg/drivers/vmware/config" - cfg "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/registry" ) func init() { err := registry.Register(registry.DriverDef{ - Name: constants.DriverVmware, - Builtin: false, - ConfigCreator: createVMwareHost, + Name: driver.VMware, + Config: configure, + Priority: registry.Default, + Status: status, }) if err != nil { panic(fmt.Sprintf("unable to register: %v", err)) } } -func createVMwareHost(config cfg.MachineConfig) interface{} { - d := vmwcfg.NewConfig(cfg.GetMachineName(), localpath.MiniPath()) - d.Boot2DockerURL = config.Downloader.GetISOFileURI(config.MinikubeISO) - d.Memory = config.Memory - d.CPU = config.CPUs - d.DiskSize = config.DiskSize +func configure(mc config.MachineConfig) interface{} { + d := vmwcfg.NewConfig(config.GetMachineName(), localpath.MiniPath()) + d.Boot2DockerURL = mc.Downloader.GetISOFileURI(mc.MinikubeISO) + d.Memory = mc.Memory + d.CPU = mc.CPUs + d.DiskSize = mc.DiskSize // TODO(frapposelli): push these defaults upstream to fixup this driver d.SSHPort = 22 d.ISO = d.ResolveStorePath("boot2docker.iso") return d } + +func status() registry.State { + _, err := exec.LookPath("docker-machine-driver-vmware") + if err != nil { + return registry.State{Error: err, Fix: "Install docker-machine-driver-vmware", Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/vmware/"} + } + _, err = exec.LookPath("vmrun") + if err != nil { + return registry.State{Error: err, Fix: "Install vmrun", Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/vmware/"} + } + return registry.State{Installed: true, Healthy: true} +} diff --git a/pkg/minikube/drivers/vmwarefusion/doc.go b/pkg/minikube/registry/drvs/vmwarefusion/doc.go similarity index 100% rename from pkg/minikube/drivers/vmwarefusion/doc.go rename to pkg/minikube/registry/drvs/vmwarefusion/doc.go diff --git a/pkg/minikube/drivers/vmwarefusion/driver.go b/pkg/minikube/registry/drvs/vmwarefusion/vmwarefusion.go similarity index 68% rename from pkg/minikube/drivers/vmwarefusion/driver.go rename to pkg/minikube/registry/drvs/vmwarefusion/vmwarefusion.go index 7f115f7f1e..cbff0022ed 100644 --- a/pkg/minikube/drivers/vmwarefusion/driver.go +++ b/pkg/minikube/registry/drvs/vmwarefusion/vmwarefusion.go @@ -20,29 +20,31 @@ package vmwarefusion import ( "fmt" + "os/exec" "github.com/docker/machine/drivers/vmwarefusion" "github.com/docker/machine/libmachine/drivers" + "github.com/pkg/errors" + cfg "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/registry" ) func init() { if err := registry.Register(registry.DriverDef{ - Name: constants.DriverVmwareFusion, - Builtin: true, - ConfigCreator: createVMwareFusionHost, - DriverCreator: func() drivers.Driver { - return vmwarefusion.NewDriver("", "") - }, + Name: driver.VMwareFusion, + Config: configure, + Status: status, + Init: func() drivers.Driver { return vmwarefusion.NewDriver("", "") }, + Priority: registry.Deprecated, }); err != nil { panic(fmt.Sprintf("register: %v", err)) } } -func createVMwareFusionHost(config cfg.MachineConfig) interface{} { +func configure(config cfg.MachineConfig) interface{} { d := vmwarefusion.NewDriver(cfg.GetMachineName(), localpath.MiniPath()).(*vmwarefusion.Driver) d.Boot2DockerURL = config.Downloader.GetISOFileURI(config.MinikubeISO) d.Memory = config.Memory @@ -54,3 +56,11 @@ func createVMwareFusionHost(config cfg.MachineConfig) interface{} { d.ISO = d.ResolveStorePath("boot2docker.iso") return d } + +func status() registry.State { + _, err := exec.LookPath("vmrun") + if err != nil { + return registry.State{Error: errors.Wrap(err, "vmrun path check"), Fix: "Install VMWare Fusion", Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/vmwarefusion/"} + } + return registry.State{Installed: true, Healthy: true} +} diff --git a/pkg/minikube/registry/global.go b/pkg/minikube/registry/global.go new file mode 100644 index 0000000000..97882296a5 --- /dev/null +++ b/pkg/minikube/registry/global.go @@ -0,0 +1,85 @@ +/* +Copyright 2018 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 registry + +import ( + "os" + + "github.com/golang/glog" +) + +var ( + // globalRegistry is a globally accessible driver registry + globalRegistry = newRegistry() +) + +// DriverState is metadata relating to a driver and status +type DriverState struct { + Name string + Priority Priority + State State +} + +func (d DriverState) String() string { + return d.Name +} + +// List lists drivers in global registry +func List() []DriverDef { + return globalRegistry.List() +} + +// Register registers driver with the global registry +func Register(driver DriverDef) error { + return globalRegistry.Register(driver) +} + +// Driver gets a named driver from the global registry +func Driver(name string) DriverDef { + return globalRegistry.Driver(name) +} + +// Installed returns a list of installed drivers in the global registry +func Installed() []DriverState { + sts := []DriverState{} + glog.Infof("Querying for installed drivers using PATH=%s", os.Getenv("PATH")) + + for _, d := range globalRegistry.List() { + if d.Status == nil { + glog.Errorf("%q does not implement Status", d.Name) + continue + } + s := d.Status() + glog.Infof("%s priority: %d, state: %+v", d.Name, d.Priority, s) + + if !s.Installed { + glog.Infof("%q not installed: %v", d.Name, s.Error) + continue + } + sts = append(sts, DriverState{Name: d.Name, Priority: d.Priority, State: s}) + } + return sts +} + +// Status returns the state of a driver within the global registry +func Status(name string) State { + d := globalRegistry.Driver(name) + if d.Empty() { + return State{} + } + return d.Status() +} diff --git a/pkg/minikube/registry/global_test.go b/pkg/minikube/registry/global_test.go new file mode 100644 index 0000000000..c6b23552c5 --- /dev/null +++ b/pkg/minikube/registry/global_test.go @@ -0,0 +1,118 @@ +/* +Copyright 2018 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 registry + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestGlobalRegister(t *testing.T) { + globalRegistry = newRegistry() + foo := DriverDef{Name: "foo"} + if err := Register(foo); err != nil { + t.Errorf("Register = %v, expected nil", err) + } + if err := Register(foo); err == nil { + t.Errorf("Register = nil, expected duplicate err") + } +} + +func TestGlobalDriver(t *testing.T) { + foo := DriverDef{Name: "foo"} + globalRegistry = newRegistry() + + if err := Register(foo); err != nil { + t.Errorf("Register = %v, expected nil", err) + } + + d := Driver("foo") + if d.Empty() { + t.Errorf("driver.Empty = true, expected false") + } + + d = Driver("bar") + if !d.Empty() { + t.Errorf("driver.Empty = false, expected true") + } +} + +func TestGlobalList(t *testing.T) { + foo := DriverDef{Name: "foo"} + globalRegistry = newRegistry() + if err := Register(foo); err != nil { + t.Errorf("register returned error: %v", err) + } + + if diff := cmp.Diff(List(), []DriverDef{foo}); diff != "" { + t.Errorf("list mismatch (-want +got):\n%s", diff) + } +} + +func TestGlobalInstalled(t *testing.T) { + globalRegistry = newRegistry() + + if err := Register(DriverDef{Name: "foo"}); err != nil { + t.Errorf("register returned error: %v", err) + } + + bar := DriverDef{ + Name: "bar", + Priority: Default, + Status: func() State { return State{Installed: true} }, + } + if err := Register(bar); err != nil { + t.Errorf("register returned error: %v", err) + } + + expected := []DriverState{ + { + Name: "bar", + Priority: Default, + State: State{ + Installed: true, + }, + }, + } + + if diff := cmp.Diff(Installed(), expected); diff != "" { + t.Errorf("installed mismatch (-want +got):\n%s", diff) + } +} + +func TestGlobalStatus(t *testing.T) { + globalRegistry = newRegistry() + + if err := Register(DriverDef{Name: "foo"}); err != nil { + t.Errorf("register returned error: %v", err) + } + + expected := State{Installed: true, Healthy: true} + bar := DriverDef{ + Name: "bar", + Priority: Default, + Status: func() State { return expected }, + } + if err := Register(bar); err != nil { + t.Errorf("register returned error: %v", err) + } + + if diff := cmp.Diff(Status("bar"), expected); diff != "" { + t.Errorf("status mismatch (-want +got):\n%s", diff) + } +} diff --git a/pkg/minikube/registry/registry.go b/pkg/minikube/registry/registry.go index f3ac464b14..2c1676fed2 100644 --- a/pkg/minikube/registry/registry.go +++ b/pkg/minikube/registry/registry.go @@ -21,18 +21,28 @@ import ( "sync" "github.com/docker/machine/libmachine/drivers" - "github.com/pkg/errors" + "k8s.io/minikube/pkg/minikube/config" ) -var ( - // ErrDriverNameExist is the error returned when trying to register a driver - // which already exists in registry - ErrDriverNameExist = errors.New("registry: duplicated driver name") +// Priority is how we determine what driver to default to +type Priority int - // ErrDriverNotFound is the error returned when driver of a given name does - // not exist in registry - ErrDriverNotFound = errors.New("registry: driver not found") +const ( + // Unknown priority + Unknown Priority = iota + // Discouraged priority + Discouraged + // Deprecated priority + Deprecated + // Fallback priority + Fallback + // Default priority + Default + // Preferred priority + Preferred + // StronglyPreferred priority + StronglyPreferred ) // Registry contains all the supported driver definitions on the host @@ -47,31 +57,49 @@ type Registry interface { List() []DriverDef } -// ConfigFactory is a function that creates a driver config from MachineConfig -type ConfigFactory func(config.MachineConfig) interface{} +// Configurator emits a struct to be marshalled into JSON for Machine Driver +type Configurator func(config.MachineConfig) interface{} -// DriverFactory is a function that load a byte stream and create a driver -type DriverFactory func() drivers.Driver +// Loader is a function that loads a byte stream and creates a driver. +type Loader func() drivers.Driver -// DriverDef defines a machine driver metadata. It tells minikube how to initialize -// and load drivers. +// StatusChecker checks if a driver is available, offering a +type StatusChecker func() State + +// State is the current state of the driver and its dependencies +type State struct { + Installed bool + Healthy bool + Error error + Fix string + Doc string +} + +// DriverDef defines how to initialize and load a machine driver type DriverDef struct { // Name of the machine driver. It has to be unique. Name string - // BuiltIn indicates if the driver is builtin minikube binary, or the driver is - // triggered through RPC. - Builtin bool + // Config is a function that emits a configured driver struct + Config Configurator - // ConfigCreator generate a raw driver object by minikube's machine config. - ConfigCreator ConfigFactory + // Init is a function that initializes a machine driver, if built-in to the minikube binary + Init Loader - // DriverCreator is the factory method that creates a machine driver instance. - DriverCreator DriverFactory + // Status returns the installation status of the driver + Status StatusChecker + + // Priority returns the prioritization for selecting a driver by default. + Priority Priority +} + +// Empty returns true if the driver is nil +func (d DriverDef) Empty() bool { + return d.Name == "" } func (d DriverDef) String() string { - return fmt.Sprintf("{name: %s, builtin: %t}", d.Name, d.Builtin) + return d.Name } type driverRegistry struct { @@ -79,43 +107,26 @@ type driverRegistry struct { lock sync.Mutex } -func createRegistry() *driverRegistry { +func newRegistry() *driverRegistry { return &driverRegistry{ drivers: make(map[string]DriverDef), } } -var ( - registry = createRegistry() -) - -// ListDrivers lists all drivers in registry -func ListDrivers() []DriverDef { - return registry.List() -} - -// Register registers driver -func Register(driver DriverDef) error { - return registry.Register(driver) -} - -// Driver gets a named driver -func Driver(name string) (DriverDef, error) { - return registry.Driver(name) -} - +// Register registers a driver func (r *driverRegistry) Register(def DriverDef) error { r.lock.Lock() defer r.lock.Unlock() if _, ok := r.drivers[def.Name]; ok { - return ErrDriverNameExist + return fmt.Errorf("%q is already registered: %+v", def.Name, def) } r.drivers[def.Name] = def return nil } +// List returns a list of registered drivers func (r *driverRegistry) List() []DriverDef { r.lock.Lock() defer r.lock.Unlock() @@ -129,13 +140,9 @@ func (r *driverRegistry) List() []DriverDef { return result } -func (r *driverRegistry) Driver(name string) (DriverDef, error) { +// Driver returns a driver given a name +func (r *driverRegistry) Driver(name string) DriverDef { r.lock.Lock() defer r.lock.Unlock() - - if driver, ok := r.drivers[name]; ok { - return driver, nil - } - - return DriverDef{}, ErrDriverNotFound + return r.drivers[name] } diff --git a/pkg/minikube/registry/registry_test.go b/pkg/minikube/registry/registry_test.go index 35e710ed62..e1d84b25ef 100644 --- a/pkg/minikube/registry/registry_test.go +++ b/pkg/minikube/registry/registry_test.go @@ -19,97 +19,47 @@ package registry import ( "testing" - "k8s.io/minikube/pkg/minikube/config" + "github.com/google/go-cmp/cmp" ) -func TestDriverString(t *testing.T) { - bar := DriverDef{ - Name: "bar", - Builtin: true, - ConfigCreator: func(_ config.MachineConfig) interface{} { - return nil - }, +func TestRegister(t *testing.T) { + r := newRegistry() + foo := DriverDef{Name: "foo"} + if err := r.Register(foo); err != nil { + t.Errorf("Register = %v, expected nil", err) } - s := bar.String() - if s != "{name: bar, builtin: true}" { - t.Fatalf("Driver bar.String() returned unexpected: %v", s) + if err := r.Register(foo); err == nil { + t.Errorf("Register = nil, expected duplicate err") } } -func TestRegistry(t *testing.T) { - foo := DriverDef{ - Name: "foo", - Builtin: true, - ConfigCreator: func(_ config.MachineConfig) interface{} { - return nil - }, - } - bar := DriverDef{ - Name: "bar", - Builtin: true, - ConfigCreator: func(_ config.MachineConfig) interface{} { - return nil - }, +func TestDriver(t *testing.T) { + foo := DriverDef{Name: "foo"} + r := newRegistry() + + if err := r.Register(foo); err != nil { + t.Errorf("Register = %v, expected nil", err) } - registry := createRegistry() - t.Run("registry.Register", func(t *testing.T) { - t.Run("foo", func(t *testing.T) { - if err := registry.Register(foo); err != nil { - t.Fatalf("error not expected but got %v", err) - } - }) - t.Run("fooAlreadyExist", func(t *testing.T) { - if err := registry.Register(foo); err != ErrDriverNameExist { - t.Fatalf("expect ErrDriverNameExist but got: %v", err) - } - }) - t.Run("bar", func(t *testing.T) { - if err := registry.Register(bar); err != nil { - t.Fatalf("error not expect but got: %v", err) - } - }) - }) - t.Run("registry.List", func(t *testing.T) { - list := registry.List() - if !(list[0].Name == "bar" && list[1].Name == "foo" || - list[0].Name == "foo" && list[1].Name == "bar") { - t.Fatalf("expect registry.List return %s; got %s", []string{"bar", "foo"}, list) - } - if drivers := ListDrivers(); len(list) == len(drivers) { - t.Fatalf("Expectect ListDrivers and registry.List() to return same number of items, but got: drivers=%v and list=%v", drivers, list) - } else if len(list) == len(drivers) { - t.Fatalf("expect len(list) to be %d; got %d", 2, len(list)) - } - }) - - t.Run("Driver", func(t *testing.T) { - driverName := "foo" - driver, err := registry.Driver(driverName) - if err != nil { - t.Fatalf("expect nil for registering foo driver, but got: %v", err) - } - if driver.Name != driverName { - t.Fatalf("expect registry.Driver(%s) returns registered driver, but got: %s", driverName, driver.Name) - } - }) - t.Run("NotExistingDriver", func(t *testing.T) { - _, err := registry.Driver("foo2") - if err != ErrDriverNotFound { - t.Fatalf("expect ErrDriverNotFound bug got: %v", err) - } - }) - t.Run("Driver", func(t *testing.T) { - if _, err := Driver("no_such_driver"); err == nil { - t.Fatal("expect to get error for not existing driver") - } - }) - if _, err := Driver("foo"); err == nil { - t.Fatal("expect to not get error during existing driver foo") + d := r.Driver("foo") + if d.Empty() { + t.Errorf("driver.Empty = true, expected false") + } + + d = r.Driver("bar") + if !d.Empty() { + t.Errorf("driver.Empty = false, expected true") + } +} + +func TestList(t *testing.T) { + foo := DriverDef{Name: "foo"} + r := newRegistry() + if err := r.Register(foo); err != nil { + t.Errorf("register returned error: %v", err) + } + + if diff := cmp.Diff(r.List(), []DriverDef{foo}); diff != "" { + t.Errorf("list mismatch (-want +got):\n%s", diff) } - t.Run("Register", func(t *testing.T) { - if err := Register(foo); err != nil { - t.Fatalf("expect to not get error during registering driver foo, but got: %v", err) - } - }) } diff --git a/pkg/minikube/service/service.go b/pkg/minikube/service/service.go index 1dd8785c45..7545f0cc1d 100644 --- a/pkg/minikube/service/service.go +++ b/pkg/minikube/service/service.go @@ -29,7 +29,6 @@ import ( "github.com/docker/machine/libmachine" "github.com/golang/glog" "github.com/olekukonko/tablewriter" - "github.com/pkg/browser" "github.com/pkg/errors" "github.com/spf13/viper" core "k8s.io/api/core/v1" @@ -41,12 +40,19 @@ import ( clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/minikube/pkg/minikube/cluster" "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/out" "k8s.io/minikube/pkg/minikube/proxy" "k8s.io/minikube/pkg/util/retry" ) +const ( + defaultK8sClientTimeout = 60 * time.Second + // DefaultWait is the default wait time, in seconds + DefaultWait = 20 + // DefaultInterval is the default interval, in seconds + DefaultInterval = 6 +) + // K8sClient represents a kubernetes client type K8sClient interface { GetCoreClient() (typed_core.CoreV1Interface, error) @@ -65,7 +71,7 @@ func init() { // GetCoreClient returns a core client func (k *K8sClientGetter) GetCoreClient() (typed_core.CoreV1Interface, error) { - client, err := k.GetClientset(constants.DefaultK8sClientTimeout) + client, err := k.GetClientset(defaultK8sClientTimeout) if err != nil { return nil, errors.Wrap(err, "getting clientset") } @@ -258,9 +264,11 @@ func PrintServiceList(writer io.Writer, data [][]string) { table.Render() } -// WaitAndMaybeOpenService waits for a service, and opens it when running -func WaitAndMaybeOpenService(api libmachine.API, namespace string, service string, urlTemplate *template.Template, urlMode bool, https bool, - wait int, interval int) error { +// WaitForService waits for a service, and return the urls when available +func WaitForService(api libmachine.API, namespace string, service string, urlTemplate *template.Template, urlMode bool, https bool, + wait int, interval int) ([]string, error) { + + var urlList []string // Convert "Amount of time to wait" and "interval of each check" to attempts if interval == 0 { interval = 1 @@ -268,12 +276,12 @@ func WaitAndMaybeOpenService(api libmachine.API, namespace string, service strin chkSVC := func() error { return CheckService(namespace, service) } if err := retry.Expo(chkSVC, time.Duration(interval)*time.Second, time.Duration(wait)*time.Second); err != nil { - return errors.Wrapf(err, "Could not find finalized endpoint being pointed to by %s", service) + return urlList, errors.Wrapf(err, "Service %s was not found in %q namespace. You may select another namespace by using 'minikube service %s -n <namespace>", service, namespace, service) } serviceURL, err := GetServiceURLsForService(api, namespace, service, urlTemplate) if err != nil { - return errors.Wrap(err, "Check that minikube is running and that you have specified the correct namespace") + return urlList, errors.Wrap(err, "Check that minikube is running and that you have specified the correct namespace") } if !urlMode { @@ -288,22 +296,14 @@ func WaitAndMaybeOpenService(api libmachine.API, namespace string, service strin if len(serviceURL.URLs) == 0 { out.T(out.Sad, "service {{.namespace_name}}/{{.service_name}} has no node port", out.V{"namespace_name": namespace, "service_name": service}) - return nil + return urlList, nil } for _, bareURLString := range serviceURL.URLs { - urlString, isHTTPSchemedURL := OptionallyHTTPSFormattedURLString(bareURLString, https) - - if urlMode || !isHTTPSchemedURL { - out.T(out.Empty, urlString) - } else { - out.T(out.Celebrate, "Opening kubernetes service {{.namespace_name}}/{{.service_name}} in default browser...", out.V{"namespace_name": namespace, "service_name": service}) - if err := browser.OpenURL(urlString); err != nil { - out.ErrT(out.Empty, "browser failed to open url: {{.error}}", out.V{"error": err}) - } - } + url, _ := OptionallyHTTPSFormattedURLString(bareURLString, https) + urlList = append(urlList, url) } - return nil + return urlList, nil } // GetServiceListByLabel returns a ServiceList by label diff --git a/pkg/minikube/service/service_test.go b/pkg/minikube/service/service_test.go index 20b9f30fc0..01426ddfac 100644 --- a/pkg/minikube/service/service_test.go +++ b/pkg/minikube/service/service_test.go @@ -97,6 +97,27 @@ var serviceNamespaces = map[string]typed_core.ServiceInterface{ "default": defaultNamespaceServiceInterface, } +var serviceNamespaceOther = map[string]typed_core.ServiceInterface{ + "default": nondefaultNamespaceServiceInterface, +} + +var nondefaultNamespaceServiceInterface = &MockServiceInterface{ + ServiceList: &core.ServiceList{ + Items: []core.Service{ + { + ObjectMeta: meta.ObjectMeta{ + Name: "non-namespace-dashboard-no-ports", + Namespace: "cannot_be_found_namespace", + Labels: map[string]string{"mock": "mock"}, + }, + Spec: core.ServiceSpec{ + Ports: []core.ServicePort{}, + }, + }, + }, + }, +} + var defaultNamespaceServiceInterface = &MockServiceInterface{ ServiceList: &core.ServiceList{ Items: []core.Service{ @@ -864,6 +885,15 @@ func TestWaitAndMaybeOpenService(t *testing.T) { api: defaultAPI, urlMode: true, https: true, + expected: []string{"https://127.0.0.1:1111", "https://127.0.0.1:2222"}, + }, + { + description: "correctly return serviceURLs, http, url mode", + namespace: "default", + service: "mock-dashboard", + api: defaultAPI, + urlMode: true, + https: false, expected: []string{"http://127.0.0.1:1111", "http://127.0.0.1:2222"}, }, { @@ -882,12 +912,79 @@ func TestWaitAndMaybeOpenService(t *testing.T) { servicesMap: serviceNamespaces, endpointsMap: endpointNamespaces, } - err := WaitAndMaybeOpenService(test.api, test.namespace, test.service, defaultTemplate, test.urlMode, test.https, 1, 0) + + var urlList []string + urlList, err := WaitForService(test.api, test.namespace, test.service, defaultTemplate, test.urlMode, test.https, 1, 0) if test.err && err == nil { - t.Fatalf("WaitAndMaybeOpenService expected to fail for test: %v", test) + t.Fatalf("WaitForService expected to fail for test: %v", test) } if !test.err && err != nil { - t.Fatalf("WaitAndMaybeOpenService not expected to fail but got err: %v", err) + t.Fatalf("WaitForService not expected to fail but got err: %v", err) + } + + if test.urlMode { + // check the size of the url slices + if len(urlList) != len(test.expected) { + t.Fatalf("WaitForService returned [%d] urls while expected is [%d] url", len(urlList), len(test.expected)) + } + + // check content of the expected url + for i, v := range test.expected { + if v != urlList[i] { + t.Fatalf("WaitForService returned [%s] urls while expected is [%s] url", urlList[i], v) + } + } + } + + }) + } +} + +func TestWaitAndMaybeOpenServiceForNotDefaultNamspace(t *testing.T) { + defaultAPI := &tests.MockAPI{ + FakeStore: tests.FakeStore{ + Hosts: map[string]*host.Host{ + config.GetMachineName(): { + Name: config.GetMachineName(), + Driver: &tests.MockDriver{}, + }, + }, + }, + } + defaultTemplate := template.Must(template.New("svc-template").Parse("http://{{.IP}}:{{.Port}}")) + + var tests = []struct { + description string + api libmachine.API + namespace string + service string + expected []string + urlMode bool + https bool + err bool + }{ + { + description: "correctly return empty serviceURLs", + namespace: "default", + service: "non-namespace-dashboard-no-ports", + api: defaultAPI, + expected: []string{}, + err: true, + }, + } + defer revertK8sClient(K8s) + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + K8s = &MockClientGetter{ + servicesMap: serviceNamespaceOther, + endpointsMap: endpointNamespaces, + } + _, err := WaitForService(test.api, test.namespace, test.service, defaultTemplate, test.urlMode, test.https, 1, 0) + if test.err && err == nil { + t.Fatalf("WaitForService expected to fail for test: %v", test) + } + if !test.err && err != nil { + t.Fatalf("WaitForService not expected to fail but got err: %v", err) } }) diff --git a/pkg/minikube/storageclass/storageclass.go b/pkg/minikube/storageclass/storageclass.go index b2843fc9c1..657d99911f 100644 --- a/pkg/minikube/storageclass/storageclass.go +++ b/pkg/minikube/storageclass/storageclass.go @@ -70,7 +70,7 @@ func SetDefaultStorageClass(storage storagev1.StorageV1Interface, name string) e return nil } -// GetStorageV1 return storage v1 interface for client +// GetStoragev1 return storage v1 interface for client func GetStoragev1() (storagev1.StorageV1Interface, error) { client, err := getClient() if err != nil { diff --git a/pkg/minikube/tests/api_mock.go b/pkg/minikube/tests/api_mock.go index 241be0023a..c8f80ad756 100644 --- a/pkg/minikube/tests/api_mock.go +++ b/pkg/minikube/tests/api_mock.go @@ -67,14 +67,14 @@ func (api *MockAPI) Close() error { } // NewHost creates a new host.Host instance. -func (api *MockAPI) NewHost(driverName string, rawDriver []byte) (*host.Host, error) { +func (api *MockAPI) NewHost(drvName string, rawDriver []byte) (*host.Host, error) { var driver MockDriver if err := json.Unmarshal(rawDriver, &driver); err != nil { return nil, errors.Wrap(err, "error unmarshalling json") } h := &host.Host{ - DriverName: driverName, + DriverName: drvName, RawDriver: rawDriver, Driver: &MockDriver{}, Name: fmt.Sprintf("mock-machine-%.8f", rand.Float64()), diff --git a/pkg/minikube/tests/driver_mock.go b/pkg/minikube/tests/driver_mock.go index f675e8d74a..1ed8c7d111 100644 --- a/pkg/minikube/tests/driver_mock.go +++ b/pkg/minikube/tests/driver_mock.go @@ -19,8 +19,6 @@ package tests import ( "testing" - "k8s.io/minikube/pkg/minikube/constants" - "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/mcnflag" "github.com/docker/machine/libmachine/state" @@ -40,111 +38,111 @@ type MockDriver struct { } // Logf logs mock interactions -func (driver *MockDriver) Logf(format string, args ...interface{}) { - if driver.T == nil { +func (d *MockDriver) Logf(format string, args ...interface{}) { + if d.T == nil { glog.Infof(format, args...) return } - driver.T.Logf(format, args...) + d.T.Logf(format, args...) } // Create creates a MockDriver instance -func (driver *MockDriver) Create() error { - driver.Logf("MockDriver.Create") - driver.CurrentState = state.Running +func (d *MockDriver) Create() error { + d.Logf("MockDriver.Create") + d.CurrentState = state.Running return nil } // GetIP returns the IP address -func (driver *MockDriver) GetIP() (string, error) { - driver.Logf("MockDriver.GetIP") - if driver.IP != "" { - return driver.IP, nil +func (d *MockDriver) GetIP() (string, error) { + d.Logf("MockDriver.GetIP") + if d.IP != "" { + return d.IP, nil } - if driver.BaseDriver.IPAddress != "" { - return driver.BaseDriver.IPAddress, nil + if d.BaseDriver.IPAddress != "" { + return d.BaseDriver.IPAddress, nil } return "127.0.0.1", nil } // GetCreateFlags returns the flags used to create a MockDriver -func (driver *MockDriver) GetCreateFlags() []mcnflag.Flag { +func (d *MockDriver) GetCreateFlags() []mcnflag.Flag { return []mcnflag.Flag{} } // GetSSHPort returns the SSH port -func (driver *MockDriver) GetSSHPort() (int, error) { - return driver.Port, nil +func (d *MockDriver) GetSSHPort() (int, error) { + return d.Port, nil } // GetSSHHostname returns the hostname for SSH -func (driver *MockDriver) GetSSHHostname() (string, error) { - if driver.HostError { +func (d *MockDriver) GetSSHHostname() (string, error) { + if d.HostError { return "", errors.New("error getting host") } return "localhost", nil } // GetSSHKeyPath returns the key path for SSH -func (driver *MockDriver) GetSSHKeyPath() string { - return driver.BaseDriver.SSHKeyPath +func (d *MockDriver) GetSSHKeyPath() string { + return d.BaseDriver.SSHKeyPath } // GetState returns the state of the driver -func (driver *MockDriver) GetState() (state.State, error) { - driver.Logf("MockDriver.GetState: %v", driver.CurrentState) - return driver.CurrentState, nil +func (d *MockDriver) GetState() (state.State, error) { + d.Logf("MockDriver.GetState: %v", d.CurrentState) + return d.CurrentState, nil } // GetURL returns the URL of the driver -func (driver *MockDriver) GetURL() (string, error) { +func (d *MockDriver) GetURL() (string, error) { return "", nil } // Kill kills the machine -func (driver *MockDriver) Kill() error { - driver.Logf("MockDriver.Kill") - driver.CurrentState = state.Stopped +func (d *MockDriver) Kill() error { + d.Logf("MockDriver.Kill") + d.CurrentState = state.Stopped return nil } // Remove removes the machine -func (driver *MockDriver) Remove() error { - driver.Logf("MockDriver.Remove") - if driver.RemoveError { +func (d *MockDriver) Remove() error { + d.Logf("MockDriver.Remove") + if d.RemoveError { return errors.New("error deleting machine") } return nil } // Restart restarts the machine -func (driver *MockDriver) Restart() error { - driver.Logf("MockDriver.Restart") - driver.CurrentState = state.Running +func (d *MockDriver) Restart() error { + d.Logf("MockDriver.Restart") + d.CurrentState = state.Running return nil } // SetConfigFromFlags sets the machine config -func (driver *MockDriver) SetConfigFromFlags(opts drivers.DriverOptions) error { +func (d *MockDriver) SetConfigFromFlags(opts drivers.DriverOptions) error { return nil } // Start starts the machine -func (driver *MockDriver) Start() error { - driver.Logf("MockDriver.Start") - driver.CurrentState = state.Running +func (d *MockDriver) Start() error { + d.Logf("MockDriver.Start") + d.CurrentState = state.Running return nil } // Stop stops the machine -func (driver *MockDriver) Stop() error { - driver.Logf("MockDriver.Stop") - driver.CurrentState = state.Stopped +func (d *MockDriver) Stop() error { + d.Logf("MockDriver.Stop") + d.CurrentState = state.Stopped return nil } // DriverName returns the name of the driver -func (driver *MockDriver) DriverName() string { - driver.Logf("MockDriver.Name") - return constants.DriverMock +func (d *MockDriver) DriverName() string { + d.Logf("MockDriver.Name") + return "mock" } diff --git a/pkg/minikube/translate/translate_test.go b/pkg/minikube/translate/translate_test.go index 00c4411810..fd7a73cf64 100644 --- a/pkg/minikube/translate/translate_test.go +++ b/pkg/minikube/translate/translate_test.go @@ -51,3 +51,53 @@ func TestSetPreferredLanguage(t *testing.T) { } } + +func TestT(t *testing.T) { + var tests = []struct { + description, input, expected string + langDef, langPref language.Tag + translations map[string]interface{} + }{ + { + description: "empty string not default language", + input: "", + expected: "", + langPref: language.English, + langDef: language.Lithuanian, + }, + { + description: "empty string and default language", + input: "", + expected: "", + langPref: language.English, + langDef: language.English, + }, + { + description: "existing translation", + input: "cat", + expected: "kot", + langPref: language.Lithuanian, + langDef: language.English, + translations: map[string]interface{}{"cat": "kot"}, + }, + { + description: "not existing translation", + input: "cat", + expected: "cat", + langPref: language.Lithuanian, + langDef: language.English, + translations: map[string]interface{}{"dog": "pies"}, + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + defaultLanguage = test.langDef + preferredLanguage = test.langPref + Translations = test.translations + got := T(test.input) + if test.expected != got { + t.Errorf("T(%v) shoud return %v, but got: %v", test.input, test.expected, got) + } + }) + } +} diff --git a/pkg/minikube/tunnel/route_darwin.go b/pkg/minikube/tunnel/route_darwin.go index 815eff6bfe..0e2f5064ff 100644 --- a/pkg/minikube/tunnel/route_darwin.go +++ b/pkg/minikube/tunnel/route_darwin.go @@ -156,21 +156,21 @@ func (router *osRouter) Cleanup(route *Route) error { if !exists { return nil } - command := exec.Command("sudo", "route", "-n", "delete", route.DestCIDR.String()) - stdInAndOut, err := command.CombinedOutput() + cmd := exec.Command("sudo", "route", "-n", "delete", route.DestCIDR.String()) + stdInAndOut, err := cmd.CombinedOutput() if err != nil { return err } - message := fmt.Sprintf("%s", stdInAndOut) - glog.V(4).Infof("%s", message) + msg := fmt.Sprintf("%s", stdInAndOut) + glog.V(4).Infof("%s", msg) re := regexp.MustCompile("^delete net ([^:]*)$") - if !re.MatchString(message) { - return fmt.Errorf("error deleting route: %s, %d", message, len(strings.Split(message, "\n"))) + if !re.MatchString(msg) { + return fmt.Errorf("error deleting route: %s, %d", msg, len(strings.Split(msg, "\n"))) } // idempotent removal of cluster domain dns resolverFile := fmt.Sprintf("/etc/resolver/%s", route.ClusterDomain) - command = exec.Command("sudo", "rm", "-f", resolverFile) - if err := command.Run(); err != nil { + cmd = exec.Command("sudo", "rm", "-f", resolverFile) + if err := cmd.Run(); err != nil { return fmt.Errorf("could not remove %s: %s", resolverFile, err) } return nil @@ -191,12 +191,12 @@ func writeResolverFile(route *Route) error { if err = tmpFile.Close(); err != nil { return err } - command := exec.Command("sudo", "mkdir", "-p", "/etc/resolver") - if err := command.Run(); err != nil { + cmd := exec.Command("sudo", "mkdir", "-p", "/etc/resolver") + if err := cmd.Run(); err != nil { return err } - command = exec.Command("sudo", "cp", "-f", tmpFile.Name(), resolverFile) - if err := command.Run(); err != nil { + cmd = exec.Command("sudo", "cp", "-f", tmpFile.Name(), resolverFile) + if err := cmd.Run(); err != nil { return err } return nil diff --git a/pkg/minikube/tunnel/route_windows.go b/pkg/minikube/tunnel/route_windows.go index 757074f190..0d561b3c1c 100644 --- a/pkg/minikube/tunnel/route_windows.go +++ b/pkg/minikube/tunnel/route_windows.go @@ -93,7 +93,7 @@ func (router *osRouter) parseTable(table []byte) routingTable { }, line: line, } - glog.V(4).Infof("adding line %s", tableLine) + glog.V(4).Infof("adding line %v", tableLine) t = append(t, tableLine) } } diff --git a/pkg/minikube/tunnel/tunnel.go b/pkg/minikube/tunnel/tunnel.go index f68b804a8b..ab83bed1d3 100644 --- a/pkg/minikube/tunnel/tunnel.go +++ b/pkg/minikube/tunnel/tunnel.go @@ -29,7 +29,7 @@ import ( "github.com/pkg/errors" typed_core "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/driver" ) // tunnel represents the basic API for a tunnel: periodically the state of the tunnel @@ -150,7 +150,7 @@ func setupRoute(t *tunnel, h *host.Host) { return } - if h.DriverName == constants.DriverHyperkit { + if h.DriverName == driver.HyperKit { // the virtio-net interface acts up with ip tunnels :( setupBridge(t) if t.status.RouteError != nil { diff --git a/pkg/minikube/tunnel/tunnel_manager.go b/pkg/minikube/tunnel/tunnel_manager.go index 69f9e972b1..4c3ed6f4d9 100644 --- a/pkg/minikube/tunnel/tunnel_manager.go +++ b/pkg/minikube/tunnel/tunnel_manager.go @@ -122,8 +122,8 @@ func (mgr *Manager) run(ctx context.Context, t controller, ready, check, done ch } } -func (mgr *Manager) cleanup(t controller) *Status { - return t.cleanup() +func (mgr *Manager) cleanup(t controller) { + t.cleanup() } // CleanupNotRunningTunnels cleans up tunnels that are not running diff --git a/pkg/minikube/vmpath/constants.go b/pkg/minikube/vmpath/constants.go new file mode 100644 index 0000000000..cd6b5205d1 --- /dev/null +++ b/pkg/minikube/vmpath/constants.go @@ -0,0 +1,30 @@ +/* +Copyright 2019 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 vmpath + +const ( + // GuestAddonsDir is the default path of the addons configration + GuestAddonsDir = "/etc/kubernetes/addons" + // GuestManifestsDir is where the kubelet should look for static Pod manifests + GuestManifestsDir = "/etc/kubernetes/manifests" + // GuestEphemeralDir is the path where ephemeral data should be stored within the VM + GuestEphemeralDir = "/var/tmp/minikube" + // GuestPersistentDir is the path where persistent data should be stored within the VM (not tmpfs) + GuestPersistentDir = "/var/lib/minikube" + // GuestCertsDir are where Kubernetes certificates are kept on the guest + GuestCertsDir = GuestPersistentDir + "/certs" +) diff --git a/pkg/provision/buildroot.go b/pkg/provision/buildroot.go index be32b4bf9d..c000472975 100644 --- a/pkg/provision/buildroot.go +++ b/pkg/provision/buildroot.go @@ -89,8 +89,8 @@ func escapeSystemdDirectives(engineConfigContext *provision.EngineConfigContext) func (p *BuildrootProvisioner) GenerateDockerOptions(dockerPort int) (*provision.DockerOptions, error) { var engineCfg bytes.Buffer - driverNameLabel := fmt.Sprintf("provider=%s", p.Driver.DriverName()) - p.EngineOptions.Labels = append(p.EngineOptions.Labels, driverNameLabel) + drvLabel := fmt.Sprintf("provider=%s", p.Driver.DriverName()) + p.EngineOptions.Labels = append(p.EngineOptions.Labels, drvLabel) noPivot := true // Using pivot_root is not supported on fstype rootfs @@ -107,7 +107,6 @@ Requires= minikube-automount.service docker.socket [Service] Type=notify - ` if noPivot { log.Warn("Using fundamentally insecure --no-pivot option") @@ -127,8 +126,11 @@ Environment=DOCKER_RAMDISK=yes # a sequence of commands, which is not the desired behavior, nor is it valid -- systemd # will catch this invalid input and refuse to start the service with an error like: # Service has more than one ExecStart= setting, which is only allowed for Type=oneshot services. + +# NOTE: default-ulimit=nofile is set to an arbitrary number for consistency with other +# container runtimes. If left unlimited, it may result in OOM issues with MySQL. ExecStart= -ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:{{.DockerPort}} -H unix:///var/run/docker.sock --tlsverify --tlscacert {{.AuthOptions.CaCertRemotePath}} --tlscert {{.AuthOptions.ServerCertRemotePath}} --tlskey {{.AuthOptions.ServerKeyRemotePath}} {{ range .EngineOptions.Labels }}--label {{.}} {{ end }}{{ range .EngineOptions.InsecureRegistry }}--insecure-registry {{.}} {{ end }}{{ range .EngineOptions.RegistryMirror }}--registry-mirror {{.}} {{ end }}{{ range .EngineOptions.ArbitraryFlags }}--{{.}} {{ end }} +ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:{{.DockerPort}} -H unix:///var/run/docker.sock --default-ulimit=nofile=1048576:1048576 --tlsverify --tlscacert {{.AuthOptions.CaCertRemotePath}} --tlscert {{.AuthOptions.ServerCertRemotePath}} --tlskey {{.AuthOptions.ServerKeyRemotePath}} {{ range .EngineOptions.Labels }}--label {{.}} {{ end }}{{ range .EngineOptions.InsecureRegistry }}--insecure-registry {{.}} {{ end }}{{ range .EngineOptions.RegistryMirror }}--registry-mirror {{.}} {{ end }}{{ range .EngineOptions.ArbitraryFlags }}--{{.}} {{ end }} ExecReload=/bin/kill -s HUP $MAINPID # Having non-zero Limit*s causes performance problems due to accounting overhead diff --git a/pkg/util/progressbar.go b/pkg/util/progressbar.go index bf6c57fd6b..fb16f6773c 100644 --- a/pkg/util/progressbar.go +++ b/pkg/util/progressbar.go @@ -29,6 +29,7 @@ import ( "github.com/hashicorp/go-getter" ) +// DefaultProgressBar is the default cheggaaa progress bar var DefaultProgressBar getter.ProgressTracker = &progressBar{} type progressBar struct { diff --git a/pkg/util/utils.go b/pkg/util/utils.go index c549ca4ebe..54ec5d4101 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -17,7 +17,6 @@ limitations under the License. package util import ( - "bufio" "bytes" "fmt" "io" @@ -34,13 +33,13 @@ import ( "k8s.io/minikube/pkg/minikube/out" ) -// ErrPrefix notes an error -const ErrPrefix = "! " - -// OutPrefix notes output -const OutPrefix = "> " - const ( + // ErrPrefix notes an error + ErrPrefix = "! " + + // OutPrefix notes output + OutPrefix = "> " + downloadURL = "https://storage.googleapis.com/minikube/releases/%s/minikube-%s-amd64%s" ) @@ -150,34 +149,6 @@ func MaybeChownDirRecursiveToMinikubeUser(dir string) error { return nil } -// TeePrefix copies bytes from a reader to writer, logging each new line. -func TeePrefix(prefix string, r io.Reader, w io.Writer, logger func(format string, args ...interface{})) error { - scanner := bufio.NewScanner(r) - scanner.Split(bufio.ScanBytes) - var line bytes.Buffer - - for scanner.Scan() { - b := scanner.Bytes() - if _, err := w.Write(b); err != nil { - return err - } - - if bytes.IndexAny(b, "\r\n") == 0 { - if line.Len() > 0 { - logger("%s%s", prefix, line.String()) - line.Reset() - } - continue - } - line.Write(b) - } - // Catch trailing output in case stream does not end with a newline - if line.Len() > 0 { - logger("%s%s", prefix, line.String()) - } - return nil -} - // ReplaceChars returns a copy of the src slice with each string modified by the replacer func ReplaceChars(src []string, replacer *strings.Replacer) []string { ret := make([]string, len(src)) diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go index 4fc94ce7bc..26a10724a4 100644 --- a/pkg/util/utils_test.go +++ b/pkg/util/utils_test.go @@ -17,10 +17,7 @@ limitations under the License. package util import ( - "bytes" - "fmt" "strings" - "sync" "testing" ) @@ -44,44 +41,6 @@ func TestGetBinaryDownloadURL(t *testing.T) { } -func TestTeePrefix(t *testing.T) { - var in bytes.Buffer - var out bytes.Buffer - var logged strings.Builder - - logSink := func(format string, args ...interface{}) { - logged.WriteString("(" + fmt.Sprintf(format, args...) + ")") - } - - // Simulate the primary use case: tee in the background. This also helps avoid I/O races. - var wg sync.WaitGroup - wg.Add(1) - go func() { - if err := TeePrefix(":", &in, &out, logSink); err != nil { - t.Errorf("TeePrefix: %v", err) - } - wg.Done() - }() - - in.Write([]byte("goo")) - in.Write([]byte("\n")) - in.Write([]byte("g\r\n\r\n")) - in.Write([]byte("le")) - wg.Wait() - - gotBytes := out.Bytes() - wantBytes := []byte("goo\ng\r\n\r\nle") - if !bytes.Equal(gotBytes, wantBytes) { - t.Errorf("output=%q, want: %q", gotBytes, wantBytes) - } - - gotLog := logged.String() - wantLog := "(:goo)(:g)(:le)" - if gotLog != wantLog { - t.Errorf("log=%q, want: %q", gotLog, wantLog) - } -} - func TestReplaceChars(t *testing.T) { testData := []struct { src []string diff --git a/site/config.toml b/site/config.toml index 85c597f291..c8f5420ef6 100644 --- a/site/config.toml +++ b/site/config.toml @@ -9,7 +9,7 @@ theme = ["docsy"] enableGitInfo = true # Language settings -contentDir = "content/en" +contentDir = "content/en" defaultContentLanguage = "en" defaultContentLanguageInSubdir = false # Useful when translating. @@ -33,6 +33,30 @@ pygmentsStyle = "tango" [permalinks] blog = "/:section/:year/:month/:day/:slug/" +[module] + [[module.mounts]] + source = "../deploy/addons/gvisor/" + target = "content/gvisor/" + [[module.mounts]] + source = "../deploy/addons/helm-tiller/" + target = "content/helm-tiller/" + [[module.mounts]] + source = "../deploy/addons/ingress-dns/" + target = "content/ingress-dns/" + [[module.mounts]] + source = "../deploy/addons/storage-provisioner-gluster/" + target = "content/storage-provisioner-gluster/" + [[module.mounts]] + source = "../deploy/addons/layouts/" + target = "layouts" + + [[module.mounts]] + source = "content/en" + target = "content" + [[module.mounts]] + source = "layouts" + target = "layouts" + ## Configuration for BlackFriday markdown parser: https://github.com/russross/blackfriday [blackfriday] plainIDAnchors = true @@ -68,7 +92,7 @@ weight = 1 [params] copyright = "The Kubernetes Authors -- " # The latest release of minikube -latest_release = "1.4.0" +latest_release = "1.5.2" privacy_policy = "" @@ -115,6 +139,11 @@ no = 'Sorry to hear that. Please <a href="https://github.com/kubernetes/minikube url = "https://kubernetes.slack.com/messages/C1F5CT6Q1" icon = "fab fa-slack" desc = "Chat with other minikube users & developers" +[[params.links.user]] + name = "minikube-users mailing list" + url = "https://groups.google.com/forum/#!forum/minikube-users" + icon = "fas fa-envelope" + desc = "Interact with the minikube Users here" # Developer relevant links. These will show up on right side of footer and in the community page if you have one. [[params.links.developer]] @@ -122,3 +151,8 @@ no = 'Sorry to hear that. Please <a href="https://github.com/kubernetes/minikube url = "https://github.com/kubernetes/minikube" icon = "fab fa-github" desc = "Development takes place here!" +[[params.links.developer]] + name = "minikube-dev mailing list" + url = "https://groups.google.com/forum/#!forum/minikube-dev" + icon = "fa fa-envelope" + desc = "Contact the minikube Dev's here" diff --git a/site/content/en/_index.html b/site/content/en/_index.html index 86a6f00cd7..8687b84c25 100644 --- a/site/content/en/_index.html +++ b/site/content/en/_index.html @@ -8,6 +8,18 @@ resources: title: "start" --- +<style> + .imagesizing { + width:auto; + text-align:center; + padding:10px; + } + img { + max-width:100%; + height:auto; + } +</style> + <!-- Welcome to minikube --> <section class="row td-box td-box--white position-relative td-box--gradient td-box--height-auto"> <div class="container td-arrow-down"> @@ -37,10 +49,10 @@ resources: <h2>Instantly productive.</h2> A single command away from reproducing your production environment, from the comfort of localhost. - -{{< imgproc "start" Fit "730x239" >}}{{< /imgproc >}} - -<h3>Highlights</h3> + <div class = "imagesizing"> + <img src="start.png"/> + </div> + <h3>Highlights</h3> <ul class="fa-ul"> <li><i class="fas fa-check"></i> Always supports the latest Kubernetes release (as well as previous versions)</li> <li><i class="fas fa-check"></i> Cross-platform (Linux, macOS, Windows)</li> diff --git a/site/content/en/docs/Contributing/addons.en.md b/site/content/en/docs/Contributing/addons.en.md index d0bd113986..5c8c1a45b6 100644 --- a/site/content/en/docs/Contributing/addons.en.md +++ b/site/content/en/docs/Contributing/addons.en.md @@ -43,19 +43,19 @@ To add a new addon to minikube the following steps are required: "efk": NewAddon([]*BinAsset{ MustBinAsset( "deploy/addons/efk/efk-configmap.yaml", - constants.GuestAddonsDir, + guestAddonsDir, "efk-configmap.yaml", "0640", false), MustBinAsset( "deploy/addons/efk/efk-rc.yaml", - constants.GuestAddonsDir, + guestAddonsDir, "efk-rc.yaml", "0640", false), MustBinAsset( "deploy/addons/efk/efk-svc.yaml", - constants.GuestAddonsDir, + guestAddonsDir, "efk-svc.yaml", "0640", false), diff --git a/site/content/en/docs/Contributing/building.en.md b/site/content/en/docs/Contributing/building.en.md index ff3f7bb1b9..f13a5472b9 100644 --- a/site/content/en/docs/Contributing/building.en.md +++ b/site/content/en/docs/Contributing/building.en.md @@ -32,6 +32,11 @@ make Note: On Windows, this will only work in Git Bash or other terminals that support bash commands. +You can also build platform specific executables like below: + 1. `make windows` will build the binary for Windows platform + 2. `make linux` will build the binary for Linux platform + 3. `make darwin` will build the binary for Darwin/Mac platform + ## Compiling minikube using Docker To cross-compile to/from different operating systems: diff --git a/site/content/en/docs/Contributing/guide.en.md b/site/content/en/docs/Contributing/guide.en.md index af7bbbaa01..c7280098c5 100644 --- a/site/content/en/docs/Contributing/guide.en.md +++ b/site/content/en/docs/Contributing/guide.en.md @@ -37,6 +37,12 @@ Once you've discovered an issue to work on: 4. Fork the minikube repository, develop and test your code changes. 5. Submit a pull request. +## Contributing larger changes + +To get feedback on a larger, more ambitious changes, create a PR containing your idea using the [MEP (minikube enhancement proposal) template](https://github.com/kubernetes/minikube/tree/master/enhancements). This way other contributors can comment on design issues early on, though you are welcome to work on the code in parallel. + +If you send out a large change without a MEP, prepare to be asked by other contributors for one to be included within the PR. + ### Style Guides For coding, refer to the [Kubernetes Coding Conventions](https://github.com/kubernetes/community/blob/master/contributors/guide/coding-conventions.md#code-conventions) diff --git a/site/content/en/docs/Contributing/gvisor.md b/site/content/en/docs/Contributing/gvisor.md new file mode 100644 index 0000000000..9d3c302c4c --- /dev/null +++ b/site/content/en/docs/Contributing/gvisor.md @@ -0,0 +1,33 @@ +--- +linkTitle: "gVisor" +title: "Releasing a gVisor image" +date: 2019-09-25 +weight: 10 +--- + +## Prerequisites + +* Credentials for `gcr.io/k8s-minikube` +* Docker +* Gcloud + +## Background + +gVisor support within minikube requires a special Docker image to be generated. After merging changes to `cmd/gvisor` or `pkg/gvisor`, this image will need to be updated. + +The image is located at `gcr.io/k8s-minikube/gvisor-addon` + +## Why is this image required? + +`gvisor` requires changes to the guest VM in order to function. The `addons` feature in minikube does not normally allow for this, so to workaround it, a custom docker image is launched, containing a binary that makes the changes. + +## What does the image do? + +- Creates log directories +- Downloads and installs the latest stable `gvisor-containerd-shim` release +- Updates the containerd configuration +- Restarts containerd and rpc-statd + +## Updating the gVisor image + +`make push-gvisor-addon-image` diff --git a/site/content/en/docs/Contributing/releasing.en.md b/site/content/en/docs/Contributing/releasing.en.md index 6188343743..21db708db1 100644 --- a/site/content/en/docs/Contributing/releasing.en.md +++ b/site/content/en/docs/Contributing/releasing.en.md @@ -79,6 +79,8 @@ This step uses the git tag to publish new binaries to GCS and create a github re After job completion, click "Console Output" to verify that the release completed without errors. This is typically where one will see brew automation fail, for instance. +**Note: If you are releasing a beta, you are done when you get here.** + ## Check releases.json This file is used for auto-update notifications, but is not active until releases.json is copied to GCS. diff --git a/site/content/en/docs/Contributing/translations.md b/site/content/en/docs/Contributing/translations.md new file mode 100644 index 0000000000..40f7cfdee1 --- /dev/null +++ b/site/content/en/docs/Contributing/translations.md @@ -0,0 +1,21 @@ +--- +title: "Translations" +date: 2019-09-30 +weight: 3 +description: > + How to add translations +--- + +All translations are stored in the top-level `translations` directory. + +### Adding Translations To an Existing Language +* Run `make extract` to make sure all strings are up to date +* Add translated strings to the appropriate json files in the 'translations' + directory. + +### Adding a New Language +* Add a new json file with the locale code of the language you want to add + translations for, e.g. en for English. +* Run `make extract` to populate that file with the strings to translate in json + form. +* Add translations to as many strings as you'd like. diff --git a/site/content/en/docs/Examples/_index.md b/site/content/en/docs/Examples/_index.md index 84092b2cb4..c9e665ae05 100755 --- a/site/content/en/docs/Examples/_index.md +++ b/site/content/en/docs/Examples/_index.md @@ -18,11 +18,11 @@ Access the Kubernetes Dashboard running within the minikube cluster: Once started, you can interact with your cluster using `kubectl`, just like any other Kubernetes cluster. For instance, starting a server: -`kubectl run hello-minikube --image=k8s.gcr.io/echoserver:1.4 --port=8080` +`kubectl create deployment hello-minikube --image=k8s.gcr.io/echoserver:1.4` Exposing a service as a NodePort -`kubectl expose deployment hello-minikube --type=NodePort` +`kubectl expose deployment hello-minikube --type=NodePort --port=8080` minikube makes it easy to open this exposed endpoint in your browser: @@ -39,3 +39,7 @@ Stop your local cluster: Delete your local cluster: `minikube delete` + +Delete all local clusters and profiles + +`minikube delete --all` diff --git a/site/content/en/docs/Reference/Commands/addons.md b/site/content/en/docs/Reference/Commands/addons.md index d15f38173b..0434ac8eef 100644 --- a/site/content/en/docs/Reference/Commands/addons.md +++ b/site/content/en/docs/Reference/Commands/addons.md @@ -4,7 +4,7 @@ linkTitle: "addons" weight: 1 date: 2019-08-01 description: > - Modifies minikube addons files using subcommands like "minikube addons enable heapster" + Modifies minikube addons files using subcommands like "minikube addons enable dashboard" --- ## Overview @@ -39,6 +39,12 @@ Enables the addon w/ADDON_NAME within minikube (example: minikube addons enable minikube addons enable ADDON_NAME [flags] ``` +or + +``` +minikube start --addons ADDON_NAME [flags] +``` + ## minikube addons list Lists all available minikube addons as well as their current statuses (enabled/disabled) diff --git a/site/content/en/docs/Reference/Commands/config.md b/site/content/en/docs/Reference/Commands/config.md index 7dd514c8c1..12f70c52bf 100644 --- a/site/content/en/docs/Reference/Commands/config.md +++ b/site/content/en/docs/Reference/Commands/config.md @@ -36,7 +36,6 @@ Configurable fields: * dashboard * addon-manager * default-storageclass - * heapster * efk * ingress * registry diff --git a/site/content/en/docs/Reference/Commands/dashboard.md b/site/content/en/docs/Reference/Commands/dashboard.md index 4457d65dae..8bd21037d3 100644 --- a/site/content/en/docs/Reference/Commands/dashboard.md +++ b/site/content/en/docs/Reference/Commands/dashboard.md @@ -4,7 +4,7 @@ linkTitle: "dashboard" weight: 1 date: 2019-08-01 description: > - Access the kubernetes dashboard running within the minikube cluster + Access the Kubernetes dashboard running within the minikube cluster --- ## Usage diff --git a/site/content/en/docs/Reference/Commands/delete.md b/site/content/en/docs/Reference/Commands/delete.md index 77fb77de51..eabf82953a 100644 --- a/site/content/en/docs/Reference/Commands/delete.md +++ b/site/content/en/docs/Reference/Commands/delete.md @@ -4,12 +4,12 @@ linkTitle: "delete" weight: 1 date: 2019-08-01 description: > - Deletes a local kubernetes cluster + Deletes a local Kubernetes cluster --- ### Overview -Deletes a local kubernetes cluster. This command deletes the VM, and removes all +Deletes a local Kubernetes cluster. This command deletes the VM, and removes all associated files. ## Usage @@ -18,6 +18,30 @@ associated files. minikube delete [flags] ``` +##### Delete all profiles +``` +minikube delete --all +``` + +##### Delete profile & `.minikube` directory +Do note that the following command only works if you have only 1 profile. If there are multiple profiles, the command will error out. +``` +minikube delete --purge +``` + +##### Delete all profiles & `.minikube` directory +This will delete all the profiles and `.minikube` directory. +``` +minikube delete --purge --all +``` + +### Flags + +``` + --all: Set flag to delete all profiles + --purge: Set this flag to delete the '.minikube' folder from your user directory. +``` + ### Options inherited from parent commands ``` diff --git a/site/content/en/docs/Reference/Commands/kubectl.md b/site/content/en/docs/Reference/Commands/kubectl.md index bd1d399090..54b8747362 100644 --- a/site/content/en/docs/Reference/Commands/kubectl.md +++ b/site/content/en/docs/Reference/Commands/kubectl.md @@ -10,7 +10,7 @@ description: > ### Overview -Run the kubernetes client, download it if necessary. +Run the Kubernetes client, download it if necessary. ### Usage diff --git a/site/content/en/docs/Reference/Commands/service.md b/site/content/en/docs/Reference/Commands/service.md index e89859fd35..67ed39d486 100644 --- a/site/content/en/docs/Reference/Commands/service.md +++ b/site/content/en/docs/Reference/Commands/service.md @@ -4,12 +4,12 @@ linkTitle: "service" weight: 1 date: 2019-08-01 description: > - Gets the kubernetes URL(s) for the specified service in your local cluster + Gets the Kubernetes URL(s) for the specified service in your local cluster --- ### Overview -Gets the kubernetes URL(s) for the specified service in your local cluster. In the case of multiple URLs they will be printed one at a time. +Gets the Kubernetes URL(s) for the specified service in your local cluster. In the case of multiple URLs they will be printed one at a time. ### Usage diff --git a/site/content/en/docs/Reference/Commands/start.md b/site/content/en/docs/Reference/Commands/start.md index 887e9f582c..6e1c1b13df 100644 --- a/site/content/en/docs/Reference/Commands/start.md +++ b/site/content/en/docs/Reference/Commands/start.md @@ -4,7 +4,7 @@ linkTitle: "start" weight: 1 date: 2019-08-01 description: > - Starts a local kubernetes cluster + Starts a local Kubernetes cluster --- ### Usage @@ -16,6 +16,7 @@ minikube start [flags] ### Options ``` +--addons Enable addons. see `minikube addons list` for a list of valid addon names. --apiserver-ips ipSlice A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine (default []) --apiserver-name string The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine (default "minikubeCA") --apiserver-names stringArray A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine diff --git a/site/content/en/docs/Reference/Commands/status.md b/site/content/en/docs/Reference/Commands/status.md index cd08136d33..90dd6e2bde 100644 --- a/site/content/en/docs/Reference/Commands/status.md +++ b/site/content/en/docs/Reference/Commands/status.md @@ -4,15 +4,15 @@ linkTitle: "status" weight: 1 date: 2019-08-01 description: > - Gets the status of a local kubernetes cluster + Gets the status of a local Kubernetes cluster --- ### Overview -Gets the status of a local kubernetes cluster. - Exit status contains the status of minikube's VM, cluster and kubernetes encoded on it's bits in this order from right to left. - Eg: 7 meaning: 1 (for minikube NOK) + 2 (for cluster NOK) + 4 (for kubernetes NOK) +Gets the status of a local Kubernetes cluster. + Exit status contains the status of minikube's VM, cluster and Kubernetes encoded on it's bits in this order from right to left. + Eg: 7 meaning: 1 (for minikube NOK) + 2 (for cluster NOK) + 4 (for Kubernetes NOK) ### Usage diff --git a/site/content/en/docs/Reference/Commands/stop.md b/site/content/en/docs/Reference/Commands/stop.md index 51f8a89b4c..0bb9e63fba 100644 --- a/site/content/en/docs/Reference/Commands/stop.md +++ b/site/content/en/docs/Reference/Commands/stop.md @@ -4,12 +4,12 @@ linkTitle: "stop" weight: 1 date: 2019-08-01 description: > - Stops a running local kubernetes cluster + Stops a running local Kubernetes cluster --- ### Overview -Stops a local kubernetes cluster running in Virtualbox. This command stops the VM +Stops a local Kubernetes cluster running in Virtualbox. This command stops the VM itself, leaving all files intact. The cluster can be started again with the "start" command. ### Usage diff --git a/site/content/en/docs/Reference/Configuration/minikube.md b/site/content/en/docs/Reference/Configuration/minikube.md index 36abf93156..608932fc93 100644 --- a/site/content/en/docs/Reference/Configuration/minikube.md +++ b/site/content/en/docs/Reference/Configuration/minikube.md @@ -77,7 +77,6 @@ Configurable fields: * dashboard * addon-manager * default-storageclass - * heapster * efk * ingress * registry diff --git a/site/content/en/docs/Reference/Drivers/includes/virtualbox_usage.inc b/site/content/en/docs/Reference/Drivers/includes/virtualbox_usage.inc index bb67f88d29..6bce73731a 100644 --- a/site/content/en/docs/Reference/Drivers/includes/virtualbox_usage.inc +++ b/site/content/en/docs/Reference/Drivers/includes/virtualbox_usage.inc @@ -4,7 +4,7 @@ ## Usage -minikube currently uses VirtualBox by default, but it can also be explicitly set: +Start a cluster using the virtualbox driver: ```shell minikube start --vm-driver=virtualbox diff --git a/site/content/en/docs/Reference/Networking/dns.md b/site/content/en/docs/Reference/Networking/dns.md new file mode 100644 index 0000000000..6c44114b7b --- /dev/null +++ b/site/content/en/docs/Reference/Networking/dns.md @@ -0,0 +1,58 @@ +--- +title: "DNS Domain" +linkTitle: "DNS Domain" +weight: 6 +date: 2019-10-09 +description: > + Use configured DNS domain in bootstrapper kubeadm +--- + +minikube by default uses **cluster.local** if none is specified via the start flag --dns-domain. The configuration file used by kubeadm are found inside **/var/tmp/minikube/kubeadm.yaml** directory inside minikube. + +Default DNS configuration will look like below + +``` +apiVersion: kubeadm.k8s.io/v1beta1 +kind: InitConfiguration +localAPIEndpoint: +...... +...... +--- +apiVersion: kubeadm.k8s.io/v1beta1 +kind: ClusterConfiguration +..... +..... +kubernetesVersion: v1.16.0 +networking: + dnsDomain: cluster.local + podSubnet: "" + serviceSubnet: 10.96.0.0/12 +--- +``` + +To change the dns pass the value when starting minikube + +``` +minikube start --dns-domain bla.blah.blah +``` + +the dns now changed to bla.blah.blah + +``` +apiVersion: kubeadm.k8s.io/v1beta1 +kind: InitConfiguration +localAPIEndpoint: +...... +...... +--- +apiVersion: kubeadm.k8s.io/v1beta1 +kind: ClusterConfiguration +..... +..... +kubernetesVersion: v1.16.0 +networking: + dnsDomain: bla.blah.blah + podSubnet: "" + serviceSubnet: 10.96.0.0/12 +--- +``` \ No newline at end of file diff --git a/site/content/en/docs/Reference/Networking/proxy.md b/site/content/en/docs/Reference/Networking/proxy.md index ef796dd637..e82fece322 100644 --- a/site/content/en/docs/Reference/Networking/proxy.md +++ b/site/content/en/docs/Reference/Networking/proxy.md @@ -40,7 +40,7 @@ To make the exported variables permanent, consider adding the declarations to ~/ ```shell set HTTP_PROXY=http://<proxy hostname:port> set HTTPS_PROXY=https://<proxy hostname:port> -set NO_PROXY=localhost,127.0.0.1,10.96.0.0/12,192.168.99.1/24,192.168.39.0/24 +set NO_PROXY=localhost,127.0.0.1,10.96.0.0/12,192.168.99.0/24,192.168.39.0/24 minikube start ``` diff --git a/site/content/en/docs/Start/macos.md b/site/content/en/docs/Start/macos.md index 4ba02d9afb..3c41e3a9b9 100644 --- a/site/content/en/docs/Start/macos.md +++ b/site/content/en/docs/Start/macos.md @@ -17,7 +17,7 @@ weight: 2 If the [Brew Package Manager](https://brew.sh/) is installed, use it to download and install minikube: ```shell -brew cask install minikube +brew install minikube ``` {{% /tab %}} @@ -40,8 +40,8 @@ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-darwin If the [Brew Package Manager](https://brew.sh/) is installed, use it to download and upgrade minikube: ```shell -rm /usr/local/bin/minikube -brew cask reinstall minikube +brew update +brew upgrade minikube ``` {{% /tab %}} diff --git a/site/content/en/docs/Tasks/Registry/insecure.md b/site/content/en/docs/Tasks/Registry/insecure.md index aceba59e66..0ccf967899 100644 --- a/site/content/en/docs/Tasks/Registry/insecure.md +++ b/site/content/en/docs/Tasks/Registry/insecure.md @@ -15,3 +15,30 @@ You can use the `--insecure-registry` flag on the One nifty hack is to allow the kubelet running in minikube to talk to registries deployed inside a pod in the cluster without backing them with TLS certificates. Because the default service cluster IP is known to be available at 10.0.0.1, users can pull images from registries deployed inside the cluster by creating the cluster with `minikube start --insecure-registry "10.0.0.0/24"`. + +### docker on macOS + +Quick guide for configuring minikube and docker on macOS, enabling docker to push images to minikube's registry. + +The first step is to enable the registry addon: + +``` +minikube addons enable registry +``` + +When enabled, the registry addon exposes its port 5000 on the minikube's virtual machine. + +In order to make docker accept pushing images to this registry, we have to redirect port 5000 on the docker virtual machine over to port 5000 on the minikube machine. We can (ab)use docker's network configuration to instantiate a container on the docker's host, and run socat there: + +``` +docker run --rm -it --network=host alpine ash -c "apk add socat && socat TCP-LISTEN:5000,reuseaddr,fork TCP:$(minikube ip):5000" +``` + +Once socat is running it's possible to push images to the minikube registry: + +``` +docker tag my/image localhost:5000/myimage +docker push localhost:5000/myimage +``` + +After the image is pushed, refer to it by `localhost:5000/{name}` in kubectl specs. diff --git a/site/content/en/docs/Tasks/addons.md b/site/content/en/docs/Tasks/addons.md index b00ee229d8..8a490185c3 100644 --- a/site/content/en/docs/Tasks/addons.md +++ b/site/content/en/docs/Tasks/addons.md @@ -11,7 +11,6 @@ minikube has a set of built-in addons that, when enabled, can be used within Kub ## Available addons * [Kubernetes Dashboard](https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dashboard) -* [Heapster](https://github.com/kubernetes/heapster): [Troubleshooting Guide](https://github.com/kubernetes/heapster/blob/master/docs/influxdb.md) Note:You will need to login to Grafana as admin/admin in order to access the console * [EFK](https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch) * [Registry](https://github.com/kubernetes/minikube/tree/master/deploy/addons/registry) * [Registry Credentials](https://github.com/upmc-enterprises/registry-creds) @@ -20,8 +19,10 @@ minikube has a set of built-in addons that, when enabled, can be used within Kub * [nvidia-driver-installer](https://github.com/GoogleCloudPlatform/container-engine-accelerators/tree/master/nvidia-driver-installer/minikube) * [nvidia-gpu-device-plugin](https://github.com/GoogleCloudPlatform/container-engine-accelerators/tree/master/cmd/nvidia_gpu) * [logviewer](https://github.com/ivans3/minikube-log-viewer) -* [gvisor](../deploy/addons/gvisor/README.md) -* [storage-provisioner-gluster](../deploy/addons/storage-provisioner-gluster/README.md) +* [gvisor](../../../gvisor/readme/) +* [storage-provisioner-gluster](../../../storage-provisioner-gluster/readme) +* [helm-tiller](../../../helm-tiller/readme) +* [ingress-dns](../../../ingress-dns/readme) ## Listing available addons @@ -37,7 +38,6 @@ Example output: - freshpod: disabled - addon-manager: enabled - dashboard: enabled -- heapster: disabled - efk: disabled - ingress: disabled - default-storageclass: enabled @@ -53,6 +53,12 @@ Example output: minikube addons enable <name> ``` +or + +```shell +minikube start --addons <name> +``` + ## Interacting with an addon For addons that expose a browser endpoint, use: diff --git a/site/content/en/docs/Tutorials/nginx_tcp_udp_ingress.md b/site/content/en/docs/Tutorials/nginx_tcp_udp_ingress.md new file mode 100644 index 0000000000..596ddf1f3d --- /dev/null +++ b/site/content/en/docs/Tutorials/nginx_tcp_udp_ingress.md @@ -0,0 +1,233 @@ +--- +title: "Ingress nginx for TCP and UDP services" +linkTitle: "Ingress nginx for TCP and UDP services" +weight: 1 +date: 2019-08-15 +description: > + How to set up a minikube ingress for TCP and UDP services +--- + +## Overview + +The minikube [ingress addon](https://github.com/kubernetes/minikube/tree/master/deploy/addons/ingress) enables developers +to route traffic from their host (Laptop, Desktop, etc) to a Kubernetes service running inside their minikube cluster. +The ingress addon uses the [ingress nginx](https://github.com/kubernetes/ingress-nginx) controller which by default +is only configured to listen on ports 80 and 443. TCP and UDP services listening on other ports can be enabled. + +## Prerequisites + +- Latest minikube binary and ISO +- Telnet command line tool +- [Kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl) command line tool +- A text editor + +## Configuring TCP and UDP services with the nginx ingress controller + +### Enable the ingress addon + +Enable the minikube ingress addon with the following command: + +```shell +minikube addons enable ingress +``` + +### Update the TCP and/or UDP services configmaps + +Borrowing from the tutorial on [configuring TCP and UDP services with the ingress nginx controller](https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/) +we will need to edit the configmap which is installed by default when enabling the minikube ingress addon. + +There are 2 configmaps, 1 for TCP services and 1 for UDP services. By default they look like this: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: tcp-services + namespace: ingress-nginx +``` + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: udp-services + namespace: ingress-nginx +``` + +Since these configmaps are centralized and may contain configurations, it is best if we only patch them rather than completely overwrite them. + +Let's use this redis deployment as an example: + +`redis-deployment.yaml` +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis-deployment + namespace: default + labels: + app: redis +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - image: redis + imagePullPolicy: Always + name: redis + ports: + - containerPort: 6379 + protocol: TCP +``` + +Create a file `redis-deployment.yaml` and paste the contents above. Then install the redis deployment with the following command: + +```shell +kubectl apply -f redis-deployment.yaml +``` + +Next we need to create a service that can route traffic to our pods: + +`redis-service.yaml` +```yaml +apiVersion: v1 +kind: Service +metadata: + name: redis-service + namespace: default +spec: + selector: + app: redis + type: ClusterIP + ports: + - name: tcp-port + port: 6379 + targetPort: 6379 + protocol: TCP +``` + +Create a file `redis-service.yaml` and paste the contents above. Then install the redis service with the following command: + +```shell +kubectl apply -f redis-service.yaml +``` + +To add a TCP service to the nginx ingress controller you can run the following command: + +```shell +kubectl patch configmap tcp-services -n kube-system --patch '{"data":{"6379":"default/redis-service:6379"}}' +``` + +Where: + +- `6379` : the port your service should listen to from outside the minikube virtual machine +- `default` : the namespace that your service is installed in +- `redis-service` : the name of the service + +We can verify that our resource was patched with the following command: + +```shell +kubectl get configmap tcp-services -n kube-system -o yaml +``` + +We should see something like this: + +```yaml +apiVersion: v1 +data: + "6379": default/redis-service:6379 +kind: ConfigMap +metadata: + creationTimestamp: "2019-10-01T16:19:57Z" + labels: + addonmanager.kubernetes.io/mode: EnsureExists + name: tcp-services + namespace: kube-system + resourceVersion: "2857" + selfLink: /api/v1/namespaces/kube-system/configmaps/tcp-services + uid: 4f7fac22-e467-11e9-b543-080027057910 +``` + +The only value you need to validate is that there is a value under the `data` property that looks like this: + +```yaml + "6379": default/redis-service:6379 +``` + +### Patch the ingress-nginx-controller + +There is one final step that must be done in order to obtain connectivity from the outside cluster. +We need to patch our nginx controller so that it is listening on port 6379 and can route traffic to your service. To do +this we need to create a patch file. + +`nginx-ingress-controller-patch.yaml` +```yaml +spec: + template: + spec: + containers: + - name: nginx-ingress-controller + ports: + - containerPort: 6379 + hostPort: 6379 +``` + +Create a file called `nginx-ingress-controller-patch.yaml` and paste the contents above. + +Next apply the changes with the following command: + +```shell +kubectl patch deployment nginx-ingress-controller --patch "$(cat nginx-ingress-controller-patch.yaml)" -n kube-system +``` + +### Test your connection + +Test that you can reach your service with telnet via the following command: + +```shell +telnet $(minikube ip) 6379 +``` + +You should see the following output: + +```text +Trying 192.168.99.179... +Connected to 192.168.99.179. +Escape character is '^]' +``` + +To exit telnet enter the `Ctrl` key and `]` at the same time. Then type `quit` and press enter. + +If you were not able to connect please review your steps above. + +## Review + +In the above example we did the following: + +- Created a redis deployment and service in the `default` namespace +- Patched the `tcp-services` configmap in the `kube-system` namespace +- Patched the `nginx-ingress-controller` deployment in the `kube-system` namespace +- Connected to our service from the host via port 6379 + +You can apply the same steps that were applied to `tcp-services` to the `udp-services` configmap as well if you have a +service that uses UDP and/or TCP + +## Caveats + +With the exception of ports 80 and 443, each minikube instance can only be configured for exactly 1 service to be listening +on any particular port. Multiple TCP and/or UDP services listening on the same port in the same minikube instance is not supported +and can not be supported until an update of the ingress spec is released. +Please see [this document](https://docs.google.com/document/d/1BxYbDovMwnEqe8lj8JwHo8YxHAt3oC7ezhlFsG_tyag/edit#) +for the latest info on these potential changes. + +## Related articles + +- [Routing traffic multiple services on ports 80 and 443 in minikube with the Kubernetes Ingress resource](https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/) +- [Use port forwarding to access applications in a cluster](https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/) + diff --git a/test.sh b/test.sh index e052ed6045..463cca159f 100755 --- a/test.sh +++ b/test.sh @@ -16,40 +16,55 @@ set -eu -o pipefail +TESTSUITE="${TESTSUITE:-all}" # if env variable not set run all the tests exitcode=0 -echo "= go mod ================================================================" -go mod download 2>&1 | grep -v "go: finding" || true -go mod tidy -v && echo ok || ((exitcode += 2)) - -echo "= make lint =============================================================" -make -s lint-ci && echo ok || ((exitcode += 4)) - -echo "= boilerplate ===========================================================" -readonly PYTHON=$(type -P python || echo docker run --rm -it -v $(pwd):/minikube -w /minikube python python) -readonly BDIR="./hack/boilerplate" -missing="$($PYTHON ${BDIR}/boilerplate.py --rootdir . --boilerplate-dir ${BDIR} | egrep -v '/assets.go|/translations.go|/site/themes/|/site/node_modules|\./out|/hugo/' || true)" -if [[ -n "${missing}" ]]; then - echo "boilerplate missing: $missing" - echo "consider running: ${BDIR}/fix.sh" - ((exitcode += 4)) -else - echo "ok" +if [[ "$TESTSUITE" = "lint" ]] || [[ "$TESTSUITE" = "all" ]] || [[ "$TESTSUITE" = "lintall" ]] +then + echo "= make lint =============================================================" + make -s lint-ci && echo ok || ((exitcode += 4)) + echo "= go mod ================================================================" + go mod download 2>&1 | grep -v "go: finding" || true + go mod tidy -v && echo ok || ((exitcode += 2)) fi -echo "= schema_check ==========================================================" -go run deploy/minikube/schema_check.go >/dev/null && echo ok || ((exitcode += 8)) -echo "= go test ===============================================================" -cov_tmp="$(mktemp)" -readonly COVERAGE_PATH=./out/coverage.txt -echo "mode: count" >"${COVERAGE_PATH}" -pkgs=$(go list -f '{{ if .TestGoFiles }}{{.ImportPath}}{{end}}' ./cmd/... ./pkg/... | xargs) -go test \ - -tags "container_image_ostree_stub containers_image_openpgp" \ - -covermode=count \ - -coverprofile="${cov_tmp}" \ - ${pkgs} && echo ok || ((exitcode += 16)) -tail -n +2 "${cov_tmp}" >>"${COVERAGE_PATH}" + +if [[ "$TESTSUITE" = "boilerplate" ]] || [[ "$TESTSUITE" = "all" ]] || [[ "$TESTSUITE" = "lintall" ]] +then + echo "= boilerplate ===========================================================" + readonly ROOT_DIR=$(pwd) + readonly BDIR="${ROOT_DIR}/hack/boilerplate" + pushd . >/dev/null + cd ${BDIR} + missing="$(go run boilerplate.go -rootdir ${ROOT_DIR} -boilerplate-dir ${BDIR} | egrep -v '/assets.go|/translations.go|/site/themes/|/site/node_modules|\./out|/hugo/' || true)" + if [[ -n "${missing}" ]]; then + echo "boilerplate missing: $missing" + echo "consider running: ${BDIR}/fix.sh" + ((exitcode += 8)) + else + echo "ok" + fi + popd >/dev/null +fi + + +if [[ "$TESTSUITE" = "unittest" ]] || [[ "$TESTSUITE" = "all" ]] +then + echo "= schema_check ==========================================================" + go run deploy/minikube/schema_check.go >/dev/null && echo ok || ((exitcode += 16)) + + echo "= go test ===============================================================" + cov_tmp="$(mktemp)" + readonly COVERAGE_PATH=./out/coverage.txt + echo "mode: count" >"${COVERAGE_PATH}" + pkgs=$(go list -f '{{ if .TestGoFiles }}{{.ImportPath}}{{end}}' ./cmd/... ./pkg/... | xargs) + go test \ + -tags "container_image_ostree_stub containers_image_openpgp" \ + -covermode=count \ + -coverprofile="${cov_tmp}" \ + ${pkgs} && echo ok || ((exitcode += 32)) + tail -n +2 "${cov_tmp}" >>"${COVERAGE_PATH}" +fi exit "${exitcode}" diff --git a/test/integration/README.md b/test/integration/README.md index 1a554492f6..7af031f6ea 100644 --- a/test/integration/README.md +++ b/test/integration/README.md @@ -10,7 +10,7 @@ To run all tests from the minikube root directory: Run a single test on an active cluster: -`make integration -e TEST_ARGS="-test.v -test.run TestFunctional/parallel/MountCmd --profile=minikube --cleanup=false"` +`make integration -e TEST_ARGS="-test.run TestFunctional/parallel/MountCmd --profile=minikube --cleanup=false"` WARNING: For this to work repeatedly, the test must be written so that it cleans up after itself. diff --git a/test/integration/a_download_only_test.go b/test/integration/a_download_only_test.go deleted file mode 100644 index 2972f3582c..0000000000 --- a/test/integration/a_download_only_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// +build integration - -/* -Copyright 2019 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. -*/ - -// a_download_only_test.go filename starts with a, for the purpose that it runs before all parallel tests and downloads the images and caches them. -package integration - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "testing" - "time" - - "k8s.io/minikube/pkg/minikube/bootstrapper/images" - "k8s.io/minikube/pkg/minikube/constants" - "k8s.io/minikube/pkg/minikube/localpath" -) - -// Note this test runs before all because filename is alphabetically first -// is used to cache images and binaries used by other parallel tests to avoid redownloading. -// TestDownloadOnly tests the --download-only option -func TestDownloadOnly(t *testing.T) { - profile := UniqueProfileName("download") - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) - defer Cleanup(t, profile, cancel) - - t.Run("group", func(t *testing.T) { - versions := []string{ - constants.OldestKubernetesVersion, - constants.DefaultKubernetesVersion, - constants.NewestKubernetesVersion, - } - for _, v := range versions { - t.Run(v, func(t *testing.T) { - args := append([]string{"start", "--download-only", "-p", profile, fmt.Sprintf("--kubernetes-version=%s", v)}, StartArgs()...) - _, err := Run(t, exec.CommandContext(ctx, Target(), args...)) - if err != nil { - t.Errorf("%s failed: %v", args, err) - } - - // None driver does not cache images, so this test will fail - if !NoneDriver() { - _, imgs := images.CachedImages("", v) - for _, img := range imgs { - img = strings.Replace(img, ":", "_", 1) // for example kube-scheduler:v1.15.2 --> kube-scheduler_v1.15.2 - fp := filepath.Join(localpath.MiniPath(), "cache", "images", img) - _, err := os.Stat(fp) - if err != nil { - t.Errorf("expected image file exist at %q but got error: %v", fp, err) - } - } - } - - // checking binaries downloaded (kubelet,kubeadm) - for _, bin := range constants.KubeadmBinaries { - fp := filepath.Join(localpath.MiniPath(), "cache", v, bin) - _, err := os.Stat(fp) - if err != nil { - t.Errorf("expected the file for binary exist at %q but got error %v", fp, err) - } - } - }) - } - }) -} diff --git a/test/integration/a_serial_test.go b/test/integration/a_serial_test.go new file mode 100644 index 0000000000..ae931662d7 --- /dev/null +++ b/test/integration/a_serial_test.go @@ -0,0 +1,141 @@ +// +build integration + +/* +Copyright 2019 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 integration + +import ( + "context" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" + + "k8s.io/minikube/pkg/minikube/bootstrapper/images" + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/localpath" +) + +func TestDownloadOnly(t *testing.T) { + profile := UniqueProfileName("download") + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) + defer Cleanup(t, profile, cancel) + + // Stores the startup run result for later error messages + var rrr *RunResult + var err error + + t.Run("group", func(t *testing.T) { + versions := []string{ + constants.OldestKubernetesVersion, + constants.DefaultKubernetesVersion, + constants.NewestKubernetesVersion, + } + for _, v := range versions { + t.Run(v, func(t *testing.T) { + // Explicitly does not pass StartArgs() to test driver default + // --force to avoid uid check + args := []string{"start", "--download-only", "-p", profile, "--force", "--alsologtostderr", fmt.Sprintf("--kubernetes-version=%s", v)} + + // Preserve the initial run-result for debugging + if rrr == nil { + rrr, err = Run(t, exec.CommandContext(ctx, Target(), args...)) + } else { + _, err = Run(t, exec.CommandContext(ctx, Target(), args...)) + } + + if err != nil { + t.Errorf("%s failed: %v", args, err) + } + + imgs := images.CachedImages("", v) + for _, img := range imgs { + img = strings.Replace(img, ":", "_", 1) // for example kube-scheduler:v1.15.2 --> kube-scheduler_v1.15.2 + fp := filepath.Join(localpath.MiniPath(), "cache", "images", img) + _, err := os.Stat(fp) + if err != nil { + t.Errorf("expected image file exist at %q but got error: %v", fp, err) + } + } + + // checking binaries downloaded (kubelet,kubeadm) + for _, bin := range constants.KubeadmBinaries { + fp := filepath.Join(localpath.MiniPath(), "cache", v, bin) + _, err := os.Stat(fp) + if err != nil { + t.Errorf("expected the file for binary exist at %q but got error %v", fp, err) + } + } + }) + } + + // Check that the profile we've created has the expected driver + t.Run("ExpectedDefaultDriver", func(t *testing.T) { + if ExpectedDefaultDriver() == "" { + t.Skipf("--expected-default-driver is unset, skipping test") + return + } + rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "--output", "json")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + var ps map[string][]config.Profile + err = json.Unmarshal(rr.Stdout.Bytes(), &ps) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + + got := "" + for _, p := range ps["valid"] { + if p.Name == profile { + got = p.Config.MachineConfig.VMDriver + } + } + + if got != ExpectedDefaultDriver() { + t.Errorf("got driver %q, expected %q\nstart output: %s", got, ExpectedDefaultDriver(), rrr.Output()) + } + }) + + // This is a weird place to test profile deletion, but this test is serial, and we have a profile to delete! + t.Run("DeleteAll", func(t *testing.T) { + if !CanCleanup() { + t.Skip("skipping, as cleanup is disabled") + } + rr, err := Run(t, exec.CommandContext(ctx, Target(), "delete", "--all")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + }) + // Delete should always succeed, even if previously partially or fully deleted. + t.Run("DeleteAlwaysSucceeds", func(t *testing.T) { + if !CanCleanup() { + t.Skip("skipping, as cleanup is disabled") + } + rr, err := Run(t, exec.CommandContext(ctx, Target(), "delete", "-p", profile)) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + }) + }) + +} diff --git a/test/integration/addons_test.go b/test/integration/addons_test.go index f363de6ef3..a575e96eb5 100644 --- a/test/integration/addons_test.go +++ b/test/integration/addons_test.go @@ -36,13 +36,13 @@ import ( // TestAddons tests addons that require no special environment -- in parallel func TestAddons(t *testing.T) { - MaybeSlowParallel(t) - + MaybeParallel(t) + WaitForStartSlot(t) profile := UniqueProfileName("addons") ctx, cancel := context.WithTimeout(context.Background(), 40*time.Minute) defer CleanupWithLogs(t, profile, cancel) - args := append([]string{"start", "-p", profile, "--wait=false", "--memory=2600", "--alsologtostderr", "-v=1"}, StartArgs()...) + args := append([]string{"start", "-p", profile, "--wait=false", "--memory=2600", "--alsologtostderr", "-v=1", "--addons=ingress", "--addons=registry"}, StartArgs()...) rr, err := Run(t, exec.CommandContext(ctx, Target(), args...)) if err != nil { t.Fatalf("%s failed: %v", rr.Args, err) @@ -65,6 +65,20 @@ func TestAddons(t *testing.T) { }) } }) + + // Assert that disable/enable works offline + rr, err = Run(t, exec.CommandContext(ctx, Target(), "stop", "-p", profile)) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + rr, err = Run(t, exec.CommandContext(ctx, Target(), "addons", "enable", "dashboard", "-p", profile)) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + rr, err = Run(t, exec.CommandContext(ctx, Target(), "addons", "disable", "dashboard", "-p", profile)) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } } func validateIngressAddon(ctx context.Context, t *testing.T, profile string) { @@ -72,11 +86,6 @@ func validateIngressAddon(ctx context.Context, t *testing.T, profile string) { t.Skipf("skipping: ssh unsupported by none") } - rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "addons", "enable", "ingress")) - if err != nil { - t.Fatalf("%s failed: %v", rr.Args, err) - } - client, err := kapi.Client(profile) if err != nil { t.Fatalf("kubernetes client: %v", client) @@ -89,7 +98,7 @@ func validateIngressAddon(ctx context.Context, t *testing.T, profile string) { t.Fatalf("wait: %v", err) } - rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "replace", "--force", "-f", filepath.Join(*testdataDir, "nginx-ing.yaml"))) + rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "replace", "--force", "-f", filepath.Join(*testdataDir, "nginx-ing.yaml"))) if err != nil { t.Errorf("%s failed: %v", rr.Args, err) } @@ -131,11 +140,6 @@ func validateIngressAddon(ctx context.Context, t *testing.T, profile string) { } func validateRegistryAddon(ctx context.Context, t *testing.T, profile string) { - rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "addons", "enable", "registry")) - if err != nil { - t.Fatalf("%s failed: %v", rr.Args, err) - } - client, err := kapi.Client(profile) if err != nil { t.Fatalf("kubernetes client: %v", client) @@ -155,7 +159,7 @@ func validateRegistryAddon(ctx context.Context, t *testing.T, profile string) { } // Test from inside the cluster (no curl available on busybox) - rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "delete", "po", "-l", "run=registry-test", "--now")) + rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "delete", "po", "-l", "run=registry-test", "--now")) if err != nil { t.Logf("pre-cleanup %s failed: %v (not a problem)", rr.Args, err) } diff --git a/test/integration/docker_test.go b/test/integration/docker_test.go index dd48ea5437..7acb0f9dcc 100644 --- a/test/integration/docker_test.go +++ b/test/integration/docker_test.go @@ -30,7 +30,8 @@ func TestDockerFlags(t *testing.T) { if NoneDriver() { t.Skip("skipping: none driver does not support ssh or bundle docker") } - MaybeSlowParallel(t) + MaybeParallel(t) + WaitForStartSlot(t) profile := UniqueProfileName("docker-flags") ctx, cancel := context.WithTimeout(context.Background(), 20*time.Minute) diff --git a/test/integration/driver_install_or_update_test.go b/test/integration/driver_install_or_update_test.go index de11940873..cff536642c 100644 --- a/test/integration/driver_install_or_update_test.go +++ b/test/integration/driver_install_or_update_test.go @@ -26,7 +26,7 @@ import ( "github.com/blang/semver" - "k8s.io/minikube/pkg/drivers" + "k8s.io/minikube/pkg/minikube/driver" ) func TestKVMDriverInstallOrUpdate(t *testing.T) { @@ -84,7 +84,7 @@ func TestKVMDriverInstallOrUpdate(t *testing.T) { t.Fatalf("Expected new semver. test: %v, got: %v", tc.name, err) } - err = drivers.InstallOrUpdate("kvm2", dir, newerVersion, true) + err = driver.InstallOrUpdate("kvm2", dir, newerVersion, true, true) if err != nil { t.Fatalf("Failed to update driver to %v. test: %s, got: %v", newerVersion, tc.name, err) } @@ -147,7 +147,7 @@ func TestHyperKitDriverInstallOrUpdate(t *testing.T) { t.Fatalf("Expected new semver. test: %v, got: %v", tc.name, err) } - err = drivers.InstallOrUpdate("hyperkit", dir, newerVersion, true) + err = driver.InstallOrUpdate("hyperkit", dir, newerVersion, true, true) if err != nil { t.Fatalf("Failed to update driver to %v. test: %s, got: %v", newerVersion, tc.name, err) } diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 69a8864e93..00379f245b 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -29,6 +29,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "strings" "testing" "time" @@ -59,6 +60,7 @@ func TestFunctional(t *testing.T) { }{ {"StartWithProxy", validateStartWithProxy}, // Set everything else up for success {"KubeContext", validateKubeContext}, // Racy: must come immediately after "minikube start" + {"KubectlGetPods", validateKubectlGetPods}, // Make sure apiserver is up {"CacheCmd", validateCacheCmd}, // Caches images needed for subsequent tests because of proxy } for _, tc := range tests { @@ -83,13 +85,16 @@ func TestFunctional(t *testing.T) { {"ConfigCmd", validateConfigCmd}, {"DashboardCmd", validateDashboardCmd}, {"DNS", validateDNS}, + {"StatusCmd", validateStatusCmd}, {"LogsCmd", validateLogsCmd}, {"MountCmd", validateMountCmd}, {"ProfileCmd", validateProfileCmd}, - {"ServicesCmd", validateServicesCmd}, + {"ServiceCmd", validateServiceCmd}, + {"AddonsCmd", validateAddonsCmd}, {"PersistentVolumeClaim", validatePersistentVolumeClaim}, {"TunnelCmd", validateTunnelCmd}, {"SSHCmd", validateSSHCmd}, + {"MySQL", validateMySQL}, } for _, tc := range tests { tc := tc @@ -139,6 +144,18 @@ func validateKubeContext(ctx context.Context, t *testing.T, profile string) { } } +// validateKubectlGetPods asserts that `kubectl get pod -A` returns non-zero content +func validateKubectlGetPods(ctx context.Context, t *testing.T, profile string) { + rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "get", "pod", "-A")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + podName := "kube-apiserver-minikube" + if !strings.Contains(rr.Stdout.String(), podName) { + t.Errorf("%s is not up in running, got: %s\n", podName, rr.Stdout.String()) + } +} + // validateAddonManager asserts that the kube-addon-manager pod is deployed properly func validateAddonManager(ctx context.Context, t *testing.T, profile string) { // If --wait=false, this may take a couple of minutes @@ -173,6 +190,46 @@ func validateComponentHealth(ctx context.Context, t *testing.T, profile string) } } +func validateStatusCmd(ctx context.Context, t *testing.T, profile string) { + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + + // Custom format + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status", "-f", "host:{{.Host}},kublet:{{.Kubelet}},apiserver:{{.APIServer}},kubeconfig:{{.Kubeconfig}}")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + match, _ := regexp.MatchString(`host:([A-z]+),kublet:([A-z]+),apiserver:([A-z]+),kubeconfig:([A-z]+)`, rr.Stdout.String()) + if !match { + t.Errorf("%s failed: %v. Output for custom format did not match", rr.Args, err) + } + + // Json output + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status", "-o", "json")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + var jsonObject map[string]interface{} + err = json.Unmarshal(rr.Stdout.Bytes(), &jsonObject) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + if _, ok := jsonObject["Host"]; !ok { + t.Errorf("%s failed: %v. Missing key %s in json object", rr.Args, err, "Host") + } + if _, ok := jsonObject["Kubelet"]; !ok { + t.Errorf("%s failed: %v. Missing key %s in json object", rr.Args, err, "Kubelet") + } + if _, ok := jsonObject["APIServer"]; !ok { + t.Errorf("%s failed: %v. Missing key %s in json object", rr.Args, err, "APIServer") + } + if _, ok := jsonObject["Kubeconfig"]; !ok { + t.Errorf("%s failed: %v. Missing key %s in json object", rr.Args, err, "Kubeconfig") + } +} + // validateDashboardCmd asserts that the dashboard command works func validateDashboardCmd(ctx context.Context, t *testing.T, profile string) { args := []string{"dashboard", "--url", "-p", profile, "--alsologtostderr", "-v=1"} @@ -241,7 +298,7 @@ func validateCacheCmd(ctx context.Context, t *testing.T, profile string) { if NoneDriver() { t.Skipf("skipping: cache unsupported by none") } - for _, img := range []string{"busybox", "busybox:1.28.4-glibc"} { + for _, img := range []string{"busybox", "busybox:1.28.4-glibc", "mysql:5.6"} { rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "cache", "add", img)) if err != nil { t.Errorf("%s failed: %v", rr.Args, err) @@ -295,22 +352,179 @@ func validateLogsCmd(ctx context.Context, t *testing.T, profile string) { } } -// validateProfileCmd asserts basic "profile" command functionality +// validateProfileCmd asserts "profile" command functionality func validateProfileCmd(ctx context.Context, t *testing.T, profile string) { rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list")) if err != nil { t.Errorf("%s failed: %v", rr.Args, err) } -} -// validateServiceCmd asserts basic "service" command functionality -func validateServicesCmd(ctx context.Context, t *testing.T, profile string) { - rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "list")) + // Table output + listLines := strings.Split(strings.TrimSpace(rr.Stdout.String()), "\n") + profileExists := false + for i := 3; i < (len(listLines) - 1); i++ { + profileLine := listLines[i] + if strings.Contains(profileLine, profile) { + profileExists = true + break + } + } + if !profileExists { + t.Errorf("%s failed: Missing profile '%s'. Got '\n%s\n'", rr.Args, profile, rr.Stdout.String()) + } + + // Json output + rr, err = Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "--output", "json")) if err != nil { t.Errorf("%s failed: %v", rr.Args, err) } - if !strings.Contains(rr.Stdout.String(), "kubernetes") { - t.Errorf("service list got %q, wanted *kubernetes*", rr.Stdout.String()) + var jsonObject map[string][]map[string]interface{} + err = json.Unmarshal(rr.Stdout.Bytes(), &jsonObject) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + validProfiles := jsonObject["valid"] + profileExists = false + for _, profileObject := range validProfiles { + if profileObject["Name"] == profile { + profileExists = true + break + } + } + if !profileExists { + t.Errorf("%s failed: Missing profile '%s'. Got '\n%s\n'", rr.Args, profile, rr.Stdout.String()) + } +} + +// validateServiceCmd asserts basic "service" command functionality +func validateServiceCmd(ctx context.Context, t *testing.T, profile string) { + rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "create", "deployment", "hello-node", "--image=gcr.io/hello-minikube-zero-install/hello-node")) + if err != nil { + t.Logf("%s failed: %v (may not be an error)", rr.Args, err) + } + rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "expose", "deployment", "hello-node", "--type=NodePort", "--port=8080")) + if err != nil { + t.Logf("%s failed: %v (may not be an error)", rr.Args, err) + } + + if _, err := PodWait(ctx, t, profile, "default", "app=hello-node", 4*time.Minute); err != nil { + t.Fatalf("wait: %v", err) + } + + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "list")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + if !strings.Contains(rr.Stdout.String(), "hello-node") { + t.Errorf("service list got %q, wanted *hello-node*", rr.Stdout.String()) + } + + // Test --https --url mode + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "--namespace=default", "--https", "--url", "hello-node")) + if err != nil { + t.Fatalf("%s failed: %v", rr.Args, err) + } + if rr.Stderr.String() != "" { + t.Errorf("unexpected stderr output: %s", rr.Stderr) + } + + endpoint := strings.TrimSpace(rr.Stdout.String()) + u, err := url.Parse(endpoint) + if err != nil { + t.Fatalf("failed to parse %q: %v", endpoint, err) + } + if u.Scheme != "https" { + t.Errorf("got scheme: %q, expected: %q", u.Scheme, "https") + } + + // Test --format=IP + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "hello-node", "--url", "--format={{.IP}}")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + if strings.TrimSpace(rr.Stdout.String()) != u.Hostname() { + t.Errorf("%s = %q, wanted %q", rr.Args, rr.Stdout.String(), u.Hostname()) + } + + // Test a regular URLminikube + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "hello-node", "--url")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + + endpoint = strings.TrimSpace(rr.Stdout.String()) + u, err = url.Parse(endpoint) + if err != nil { + t.Fatalf("failed to parse %q: %v", endpoint, err) + } + if u.Scheme != "http" { + t.Fatalf("got scheme: %q, expected: %q", u.Scheme, "http") + } + + t.Logf("url: %s", endpoint) + resp, err := retryablehttp.Get(endpoint) + if err != nil { + t.Fatalf("get failed: %v\nresp: %v", err, resp) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("%s = status code %d, want %d", u, resp.StatusCode, http.StatusOK) + } +} + +// validateAddonsCmd asserts basic "addon" command functionality +func validateAddonsCmd(ctx context.Context, t *testing.T, profile string) { + + // Default output + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "addons", "list")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + listLines := strings.Split(strings.TrimSpace(rr.Stdout.String()), "\n") + r := regexp.MustCompile(`-\s[a-z|-]+:\s(enabled|disabled)`) + for _, line := range listLines { + match := r.MatchString(line) + if !match { + t.Errorf("Plugin output did not match expected format. Got: %s", line) + } + } + + // Custom format + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "addons", "list", "--format", `"{{.AddonName}}":"{{.AddonStatus}}"`)) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + listLines = strings.Split(strings.TrimSpace(rr.Stdout.String()), "\n") + r = regexp.MustCompile(`"[a-z|-]+":"(enabled|disabled)"`) + for _, line := range listLines { + match := r.MatchString(line) + if !match { + t.Errorf("Plugin output did not match expected custom format. Got: %s", line) + } + } + + // Custom format shorthand + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "addons", "list", "-f", `"{{.AddonName}}":"{{.AddonStatus}}"`)) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + listLines = strings.Split(strings.TrimSpace(rr.Stdout.String()), "\n") + r = regexp.MustCompile(`"[a-z|-]+":"(enabled|disabled)"`) + for _, line := range listLines { + match := r.MatchString(line) + if !match { + t.Errorf("Plugin output did not match expected custom format. Got: %s", line) + } + } + + // Json output + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "addons", "list", "-o", "json")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + var jsonObject map[string]interface{} + err = json.Unmarshal(rr.Stdout.Bytes(), &jsonObject) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) } } @@ -329,6 +543,27 @@ func validateSSHCmd(ctx context.Context, t *testing.T, profile string) { } } +// validateMySQL validates a minimalist MySQL deployment +func validateMySQL(ctx context.Context, t *testing.T, profile string) { + rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "replace", "--force", "-f", filepath.Join(*testdataDir, "mysql.yaml"))) + if err != nil { + t.Fatalf("%s failed: %v", rr.Args, err) + } + + // Retry, as mysqld first comes up without users configured. Scan for names in case of a reschedule. + mysql := func() error { + names, err := PodWait(ctx, t, profile, "default", "app=mysql", 5*time.Second) + if err != nil { + return err + } + rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "exec", names[0], "--", "mysql", "-ppassword", "-e", "show databases;")) + return err + } + if err = retry.Expo(mysql, 1*time.Second, 2*time.Minute); err != nil { + t.Errorf("mysql failing: %v", err) + } +} + // startHTTPProxy runs a local http proxy and sets the env vars for it. func startHTTPProxy(t *testing.T) (*http.Server, error) { port, err := freeport.GetFreePort() diff --git a/test/integration/guest_env_test.go b/test/integration/guest_env_test.go index e9f4925a27..0e7dfb6634 100644 --- a/test/integration/guest_env_test.go +++ b/test/integration/guest_env_test.go @@ -27,7 +27,9 @@ import ( ) func TestGuestEnvironment(t *testing.T) { - MaybeSlowParallel(t) + MaybeParallel(t) + WaitForStartSlot(t) + profile := UniqueProfileName("guest") ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) defer CleanupWithLogs(t, profile, cancel) diff --git a/test/integration/gvisor_addon_test.go b/test/integration/gvisor_addon_test.go index e801aef446..f8ffb4d2c0 100644 --- a/test/integration/gvisor_addon_test.go +++ b/test/integration/gvisor_addon_test.go @@ -27,17 +27,25 @@ import ( ) func TestGvisorAddon(t *testing.T) { - // TODO(tstromberg): Fix or remove addon. - t.Skip("SKIPPING: Currently broken (gvisor-containerd-shim.toml CrashLoopBackoff): https://github.com/kubernetes/minikube/issues/5305") - if NoneDriver() { t.Skip("Can't run containerd backend with none driver") } - MaybeSlowParallel(t) + if !*enableGvisor { + t.Skip("skipping test because --gvisor=false") + } + MaybeParallel(t) + WaitForStartSlot(t) profile := UniqueProfileName("gvisor") - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Minute) + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Minute) defer func() { + if t.Failed() { + rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "logs", "gvisor", "-n", "kube-system")) + if err != nil { + t.Logf("failed to get gvisor post-mortem logs: %v", err) + } + t.Logf("gvisor post-mortem: %s:\n%s\n", rr.Command(), rr.Output()) + } CleanupWithLogs(t, profile, cancel) }() @@ -47,10 +55,10 @@ func TestGvisorAddon(t *testing.T) { t.Fatalf("%s failed: %v", rr.Args, err) } - // TODO: Re-examine if we should be pulling in an image which users don't normally invoke - rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "cache", "add", "gcr.io/k8s-minikube/gvisor-addon:latest")) + // If it exists, include a locally built gvisor image + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "cache", "add", "gcr.io/k8s-minikube/gvisor-addon:2")) if err != nil { - t.Errorf("%s failed: %v", rr.Args, err) + t.Logf("%s failed: %v (won't test local image)", rr.Args, err) } // NOTE: addons are global, but the addon must assert that the runtime is containerd diff --git a/test/integration/helpers.go b/test/integration/helpers.go index 7c3688bac3..505d1aeea1 100644 --- a/test/integration/helpers.go +++ b/test/integration/helpers.go @@ -42,8 +42,10 @@ import ( ) var ( - antiRaceCounter = 0 - antiRaceMutex = &sync.Mutex{} + // startTimes is a list of startup times, to guarantee --start-offset + startTimes = []time.Time{} + // startTimesMutex is a lock to update startTimes without a race condition + startTimesMutex = &sync.Mutex{} ) // RunResult stores the result of an cmd.Run call @@ -177,7 +179,7 @@ func CleanupWithLogs(t *testing.T, profile string, cancel context.CancelFunc) { t.Helper() if t.Failed() && *postMortemLogs { t.Logf("%s failed, collecting logs ...", t.Name()) - rr, err := Run(t, exec.Command(Target(), "-p", profile, "logs", "-n", "100")) + rr, err := Run(t, exec.Command(Target(), "-p", profile, "logs", "--problems")) if err != nil { t.Logf("failed logs error: %v", err) } @@ -227,9 +229,10 @@ func PodWait(ctx context.Context, t *testing.T, profile string, ns string, selec f := func() (bool, error) { pods, err := client.CoreV1().Pods(ns).List(listOpts) if err != nil { - t.Logf("Pod(%s).List(%v) returned error: %v", ns, selector, err) - // Don't bother to retry: something is very wrong. - return true, err + t.Logf("WARNING: pod list for %q %q returned: %v", ns, selector, err) + // Don't return the error upwards so that this is retried, in case the apiserver is rescheduled + podStart = time.Time{} + return false, nil } if len(pods.Items) == 0 { podStart = time.Time{} @@ -331,25 +334,32 @@ func MaybeParallel(t *testing.T) { t.Parallel() } -// MaybeSlowParallel is a terrible workaround for tests which start clusters in a race-filled world -// TODO: Try removing this hack once certificates are deployed per-profile -func MaybeSlowParallel(t *testing.T) { - // NoneDriver shouldn't parallelize "minikube start" +// WaitForStartSlot enforces --start-offset to avoid startup race conditions +func WaitForStartSlot(t *testing.T) { + // Not parallel if NoneDriver() { return } - antiRaceMutex.Lock() - antiRaceCounter++ - antiRaceMutex.Unlock() - - if antiRaceCounter > 0 { - // Slow enough to offset start, but not slow to be a major source of delay - penalty := time.Duration(5*antiRaceCounter) * time.Second - t.Logf("MaybeSlowParallel: Sleeping %s to avoid start race ...", penalty) - time.Sleep(penalty) + wakeup := time.Now() + startTimesMutex.Lock() + if len(startTimes) > 0 { + nextStart := startTimes[len(startTimes)-1].Add(*startOffset) + // Ignore nextStart if it is in the past - to guarantee offset for next caller + if time.Now().Before(nextStart) { + wakeup = nextStart + } + } + startTimes = append(startTimes, wakeup) + startTimesMutex.Unlock() + + if time.Now().Before(wakeup) { + d := time.Until(wakeup) + t.Logf("Waiting for start slot at %s (sleeping %s) ...", wakeup, d) + time.Sleep(d) + } else { + t.Logf("No need to wait for start slot, it is already %s", time.Now()) } - t.Parallel() } // killProcessFamily kills a pid and all of its children diff --git a/test/integration/main.go b/test/integration/main.go index 02ce476cf9..fe351e070d 100644 --- a/test/integration/main.go +++ b/test/integration/main.go @@ -27,10 +27,13 @@ import ( // General configuration: used to set the VM Driver var startArgs = flag.String("minikube-start-args", "", "Arguments to pass to minikube start") +var defaultDriver = flag.String("expected-default-driver", "", "Expected default driver") // Flags for faster local integration testing var forceProfile = flag.String("profile", "", "force tests to run against a particular profile") var cleanup = flag.Bool("cleanup", true, "cleanup failed test run") +var enableGvisor = flag.Bool("gvisor", false, "run gvisor integration test (slow)") +var startOffset = flag.Duration("start-offset", 30*time.Second, "how much time to offset between cluster starts") var postMortemLogs = flag.Bool("postmortem-logs", true, "show logs after a failed test run") // Paths to files - normally set for CI @@ -65,3 +68,13 @@ func NoneDriver() bool { func HyperVDriver() bool { return strings.Contains(*startArgs, "--vm-driver=hyperv") } + +// ExpectedDefaultDriver returns the expected default driver, if any +func ExpectedDefaultDriver() string { + return *defaultDriver +} + +// CanCleanup returns if cleanup is allowed +func CanCleanup() bool { + return *cleanup +} diff --git a/test/integration/start_stop_delete_test.go b/test/integration/start_stop_delete_test.go index bf575baeee..52ac7a4202 100644 --- a/test/integration/start_stop_delete_test.go +++ b/test/integration/start_stop_delete_test.go @@ -23,6 +23,7 @@ import ( "fmt" "os/exec" "path/filepath" + "strconv" "strings" "testing" "time" @@ -74,7 +75,8 @@ func TestStartStop(t *testing.T) { for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { - MaybeSlowParallel(t) + MaybeParallel(t) + WaitForStartSlot(t) if !strings.Contains(tc.name, "docker") && NoneDriver() { t.Skipf("skipping %s - incompatible with none driver", t.Name()) @@ -84,7 +86,7 @@ func TestStartStop(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 40*time.Minute) defer CleanupWithLogs(t, profile, cancel) - startArgs := append([]string{"start", "-p", profile, "--alsologtostderr", "-v=3"}, tc.args...) + startArgs := append([]string{"start", "-p", profile, "--alsologtostderr", "-v=3", "--wait=true"}, tc.args...) startArgs = append(startArgs, StartArgs()...) rr, err := Run(t, exec.CommandContext(ctx, Target(), startArgs...)) if err != nil { @@ -102,9 +104,27 @@ func TestStartStop(t *testing.T) { t.Fatalf("%s failed: %v", rr.Args, err) } - if _, err := PodWait(ctx, t, profile, "default", "integration-test=busybox", 2*time.Minute); err != nil { + names, err := PodWait(ctx, t, profile, "default", "integration-test=busybox", 4*time.Minute) + if err != nil { t.Fatalf("wait: %v", err) } + + // Use this pod to confirm that the runtime resource limits are sane + rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "exec", names[0], "--", "/bin/sh", "-c", "ulimit -n")) + if err != nil { + t.Fatalf("ulimit: %v", err) + } + + got, err := strconv.ParseInt(strings.TrimSpace(rr.Stdout.String()), 10, 64) + if err != nil { + t.Errorf("ParseInt(%q): %v", rr.Stdout.String(), err) + } + + // Arbitrary value set by some container runtimes. If higher, apps like MySQL may make bad decisions. + expected := int64(1048576) + if got != expected { + t.Errorf("'ulimit -n' returned %d, expected %d", got, expected) + } } rr, err = Run(t, exec.CommandContext(ctx, Target(), "stop", "-p", profile, "--alsologtostderr", "-v=3")) @@ -117,6 +137,7 @@ func TestStartStop(t *testing.T) { t.Errorf("status = %q; want = %q", got, state.Stopped) } + WaitForStartSlot(t) rr, err = Run(t, exec.CommandContext(ctx, Target(), startArgs...)) if err != nil { // Explicit fatal so that failures don't move directly to deletion @@ -134,10 +155,12 @@ func TestStartStop(t *testing.T) { t.Errorf("status = %q; want = %q", got, state.Running) } - // Normally handled by cleanuprofile, but not fatal there - rr, err = Run(t, exec.CommandContext(ctx, Target(), "delete", "-p", profile)) - if err != nil { - t.Errorf("%s failed: %v", rr.Args, err) + if !*cleanup { + // Normally handled by cleanuprofile, but not fatal there + rr, err = Run(t, exec.CommandContext(ctx, Target(), "delete", "-p", profile)) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } } }) } diff --git a/test/integration/testdata/mysql.yaml b/test/integration/testdata/mysql.yaml new file mode 100644 index 0000000000..fd90187228 --- /dev/null +++ b/test/integration/testdata/mysql.yaml @@ -0,0 +1,35 @@ +apiVersion: v1 +kind: Service +metadata: + name: mysql +spec: + ports: + - port: 3306 + selector: + app: mysql +--- +apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2 +kind: Deployment +metadata: + name: mysql +spec: + selector: + matchLabels: + app: mysql + strategy: + type: Recreate + template: + metadata: + labels: + app: mysql + spec: + containers: + - image: mysql:5.6 + name: mysql + env: + # Use secret in real usage + - name: MYSQL_ROOT_PASSWORD + value: password + ports: + - containerPort: 3306 + name: mysql diff --git a/test/integration/version_upgrade_test.go b/test/integration/version_upgrade_test.go index 09de47d331..78bc9ef712 100644 --- a/test/integration/version_upgrade_test.go +++ b/test/integration/version_upgrade_test.go @@ -39,9 +39,10 @@ import ( // the odlest supported k8s version and then runs the current head minikube // and it tries to upgrade from the older supported k8s to news supported k8s func TestVersionUpgrade(t *testing.T) { + MaybeParallel(t) + WaitForStartSlot(t) profile := UniqueProfileName("vupgrade") - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) - MaybeSlowParallel(t) + ctx, cancel := context.WithTimeout(context.Background(), 55*time.Minute) defer CleanupWithLogs(t, profile, cancel) @@ -89,6 +90,7 @@ func TestVersionUpgrade(t *testing.T) { t.Errorf("status = %q; want = %q", got, state.Stopped.String()) } + WaitForStartSlot(t) args = append([]string{"start", "-p", profile, fmt.Sprintf("--kubernetes-version=%s", constants.NewestKubernetesVersion), "--alsologtostderr", "-v=1"}, StartArgs()...) rr, err = Run(t, exec.CommandContext(ctx, Target(), args...)) if err != nil { diff --git a/third_party/go9p/clnt_clnt.go b/third_party/go9p/clnt_clnt.go index 5003cc7eda..23af7022f3 100644 --- a/third_party/go9p/clnt_clnt.go +++ b/third_party/go9p/clnt_clnt.go @@ -22,7 +22,7 @@ type Clnt struct { Debuglevel int // =0 don't print anything, >0 print Fcalls, >1 print raw packets Msize uint32 // Maximum size of the 9P messages Dotu bool // If true, 9P2000.u protocol is spoken - Root *Fid // Fid that points to the rood directory + Root *Fid // Fid that points to the root directory Id string // Used when printing debug messages Log *Logger diff --git a/third_party/go9p/ufs_darwin.go b/third_party/go9p/ufs_darwin.go index eea436c5b8..da9a10fae2 100644 --- a/third_party/go9p/ufs_darwin.go +++ b/third_party/go9p/ufs_darwin.go @@ -23,14 +23,12 @@ func atime(stat *syscall.Stat_t) time.Time { func isBlock(d os.FileInfo) bool { stat := d.Sys().(*syscall.Stat_t) return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK - return true } // IsChar reports if the file is a character device func isChar(d os.FileInfo) bool { stat := d.Sys().(*syscall.Stat_t) return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR - return true } func dir2Qid(d os.FileInfo) *Qid { diff --git a/third_party/go9p/unpack.go b/third_party/go9p/unpack.go index 8c19e0bc2d..6871bd7ada 100644 --- a/third_party/go9p/unpack.go +++ b/third_party/go9p/unpack.go @@ -218,7 +218,7 @@ func Unpack(buf []byte, dotu bool) (fc *Fcall, err error, fcsz int) { goto szerror } - return + return //NOSONAR szerror: return nil, &Error{"invalid size", EINVAL}, 0 diff --git a/translations/de.json b/translations/de.json new file mode 100644 index 0000000000..93f34176cf --- /dev/null +++ b/translations/de.json @@ -0,0 +1,519 @@ +{ + "\"{{.minikube_addon}}\" was successfully disabled": "", + "\"{{.name}}\" cluster does not exist. Proceeding ahead with cleanup.": "", + "\"{{.name}}\" profile does not exist": "", + "\"{{.profile_name}}\" VM does not exist, nothing to stop": "", + "\"{{.profile_name}}\" host does not exist, unable to show an IP": "", + "\"{{.profile_name}}\" stopped.": "", + "'none' driver does not support 'minikube docker-env' command": "", + "'none' driver does not support 'minikube mount' command": "", + "'none' driver does not support 'minikube ssh' command": "", + "A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "", + "A firewall is blocking Docker within the minikube VM from reaching the internet. You may need to configure it to use a proxy.": "", + "A firewall is interfering with minikube's ability to make outgoing HTTPS requests. You may need to change the value of the HTTPS_PROXY environment variable.": "", + "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.": "", + "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "Eine Reihe von IP-Adressen des API-Servers, die im generierten Zertifikat für Kubernetes verwendet werden. Damit kann der API-Server von außerhalb des Computers verfügbar gemacht werden.", + "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "Eine Reihe von Namen des API-Servers, die im generierten Zertifikat für Kubernetes verwendet werden. Damit kann der API-Server von außerhalb des Computers verfügbar gemacht werden.", + "A set of key=value pairs that describe configuration that may be passed to different components.\nThe key should be '.' separated, and the first part before the dot is the component to apply the configuration to.\nValid components are: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy, scheduler\nValid kubeadm parameters:": "Eine Reihe von Schlüssel/Wert-Paaren, die eine Konfiguration beschreiben, die an verschiedene Komponenten weitergegeben wird.\nDer Schlüssel sollte durch \".\" getrennt werden. Der erste Teil vor dem Punkt bezeichnet die Komponente, auf die die Konfiguration angewendet wird.\nGültige Komponenten sind: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy, scheduler\nGültige Parameter für kubeadm:", + "A set of key=value pairs that describe feature gates for alpha/experimental features.": "Eine Reihe von Schlüssel/Wert-Paaren, die Funktions-Gates für Alpha- oder experimentelle Funktionen beschreiben.", + "Access the kubernetes dashboard running within the minikube cluster": "", + "Add an image to local cache.": "", + "Add machine IP to NO_PROXY environment variable": "", + "Add or delete an image from the local cache.": "", + "Additional help topics": "", + "Additional mount options, such as cache=fscache": "", + "Advanced Commands:": "", + "Aliases": "", + "Allow user prompts for more information": "", + "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \\\"auto\\\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers": "Alternatives Bild-Repository zum Abrufen von Docker-Images. Dies ist hilfreich, wenn Sie nur eingeschränkten Zugriff auf gcr.io haben. Stellen Sie \\\"auto\\\" ein, dann wählt minikube eins für sie aus. Nutzer vom chinesischen Festland können einen lokalen gcr.io-Mirror wie registry.cn-hangzhou.aliyuncs.com/google_containers verwenden.", + "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "Größe des der minikube-VM zugewiesenen Arbeitsspeichers (Format: \u003cNummer\u003e [\u003cEinheit\u003e], wobei Einheit = b, k, m oder g)", + "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "", + "Amount of time to wait for a service in seconds": "", + "Amount of time to wait for service in seconds": "", + "Available Commands": "", + "Basic Commands:": "", + "Cannot find directory {{.path}} for mount": "", + "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.": "", + "Check that your --kubernetes-version has a leading 'v'. For example: 'v1.1.14'": "", + "Check that your apiserver flags are valid, or run 'minikube delete'": "", + "Check your firewall rules for interference, and run 'virt-host-validate' to check for KVM configuration issues. If you are running minikube within a VM, consider using --vm-driver=none": "", + "Configuration and Management Commands:": "", + "Configure an external network switch following the official documentation, then add `--hyperv-virtual-switch=\u003cswitch-name\u003e` to `minikube start`": "", + "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list": "", + "Configuring local host environment ...": "", + "Confirm that you have a working internet connection and that your VM has not run out of resources by using: 'minikube logs'": "", + "Confirm that you have supplied the correct value to --hyperv-virtual-switch using the 'Get-VMSwitch' command": "", + "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn.": "Ländercode des zu verwendenden Image Mirror. Lassen Sie dieses Feld leer, um den globalen zu verwenden. Nutzer vom chinesischen Festland stellen cn ein.", + "Created a new profile : {{.profile_name}}": "", + "Creating a new profile failed": "", + "Creating mount {{.name}} ...": "Bereitstellung {{.name}} wird erstellt...", + "Creating {{.driver_name}} VM (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "", + "Default group id used for the mount": "", + "Default user id used for the mount": "", + "Delete an image from the local cache.": "", + "Deletes a local kubernetes cluster": "", + "Deletes a local kubernetes cluster. This command deletes the VM, and removes all\nassociated files.": "", + "Deletes a local kubernetes cluster. This command deletes the VM, and removes all associated files.": "Damit wird ein lokaler Kubernetes-Cluster gelöscht. Mit diesem Befehl wird die VM entfernt und alle zugehörigen Dateien gelöscht.", + "Deleting \"{{.profile_name}}\" in {{.driver_name}} ...": "{{.profile_name}}\" in {{.driver_name}} wird gelöscht...", + "Disable checking for the availability of hardware virtualization before the vm is started (virtualbox driver only)": "Deaktivieren Sie die Überprüfung der Verfügbarkeit der Hardwarevirtualisierung vor dem Starten der VM (nur Virtualbox-Treiber)", + "Disable dynamic memory in your VM manager, or pass in a larger --memory value": "", + "Disables the addon w/ADDON_NAME within minikube (example: minikube addons disable dashboard). For a list of available addons use: minikube addons list": "", + "Disables the filesystem mounts provided by the hypervisors": "Deaktiviert die von den Hypervisoren bereitgestellten Dateisystembereitstellungen", + "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "Größe des der minikube-VM zugewiesenen Festplatte (Format: \u003cNummer\u003e [\u003cEinheit\u003e], wobei Einheit = b, k, m oder g)", + "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "", + "Display dashboard URL instead of opening a browser": "", + "Display the kubernetes addons URL in the CLI instead of opening it in the default browser": "", + "Display the kubernetes service URL in the CLI instead of opening it in the default browser": "", + "Display values currently set in the minikube config file": "", + "Display values currently set in the minikube config file.": "", + "Docker inside the VM is unavailable. Try running 'minikube delete' to reset the VM.": "", + "Docs have been saved at - {{.path}}": "", + "Documentation: {{.url}}": "", + "Done! kubectl is now configured to use \"{{.name}}": "Fertig! kubectl ist jetzt für die Verwendung von \"{{.name}} konfiguriert", + "Done! kubectl is now configured to use \"{{.name}}\"": "", + "Done! kubectl is now configured to use \"{{.name}}__1": "Fertig! kubectl ist jetzt für die Verwendung von \"{{.name}}\" konfiguriert", + "Download complete!": "Download abgeschlossen!", + "Downloading VM boot image ...": "", + "Downloading driver {{.driver}}:": "", + "Downloading {{.name}} {{.version}}": "", + "ERROR creating `registry-creds-dpr` secret": "", + "ERROR creating `registry-creds-ecr` secret: {{.error}}": "", + "ERROR creating `registry-creds-gcr` secret: {{.error}}": "", + "Either systemctl is not installed, or Docker is broken. Run 'sudo systemctl start docker' and 'journalctl -u docker'": "", + "Enable experimental NVIDIA GPU support in minikube": "Experimentellen NVIDIA GPU-Support in minikube aktivieren", + "Enable host resolver for NAT DNS requests (virtualbox driver only)": "Host Resolver für NAT DNS-Anfragen aktivieren (nur Virtualbox-Treiber)", + "Enable proxy for NAT DNS requests (virtualbox driver only)": "Proxy für NAT-DNS-Anforderungen aktivieren (nur Virtualbox-Treiber)", + "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\": "Standard-CNI-Plugin-in (/etc/cni/net.d/k8s.conf) aktivieren. Wird in Verbindung mit \"--network-plugin = cni\" verwendet", + "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\\".": "", + "Enables the addon w/ADDON_NAME within minikube (example: minikube addons enable dashboard). For a list of available addons use: minikube addons list": "", + "Enabling dashboard ...": "", + "Environment variables to pass to the Docker daemon. (format: key=value)": "Umgebungsvariablen, die an den Docker-Daemon übergeben werden. (Format: Schlüssel = Wert)", + "Error checking driver version: {{.error}}": "Fehler beim Prüfen der Treiberversion: {{.error}}", + "Error creating list template": "", + "Error creating minikube directory": "", + "Error creating status template": "", + "Error creating view template": "", + "Error executing list template": "", + "Error executing status template": "", + "Error executing template": "", + "Error executing view template": "", + "Error finding port for mount": "", + "Error getting IP": "", + "Error getting bootstrapper": "", + "Error getting client": "", + "Error getting client: {{.error}}": "", + "Error getting cluster": "", + "Error getting cluster bootstrapper": "", + "Error getting config": "", + "Error getting host": "", + "Error getting host status": "", + "Error getting machine logs": "", + "Error getting machine status": "", + "Error getting service status": "", + "Error getting service with namespace: {{.namespace}} and labels {{.labelName}}:{{.addonName}}: {{.error}}": "", + "Error getting the host IP address to use from within the VM": "", + "Error host driver ip status": "", + "Error killing mount process": "", + "Error loading api": "", + "Error loading profile config": "", + "Error loading profile config: {{.error}}": "", + "Error loading profile {{.name}}: {{.error}}": "Fehler beim Laden des Profils {{.name}}: {{.error}}", + "Error opening service": "", + "Error parsing minikube version: {{.error}}": "Fehler beim Parsen der minikube-Version: {{.error}}", + "Error parsing vmDriver version: {{.error}}": "Fehler beim Parsen der vmDriver-Version: {{.error}}", + "Error reading {{.path}}: {{.error}}": "", + "Error restarting cluster": "", + "Error setting shell variables": "", + "Error starting cluster": "", + "Error starting mount": "", + "Error unsetting shell variables": "", + "Error while setting kubectl current context : {{.error}}": "", + "Error writing mount pid": "", + "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}\"": "", + "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}": "Fehler: Sie haben Kubernetes v{{.new}} ausgewählt, aber auf dem vorhandenen Cluster für Ihr Profil wird Kubernetes v{{.old}} ausgeführt. Zerstörungsfreie Downgrades werden nicht unterstützt. Sie können jedoch mit einer der folgenden Optionen fortfahren:\n* Erstellen Sie den Cluster mit Kubernetes v{{.new}} neu: Führen Sie \"minikube delete {{.profile}}\" und dann \"minikube start {{.profile}} - kubernetes-version = {{.new}}\" aus.\n* Erstellen Sie einen zweiten Cluster mit Kubernetes v{{.new}}: Führen Sie \"minikube start -p \u003cnew name\u003e --kubernetes-version = {{.new}}\" aus.\n* Verwenden Sie den vorhandenen Cluster mit Kubernetes v {{.old}} oder höher: Führen Sie \"minikube start {{.profile}} --kubernetes-version = {{.old}}\" aus.", + "Error: [{{.id}}] {{.error}}": "", + "Examples": "", + "Exiting": "Wird beendet", + "Exiting due to driver incompatibility": "", + "Failed runtime": "", + "Failed to cache ISO": "", + "Failed to cache and load images": "", + "Failed to cache binaries": "", + "Failed to cache images": "", + "Failed to change permissions for {{.minikube_dir_path}}: {{.error}}": "Fehler beim Ändern der Berechtigungen für {{.minikube_dir_path}}: {{.error}}", + "Failed to check if machine exists": "", + "Failed to check main repository and mirrors for images for images": "", + "Failed to delete cluster: {{.error}}": "Fehler beim Löschen des Clusters: {{.error}}", + "Failed to delete cluster: {{.error}}__1": "Fehler beim Löschen des Clusters: {{.error}}", + "Failed to delete images": "", + "Failed to delete images from config": "", + "Failed to download kubectl": "", + "Failed to enable container runtime": "", + "Failed to generate config": "", + "Failed to get bootstrapper": "", + "Failed to get command runner": "", + "Failed to get driver URL": "", + "Failed to get image map": "", + "Failed to get machine client": "", + "Failed to get service URL: {{.error}}": "", + "Failed to kill mount process: {{.error}}": "Fehler beim Beenden des Bereitstellungsprozesses: {{.error}}", + "Failed to list cached images": "", + "Failed to remove profile": "", + "Failed to save config": "", + "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}": "NO_PROXY Env konnte nicht festgelegt werden. Benutzen Sie `export NO_PROXY = $ NO_PROXY, {{. Ip}}", + "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}`.": "", + "Failed to setup certs": "", + "Failed to setup kubeconfig": "", + "Failed to update cluster": "", + "Failed to update config": "", + "Failed unmount: {{.error}}": "", + "File permissions used for the mount": "", + "Flags": "", + "Follow": "", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "Für beste Ergebnisse installieren Sie kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/__1": "Für beste Ergebnisse installieren Sie kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/", + "For more information, see:": "Weitere Informationen:", + "Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect": "", + "Force minikube to perform possibly dangerous operations": "minikube zwingen, möglicherweise gefährliche Operationen durchzuführen", + "Found network options:": "Gefundene Netzwerkoptionen:", + "Found {{.number}} invalid profile(s) !": "", + "Gets the kubernetes URL(s) for the specified service in your local cluster": "", + "Gets the kubernetes URL(s) for the specified service in your local cluster. In the case of multiple URLs they will be printed one at a time.": "", + "Gets the logs of the running instance, used for debugging minikube, not user code.": "", + "Gets the status of a local kubernetes cluster": "", + "Gets the status of a local kubernetes cluster.\n\tExit status contains the status of minikube's VM, cluster and kubernetes encoded on it's bits in this order from right to left.\n\tEg: 7 meaning: 1 (for minikube NOK) + 2 (for cluster NOK) + 4 (for kubernetes NOK)": "", + "Gets the value of PROPERTY_NAME from the minikube config file": "", + "Global Flags": "", + "Go template format string for the addon list output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd/config#AddonListTemplate": "", + "Go template format string for the cache list output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#CacheListTemplate": "", + "Go template format string for the config view output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd/config#ConfigViewTemplate": "", + "Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#Status": "", + "Group ID: {{.groupID}}": "", + "Have you set up libvirt correctly?": "", + "Hide the hypervisor signature from the guest in minikube (kvm2 driver only)": "Hypervisor-Signatur vor dem Gast in minikube verbergen (nur kvm2-Treiber)", + "If the above advice does not help, please let us know:": "", + "If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --vm-driver=none.": "Wenn true, speichern Sie Docker-Images für den aktuellen Bootstrapper zwischen und laden Sie sie auf den Computer. Immer falsch mit --vm-driver = none.", + "If true, only download and cache files for later use - don't install or start anything.": "Wenn true, laden Sie nur Dateien für die spätere Verwendung herunter und speichern Sie sie – installieren oder starten Sie nichts.", + "If using the none driver, ensure that systemctl is installed": "", + "If you are running minikube within a VM, consider using --vm-driver=none:": "", + "Images Commands:": "", + "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "", + "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "Unsichere Docker-Registrys, die an den Docker-Daemon übergeben werden. Der CIDR-Bereich des Standarddienstes wird automatisch hinzugefügt.", + "Install VirtualBox, or select an alternative value for --vm-driver": "", + "Install the latest hyperkit binary, and run 'minikube delete'": "", + "Invalid size passed in argument: {{.error}}": "", + "IsEnabled failed": "", + "Kill the mount process spawned by minikube start": "", + "Kubernetes {{.version}} is not supported by this release of minikube": "", + "Launching Kubernetes ...": "Kubernetes wird gestartet...", + "Launching proxy ...": "", + "List all available images from the local cache.": "", + "List of guest VSock ports that should be exposed as sockets on the host (hyperkit driver only)": "Liste der Gast-VSock-Ports, die als Sockets auf dem Host verfügbar gemacht werden (nur Hyperkit-Treiber)", + "Lists all available minikube addons as well as their current statuses (enabled/disabled)": "", + "Lists all minikube profiles.": "", + "Lists all valid minikube profiles and detects all possible invalid profiles.": "", + "Lists the URLs for the services in your local cluster": "", + "Local folders to share with Guest via NFS mounts (hyperkit driver only)": "Lokale Ordner, die über NFS-Bereitstellungen für Gast freigegeben werden (nur Hyperkit-Treiber)", + "Location of the VPNKit socket used for networking. If empty, disables Hyperkit VPNKitSock, if 'auto' uses Docker for Mac VPNKit connection, otherwise uses the specified VSock (hyperkit driver only)": "Speicherort des VPNKit-Sockets, der für das Netzwerk verwendet wird. Wenn leer, wird Hyperkit VPNKitSock deaktiviert. Wenn 'auto' die Docker for Mac VPNKit-Verbindung verwendet, wird andernfalls der angegebene VSock verwendet (nur Hyperkit-Treiber).", + "Location of the minikube iso": "Speicherort der minikube-ISO", + "Location of the minikube iso.": "", + "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'": "", + "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'.": "", + "Message Size: {{.size}}": "", + "Minikube is a CLI tool that provisions and manages single-node Kubernetes clusters optimized for development workflows.": "", + "Minikube is a tool for managing local Kubernetes clusters.": "", + "Modify minikube config": "", + "Modify minikube's kubernetes addons": "", + "Mount type: {{.name}}": "", + "Mounting host path {{.sourcePath}} into VM as {{.destinationPath}} ...": "", + "Mounts the specified directory into minikube": "", + "Mounts the specified directory into minikube.": "", + "NOTE: This process must stay alive for the mount to be accessible ...": "", + "Networking and Connectivity Commands:": "", + "No minikube profile was found. You can create one using `minikube start`.": "", + "None of the known repositories in your location are accessible. Using {{.image_repository_name}} as fallback.": "Keines der bekannten Repositories an Ihrem Standort ist zugänglich. {{.image_repository_name}} wird als Fallback verwendet.", + "None of the known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag": "Keines der bekannten Repositories ist zugänglich. Erwägen Sie, ein alternatives Image-Repository mit der Kennzeichnung --image-repository anzugeben", + "Not passing {{.name}}={{.value}} to docker env.": "", + "Number of CPUs allocated to the minikube VM": "Anzahl der CPUs, die der minikube-VM zugeordnet sind", + "Number of CPUs allocated to the minikube VM.": "", + "Number of lines back to go within the log": "", + "OS release is {{.pretty_name}}": "", + "Open the addons URL with https instead of http": "", + "Open the service URL with https instead of http": "", + "Opening kubernetes service {{.namespace_name}}/{{.service_name}} in default browser...": "", + "Opening {{.url}} in your default browser...": "", + "Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list": "", + "Options: {{.options}}": "", + "Outputs minikube shell completion for the given shell (bash or zsh)": "", + "Outputs minikube shell completion for the given shell (bash or zsh)\n\n\tThis depends on the bash-completion binary. Example installation instructions:\n\tOS X:\n\t\t$ brew install bash-completion\n\t\t$ source $(brew --prefix)/etc/bash_completion\n\t\t$ minikube completion bash \u003e ~/.minikube-completion # for bash users\n\t\t$ minikube completion zsh \u003e ~/.minikube-completion # for zsh users\n\t\t$ source ~/.minikube-completion\n\tUbuntu:\n\t\t$ apt-get install bash-completion\n\t\t$ source /etc/bash-completion\n\t\t$ source \u003c(minikube completion bash) # for bash users\n\t\t$ source \u003c(minikube completion zsh) # for zsh users\n\n\tAdditionally, you may want to output the completion to a file and source in your .bashrc\n\n\tNote for zsh users: [1] zsh completions are only supported in versions of zsh \u003e= 5.2": "", + "Permissions: {{.octalMode}} ({{.writtenMode}})": "", + "Please enter a value:": "", + "Please install the minikube hyperkit VM driver, or select an alternative --vm-driver": "", + "Please install the minikube kvm2 VM driver, or select an alternative --vm-driver": "", + "Please make sure the service you are looking for is deployed or is in the correct namespace.": "", + "Please specify the directory to be mounted: \n\tminikube mount \u003csource directory\u003e:\u003ctarget directory\u003e (example: \"/host-home:/vm-home\")": "", + "Please upgrade the '{{.driver_executable}}'. {{.documentation_url}}": "Aktualisieren Sie '{{.driver_executable}}'. {{.documentation_url}}", + "Populates the specified folder with documentation in markdown about minikube": "", + "Powering off \"{{.profile_name}}\" via SSH ...": "{{.profile_name}}\" wird über SSH ausgeschaltet...", + "Preparing Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}} ...": "Vorbereiten von Kubernetes {{.k8sVersion}} auf {{.runtime}} {{.runtimeVersion}}...", + "Print current and latest version number": "", + "Print the version of minikube": "", + "Print the version of minikube.": "", + "Problems detected in {{.entry}}:": "", + "Problems detected in {{.name}}:": "", + "Profile gets or sets the current minikube profile": "", + "Provide VM UUID to restore MAC address (hyperkit driver only)": "Geben Sie die VM-UUID an, um die MAC-Adresse wiederherzustellen (nur Hyperkit-Treiber)", + "Pulling images ...": "", + "Reboot to complete VirtualBox installation, and verify that VirtualBox is not blocked by your system": "", + "Rebuild libvirt with virt-network support": "", + "Received {{.name}} signal": "", + "Registry mirrors to pass to the Docker daemon": "Registry-Mirror, die an den Docker-Daemon übergeben werden", + "Reinstall VirtualBox and reboot. Alternatively, try the kvm2 driver: https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/": "", + "Reinstall VirtualBox and verify that it is not blocked: System Preferences -\u003e Security \u0026 Privacy -\u003e General -\u003e Some system software was blocked from loading": "", + "Related issues:": "", + "Relaunching Kubernetes using {{.bootstrapper}} ...": "Kubernetes mit {{.bootstrapper}} neu starten...", + "Removing {{.directory}} ...": "{{.directory}} wird entfernt...", + "Requested CPU count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}": "", + "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}": "Die angeforderte Festplattengröße {{.requested_size}} liegt unter dem Mindestwert von {{.minimum_size}}.", + "Requested memory allocation ({{.memory}}MB) is less than the default memory allocation of {{.default_memorysize}}MB. Beware that minikube might not work correctly or crash unexpectedly.": "Die angeforderte Speicherzuordnung ({{.memory}} MB) ist geringer als die Standardspeicherzuordnung von {{.default_memorysize}} MB. Beachten Sie, dass minikube möglicherweise nicht richtig funktioniert oder unerwartet abstürzt.", + "Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}": "Die angeforderte Speicherzuweisung {{.requested_size}} liegt unter dem zulässigen Mindestwert von {{.minimum_size}}.", + "Retriable failure: {{.error}}": "", + "Retrieve the ssh identity key path of the specified cluster": "", + "Retrieve the ssh identity key path of the specified cluster.": "", + "Retrieves the IP address of the running cluster": "", + "Retrieves the IP address of the running cluster, and writes it to STDOUT.": "", + "Retrieves the IP address of the running cluster, checks it\n\t\t\twith IP in kubeconfig, and corrects kubeconfig if incorrect.": "", + "Returns the value of PROPERTY_NAME from the minikube config file. Can be overwritten at runtime by flags or environmental variables.": "", + "Run 'kubectl describe pod coredns -n kube-system' and check for a firewall or DNS conflict": "", + "Run 'minikube delete' to delete the stale VM": "", + "Run kubectl": "", + "Run minikube from the C: drive.": "", + "Run the kubernetes client, download it if necessary.\nExamples:\nminikube kubectl -- --help\nkubectl get pods --namespace kube-system": "", + "Run the minikube command as an Administrator": "", + "Running on localhost (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "", + "Set failed": "", + "Sets an individual value in a minikube config file": "", + "Sets the PROPERTY_NAME config value to PROPERTY_VALUE\n\tThese values can be overwritten by flags or environment variables at runtime.": "", + "Sets up docker env variables; similar to '$(docker-machine env)'": "", + "Sets up docker env variables; similar to '$(docker-machine env)'.": "", + "Setting profile failed": "", + "Show only log entries which point to known problems": "", + "Show only the most recent journal entries, and continuously print new entries as they are appended to the journal.": "", + "Skipped switching kubectl context for {{.profile_name}} because --keep-context was set.": "", + "Sorry that minikube crashed. If this was unexpected, we would love to hear from you:": "", + "Sorry, Kubernetes {{.version}} is not supported by this release of minikube": "", + "Sorry, completion support is not yet implemented for {{.name}}": "", + "Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config": "Leider wird der Parameter kubeadm.{{.parameter_name}} momentan von --extra-config nicht unterstützt.", + "Sorry, the url provided with the --registry-mirror flag is invalid: {{.url}}": "Die angegebene URL mit dem Flag --registry-mirror ist ungültig: {{.url}}.", + "Specified Kubernetes version {{.specified}} is less than the oldest supported version: {{.oldest}}": "", + "Specify --kubernetes-version in v\u003cmajor\u003e.\u003cminor.\u003cbuild\u003e form. example: 'v1.1.14'": "", + "Specify an alternate --host-only-cidr value, such as 172.16.0.1/24": "", + "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)": "Spezifizieren Sie arbiträre Flags, die an den Docker-Daemon übergeben werden. (Format: Schlüssel = Wert)", + "Specify the 9p version that the mount should use": "", + "Specify the ip that the mount should be setup on": "", + "Specify the mount filesystem type (supported types: 9p)": "", + "Starting existing {{.driver_name}} VM for \"{{.profile_name}}\" ...": "", + "Starts a local kubernetes cluster": "Startet einen lokalen Kubernetes-Cluster", + "Stopping \"{{.profile_name}}\" in {{.driver_name}} ...": "", + "Stops a local kubernetes cluster running in Virtualbox. This command stops the VM\nitself, leaving all files intact. The cluster can be started again with the \"start\" command.": "", + "Stops a running local kubernetes cluster": "", + "Successfully mounted {{.sourcePath}} to {{.destinationPath}}": "", + "Suggestion: {{.advice}}": "", + "Target directory {{.path}} must be an absolute path": "", + "The \"{{.driver_name}}\" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}": "Der Treiber \"{{.driver_name}}\" benötigt Root-Rechte. Führen Sie minikube aus mit 'sudo minikube --vm-driver = {{. Driver_name}}.", + "The \"{{.driver_name}}\" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}'.": "", + "The \"{{.driver_name}}\" driver should not be used with root privileges.": "", + "The \"{{.name}}\" cluster has been deleted.": "Der Cluster \"{{.name}}\" wurde gelöscht.", + "The \"{{.name}}\" cluster has been deleted.__1": "Der Cluster \"{{.name}}\" wurde gelöscht.", + "The 'none' driver does not support multiple profiles: https://minikube.sigs.k8s.io/docs/reference/drivers/none/": "", + "The 'none' driver provides limited isolation and may reduce system security and reliability.": "Der Treiber \"Keine\" bietet eine eingeschränkte Isolation und beeinträchtigt möglicherweise Sicherheit und Zuverlässigkeit des Systems.", + "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\\n\\n{{ .example }}\\n": "", + "The CIDR to be used for service cluster IPs.": "Die CIDR, die für Service-Cluster-IPs verwendet werden soll.", + "The CIDR to be used for the minikube VM (virtualbox driver only)": "Die CIDR, die für die minikube-VM verwendet werden soll (nur Virtualbox-Treiber)", + "The KVM QEMU connection URI. (kvm2 driver only)": "Der KVM-QEMU-Verbindungs-URI. (Nur kvm2-Treiber)", + "The KVM driver is unable to resurrect this old VM. Please run `minikube delete` to delete it and try again.": "", + "The KVM network name. (kvm2 driver only)": "Der KVM-Netzwerkname. (Nur kvm2-Treiber)", + "The VM driver crashed. Run 'minikube start --alsologtostderr -v=8' to see the VM driver error message": "", + "The VM driver exited with an error, and may be corrupt. Run 'minikube start' with --alsologtostderr -v=8 to see the error": "", + "The VM that minikube is configured for no longer exists. Run 'minikube delete'": "", + "The apiserver listening port": "Der Überwachungsport des API-Servers", + "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "Der API-Servername, der im generierten Zertifikat für Kubernetes verwendet wird. Damit kann der API-Server von außerhalb des Computers verfügbar gemacht werden.", + "The argument to pass the minikube mount command on start": "Das Argument, um den Bereitstellungsbefehl für minikube beim Start zu übergeben", + "The argument to pass the minikube mount command on start.": "", + "The cluster dns domain name used in the kubernetes cluster": "Der DNS-Domänenname des Clusters, der im Kubernetes-Cluster verwendet wird", + "The container runtime to be used (docker, crio, containerd)": "Die zu verwendende Container-Laufzeit (Docker, Crio, Containerd)", + "The container runtime to be used (docker, crio, containerd).": "", + "The cri socket path to be used": "Der zu verwendende Cri-Socket-Pfad", + "The cri socket path to be used.": "", + "The docker host is currently not running": "", + "The docker service is currently not active": "", + "The driver '{{.driver}}' is not supported on {{.os}}": "Der Treiber '{{.driver}}' wird auf {{.os}} nicht unterstützt", + "The existing \"{{.profile_name}}\" VM that was created using the \"{{.old_driver}}\" driver, and is incompatible with the \"{{.driver}}\" driver.": "", + "The hyperv virtual switch name. Defaults to first found. (hyperv driver only)": "Der Name des virtuellen Hyperv-Switch. Standardmäßig zuerst gefunden. (nur Hyperv-Treiber)", + "The initial time interval for each check that wait performs in seconds": "", + "The kubernetes version that the minikube VM will use (ex: v1.2.3)": "Die von der minikube-VM verwendete Kubernetes-Version (Beispiel: v1.2.3)", + "The minikube VM is offline. Please run 'minikube start' to start it again.": "", + "The name of the network plugin": "Der Name des Netzwerk-Plugins", + "The name of the network plugin.": "", + "The number of bytes to use for 9p packet payload": "", + "The path on the file system where the docs in markdown need to be saved": "", + "The service namespace": "", + "The services namespace": "", + "The time interval for each check that wait performs in seconds": "", + "The value passed to --format is invalid": "", + "The value passed to --format is invalid: {{.error}}": "", + "The vmwarefusion driver is deprecated and support for it will be removed in a future release.\n\t\t\tPlease consider switching to the new vmware unified driver, which is intended to replace the vmwarefusion driver.\n\t\t\tSee https://minikube.sigs.k8s.io/docs/reference/drivers/vmware/ for more information.\n\t\t\tTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "", + "The {{.driver_name}} driver should not be used with root privileges.": "Der Treiber {{.driver_name}} sollte nicht mit Root-Rechten verwendet werden.", + "There appears to be another hypervisor conflicting with KVM. Please stop the other hypervisor, or use --vm-driver to switch to it.": "", + "There's a new version for '{{.driver_executable}}'. Please consider upgrading. {{.documentation_url}}": "Es gibt eine neue Version für '{{.driver_executable}}'. Bitte erwägen Sie ein Upgrade. {{.documentation_url}}", + "These changes will take effect upon a minikube delete and then a minikube start": "", + "This addon does not have an endpoint defined for the 'addons open' command.\nYou can add one by annotating a service with the label {{.labelName}}:{{.addonName}}": "", + "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true": "Dies kann auch automatisch erfolgen, indem Sie die env var CHANGE_MINIKUBE_NONE_USER = true setzen", + "This will keep the existing kubectl context and will create a minikube context.": "Dadurch wird der vorhandene Kubectl-Kontext beibehalten und ein minikube-Kontext erstellt.", + "This will start the mount daemon and automatically mount files into minikube": "Dadurch wird der Mount-Daemon gestartet und die Dateien werden automatisch in minikube geladen", + "This will start the mount daemon and automatically mount files into minikube.": "", + "Tip: To remove this root owned cluster, run: sudo {{.cmd}} delete": "Tipp: Um diesen Root-Cluster zu entfernen, führen Sie Folgendes aus: sudo {{.cmd}} delete", + "Tip: Use 'minikube start -p \u003cname\u003e' to create a new cluster, or 'minikube delete' to delete this one.": "", + "To connect to this cluster, use: kubectl --context={{.name}}": "Verwenden Sie zum Herstellen einer Verbindung zu diesem Cluster: kubectl --context = {{.name}}", + "To connect to this cluster, use: kubectl --context={{.name}}__1": "Verwenden Sie zum Herstellen einer Verbindung zu diesem Cluster: kubectl --context = {{.name}}", + "To connect to this cluster, use: kubectl --context={{.profile_name}}": "", + "To disable this notice, run: 'minikube config set WantUpdateNotification false'\\n": "", + "To proceed, either:\n 1) Delete the existing VM using: '{{.command}} delete'\n or\n 2) Restart with the existing driver: '{{.command}} start --vm-driver={{.old_driver}}'": "", + "To start minikube with HyperV Powershell must be in your PATH`": "", + "To use kubectl or minikube commands as your own user, you may need to relocate them. For example, to overwrite your own settings, run:": "Möglicherweise müssen Sie Kubectl- oder minikube-Befehle verschieben, um sie als eigenen Nutzer zu verwenden. Um beispielsweise Ihre eigenen Einstellungen zu überschreiben, führen Sie aus:", + "Troubleshooting Commands:": "", + "Unable to bind flags": "", + "Unable to enable dashboard": "", + "Unable to fetch latest version info": "", + "Unable to generate docs": "", + "Unable to generate the documentation. Please ensure that the path specified is a directory, exists \u0026 you have permission to write to it.": "", + "Unable to get VM IP address": "", + "Unable to get bootstrapper: {{.error}}": "Bootstrapper kann nicht abgerufen werden: {{.error}}", + "Unable to get current user": "", + "Unable to get runtime": "", + "Unable to get the status of the cluster.": "", + "Unable to kill mount process: {{.error}}": "", + "Unable to load cached images from config file.": "Zwischengespeicherte Bilder können nicht aus der Konfigurationsdatei geladen werden.", + "Unable to load cached images: {{.error}}": "", + "Unable to load config: {{.error}}": "Konfig kann nicht geladen werden: {{.error}}", + "Unable to parse \"{{.kubernetes_version}}\": {{.error}}": "\"{{.Kubernetes_version}}\" kann nicht geparst werden: {{.error}}", + "Unable to parse oldest Kubernetes version from constants: {{.error}}": "", + "Unable to pull images, which may be OK: {{.error}}": "Bilder können nicht abgerufen werden, was möglicherweise kein Problem darstellt: {{.error}}", + "Unable to remove machine directory: %v": "", + "Unable to start VM": "", + "Unable to stop VM": "", + "Unable to update {{.driver}} driver: {{.error}}": "", + "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...": "Kubernetes {{.kubernetes_version}} wird mit {{.bootstrapper_name}} deinstalliert...", + "Unmounting {{.path}} ...": "", + "Unset the KUBECONFIG environment variable, or verify that it does not point to an empty or otherwise invalid path": "", + "Unset variables instead of setting them": "", + "Update server returned an empty list": "", + "Upgrade to QEMU v3.1.0+, run 'virt-host-validate', or ensure that you are not running in a nested VM environment.": "", + "Upgrading from Kubernetes {{.old}} to {{.new}}": "Upgrade von Kubernetes {{.old}} auf {{.new}}", + "Usage": "", + "Usage: minikube completion SHELL": "", + "Usage: minikube delete": "", + "Use \"{{.CommandPath}} [command] --help\" for more information about a command.": "", + "Use VirtualBox to remove the conflicting VM and/or network interfaces": "", + "Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'.": "", + "User ID: {{.userID}}": "", + "Userspace file server is shutdown": "", + "Userspace file server:": "", + "Using image repository {{.name}}": "Verwenden des Image-Repositorys {{.name}}", + "Using the running {{.driver_name}} \"{{.profile_name}}\" VM ...": "", + "VM driver is one of: %v": "VM-Treiber ist einer von: %v", + "Verify that your HTTP_PROXY and HTTPS_PROXY environment variables are set correctly.": "", + "Verify the IP address of the running cluster in kubeconfig.": "", + "Verifying dashboard health ...": "", + "Verifying proxy health ...": "", + "Version: {{.version}}": "", + "VirtualBox and Hyper-V are having a conflict. Use '--vm-driver=hyperv' or disable Hyper-V using: 'bcdedit /set hypervisorlaunchtype off'": "", + "VirtualBox cannot create a network, probably because it conflicts with an existing network that minikube no longer knows about. Try running 'minikube delete'": "", + "VirtualBox is broken. Disable real-time anti-virus software, reboot, and reinstall VirtualBox if the problem continues.": "", + "VirtualBox is broken. Reinstall VirtualBox, reboot, and run 'minikube delete'.": "", + "Wait failed": "", + "Wait failed: {{.error}}": "", + "Wait until Kubernetes core services are healthy before exiting": "Warten Sie vor dem Beenden, bis die Kerndienste von Kubernetes fehlerfrei arbeiten", + "Wait until Kubernetes core services are healthy before exiting.": "", + "Waiting for the host to be provisioned ...": "", + "Waiting for:": "", + "Where to root the NFS Shares, defaults to /nfsshares (hyperkit driver only)": "Als Root für die NFS-Freigaben wird standardmäßig /nfsshares verwendet (nur Hyperkit-Treiber)", + "You appear to be using a proxy, but your NO_PROXY environment does not include the minikube IP ({{.ip_address}}). Please see {{.documentation_url}} for more details": "Sie scheinen einen Proxy zu verwenden, aber Ihre NO_PROXY-Umgebung enthält keine minikube-IP ({{.ip_address}}). Weitere Informationen finden Sie unter {{.documentation_url}}", + "You can delete them using the following command(s):": "", + "You may need to manually remove the \"{{.name}}\" VM from your hypervisor": "Möglicherweise müssen Sie die VM \"{{.name}}\" manuell von Ihrem Hypervisor entfernen", + "You must specify a service name": "", + "Your host does not support KVM virtualization. Ensure that qemu-kvm is installed, and run 'virt-host-validate' to debug the problem": "", + "Your host does not support virtualization. If you are running minikube within a VM, try '--vm-driver=none'. Otherwise, enable virtualization in your BIOS": "", + "Your host is failing to route packets to the minikube VM. If you have VPN software, try turning it off or configuring it so that it does not re-route traffic to the VM IP. If not, check your VM environment routing options.": "", + "Your minikube config refers to an unsupported driver. Erase ~/.minikube, and try again.": "", + "Your minikube vm is not running, try minikube start.": "", + "addon '{{.name}}' is currently not enabled.\nTo enable this addon run:\nminikube addons enable {{.name}}": "", + "addon '{{.name}}' is not a valid addon packaged with minikube.\nTo see the list of available addons run:\nminikube addons list": "", + "addon list failed": "", + "addons modifies minikube addons files using subcommands like \"minikube addons enable heapster\"": "", + "api load": "", + "bash completion failed": "", + "browser failed to open url: {{.error}}": "", + "call with cleanup=true to remove old tunnels": "", + "command runner": "", + "config modifies minikube config files using subcommands like \"minikube config set vm-driver kvm\"\nConfigurable fields:\\n\\n": "", + "config view failed": "", + "dashboard service is not running: {{.error}}": "", + "disable failed": "", + "enable failed": "", + "error creating clientset": "", + "error creating machine client": "", + "error getting driver": "", + "error parsing the input ip address for mount": "", + "error starting tunnel": "", + "failed to open browser: {{.error}}": "", + "if true, will embed the certs in kubeconfig.": "", + "kubectl and minikube configuration will be stored in {{.home_folder}}": "Konfiguration von Kubectl und minikube wird in {{.home_folder}} gespeichert", + "kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "", + "kubectl proxy": "", + "logdir set failed": "", + "max time to wait per Kubernetes core services to be healthy.": "", + "minikube is not running, so the service cannot be accessed": "", + "minikube is unable to access the Google Container Registry. You may need to configure it to use a HTTP proxy.": "", + "minikube profile was successfully set to {{.profile_name}}": "", + "minikube {{.version}} is available! Download it: {{.url}}": "", + "mount argument \"{{.value}}\" must be in form: \u003csource directory\u003e:\u003ctarget directory\u003e": "", + "mount failed": "", + "not enough arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", + "profile sets the current minikube profile, or gets the current profile if no arguments are provided. This is used to run and manage multiple minikube instance. You can return to the default minikube profile by running `minikube profile default`": "", + "service {{.namespace_name}}/{{.service_name}} has no node port": "", + "stat failed": "", + "toom any arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", + "tunnel creates a route to services deployed with type LoadBalancer and sets their Ingress to their ClusterIP": "", + "tunnel makes services of type LoadBalancer accessible on localhost": "", + "unable to bind flags": "", + "unable to set logtostderr": "", + "unset failed": "", + "unset minikube profile": "", + "unsets PROPERTY_NAME from the minikube config file. Can be overwritten by flags or environmental variables": "", + "unsets an individual value in a minikube config file": "", + "unsupported driver: {{.name}}": "", + "update config": "", + "usage: minikube addons configure ADDON_NAME": "", + "usage: minikube addons disable ADDON_NAME": "", + "usage: minikube addons enable ADDON_NAME": "", + "usage: minikube addons list": "", + "usage: minikube addons open ADDON_NAME": "", + "usage: minikube config unset PROPERTY_NAME": "", + "usage: minikube profile [MINIKUBE_PROFILE_NAME]": "", + "zsh completion failed": "", + "{{.addonName}} was successfully enabled": "", + "{{.extra_option_component_name}}.{{.key}}={{.value}}": "", + "{{.machine}} IP has been updated to point at {{.ip}}": "", + "{{.machine}} IP was already correctly configured for {{.ip}}": "", + "{{.name}} cluster does not exist": "", + "{{.name}} has no available configuration options": "", + "{{.name}} was successfully configured": "", + "{{.name}}\" profile does not exist": "Profil \"{{.name}}\" existiert nicht", + "{{.prefix}}minikube {{.version}} on {{.platform}}": "{{.prefix}}minikube {{.version}} auf {{.platform}}", + "{{.type}} is not yet a supported filesystem. We will try anyways!": "", + "{{.url}} is not accessible: {{.error}}": "" +} \ No newline at end of file diff --git a/translations/es.json b/translations/es.json new file mode 100644 index 0000000000..e1c17cc7cb --- /dev/null +++ b/translations/es.json @@ -0,0 +1,518 @@ +{ + "\"{{.minikube_addon}}\" was successfully disabled": "", + "\"{{.name}}\" cluster does not exist. Proceeding ahead with cleanup.": "", + "\"{{.name}}\" profile does not exist": "El perfil \"{{.name}}\" no existe", + "\"{{.profile_name}}\" VM does not exist, nothing to stop": "", + "\"{{.profile_name}}\" host does not exist, unable to show an IP": "", + "\"{{.profile_name}}\" stopped.": "", + "'none' driver does not support 'minikube docker-env' command": "", + "'none' driver does not support 'minikube mount' command": "", + "'none' driver does not support 'minikube ssh' command": "", + "A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "", + "A firewall is blocking Docker within the minikube VM from reaching the internet. You may need to configure it to use a proxy.": "", + "A firewall is interfering with minikube's ability to make outgoing HTTPS requests. You may need to change the value of the HTTPS_PROXY environment variable.": "", + "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.": "", + "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "Un conjunto de direcciones IP de apiserver que se usan en el certificado de Kubernetes generado. Se pueden utilizar para que sea posible acceder al apiserver desde fuera de la máquina", + "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "Un conjunto de nombres de apiserver que se usan en el certificado de Kubernetes generado. Se pueden utilizar para que sea posible acceder al apiserver desde fuera de la máquina", + "A set of key=value pairs that describe configuration that may be passed to different components.\nThe key should be '.' separated, and the first part before the dot is the component to apply the configuration to.\nValid components are: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy, scheduler\nValid kubeadm parameters:": "Un conjunto de pares clave=valor que describen la configuración que se puede transferir a diferentes componentes.\nLa clave debe estar separada por un \".\", y la primera parte antes del punto es el componente al que se quiere aplicar la configuración.\nEstos son los componentes válidos: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy y scheduler\nEstos son los parámetros de kubeadm válidos:", + "A set of key=value pairs that describe feature gates for alpha/experimental features.": "Un conjunto de pares clave=valor que indican si las funciones experimentales o en versión alfa deben estar o no habilitadas.", + "Access the kubernetes dashboard running within the minikube cluster": "", + "Add an image to local cache.": "", + "Add machine IP to NO_PROXY environment variable": "", + "Add or delete an image from the local cache.": "", + "Additional help topics": "", + "Additional mount options, such as cache=fscache": "", + "Advanced Commands:": "", + "Aliases": "", + "Allow user prompts for more information": "", + "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \\\"auto\\\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers": "Repositorio de imágenes alternativo del que extraer imágenes de Docker. Puedes usarlo cuando tengas acceso limitado a gcr.io. Si quieres que minikube elija uno por ti, solo tienes que definir el valor como \"auto\". Los usuarios de China continental pueden utilizar réplicas locales de gcr.io, como registry.cn-hangzhou.aliyuncs.com/google_containers", + "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "Cantidad de RAM asignada a la VM de minikube (formato: \u003cnúmero\u003e[\u003cunidad\u003e], donde unidad = b, k, m o g)", + "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "", + "Amount of time to wait for a service in seconds": "", + "Amount of time to wait for service in seconds": "", + "Available Commands": "", + "Basic Commands:": "", + "Cannot find directory {{.path}} for mount": "", + "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.": "", + "Check that your --kubernetes-version has a leading 'v'. For example: 'v1.1.14'": "", + "Check that your apiserver flags are valid, or run 'minikube delete'": "", + "Check your firewall rules for interference, and run 'virt-host-validate' to check for KVM configuration issues. If you are running minikube within a VM, consider using --vm-driver=none": "", + "Configuration and Management Commands:": "", + "Configure an external network switch following the official documentation, then add `--hyperv-virtual-switch=\u003cswitch-name\u003e` to `minikube start`": "", + "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list": "", + "Configuring local host environment ...": "", + "Confirm that you have a working internet connection and that your VM has not run out of resources by using: 'minikube logs'": "", + "Confirm that you have supplied the correct value to --hyperv-virtual-switch using the 'Get-VMSwitch' command": "", + "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn.": "Código de país de la réplica de imagen que quieras utilizar. Déjalo en blanco para usar el valor global. Los usuarios de China continental deben definirlo como cn.", + "Created a new profile : {{.profile_name}}": "", + "Creating a new profile failed": "", + "Creating mount {{.name}} ...": "Creando la activación {{.name}}...", + "Creating {{.driver_name}} VM (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "", + "Default group id used for the mount": "", + "Default user id used for the mount": "", + "Delete an image from the local cache.": "", + "Deletes a local kubernetes cluster": "", + "Deletes a local kubernetes cluster. This command deletes the VM, and removes all\nassociated files.": "", + "Deletes a local kubernetes cluster. This command deletes the VM, and removes all associated files.": "Elimina un clúster local de Kubernetes. Este comando borra la VM y todos los archivos asociados.", + "Deleting \"{{.profile_name}}\" in {{.driver_name}} ...": "Eliminando \"{{.profile_name}}\" en {{.driver_name}}...", + "Disable checking for the availability of hardware virtualization before the vm is started (virtualbox driver only)": "Permite inhabilitar la comprobación de disponibilidad de la virtualización de hardware antes de iniciar la VM (solo con el controlador de Virtualbox)", + "Disable dynamic memory in your VM manager, or pass in a larger --memory value": "", + "Disables the addon w/ADDON_NAME within minikube (example: minikube addons disable dashboard). For a list of available addons use: minikube addons list": "", + "Disables the filesystem mounts provided by the hypervisors": "Inhabilita las activaciones de sistemas de archivos proporcionadas por los hipervisores", + "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "Tamaño de disco asignado a la VM de minikube (formato: \u003cnúmero\u003e[\u003cunidad\u003e], donde unidad = b, k, m o g)", + "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "", + "Display dashboard URL instead of opening a browser": "", + "Display the kubernetes addons URL in the CLI instead of opening it in the default browser": "", + "Display the kubernetes service URL in the CLI instead of opening it in the default browser": "", + "Display values currently set in the minikube config file": "", + "Display values currently set in the minikube config file.": "", + "Docker inside the VM is unavailable. Try running 'minikube delete' to reset the VM.": "", + "Docs have been saved at - {{.path}}": "", + "Documentation: {{.url}}": "", + "Done! kubectl is now configured to use \"{{.name}}": "¡Listo! Se ha configurado kubectl para que use \"{{.name}}", + "Done! kubectl is now configured to use \"{{.name}}\"": "", + "Done! kubectl is now configured to use \"{{.name}}__1": "¡Listo! Se ha configurado kubectl para que use \"{{.name}}", + "Download complete!": "Se ha completado la descarga", + "Downloading VM boot image ...": "", + "Downloading driver {{.driver}}:": "", + "Downloading {{.name}} {{.version}}": "", + "ERROR creating `registry-creds-dpr` secret": "", + "ERROR creating `registry-creds-ecr` secret: {{.error}}": "", + "ERROR creating `registry-creds-gcr` secret: {{.error}}": "", + "Either systemctl is not installed, or Docker is broken. Run 'sudo systemctl start docker' and 'journalctl -u docker'": "", + "Enable experimental NVIDIA GPU support in minikube": "Permite habilitar la compatibilidad experimental con GPUs NVIDIA en minikube", + "Enable host resolver for NAT DNS requests (virtualbox driver only)": "Permite habilitar la resolución del host en las solicitudes DNS con traducción de direcciones de red (NAT) aplicada (solo con el controlador de Virtualbox)", + "Enable proxy for NAT DNS requests (virtualbox driver only)": "Permite habilitar el uso de proxies en las solicitudes de DNS con traducción de direcciones de red (NAT) aplicada (solo con el controlador de Virtualbox)", + "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\": "Permite habilitar el complemento CNI predeterminado (/etc/cni/net.d/k8s.conf). Se utiliza junto con \"--network-plugin=cni", + "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\\".": "", + "Enables the addon w/ADDON_NAME within minikube (example: minikube addons enable dashboard). For a list of available addons use: minikube addons list": "", + "Enabling dashboard ...": "", + "Environment variables to pass to the Docker daemon. (format: key=value)": "Variables de entorno que se transferirán al daemon de Docker. Formato: clave=valor", + "Error checking driver version: {{.error}}": "No se ha podido comprobar la versión del controlador: {{.error}}", + "Error creating list template": "", + "Error creating minikube directory": "", + "Error creating status template": "", + "Error creating view template": "", + "Error executing list template": "", + "Error executing status template": "", + "Error executing template": "", + "Error executing view template": "", + "Error finding port for mount": "", + "Error getting IP": "", + "Error getting bootstrapper": "", + "Error getting client": "", + "Error getting client: {{.error}}": "", + "Error getting cluster": "", + "Error getting cluster bootstrapper": "", + "Error getting config": "", + "Error getting host": "", + "Error getting host status": "", + "Error getting machine logs": "", + "Error getting machine status": "", + "Error getting service status": "", + "Error getting service with namespace: {{.namespace}} and labels {{.labelName}}:{{.addonName}}: {{.error}}": "", + "Error getting the host IP address to use from within the VM": "", + "Error host driver ip status": "", + "Error killing mount process": "", + "Error loading api": "", + "Error loading profile config": "", + "Error loading profile config: {{.error}}": "", + "Error loading profile {{.name}}: {{.error}}": "No se ha podido cargar el perfil {{.name}}: {{.error}}", + "Error opening service": "", + "Error parsing minikube version: {{.error}}": "No se ha podido analizar la versión de minikube: {{.error}}", + "Error parsing vmDriver version: {{.error}}": "No se ha podido analizar la versión de vmDriver: {{.error}}", + "Error reading {{.path}}: {{.error}}": "", + "Error restarting cluster": "", + "Error setting shell variables": "", + "Error starting cluster": "", + "Error starting mount": "", + "Error unsetting shell variables": "", + "Error while setting kubectl current context : {{.error}}": "", + "Error writing mount pid": "", + "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}\"": "", + "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}": "Error: Has seleccionado Kubernetes {{.new}}, pero el clúster de tu perfil utiliza la versión {{.old}}. No se puede cambiar a una versión inferior sin eliminar todos los datos y recursos pertinentes, pero dispones de las siguientes opciones para continuar con la operación:\n* Volver a crear el clúster con Kubernetes {{.new}}: ejecuta \"minikube delete {{.profile}}\" y, luego, \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Crear un segundo clúster con Kubernetes {{.new}}: ejecuta \"minikube start -p \u003cnuevo nombre\u003e --kubernetes-version={{.new}}\"\n* Reutilizar el clúster actual con Kubernetes {{.old}} o una versión posterior: ejecuta \"minikube start {{.profile}} --kubernetes-version={{.old}}", + "Error: [{{.id}}] {{.error}}": "", + "Examples": "", + "Exiting": "Saliendo", + "Exiting due to driver incompatibility": "", + "Failed runtime": "", + "Failed to cache ISO": "", + "Failed to cache and load images": "", + "Failed to cache binaries": "", + "Failed to cache images": "", + "Failed to change permissions for {{.minikube_dir_path}}: {{.error}}": "No se han podido cambiar los permisos de {{.minikube_dir_path}}: {{.error}}", + "Failed to check if machine exists": "", + "Failed to check main repository and mirrors for images for images": "", + "Failed to delete cluster: {{.error}}": "No se ha podido eliminar el clúster: {{.error}}", + "Failed to delete cluster: {{.error}}__1": "No se ha podido eliminar el clúster: {{.error}}", + "Failed to delete images": "", + "Failed to delete images from config": "", + "Failed to download kubectl": "", + "Failed to enable container runtime": "", + "Failed to generate config": "", + "Failed to get bootstrapper": "", + "Failed to get command runner": "", + "Failed to get driver URL": "", + "Failed to get image map": "", + "Failed to get machine client": "", + "Failed to get service URL: {{.error}}": "", + "Failed to kill mount process: {{.error}}": "No se ha podido detener el proceso de activación: {{.error}}", + "Failed to list cached images": "", + "Failed to remove profile": "", + "Failed to save config": "", + "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}": "No se ha podido definir la variable de entorno NO_PROXY. Utiliza export NO_PROXY=$NO_PROXY,{{.ip}}", + "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}`.": "", + "Failed to setup certs": "", + "Failed to setup kubeconfig": "", + "Failed to update cluster": "", + "Failed to update config": "", + "Failed unmount: {{.error}}": "", + "File permissions used for the mount": "", + "Flags": "", + "Follow": "", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "Para disfrutar de un funcionamiento óptimo, instala kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/__1": "Para disfrutar de un funcionamiento óptimo, instala kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/", + "For more information, see:": "Para obtener más información, consulta lo siguiente:", + "Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect": "", + "Force minikube to perform possibly dangerous operations": "Permite forzar minikube para que realice operaciones potencialmente peligrosas", + "Found network options:": "Se han encontrado las siguientes opciones de red:", + "Found {{.number}} invalid profile(s) !": "", + "Gets the kubernetes URL(s) for the specified service in your local cluster": "", + "Gets the kubernetes URL(s) for the specified service in your local cluster. In the case of multiple URLs they will be printed one at a time.": "", + "Gets the logs of the running instance, used for debugging minikube, not user code.": "", + "Gets the status of a local kubernetes cluster": "", + "Gets the status of a local kubernetes cluster.\n\tExit status contains the status of minikube's VM, cluster and kubernetes encoded on it's bits in this order from right to left.\n\tEg: 7 meaning: 1 (for minikube NOK) + 2 (for cluster NOK) + 4 (for kubernetes NOK)": "", + "Gets the value of PROPERTY_NAME from the minikube config file": "", + "Global Flags": "", + "Go template format string for the addon list output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd/config#AddonListTemplate": "", + "Go template format string for the cache list output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#CacheListTemplate": "", + "Go template format string for the config view output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd/config#ConfigViewTemplate": "", + "Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#Status": "", + "Group ID: {{.groupID}}": "", + "Have you set up libvirt correctly?": "", + "Hide the hypervisor signature from the guest in minikube (kvm2 driver only)": "Permite ocultar la firma del hipervisor al invitado en minikube (solo con el controlador de kvm2)", + "If the above advice does not help, please let us know:": "", + "If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --vm-driver=none.": "Si el valor es \"true\", las imágenes de Docker del programa previo actual se almacenan en caché y se cargan en la máquina. Siempre es \"false\" si se especifica --vm-driver=none.", + "If true, only download and cache files for later use - don't install or start anything.": "Si el valor es \"true\", los archivos solo se descargan y almacenan en caché (no se instala ni inicia nada).", + "If using the none driver, ensure that systemctl is installed": "", + "If you are running minikube within a VM, consider using --vm-driver=none:": "", + "Images Commands:": "", + "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "", + "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "Registros de Docker que no son seguros y que se transferirán al daemon de Docker. Se añadirá automáticamente el intervalo CIDR de servicio predeterminado.", + "Install VirtualBox, or select an alternative value for --vm-driver": "", + "Install the latest hyperkit binary, and run 'minikube delete'": "", + "Invalid size passed in argument: {{.error}}": "", + "IsEnabled failed": "", + "Kill the mount process spawned by minikube start": "", + "Kubernetes {{.version}} is not supported by this release of minikube": "", + "Launching Kubernetes ...": "Iniciando Kubernetes...", + "Launching proxy ...": "", + "List all available images from the local cache.": "", + "List of guest VSock ports that should be exposed as sockets on the host (hyperkit driver only)": "Lista de puertos del VSock invitado que se deben mostrar como sockets en el host (solo con el controlador de hyperkit)", + "Lists all available minikube addons as well as their current statuses (enabled/disabled)": "", + "Lists all minikube profiles.": "", + "Lists all valid minikube profiles and detects all possible invalid profiles.": "", + "Lists the URLs for the services in your local cluster": "", + "Local folders to share with Guest via NFS mounts (hyperkit driver only)": "Carpetas locales que se compartirán con el invitado mediante activaciones de NFS (solo con el controlador de hyperkit)", + "Location of the VPNKit socket used for networking. If empty, disables Hyperkit VPNKitSock, if 'auto' uses Docker for Mac VPNKit connection, otherwise uses the specified VSock (hyperkit driver only)": "Ubicación del socket de VPNKit que se utiliza para ofrecer funciones de red. Si se deja en blanco, se inhabilita VPNKitSock de Hyperkit; si se define como \"auto\", se utiliza Docker para las conexiones de VPNKit en Mac. Con cualquier otro valor, se utiliza el VSock especificado (solo con el controlador de hyperkit)", + "Location of the minikube iso": "Ubicación de la ISO de minikube", + "Location of the minikube iso.": "", + "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'": "", + "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'.": "", + "Message Size: {{.size}}": "", + "Minikube is a CLI tool that provisions and manages single-node Kubernetes clusters optimized for development workflows.": "", + "Minikube is a tool for managing local Kubernetes clusters.": "", + "Modify minikube config": "", + "Modify minikube's kubernetes addons": "", + "Mount type: {{.name}}": "", + "Mounting host path {{.sourcePath}} into VM as {{.destinationPath}} ...": "", + "Mounts the specified directory into minikube": "", + "Mounts the specified directory into minikube.": "", + "NOTE: This process must stay alive for the mount to be accessible ...": "", + "Networking and Connectivity Commands:": "", + "No minikube profile was found. You can create one using `minikube start`.": "", + "None of the known repositories in your location are accessible. Using {{.image_repository_name}} as fallback.": "No se puede acceder a ninguno de los repositorios conocidos de tu ubicación. Se utilizará {{.image_repository_name}} como alternativa.", + "None of the known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag": "No se puede acceder a ninguno de los repositorios conocidos. Plantéate indicar un repositorio de imágenes alternativo con la marca --image-repository.", + "Not passing {{.name}}={{.value}} to docker env.": "", + "Number of CPUs allocated to the minikube VM": "Número de CPU asignadas a la VM de minikube", + "Number of CPUs allocated to the minikube VM.": "", + "Number of lines back to go within the log": "", + "OS release is {{.pretty_name}}": "", + "Open the addons URL with https instead of http": "", + "Open the service URL with https instead of http": "", + "Opening kubernetes service {{.namespace_name}}/{{.service_name}} in default browser...": "", + "Opening {{.url}} in your default browser...": "", + "Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list": "", + "Options: {{.options}}": "", + "Outputs minikube shell completion for the given shell (bash or zsh)": "", + "Outputs minikube shell completion for the given shell (bash or zsh)\n\n\tThis depends on the bash-completion binary. Example installation instructions:\n\tOS X:\n\t\t$ brew install bash-completion\n\t\t$ source $(brew --prefix)/etc/bash_completion\n\t\t$ minikube completion bash \u003e ~/.minikube-completion # for bash users\n\t\t$ minikube completion zsh \u003e ~/.minikube-completion # for zsh users\n\t\t$ source ~/.minikube-completion\n\tUbuntu:\n\t\t$ apt-get install bash-completion\n\t\t$ source /etc/bash-completion\n\t\t$ source \u003c(minikube completion bash) # for bash users\n\t\t$ source \u003c(minikube completion zsh) # for zsh users\n\n\tAdditionally, you may want to output the completion to a file and source in your .bashrc\n\n\tNote for zsh users: [1] zsh completions are only supported in versions of zsh \u003e= 5.2": "", + "Permissions: {{.octalMode}} ({{.writtenMode}})": "", + "Please enter a value:": "", + "Please install the minikube hyperkit VM driver, or select an alternative --vm-driver": "", + "Please install the minikube kvm2 VM driver, or select an alternative --vm-driver": "", + "Please make sure the service you are looking for is deployed or is in the correct namespace.": "", + "Please specify the directory to be mounted: \n\tminikube mount \u003csource directory\u003e:\u003ctarget directory\u003e (example: \"/host-home:/vm-home\")": "", + "Please upgrade the '{{.driver_executable}}'. {{.documentation_url}}": "Actualiza \"{{.driver_executable}}\". {{.documentation_url}}", + "Populates the specified folder with documentation in markdown about minikube": "", + "Powering off \"{{.profile_name}}\" via SSH ...": "Apagando \"{{.profile_name}}\" mediante SSH...", + "Preparing Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}} ...": "Preparando Kubernetes {{.k8sVersion}} en {{.runtime}} {{.runtimeVersion}}...", + "Print current and latest version number": "", + "Print the version of minikube": "", + "Print the version of minikube.": "", + "Problems detected in {{.entry}}:": "", + "Problems detected in {{.name}}:": "", + "Profile gets or sets the current minikube profile": "", + "Provide VM UUID to restore MAC address (hyperkit driver only)": "Permite especificar un UUID de VM para restaurar la dirección MAC (solo con el controlador de hyperkit)", + "Pulling images ...": "", + "Reboot to complete VirtualBox installation, and verify that VirtualBox is not blocked by your system": "", + "Rebuild libvirt with virt-network support": "", + "Received {{.name}} signal": "", + "Registry mirrors to pass to the Docker daemon": "Réplicas del registro que se transferirán al daemon de Docker", + "Reinstall VirtualBox and reboot. Alternatively, try the kvm2 driver: https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/": "", + "Reinstall VirtualBox and verify that it is not blocked: System Preferences -\u003e Security \u0026 Privacy -\u003e General -\u003e Some system software was blocked from loading": "", + "Related issues:": "", + "Relaunching Kubernetes using {{.bootstrapper}} ...": "Reiniciando Kubernetes con {{.bootstrapper}}...", + "Removing {{.directory}} ...": "Eliminando {{.directory}}...", + "Requested CPU count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}": "", + "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}": "El tamaño de disco de {{.requested_size}} que se ha solicitado es inferior al tamaño mínimo de {{.minimum_size}}", + "Requested memory allocation ({{.memory}}MB) is less than the default memory allocation of {{.default_memorysize}}MB. Beware that minikube might not work correctly or crash unexpectedly.": "El valor de la asignación de memoria ({{.memory}} MB) solicitada es inferior a la asignación de memoria predeterminada de {{.default_memorysize}} MB. minikube podría no funcionar correctamente o fallar de manera inesperada.", + "Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}": "El valor de la asignación de memoria de {{.requested_size}} solicitada es inferior al valor mínimo de {{.minimum_size}}", + "Retriable failure: {{.error}}": "", + "Retrieve the ssh identity key path of the specified cluster": "", + "Retrieve the ssh identity key path of the specified cluster.": "", + "Retrieves the IP address of the running cluster": "", + "Retrieves the IP address of the running cluster, and writes it to STDOUT.": "", + "Retrieves the IP address of the running cluster, checks it\n\t\t\twith IP in kubeconfig, and corrects kubeconfig if incorrect.": "", + "Returns the value of PROPERTY_NAME from the minikube config file. Can be overwritten at runtime by flags or environmental variables.": "", + "Run 'kubectl describe pod coredns -n kube-system' and check for a firewall or DNS conflict": "", + "Run 'minikube delete' to delete the stale VM": "", + "Run kubectl": "", + "Run minikube from the C: drive.": "", + "Run the kubernetes client, download it if necessary.\nExamples:\nminikube kubectl -- --help\nkubectl get pods --namespace kube-system": "", + "Run the minikube command as an Administrator": "", + "Running on localhost (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "", + "Set failed": "", + "Sets an individual value in a minikube config file": "", + "Sets the PROPERTY_NAME config value to PROPERTY_VALUE\n\tThese values can be overwritten by flags or environment variables at runtime.": "", + "Sets up docker env variables; similar to '$(docker-machine env)'": "", + "Sets up docker env variables; similar to '$(docker-machine env)'.": "", + "Setting profile failed": "", + "Show only log entries which point to known problems": "", + "Show only the most recent journal entries, and continuously print new entries as they are appended to the journal.": "", + "Skipped switching kubectl context for {{.profile_name}} because --keep-context was set.": "", + "Sorry that minikube crashed. If this was unexpected, we would love to hear from you:": "", + "Sorry, Kubernetes {{.version}} is not supported by this release of minikube": "", + "Sorry, completion support is not yet implemented for {{.name}}": "", + "Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config": "De momento, --extra-config no admite el parámetro kubeadm.{{.parameter_name}}", + "Sorry, the url provided with the --registry-mirror flag is invalid: {{.url}}": "La URL proporcionada con la marca --registry-mirror no es válida: {{.url}}", + "Specified Kubernetes version {{.specified}} is less than the oldest supported version: {{.oldest}}": "", + "Specify --kubernetes-version in v\u003cmajor\u003e.\u003cminor.\u003cbuild\u003e form. example: 'v1.1.14'": "", + "Specify an alternate --host-only-cidr value, such as 172.16.0.1/24": "", + "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)": "Permite indicar marcas arbitrarias que se transferirán al daemon de Docker (el formato es \"clave=valor\").", + "Specify the 9p version that the mount should use": "", + "Specify the ip that the mount should be setup on": "", + "Specify the mount filesystem type (supported types: 9p)": "", + "Starting existing {{.driver_name}} VM for \"{{.profile_name}}\" ...": "", + "Starts a local kubernetes cluster": "Inicia un clúster de Kubernetes local", + "Stopping \"{{.profile_name}}\" in {{.driver_name}} ...": "", + "Stops a local kubernetes cluster running in Virtualbox. This command stops the VM\nitself, leaving all files intact. The cluster can be started again with the \"start\" command.": "", + "Stops a running local kubernetes cluster": "", + "Successfully mounted {{.sourcePath}} to {{.destinationPath}}": "", + "Suggestion: {{.advice}}": "", + "Target directory {{.path}} must be an absolute path": "", + "The \"{{.driver_name}}\" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}": "El controlador \"{{.driver_name}}\" requiere privilegios de raíz. Ejecuta minikube mediante sudo minikube --vm-driver={{.driver_name}}", + "The \"{{.driver_name}}\" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}'.": "", + "The \"{{.driver_name}}\" driver should not be used with root privileges.": "", + "The \"{{.name}}\" cluster has been deleted.": "Se ha eliminado el clúster \"{{.name}}\".", + "The \"{{.name}}\" cluster has been deleted.__1": "Se ha eliminado el clúster \"{{.name}}\".", + "The 'none' driver does not support multiple profiles: https://minikube.sigs.k8s.io/docs/reference/drivers/none/": "", + "The 'none' driver provides limited isolation and may reduce system security and reliability.": "La opción de controlador \"none\" proporciona un aislamiento limitado y puede reducir la seguridad y la fiabilidad del sistema.", + "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\\n\\n{{ .example }}\\n": "", + "The CIDR to be used for service cluster IPs.": "El CIDR de las IP del clúster de servicio.", + "The CIDR to be used for the minikube VM (virtualbox driver only)": "El CIDR de la VM de minikube (solo con el controlador de Virtualbox)", + "The KVM QEMU connection URI. (kvm2 driver only)": "El URI de la conexión de QEMU de la KVM (solo con el controlador de kvm2).", + "The KVM driver is unable to resurrect this old VM. Please run `minikube delete` to delete it and try again.": "", + "The KVM network name. (kvm2 driver only)": "El nombre de la red de KVM (solo con el controlador de kvm2).", + "The VM driver crashed. Run 'minikube start --alsologtostderr -v=8' to see the VM driver error message": "", + "The VM driver exited with an error, and may be corrupt. Run 'minikube start' with --alsologtostderr -v=8 to see the error": "", + "The VM that minikube is configured for no longer exists. Run 'minikube delete'": "", + "The apiserver listening port": "El puerto de escucha del apiserver", + "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "El nombre del apiserver del certificado de Kubernetes generado. Se puede utilizar para que sea posible acceder al apiserver desde fuera de la máquina", + "The argument to pass the minikube mount command on start": "El argumento para ejecutar el comando de activación de minikube durante el inicio", + "The argument to pass the minikube mount command on start.": "", + "The cluster dns domain name used in the kubernetes cluster": "El nombre de dominio de DNS del clúster de Kubernetes", + "The container runtime to be used (docker, crio, containerd)": "El entorno de ejecución del contenedor (Docker, cri-o, containerd)", + "The container runtime to be used (docker, crio, containerd).": "", + "The cri socket path to be used": "La ruta del socket de cri", + "The cri socket path to be used.": "", + "The docker host is currently not running": "", + "The docker service is currently not active": "", + "The driver '{{.driver}}' is not supported on {{.os}}": "El controlador \"{{.driver}}\" no se puede utilizar en {{.os}}", + "The existing \"{{.profile_name}}\" VM that was created using the \"{{.old_driver}}\" driver, and is incompatible with the \"{{.driver}}\" driver.": "", + "The hyperv virtual switch name. Defaults to first found. (hyperv driver only)": "El nombre del conmutador virtual de hyperv. El valor predeterminado será el primer nombre que se encuentre (solo con el controlador de hyperv).", + "The initial time interval for each check that wait performs in seconds": "", + "The kubernetes version that the minikube VM will use (ex: v1.2.3)": "La versión de Kubernetes que utilizará la VM de minikube (p. ej.: versión 1.2.3)", + "The minikube VM is offline. Please run 'minikube start' to start it again.": "", + "The name of the network plugin": "El nombre del complemento de red", + "The name of the network plugin.": "", + "The number of bytes to use for 9p packet payload": "", + "The path on the file system where the docs in markdown need to be saved": "", + "The service namespace": "", + "The services namespace": "", + "The time interval for each check that wait performs in seconds": "", + "The value passed to --format is invalid": "", + "The value passed to --format is invalid: {{.error}}": "", + "The vmwarefusion driver is deprecated and support for it will be removed in a future release.\n\t\t\tPlease consider switching to the new vmware unified driver, which is intended to replace the vmwarefusion driver.\n\t\t\tSee https://minikube.sigs.k8s.io/docs/reference/drivers/vmware/ for more information.\n\t\t\tTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "", + "The {{.driver_name}} driver should not be used with root privileges.": "El controlador {{.driver_name}} no se debe utilizar con privilegios de raíz.", + "There appears to be another hypervisor conflicting with KVM. Please stop the other hypervisor, or use --vm-driver to switch to it.": "", + "There's a new version for '{{.driver_executable}}'. Please consider upgrading. {{.documentation_url}}": "Hay una nueva versión de \"{{.driver_executable}}\". Te recomendamos que realices la actualización. {{.documentation_url}}", + "These changes will take effect upon a minikube delete and then a minikube start": "", + "This addon does not have an endpoint defined for the 'addons open' command.\nYou can add one by annotating a service with the label {{.labelName}}:{{.addonName}}": "", + "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true": "El proceso se puede automatizar si se define la variable de entorno CHANGE_MINIKUBE_NONE_USER=true", + "This will keep the existing kubectl context and will create a minikube context.": "Se conservará el contexto de kubectl actual y se creará uno de minikube.", + "This will start the mount daemon and automatically mount files into minikube": "Se iniciará el daemon de activación y se activarán automáticamente los archivos en minikube", + "This will start the mount daemon and automatically mount files into minikube.": "", + "Tip: To remove this root owned cluster, run: sudo {{.cmd}} delete": "Para eliminar este clúster de raíz, ejecuta: sudo {{.cmd}} delete", + "Tip: Use 'minikube start -p \u003cname\u003e' to create a new cluster, or 'minikube delete' to delete this one.": "", + "To connect to this cluster, use: kubectl --context={{.name}}": "Para conectarte a este clúster, usa: kubectl --context={{.name}}", + "To connect to this cluster, use: kubectl --context={{.name}}__1": "Para conectarte a este clúster, usa: kubectl --context={{.name}}", + "To connect to this cluster, use: kubectl --context={{.profile_name}}": "", + "To disable this notice, run: 'minikube config set WantUpdateNotification false'\\n": "", + "To proceed, either:\n 1) Delete the existing VM using: '{{.command}} delete'\n or\n 2) Restart with the existing driver: '{{.command}} start --vm-driver={{.old_driver}}'": "", + "To start minikube with HyperV Powershell must be in your PATH`": "", + "To use kubectl or minikube commands as your own user, you may need to relocate them. For example, to overwrite your own settings, run:": "Para usar comandos de kubectl o minikube como tu propio usuario, puede que debas reubicarlos. Por ejemplo, para sobrescribir tu configuración, ejecuta:", + "Troubleshooting Commands:": "", + "Unable to bind flags": "", + "Unable to enable dashboard": "", + "Unable to fetch latest version info": "", + "Unable to generate docs": "", + "Unable to generate the documentation. Please ensure that the path specified is a directory, exists \u0026 you have permission to write to it.": "", + "Unable to get VM IP address": "", + "Unable to get bootstrapper: {{.error}}": "No se ha podido obtener el programa previo: {{.error}}", + "Unable to get current user": "", + "Unable to get runtime": "", + "Unable to get the status of the cluster.": "", + "Unable to kill mount process: {{.error}}": "", + "Unable to load cached images from config file.": "No se han podido cargar las imágenes almacenadas en caché del archivo de configuración.", + "Unable to load cached images: {{.error}}": "", + "Unable to load config: {{.error}}": "No se ha podido cargar la configuración: {{.error}}", + "Unable to parse \"{{.kubernetes_version}}\": {{.error}}": "No se ha podido analizar la versión \"{{.kubernetes_version}}\": {{.error}}", + "Unable to parse oldest Kubernetes version from constants: {{.error}}": "", + "Unable to pull images, which may be OK: {{.error}}": "No se ha podido recuperar imágenes, que podrían estar en buen estado: {{.error}}", + "Unable to remove machine directory: %v": "", + "Unable to start VM": "", + "Unable to stop VM": "", + "Unable to update {{.driver}} driver: {{.error}}": "", + "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...": "Desinstalando Kubernetes {{.kubernetes_version}} mediante {{.bootstrapper_name}}...", + "Unmounting {{.path}} ...": "", + "Unset the KUBECONFIG environment variable, or verify that it does not point to an empty or otherwise invalid path": "", + "Unset variables instead of setting them": "", + "Update server returned an empty list": "", + "Upgrade to QEMU v3.1.0+, run 'virt-host-validate', or ensure that you are not running in a nested VM environment.": "", + "Upgrading from Kubernetes {{.old}} to {{.new}}": "Actualizando la versión de Kubernetes de {{.old}} a {{.new}}", + "Usage": "", + "Usage: minikube completion SHELL": "", + "Usage: minikube delete": "", + "Use \"{{.CommandPath}} [command] --help\" for more information about a command.": "", + "Use VirtualBox to remove the conflicting VM and/or network interfaces": "", + "Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'.": "", + "User ID: {{.userID}}": "", + "Userspace file server is shutdown": "", + "Userspace file server:": "", + "Using image repository {{.name}}": "Utilizando el repositorio de imágenes {{.name}}", + "Using the running {{.driver_name}} \"{{.profile_name}}\" VM ...": "", + "VM driver is one of: %v": "El controlador de la VM es uno de los siguientes: %v", + "Verify that your HTTP_PROXY and HTTPS_PROXY environment variables are set correctly.": "", + "Verify the IP address of the running cluster in kubeconfig.": "", + "Verifying dashboard health ...": "", + "Verifying proxy health ...": "", + "Version: {{.version}}": "", + "VirtualBox and Hyper-V are having a conflict. Use '--vm-driver=hyperv' or disable Hyper-V using: 'bcdedit /set hypervisorlaunchtype off'": "", + "VirtualBox cannot create a network, probably because it conflicts with an existing network that minikube no longer knows about. Try running 'minikube delete'": "", + "VirtualBox is broken. Disable real-time anti-virus software, reboot, and reinstall VirtualBox if the problem continues.": "", + "VirtualBox is broken. Reinstall VirtualBox, reboot, and run 'minikube delete'.": "", + "Wait failed": "", + "Wait failed: {{.error}}": "", + "Wait until Kubernetes core services are healthy before exiting": "Espera hasta que los servicios principales de Kubernetes se encuentren en buen estado antes de salir", + "Wait until Kubernetes core services are healthy before exiting.": "", + "Waiting for the host to be provisioned ...": "", + "Waiting for:": "", + "Where to root the NFS Shares, defaults to /nfsshares (hyperkit driver only)": "Ruta en la raíz de los recursos compartidos de NFS. Su valor predeterminado es /nfsshares (solo con el controlador de hyperkit)", + "You appear to be using a proxy, but your NO_PROXY environment does not include the minikube IP ({{.ip_address}}). Please see {{.documentation_url}} for more details": "Parece que estás usando un proxy, pero tu entorno NO_PROXY no incluye la dirección IP de minikube ({{.ip_address}}). Consulta {{.documentation_url}} para obtener más información", + "You can delete them using the following command(s):": "", + "You may need to manually remove the \"{{.name}}\" VM from your hypervisor": "Puede que tengas que retirar manualmente la VM \"{{.name}}\" de tu hipervisor", + "You must specify a service name": "", + "Your host does not support KVM virtualization. Ensure that qemu-kvm is installed, and run 'virt-host-validate' to debug the problem": "", + "Your host does not support virtualization. If you are running minikube within a VM, try '--vm-driver=none'. Otherwise, enable virtualization in your BIOS": "", + "Your host is failing to route packets to the minikube VM. If you have VPN software, try turning it off or configuring it so that it does not re-route traffic to the VM IP. If not, check your VM environment routing options.": "", + "Your minikube config refers to an unsupported driver. Erase ~/.minikube, and try again.": "", + "Your minikube vm is not running, try minikube start.": "", + "addon '{{.name}}' is currently not enabled.\nTo enable this addon run:\nminikube addons enable {{.name}}": "", + "addon '{{.name}}' is not a valid addon packaged with minikube.\nTo see the list of available addons run:\nminikube addons list": "", + "addon list failed": "", + "addons modifies minikube addons files using subcommands like \"minikube addons enable heapster\"": "", + "api load": "", + "bash completion failed": "", + "browser failed to open url: {{.error}}": "", + "call with cleanup=true to remove old tunnels": "", + "command runner": "", + "config modifies minikube config files using subcommands like \"minikube config set vm-driver kvm\"\nConfigurable fields:\\n\\n": "", + "config view failed": "", + "dashboard service is not running: {{.error}}": "", + "disable failed": "", + "enable failed": "", + "error creating clientset": "", + "error creating machine client": "", + "error getting driver": "", + "error parsing the input ip address for mount": "", + "error starting tunnel": "", + "failed to open browser: {{.error}}": "", + "if true, will embed the certs in kubeconfig.": "", + "kubectl and minikube configuration will be stored in {{.home_folder}}": "La configuración de kubectl y de minikube se almacenará en {{.home_folder}}", + "kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "", + "kubectl proxy": "", + "logdir set failed": "", + "max time to wait per Kubernetes core services to be healthy.": "", + "minikube is not running, so the service cannot be accessed": "", + "minikube is unable to access the Google Container Registry. You may need to configure it to use a HTTP proxy.": "", + "minikube profile was successfully set to {{.profile_name}}": "", + "minikube {{.version}} is available! Download it: {{.url}}": "", + "mount argument \"{{.value}}\" must be in form: \u003csource directory\u003e:\u003ctarget directory\u003e": "", + "mount failed": "", + "not enough arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", + "profile sets the current minikube profile, or gets the current profile if no arguments are provided. This is used to run and manage multiple minikube instance. You can return to the default minikube profile by running `minikube profile default`": "", + "service {{.namespace_name}}/{{.service_name}} has no node port": "", + "stat failed": "", + "toom any arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", + "tunnel creates a route to services deployed with type LoadBalancer and sets their Ingress to their ClusterIP": "", + "tunnel makes services of type LoadBalancer accessible on localhost": "", + "unable to bind flags": "", + "unable to set logtostderr": "", + "unset failed": "", + "unset minikube profile": "", + "unsets PROPERTY_NAME from the minikube config file. Can be overwritten by flags or environmental variables": "", + "unsets an individual value in a minikube config file": "", + "unsupported driver: {{.name}}": "", + "update config": "", + "usage: minikube addons configure ADDON_NAME": "", + "usage: minikube addons disable ADDON_NAME": "", + "usage: minikube addons enable ADDON_NAME": "", + "usage: minikube addons list": "", + "usage: minikube addons open ADDON_NAME": "", + "usage: minikube config unset PROPERTY_NAME": "", + "usage: minikube profile [MINIKUBE_PROFILE_NAME]": "", + "zsh completion failed": "", + "{{.addonName}} was successfully enabled": "", + "{{.extra_option_component_name}}.{{.key}}={{.value}}": "", + "{{.machine}} IP has been updated to point at {{.ip}}": "", + "{{.machine}} IP was already correctly configured for {{.ip}}": "", + "{{.name}} cluster does not exist": "", + "{{.name}} has no available configuration options": "", + "{{.name}} was successfully configured": "", + "{{.prefix}}minikube {{.version}} on {{.platform}}": "{{.prefix}}minikube {{.version}} en {{.platform}}", + "{{.type}} is not yet a supported filesystem. We will try anyways!": "", + "{{.url}} is not accessible: {{.error}}": "" +} \ No newline at end of file diff --git a/translations/fr-FR.json b/translations/fr.json similarity index 55% rename from translations/fr-FR.json rename to translations/fr.json index 8c4ebf2b51..83c5a9ec0c 100644 --- a/translations/fr-FR.json +++ b/translations/fr.json @@ -1,20 +1,23 @@ { - "\n\tOutputs minikube shell completion for the given shell (bash or zsh)\n\n\tThis depends on the bash-completion binary. Example installation instructions:\n\tOS X:\n\t\t$ brew install bash-completion\n\t\t$ source $(brew --prefix)/etc/bash_completion\n\t\t$ minikube completion bash \u003e ~/.minikube-completion # for bash users\n\t\t$ minikube completion zsh \u003e ~/.minikube-completion # for zsh users\n\t\t$ source ~/.minikube-completion\n\tUbuntu:\n\t\t$ apt-get install bash-completion\n\t\t$ source /etc/bash-completion\n\t\t$ source \u003c(minikube completion bash) # for bash users\n\t\t$ source \u003c(minikube completion zsh) # for zsh users\n\n\tAdditionally, you may want to output the completion to a file and source in your .bashrc\n\n\tNote for zsh users: [1] zsh completions are only supported in versions of zsh \u003e= 5.2\n": "", "\"{{.minikube_addon}}\" was successfully disabled": "", - "\"{{.name}}\" cluster does not exist": "", - "\"{{.name}}\" profile does not exist": "", + "\"{{.name}}\" cluster does not exist. Proceeding ahead with cleanup.": "", + "\"{{.name}}\" profile does not exist": "Le profil \"{{.name}}\" n'existe pas.", "\"{{.profile_name}}\" VM does not exist, nothing to stop": "", "\"{{.profile_name}}\" host does not exist, unable to show an IP": "", "\"{{.profile_name}}\" stopped.": "\"{{.profile_name}}\" est arrêté.", "'none' driver does not support 'minikube docker-env' command": "", "'none' driver does not support 'minikube mount' command": "", "'none' driver does not support 'minikube ssh' command": "", + "A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "", "A firewall is blocking Docker within the minikube VM from reaching the internet. You may need to configure it to use a proxy.": "", "A firewall is interfering with minikube's ability to make outgoing HTTPS requests. You may need to change the value of the HTTPS_PROXY environment variable.": "", "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.": "", "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "Ensemble d'adresses IP de serveur d'API utilisées dans le certificat généré pour Kubernetes. Vous pouvez les utiliser si vous souhaitez que le serveur d'API soit disponible en dehors de la machine.", "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", - "A set of key=value pairs that describe feature gates for alpha/experimental features.": "", + "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "Ensemble de noms de serveur d'API utilisés dans le certificat généré pour Kubernetes. Vous pouvez les utiliser si vous souhaitez que le serveur d'API soit disponible en dehors de la machine.", + "A set of key=value pairs that describe configuration that may be passed to different components.\nThe key should be '.' separated, and the first part before the dot is the component to apply the configuration to.\nValid components are: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy, scheduler\nValid kubeadm parameters:": "Ensemble de paires clé = valeur qui décrivent la configuration pouvant être transmise à différents composants.\nLa clé doit être séparée par le caractère \".\", la première partie placée avant le point étant le composant auquel la configuration est appliquée.\nVoici la liste des composants valides : apiserver, controller-manager, etcd, kubeadm, kubelet, proxy et scheduler.\nParamètres valides pour le composant kubeadm :", + "A set of key=value pairs that describe feature gates for alpha/experimental features.": "Ensemble de paires clé = valeur qui décrivent l'entrée de configuration pour des fonctionnalités alpha ou expérimentales.", "Access the kubernetes dashboard running within the minikube cluster": "", "Add an image to local cache.": "", "Add machine IP to NO_PROXY environment variable": "", @@ -23,8 +26,9 @@ "Additional mount options, such as cache=fscache": "", "Advanced Commands:": "", "Aliases": "", - "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \\\"auto\\\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers": "", - "Alternatively, you may delete the existing VM using `minikube delete -p {{.profile_name}}`": "", + "Allow user prompts for more information": "", + "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \\\"auto\\\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers": "Autre dépôt d'images d'où extraire des images Docker. Il peut être utilisé en cas d'accès limité à gcr.io. Définissez-le sur \\\"auto\\\" pour permettre à minikube de choisir la valeur à votre place. Pour les utilisateurs situés en Chine continentale, vous pouvez utiliser des miroirs gcr.io locaux tels que registry.cn-hangzhou.aliyuncs.com/google_containers.", + "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "Quantité de mémoire RAM allouée à la VM minikube (format : \u003cnombre\u003e[\u003cunité\u003e], où \"unité\" = b, k, m ou g).", "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "", "Amount of time to wait for a service in seconds": "", "Amount of time to wait for service in seconds": "", @@ -33,29 +37,32 @@ "Cannot find directory {{.path}} for mount": "", "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.": "", "Check that your --kubernetes-version has a leading 'v'. For example: 'v1.1.14'": "", + "Check that your apiserver flags are valid, or run 'minikube delete'": "", + "Check your firewall rules for interference, and run 'virt-host-validate' to check for KVM configuration issues. If you are running minikube within a VM, consider using --vm-driver=none": "", "Configuration and Management Commands:": "", "Configure an external network switch following the official documentation, then add `--hyperv-virtual-switch=\u003cswitch-name\u003e` to `minikube start`": "", - "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list ": "", + "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list": "", "Configuring environment for Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}}": "Configuration de l'environment pour Kubernetes {{.k8sVersion}} sur {{.runtime}} {{.runtimeVersion}}", "Configuring local host environment ...": "", - "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn.": "", + "Confirm that you have a working internet connection and that your VM has not run out of resources by using: 'minikube logs'": "", + "Confirm that you have supplied the correct value to --hyperv-virtual-switch using the 'Get-VMSwitch' command": "", + "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn.": "Code pays du miroir d'images à utiliser. Laissez ce paramètre vide pour utiliser le miroir international. Pour les utilisateurs situés en Chine continentale, définissez sa valeur sur \"cn\".", "Created a new profile : {{.profile_name}}": "", - "Creating %s VM (CPUs=%d, Memory=%dMB, Disk=%dMB) ...": "Création d'une VM %s (CPUs=%d, Mémoire=%dMB, Disque=%dMB)...", "Creating a new profile failed": "", - "Creating mount {{.name}} ...": "", + "Creating mount {{.name}} ...": "Création de l'installation {{.name}}…", "Creating {{.driver_name}} VM (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "Création d'une VM {{.driver_name}} (CPUs={{.number_of_cpus}}, Mémoire={{.memory_size}}MB, Disque={{.disk_size}}MB)...", "Default group id used for the mount": "", "Default user id used for the mount": "", "Delete an image from the local cache.": "", "Deletes a local kubernetes cluster": "", "Deletes a local kubernetes cluster. This command deletes the VM, and removes all\nassociated files.": "", - "Deleting \"{{.profile_name}}\" in {{.driver_name}} ...": "Suppression de \"{{.profile_name}}\" sur {{.driver_name}}...", - "Disable Hyper-V when you want to run VirtualBox to boot the VM": "", - "Disable checking for the availability of hardware virtualization before the vm is started (virtualbox driver only)": "", + "Deletes a local kubernetes cluster. This command deletes the VM, and removes all associated files.": "Supprime le cluster Kubernetes local. Cette commande supprime la VM ainsi que tous les fichiers associés.", + "Deleting \"{{.profile_name}}\" in {{.driver_name}} ...": "Suppression de \"{{.profile_name}}\" dans {{.driver_name}}...", + "Disable checking for the availability of hardware virtualization before the vm is started (virtualbox driver only)": "Désactive la vérification de la disponibilité de la virtualisation du matériel avant le démarrage de la VM (pilote virtualbox uniquement).", "Disable dynamic memory in your VM manager, or pass in a larger --memory value": "", - "Disable real-time anti-virus software, reboot, and reinstall VirtualBox if the problem continues.": "", - "Disables the addon w/ADDON_NAME within minikube (example: minikube addons disable dashboard). For a list of available addons use: minikube addons list ": "", - "Disables the filesystem mounts provided by the hypervisors": "", + "Disables the addon w/ADDON_NAME within minikube (example: minikube addons disable dashboard). For a list of available addons use: minikube addons list": "", + "Disables the filesystem mounts provided by the hypervisors": "Désactive les installations de systèmes de fichiers fournies par les hyperviseurs.", + "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "Taille de disque allouée à la VM minikube (format : \u003cnombre\u003e[\u003cunité\u003e], où \"unité\" = b, k, m ou g)", "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "", "Display dashboard URL instead of opening a browser": "", "Display the kubernetes addons URL in the CLI instead of opening it in the default browser": "", @@ -65,21 +72,25 @@ "Docker inside the VM is unavailable. Try running 'minikube delete' to reset the VM.": "", "Docs have been saved at - {{.path}}": "", "Documentation: {{.url}}": "", - "Done! kubectl is now configured to use \"{{.name}}\"": "Terminé! kubectl est maintenant configuré pour utiliser \"{{.name}}\".", - "Download complete!": "", + "Done! kubectl is now configured to use \"{{.name}}": "Terminé ! kubectl est maintenant configuré pour utiliser \"{{.name}}\".", + "Done! kubectl is now configured to use \"{{.name}}\"": "", + "Download complete!": "Téléchargement terminé !", "Downloading VM boot image ...": "", + "Downloading driver {{.driver}}:": "", "Downloading {{.name}} {{.version}}": "", "ERROR creating `registry-creds-dpr` secret": "", "ERROR creating `registry-creds-ecr` secret: {{.error}}": "", "ERROR creating `registry-creds-gcr` secret: {{.error}}": "", - "Enable experimental NVIDIA GPU support in minikube": "", - "Enable host resolver for NAT DNS requests (virtualbox driver only)": "", - "Enable proxy for NAT DNS requests (virtualbox driver only)": "", + "Either systemctl is not installed, or Docker is broken. Run 'sudo systemctl start docker' and 'journalctl -u docker'": "", + "Enable experimental NVIDIA GPU support in minikube": "Active l'assistance expérimentale du GPU NVIDIA dans minikube.", + "Enable host resolver for NAT DNS requests (virtualbox driver only)": "Active le résolveur d'hôte pour les requêtes DNS NAT (pilote VirtualBox uniquement).", + "Enable proxy for NAT DNS requests (virtualbox driver only)": "Active le proxy pour les requêtes DNS NAT (pilote VirtualBox uniquement).", + "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\": "Active le plug-in CNI par défaut (/etc/cni/net.d/k8s.conf). Utilisé en association avec \\\"--network-plugin=cni\\\".", "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\\".": "", - "Enables the addon w/ADDON_NAME within minikube (example: minikube addons enable dashboard). For a list of available addons use: minikube addons list ": "", + "Enables the addon w/ADDON_NAME within minikube (example: minikube addons enable dashboard). For a list of available addons use: minikube addons list": "", "Enabling dashboard ...": "", - "Environment variables to pass to the Docker daemon. (format: key=value)": "", - "Error checking driver version: {{.error}}": "", + "Environment variables to pass to the Docker daemon. (format: key=value)": "Variables d'environment à transmettre au daemon Docker (format : clé = valeur).", + "Error checking driver version: {{.error}}": "Erreur lors de la vérification de la version du driver : {{.error}}", "Error creating list template": "", "Error creating minikube directory": "", "Error creating status template": "", @@ -100,6 +111,7 @@ "Error getting host status": "", "Error getting machine logs": "", "Error getting machine status": "", + "Error getting profiles to delete": "", "Error getting service status": "", "Error getting service with namespace: {{.namespace}} and labels {{.labelName}}:{{.addonName}}: {{.error}}": "", "Error getting the host IP address to use from within the VM": "", @@ -108,10 +120,10 @@ "Error loading api": "", "Error loading profile config": "", "Error loading profile config: {{.error}}": "", - "Error loading profile {{.name}}: {{.error}}": "", + "Error loading profile {{.name}}: {{.error}}": "Erreur lors du chargement du profil {{.name}} : {{.error}}", "Error opening service": "", - "Error parsing minikube version: {{.error}}": "", - "Error parsing vmDriver version: {{.error}}": "", + "Error parsing minikube version: {{.error}}": "Erreur lors de l'analyse de la version de minikube : {{.error}}", + "Error parsing vmDriver version: {{.error}}": "Erreur lors de l'analyse de la version du pilote de la VM : {{.error}}", "Error reading {{.path}}: {{.error}}": "", "Error restarting cluster": "", "Error setting shell variables": "", @@ -121,18 +133,21 @@ "Error while setting kubectl current context : {{.error}}": "", "Error writing mount pid": "", "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}\"": "", + "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}": "Erreur : Vous avez sélectionné Kubernetes v{{.new}}, mais le cluster existent pour votre profil exécute Kubernetes v{{.old}}. Les rétrogradations non-destructives ne sont pas compatibles. Toutefois, vous pouvez poursuivre le processus en réalisant l'une des trois actions suivantes :\n* Créer à nouveau le cluster en utilisant Kubernetes v{{.new}} – exécutez \"minikube delete {{.profile}}\", puis \"minikube start {{.profile}} --kubernetes-version={{.new}}\".\n* Créer un second cluster avec Kubernetes v{{.new}} – exécutez \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\".\n* Réutiliser le cluster existent avec Kubernetes v{{.old}} ou version ultérieure – exécutez \"minikube start {{.profile}} --kubernetes-version={{.old}}\".", "Error: [{{.id}}] {{.error}}": "", "Examples": "", - "Exiting": "", + "Exiting": "Fermeture…", + "Exiting due to driver incompatibility": "", "Failed runtime": "", "Failed to cache ISO": "", "Failed to cache and load images": "", "Failed to cache binaries": "", "Failed to cache images": "", - "Failed to change permissions for {{.minikube_dir_path}}: {{.error}}": "", + "Failed to change permissions for {{.minikube_dir_path}}: {{.error}}": "Échec de la modification des autorisations pour {{.minikube_dir_path}} : {{.error}}", "Failed to check if machine exists": "", "Failed to check main repository and mirrors for images for images": "", - "Failed to delete cluster: {{.error}}": "", + "Failed to delete cluster: {{.error}}": "Échec de la suppression du cluster : {{.error}}", + "Failed to delete cluster: {{.error}}__1": "Échec de la suppression du cluster : {{.error}}", "Failed to delete images": "", "Failed to delete images from config": "", "Failed to download kubectl": "", @@ -144,10 +159,11 @@ "Failed to get image map": "", "Failed to get machine client": "", "Failed to get service URL: {{.error}}": "", - "Failed to kill mount process: {{.error}}": "", + "Failed to kill mount process: {{.error}}": "Échec de l'arrêt du processus d'installation : {{.error}}", "Failed to list cached images": "", "Failed to remove profile": "", "Failed to save config": "", + "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}": "Échec de la définition de NO_PROXY Env. Veuillez utiliser `export NO_PROXY=$NO_PROXY,{{.ip}}.", "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}`.": "", "Failed to setup certs": "", "Failed to setup kubeconfig": "", @@ -157,12 +173,13 @@ "File permissions used for the mount": "", "Flags": "", "Follow": "", - "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "", - "For more information, see:": "", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "Pour des résultats optimaux, installez kubectl à l'adresse suivante : https://kubernetes.io/docs/tasks/tools/install-kubectl/", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/__1": "Pour des résultats optimaux, installez kubectl à l'adresse suivante : https://kubernetes.io/docs/tasks/tools/install-kubectl/", + "For more information, see:": "Pour en savoir plus, consultez les pages suivantes :", "Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect": "", - "Force minikube to perform possibly dangerous operations": "", - "Found network options:": "", - "Found {{.number}} invalid profile(s) ! ": "", + "Force minikube to perform possibly dangerous operations": "Oblige minikube à réaliser des opérations possiblement dangereuses.", + "Found network options:": "Options de réseau trouvées :", + "Found {{.number}} invalid profile(s) !": "", "Gets the kubernetes URL(s) for the specified service in your local cluster": "", "Gets the kubernetes URL(s) for the specified service in your local cluster. In the case of multiple URLs they will be printed one at a time.": "", "Gets the logs of the running instance, used for debugging minikube, not user code.": "", @@ -176,31 +193,32 @@ "Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#Status": "", "Group ID: {{.groupID}}": "", "Have you set up libvirt correctly?": "", - "Hide the hypervisor signature from the guest in minikube (kvm2 driver only)": "", - "If the above advice does not help, please let us know: ": "", - "If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --vm-driver=none.": "", - "If true, only download and cache files for later use - don't install or start anything.": "", + "Hide the hypervisor signature from the guest in minikube (kvm2 driver only)": "Masque la signature de l'hyperviseur de l'invité dans minikube (pilote kvm2 uniquement).", + "If the above advice does not help, please let us know:": "", + "If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --vm-driver=none.": "Si la valeur est \"true\", mettez les images Docker en cache pour l'amorceur actuel et chargez-les dans la machine. La valeur est toujours \"false\" avec --vm-driver=none.", + "If true, only download and cache files for later use - don't install or start anything.": "Si la valeur est \"true\", téléchargez les fichiers et mettez-les en cache uniquement pour une utilisation future. Ne lancez pas d'installation et ne commencez aucun processus.", "If using the none driver, ensure that systemctl is installed": "", - "Ignoring --vm-driver={{.driver_name}}, as the existing \"{{.profile_name}}\" VM was created using the {{.driver_name2}} driver.": "", + "If you are running minikube within a VM, consider using --vm-driver=none:": "", "Images Commands:": "", - "In some environments, this message is incorrect. Try 'minikube start --no-vtx-check'": "", "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "", - "Install VirtualBox, ensure that VBoxManage is executable and in path, or select an alternative value for --vm-driver": "", - "Install the latest kvm2 driver and run 'virt-host-validate'": "", - "Install the latest minikube hyperkit driver, and run 'minikube delete'": "", + "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "Registres Docker non sécurisés à transmettre au daemon Docker. La plage CIDR par défaut du service sera ajoutée automatiquement.", + "Install VirtualBox, or select an alternative value for --vm-driver": "", + "Install the latest hyperkit binary, and run 'minikube delete'": "", "Invalid size passed in argument: {{.error}}": "", "IsEnabled failed": "", "Kill the mount process spawned by minikube start": "", - "Launching Kubernetes ... ": "Lancement de Kubernetes...", + "Kubernetes {{.version}} is not supported by this release of minikube": "", + "Launching Kubernetes ...": "Lancement de Kubernetes...", "Launching proxy ...": "", "List all available images from the local cache.": "", - "List of guest VSock ports that should be exposed as sockets on the host (hyperkit driver only)": "", + "List of guest VSock ports that should be exposed as sockets on the host (hyperkit driver only)": "Liste de ports VSock invités qui devraient être exposés comme sockets sur l'hôte (pilote hyperkit uniquement).", "Lists all available minikube addons as well as their current statuses (enabled/disabled)": "", "Lists all minikube profiles.": "", "Lists all valid minikube profiles and detects all possible invalid profiles.": "", "Lists the URLs for the services in your local cluster": "", - "Local folders to share with Guest via NFS mounts (hyperkit driver only)": "", - "Location of the VPNKit socket used for networking. If empty, disables Hyperkit VPNKitSock, if 'auto' uses Docker for Mac VPNKit connection, otherwise uses the specified VSock (hyperkit driver only)": "", + "Local folders to share with Guest via NFS mounts (hyperkit driver only)": "Dossiers locaux à partager avec l'invité par des installations NFS (pilote hyperkit uniquement).", + "Location of the VPNKit socket used for networking. If empty, disables Hyperkit VPNKitSock, if 'auto' uses Docker for Mac VPNKit connection, otherwise uses the specified VSock (hyperkit driver only)": "Emplacement du socket VPNKit exploité pour la mise en réseau. Si la valeur est vide, désactive Hyperkit VPNKitSock. Si la valeur affiche \"auto\", utilise la connexion VPNKit de Docker pour Mac. Sinon, utilise le VSock spécifié (pilote hyperkit uniquement).", + "Location of the minikube iso": "Emplacement de l'ISO minikube.", "Location of the minikube iso.": "", "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'": "", "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'.": "", @@ -216,8 +234,10 @@ "NOTE: This process must stay alive for the mount to be accessible ...": "", "Networking and Connectivity Commands:": "", "No minikube profile was found. You can create one using `minikube start`.": "", - "None of the known repositories in your location are accessible. Using {{.image_repository_name}} as fallback.": "", - "None of the known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag": "", + "None of the known repositories in your location are accessible. Using {{.image_repository_name}} as fallback.": "Aucun dépôt connu dans votre emplacement n'est accessible. {{.image_repository_name}} est utilisé comme dépôt de remplacement.", + "None of the known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag": "Aucun dépôt connu n'est accessible. Pensez à spécifier un autre dépôt d'images à l'aide de l'indicateur \"--image-repository\".", + "Not passing {{.name}}={{.value}} to docker env.": "", + "Number of CPUs allocated to the minikube VM": "Nombre de processeurs alloués à la VM minikube.", "Number of CPUs allocated to the minikube VM.": "", "Number of lines back to go within the log": "", "OS release is {{.pretty_name}}": "", @@ -225,19 +245,19 @@ "Open the service URL with https instead of http": "", "Opening kubernetes service {{.namespace_name}}/{{.service_name}} in default browser...": "", "Opening {{.url}} in your default browser...": "", - "Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list ": "", + "Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list": "", "Options: {{.options}}": "", "Outputs minikube shell completion for the given shell (bash or zsh)": "", + "Outputs minikube shell completion for the given shell (bash or zsh)\n\n\tThis depends on the bash-completion binary. Example installation instructions:\n\tOS X:\n\t\t$ brew install bash-completion\n\t\t$ source $(brew --prefix)/etc/bash_completion\n\t\t$ minikube completion bash \u003e ~/.minikube-completion # for bash users\n\t\t$ minikube completion zsh \u003e ~/.minikube-completion # for zsh users\n\t\t$ source ~/.minikube-completion\n\tUbuntu:\n\t\t$ apt-get install bash-completion\n\t\t$ source /etc/bash-completion\n\t\t$ source \u003c(minikube completion bash) # for bash users\n\t\t$ source \u003c(minikube completion zsh) # for zsh users\n\n\tAdditionally, you may want to output the completion to a file and source in your .bashrc\n\n\tNote for zsh users: [1] zsh completions are only supported in versions of zsh \u003e= 5.2": "", "Permissions: {{.octalMode}} ({{.writtenMode}})": "", - "Please check your BIOS, and ensure that you are running without HyperV or other nested virtualization that may interfere": "", "Please enter a value:": "", "Please install the minikube hyperkit VM driver, or select an alternative --vm-driver": "", "Please install the minikube kvm2 VM driver, or select an alternative --vm-driver": "", "Please make sure the service you are looking for is deployed or is in the correct namespace.": "", "Please specify the directory to be mounted: \n\tminikube mount \u003csource directory\u003e:\u003ctarget directory\u003e (example: \"/host-home:/vm-home\")": "", - "Please upgrade the '{{.driver_executable}}'. {{.documentation_url}}": "", + "Please upgrade the '{{.driver_executable}}'. {{.documentation_url}}": "Veuillez mettre à niveau l'exécutable \"{{.driver_executable}}\". {{.documentation_url}}", "Populates the specified folder with documentation in markdown about minikube": "", - "Powering off \"{{.profile_name}}\" via SSH ...": "", + "Powering off \"{{.profile_name}}\" via SSH ...": "Mise hors tension du profil \"{{.profile_name}}\" via SSH…", "Preparing Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}} ...": "Préparation de Kubernetes {{.k8sVersion}} sur {{.runtime}} {{.runtimeVersion}}...", "Print current and latest version number": "", "Print the version of minikube": "", @@ -245,32 +265,34 @@ "Problems detected in {{.entry}}:": "", "Problems detected in {{.name}}:": "", "Profile gets or sets the current minikube profile": "", - "Provide VM UUID to restore MAC address (hyperkit driver only)": "", + "Provide VM UUID to restore MAC address (hyperkit driver only)": "Fournit l'identifiant unique universel (UUID) de la VM pour restaurer l'adresse MAC (pilote hyperkit uniquement).", "Pulling images ...": "Extraction des images... ", - "Re-run 'minikube start' with --alsologtostderr -v=8 to see the VM driver error message": "", "Reboot to complete VirtualBox installation, and verify that VirtualBox is not blocked by your system": "", "Rebuild libvirt with virt-network support": "", "Received {{.name}} signal": "", - "Registry mirrors to pass to the Docker daemon": "", + "Registry mirrors to pass to the Docker daemon": "Miroirs de dépôt à transmettre au daemon Docker.", + "Reinstall VirtualBox and reboot. Alternatively, try the kvm2 driver: https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/": "", "Reinstall VirtualBox and verify that it is not blocked: System Preferences -\u003e Security \u0026 Privacy -\u003e General -\u003e Some system software was blocked from loading": "", "Related issues:": "", - "Relaunching Kubernetes using {{.bootstrapper}} ... ": "", - "Removing {{.directory}} ...": "", - "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}": "", - "Requested memory allocation ({{.memory}}MB) is less than the default memory allocation of {{.default_memorysize}}MB. Beware that minikube might not work correctly or crash unexpectedly.": "", - "Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}": "", + "Relaunching Kubernetes using {{.bootstrapper}} ...": "Redémarrage de Kubernetes à l'aide de {{.bootstrapper}}…", + "Removing {{.directory}} ...": "Suppression du répertoire {{.directory}}…", + "Requested CPU count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}": "", + "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}": "La taille de disque demandée ({{.requested_size}}) est inférieure à la taille minimale ({{.minimum_size}}).", + "Requested memory allocation ({{.memory}}MB) is less than the default memory allocation of {{.default_memorysize}}MB. Beware that minikube might not work correctly or crash unexpectedly.": "L'allocation de mémoire demandée ({{.memory}} Mo) est inférieure à l'allocation de mémoire par défaut ({{.default_memorysize}} Mo). Sachez que minikube pourrait ne pas fonctionner correctement ou planter de manière inattendue.", + "Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}": "L'allocation de mémoire demandée ({{.requested_size}}) est inférieure au minimum autorisé ({{.minimum_size}}).", + "Retriable failure: {{.error}}": "", "Retrieve the ssh identity key path of the specified cluster": "", "Retrieve the ssh identity key path of the specified cluster.": "", "Retrieves the IP address of the running cluster": "", "Retrieves the IP address of the running cluster, and writes it to STDOUT.": "", "Retrieves the IP address of the running cluster, checks it\n\t\t\twith IP in kubeconfig, and corrects kubeconfig if incorrect.": "", "Returns the value of PROPERTY_NAME from the minikube config file. Can be overwritten at runtime by flags or environmental variables.": "", + "Run 'kubectl describe pod coredns -n kube-system' and check for a firewall or DNS conflict": "", "Run 'minikube delete' to delete the stale VM": "", - "Run 'minikube delete'. If the problem persists, check your proxy or firewall configuration": "", - "Run 'sudo modprobe vboxdrv' and reinstall VirtualBox if it fails.": "", "Run kubectl": "", "Run minikube from the C: drive.": "", "Run the kubernetes client, download it if necessary.\nExamples:\nminikube kubectl -- --help\nkubectl get pods --namespace kube-system": "", + "Run the minikube command as an Administrator": "", "Running on localhost (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "", "Set failed": "", "Sets an individual value in a minikube config file": "", @@ -282,46 +304,59 @@ "Show only the most recent journal entries, and continuously print new entries as they are appended to the journal.": "", "Skipped switching kubectl context for {{.profile_name}} because --keep-context was set.": "", "Sorry that minikube crashed. If this was unexpected, we would love to hear from you:": "", + "Sorry, Kubernetes {{.version}} is not supported by this release of minikube": "", "Sorry, completion support is not yet implemented for {{.name}}": "", - "Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config": "", - "Sorry, the url provided with the --registry-mirror flag is invalid: {{.url}}": "", + "Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config": "Désolé, le paramètre kubeadm.{{.parameter_name}} ne peut actuellement pas être utilisé avec \"--extra-config\".", + "Sorry, the url provided with the --registry-mirror flag is invalid: {{.url}}": "Désolé, l'URL fournie avec l'indicateur \"--registry-mirror\" n'est pas valide : {{.url}}", + "Specified Kubernetes version {{.specified}} is less than the oldest supported version: {{.oldest}}": "", "Specify --kubernetes-version in v\u003cmajor\u003e.\u003cminor.\u003cbuild\u003e form. example: 'v1.1.14'": "", "Specify an alternate --host-only-cidr value, such as 172.16.0.1/24": "", - "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)": "", + "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)": "Spécifie des indicateurs arbitraires à transmettre au daemon Docker (format : clé = valeur).", "Specify the 9p version that the mount should use": "", "Specify the ip that the mount should be setup on": "", "Specify the mount filesystem type (supported types: 9p)": "", "Starting existing {{.driver_name}} VM for \"{{.profile_name}}\" ...": "", - "Starts a local kubernetes cluster": "Démarrage d'un cluster Kubernetes", + "Starts a local kubernetes cluster": "Démarre un cluster Kubernetes local.", "Stopping \"{{.profile_name}}\" in {{.driver_name}} ...": "Arrêt de \"{{.profile_name}}\" sur {{.driver_name}}...", "Stops a local kubernetes cluster running in Virtualbox. This command stops the VM\nitself, leaving all files intact. The cluster can be started again with the \"start\" command.": "", "Stops a running local kubernetes cluster": "", "Successfully mounted {{.sourcePath}} to {{.destinationPath}}": "", "Suggestion: {{.advice}}": "", "Target directory {{.path}} must be an absolute path": "", - "The \"{{.cluster_name}}\" cluster has been deleted.": "Le cluster \"{{.cluster_name}}\" a été supprimé.", + "The \"{{.driver_name}}\" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}": "Le pilote \"{{.driver_name}}\" nécessite de disposer de droits racine. Veuillez exécuter minikube à l'aide de \"sudo minikube --vm-driver={{.driver_name}}\".", "The \"{{.driver_name}}\" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}'.": "", - "The \"{{.name}}\" cluster has been deleted.": "", - "The 'none' driver provides limited isolation and may reduce system security and reliability.": "", - "The CIDR to be used for service cluster IPs.": "", - "The CIDR to be used for the minikube VM (virtualbox driver only)": "", - "The KVM QEMU connection URI. (kvm2 driver only)": "", + "The \"{{.driver_name}}\" driver should not be used with root privileges.": "", + "The \"{{.name}}\" cluster has been deleted.": "Le cluster \"{{.name}}\" a été supprimé.", + "The 'none' driver does not support multiple profiles: https://minikube.sigs.k8s.io/docs/reference/drivers/none/": "", + "The 'none' driver provides limited isolation and may reduce system security and reliability.": "L'isolation fournie par le pilote \"none\" (aucun) est limitée, ce qui peut diminuer la sécurité et la fiabilité du système.", + "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\\n\\n{{ .example }}\\n": "", + "The CIDR to be used for service cluster IPs.": "Méthode CIDR à exploiter pour les adresses IP des clusters du service.", + "The CIDR to be used for the minikube VM (virtualbox driver only)": "Méthode CIDR à exploiter pour la VM minikube (pilote virtualbox uniquement).", + "The KVM QEMU connection URI. (kvm2 driver only)": "URI de connexion QEMU de la KVM (pilote kvm2 uniquement).", "The KVM driver is unable to resurrect this old VM. Please run `minikube delete` to delete it and try again.": "", - "The KVM network name. (kvm2 driver only)": "", + "The KVM network name. (kvm2 driver only)": "Nom du réseau de la KVM (pilote kvm2 uniquement).", + "The VM driver crashed. Run 'minikube start --alsologtostderr -v=8' to see the VM driver error message": "", "The VM driver exited with an error, and may be corrupt. Run 'minikube start' with --alsologtostderr -v=8 to see the error": "", - "The apiserver listening port": "", + "The VM that minikube is configured for no longer exists. Run 'minikube delete'": "", + "The apiserver listening port": "Port d'écoute du serveur d'API.", "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "Nom du serveur d'API utilisé dans le certificat généré pour Kubernetes. Vous pouvez l'utiliser si vous souhaitez que le serveur d'API soit disponible en dehors de la machine.", + "The argument to pass the minikube mount command on start": "Argument à transmettre à la commande d'installation de minikube au démarrage.", "The argument to pass the minikube mount command on start.": "", - "The cluster dns domain name used in the kubernetes cluster": "", + "The cluster dns domain name used in the kubernetes cluster": "Nom du domaine DNS du cluster utilisé dans le cluster Kubernetes.", + "The container runtime to be used (docker, crio, containerd)": "environment d'exécution du conteneur à utiliser (docker, crio, containerd).", "The container runtime to be used (docker, crio, containerd).": "", + "The cri socket path to be used": "Chemin d'accès au socket CRI à utiliser.", "The cri socket path to be used.": "", "The docker host is currently not running": "", "The docker service is currently not active": "", - "The driver '{{.driver}}' is not supported on {{.os}}": "", - "The hyperv virtual switch name. Defaults to first found. (hyperv driver only)": "", + "The driver '{{.driver}}' is not supported on {{.os}}": "Le pilote \"{{.driver}}\" n'est pas compatible avec {{.os}}.", + "The existing \"{{.profile_name}}\" VM that was created using the \"{{.old_driver}}\" driver, and is incompatible with the \"{{.driver}}\" driver.": "", + "The hyperv virtual switch name. Defaults to first found. (hyperv driver only)": "Nom du commutateur virtuel hyperv. La valeur par défaut affiche le premier commutateur trouvé (pilote hyperv uniquement).", "The initial time interval for each check that wait performs in seconds": "", - "The kubernetes version that the minikube VM will use (ex: v1.2.3)": "", + "The kubernetes version that the minikube VM will use (ex: v1.2.3)": "Version de Kubernetes qu'utilisera la VM minikube (exemple : v1.2.3).", "The minikube VM is offline. Please run 'minikube start' to start it again.": "", + "The name of the network plugin": "Nom du plug-in réseau.", "The name of the network plugin.": "", "The number of bytes to use for 9p packet payload": "", "The path on the file system where the docs in markdown need to be saved": "", @@ -331,21 +366,24 @@ "The value passed to --format is invalid": "", "The value passed to --format is invalid: {{.error}}": "", "The vmwarefusion driver is deprecated and support for it will be removed in a future release.\n\t\t\tPlease consider switching to the new vmware unified driver, which is intended to replace the vmwarefusion driver.\n\t\t\tSee https://minikube.sigs.k8s.io/docs/reference/drivers/vmware/ for more information.\n\t\t\tTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "", - "The {{.driver_name}} driver should not be used with root privileges.": "", - "There's a new version for '{{.driver_executable}}'. Please consider upgrading. {{.documentation_url}}": "", + "The {{.driver_name}} driver should not be used with root privileges.": "Le pilote {{.driver_name}} ne doit pas être utilisé avec des droits racine.", + "There appears to be another hypervisor conflicting with KVM. Please stop the other hypervisor, or use --vm-driver to switch to it.": "", + "There's a new version for '{{.driver_executable}}'. Please consider upgrading. {{.documentation_url}}": "Une nouvelle version de \"{{.driver_executable}}\" est disponible. Pensez à effectuer la mise à niveau. {{.documentation_url}}", "These changes will take effect upon a minikube delete and then a minikube start": "", "This addon does not have an endpoint defined for the 'addons open' command.\nYou can add one by annotating a service with the label {{.labelName}}:{{.addonName}}": "", - "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true": "", - "This will keep the existing kubectl context and will create a minikube context.": "", + "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true": "Cette opération peut également être réalisée en définissant la variable d'environment \"CHANGE_MINIKUBE_NONE_USER=true\".", + "This will keep the existing kubectl context and will create a minikube context.": "Cela permet de conserver le contexte kubectl existent et de créer un contexte minikube.", + "This will start the mount daemon and automatically mount files into minikube": "Cela permet de lancer le daemon d'installation et d'installer automatiquement les fichiers dans minikube.", "This will start the mount daemon and automatically mount files into minikube.": "", - "Tip: To remove this root owned cluster, run: sudo {{.cmd}} delete": "", + "Tip: To remove this root owned cluster, run: sudo {{.cmd}} delete": "Conseil : Pour supprimer ce cluster appartenant à la racine, exécutez la commande \"sudo {{.cmd}} delete\".", "Tip: Use 'minikube start -p \u003cname\u003e' to create a new cluster, or 'minikube delete' to delete this one.": "", - "To connect to this cluster, use: kubectl --context={{.name}}": "", + "To connect to this cluster, use: kubectl --context={{.name}}": "Pour vous connecter à ce cluster, utilisez la commande \"kubectl --context={{.name}}\".", + "To connect to this cluster, use: kubectl --context={{.name}}__1": "Pour vous connecter à ce cluster, utilisez la commande \"kubectl --context={{.name}}\".", "To connect to this cluster, use: kubectl --context={{.profile_name}}": "", - "To disable this notice, run: 'minikube config set WantUpdateNotification false'": "", + "To disable this notice, run: 'minikube config set WantUpdateNotification false'\\n": "", + "To proceed, either:\n 1) Delete the existing VM using: '{{.command}} delete'\n or\n 2) Restart with the existing driver: '{{.command}} start --vm-driver={{.old_driver}}'": "", "To start minikube with HyperV Powershell must be in your PATH`": "", - "To switch drivers, you may create a new VM using `minikube start -p \u003cname\u003e --vm-driver={{.driver_name}}`": "", - "To use kubectl or minikube commands as your own user, you may need to relocate them. For example, to overwrite your own settings, run:": "", + "To use kubectl or minikube commands as your own user, you may need to relocate them. For example, to overwrite your own settings, run:": "Pour utiliser les commandes kubectl ou minikube sous votre propre nom d'utilisateur, vous devrez peut-être les déplacer. Par exemple, pour écraser vos propres paramètres, exécutez la commande suivante :", "Troubleshooting Commands:": "", "Unable to bind flags": "", "Unable to enable dashboard": "", @@ -353,51 +391,66 @@ "Unable to generate docs": "", "Unable to generate the documentation. Please ensure that the path specified is a directory, exists \u0026 you have permission to write to it.": "", "Unable to get VM IP address": "", - "Unable to get bootstrapper: {{.error}}": "", + "Unable to get bootstrapper: {{.error}}": "Impossible d'obtenir l'amorceur : {{.error}}", + "Unable to get current user": "", "Unable to get runtime": "", + "Unable to get the status of the cluster.": "", "Unable to kill mount process: {{.error}}": "", - "Unable to load cached images from config file.": "", + "Unable to load cached images from config file.": "Impossible de charger les images mises en cache depuis le fichier de configuration.", "Unable to load cached images: {{.error}}": "", - "Unable to load config: {{.error}}": "", - "Unable to parse \"{{.kubernetes_version}}\": {{.error}}": "", - "Unable to pull images, which may be OK: {{.error}}": "", + "Unable to load config: {{.error}}": "Impossible de charger la configuration : {{.error}}", + "Unable to parse \"{{.kubernetes_version}}\": {{.error}}": "Impossible d'analyser la version \"{{.kubernetes_version}}\" : {{.error}}", + "Unable to parse oldest Kubernetes version from constants: {{.error}}": "", + "Unable to pull images, which may be OK: {{.error}}": "Impossible d'extraire des images, qui sont peut-être au bon format : {{.error}}", "Unable to remove machine directory: %v": "", "Unable to start VM": "", "Unable to stop VM": "", - "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...": "", + "Unable to update {{.driver}} driver: {{.error}}": "", + "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...": "Désinstallation de Kubernetes {{.kubernetes_version}} à l'aide de {{.bootstrapper_name}}…", "Unmounting {{.path}} ...": "", + "Unset the KUBECONFIG environment variable, or verify that it does not point to an empty or otherwise invalid path": "", "Unset variables instead of setting them": "", "Update server returned an empty list": "", "Upgrade to QEMU v3.1.0+, run 'virt-host-validate', or ensure that you are not running in a nested VM environment.": "", - "Upgrading from Kubernetes {{.old}} to {{.new}}": "", + "Upgrading from Kubernetes {{.old}} to {{.new}}": "Mise à niveau de Kubernetes de la version {{.old}} à la version {{.new}}…", "Usage": "Usage", "Usage: minikube completion SHELL": "", "Usage: minikube delete": "", "Use \"{{.CommandPath}} [command] --help\" for more information about a command.": "", + "Use VirtualBox to remove the conflicting VM and/or network interfaces": "", + "Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'.": "", "User ID: {{.userID}}": "", "Userspace file server is shutdown": "", - "Userspace file server: ": "", - "Using image repository {{.name}}": "", + "Userspace file server:": "", + "Using image repository {{.name}}": "Utilisation du dépôt d'images {{.name}}…", "Using the running {{.driver_name}} \"{{.profile_name}}\" VM ...": "", + "VM driver is one of: %v": "Le pilote de la VM appartient à : %v", "Verify that your HTTP_PROXY and HTTPS_PROXY environment variables are set correctly.": "", "Verify the IP address of the running cluster in kubeconfig.": "", "Verifying dashboard health ...": "", "Verifying proxy health ...": "", "Verifying:": "Vérification :", "Version: {{.version}}": "", + "VirtualBox and Hyper-V are having a conflict. Use '--vm-driver=hyperv' or disable Hyper-V using: 'bcdedit /set hypervisorlaunchtype off'": "", + "VirtualBox cannot create a network, probably because it conflicts with an existing network that minikube no longer knows about. Try running 'minikube delete'": "", + "VirtualBox is broken. Disable real-time anti-virus software, reboot, and reinstall VirtualBox if the problem continues.": "", + "VirtualBox is broken. Reinstall VirtualBox, reboot, and run 'minikube delete'.": "", "Wait failed": "", "Wait failed: {{.error}}": "", + "Wait until Kubernetes core services are healthy before exiting": "Avant de quitter, veuillez patienter jusqu'à ce que les principaux services Kubernetes soient opérationnels.", "Wait until Kubernetes core services are healthy before exiting.": "", "Waiting for SSH access ...": "En attente de l'accès SSH...", "Waiting for the host to be provisioned ...": "", "Waiting for:": "En attente de :", - "Where to root the NFS Shares, defaults to /nfsshares (hyperkit driver only)": "", - "You appear to be using a proxy, but your NO_PROXY environment does not include the minikube IP ({{.ip_address}}). Please see {{.documentation_url}} for more details": "", - "You can delete them using the following command(s): ": "", - "You may need to manually remove the \"{{.name}}\" VM from your hypervisor": "", + "Where to root the NFS Shares, defaults to /nfsshares (hyperkit driver only)": "Emplacement permettant d'accéder aux partages NFS en mode root, la valeur par défaut affichant /nfsshares (pilote hyperkit uniquement).", + "You appear to be using a proxy, but your NO_PROXY environment does not include the minikube IP ({{.ip_address}}). Please see {{.documentation_url}} for more details": "Il semble que vous utilisiez un proxy, mais votre environment NO_PROXY n'inclut pas l'adresse IP ({{.ip_address}}) de minikube. Consultez la documentation à l'adresse {{.documentation_url}} pour en savoir plus.", + "You can delete them using the following command(s):": "", + "You may need to manually remove the \"{{.name}}\" VM from your hypervisor": "Vous devrez peut-être supprimer la VM \"{{.name}}\" manuellement de votre hyperviseur.", "You must specify a service name": "", "Your host does not support KVM virtualization. Ensure that qemu-kvm is installed, and run 'virt-host-validate' to debug the problem": "", + "Your host does not support virtualization. If you are running minikube within a VM, try '--vm-driver=none'. Otherwise, enable virtualization in your BIOS": "", "Your host is failing to route packets to the minikube VM. If you have VPN software, try turning it off or configuring it so that it does not re-route traffic to the VM IP. If not, check your VM environment routing options.": "", + "Your minikube config refers to an unsupported driver. Erase ~/.minikube, and try again.": "", "Your minikube vm is not running, try minikube start.": "", "addon '{{.name}}' is currently not enabled.\nTo enable this addon run:\nminikube addons enable {{.name}}": "", "addon '{{.name}}' is not a valid addon packaged with minikube.\nTo see the list of available addons run:\nminikube addons list": "", @@ -408,7 +461,7 @@ "browser failed to open url: {{.error}}": "", "call with cleanup=true to remove old tunnels": "", "command runner": "", - "config modifies minikube config files using subcommands like \"minikube config set vm-driver kvm\"\nConfigurable fields: \\n\\n": "", + "config modifies minikube config files using subcommands like \"minikube config set vm-driver kvm\"\nConfigurable fields:\\n\\n": "", "config view failed": "", "dashboard service is not running: {{.error}}": "", "disable failed": "", @@ -420,7 +473,7 @@ "error starting tunnel": "", "failed to open browser: {{.error}}": "", "if true, will embed the certs in kubeconfig.": "", - "kubectl and minikube configuration will be stored in {{.home_folder}}": "", + "kubectl and minikube configuration will be stored in {{.home_folder}}": "Les configurations kubectl et minikube seront stockées dans le dossier {{.home_folder}}.", "kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "", "kubectl proxy": "", "logdir set failed": "", @@ -429,12 +482,13 @@ "minikube is unable to access the Google Container Registry. You may need to configure it to use a HTTP proxy.": "", "minikube profile was successfully set to {{.profile_name}}": "", "minikube {{.version}} is available! Download it: {{.url}}": "", - "minikube {{.version}} on {{.os}} ({{.arch}})": "minikube {{.version}} sur {{.os}} ({{.arch}})", "mount argument \"{{.value}}\" must be in form: \u003csource directory\u003e:\u003ctarget directory\u003e": "", "mount failed": "", + "not enough arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", "profile sets the current minikube profile, or gets the current profile if no arguments are provided. This is used to run and manage multiple minikube instance. You can return to the default minikube profile by running `minikube profile default`": "", "service {{.namespace_name}}/{{.service_name}} has no node port": "", "stat failed": "", + "toom any arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", "tunnel creates a route to services deployed with type LoadBalancer and sets their Ingress to their ClusterIP": "", "tunnel makes services of type LoadBalancer accessible on localhost": "", "unable to bind flags": "", @@ -450,8 +504,9 @@ "usage: minikube addons enable ADDON_NAME": "", "usage: minikube addons list": "", "usage: minikube addons open ADDON_NAME": "", - "usage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", "usage: minikube config unset PROPERTY_NAME": "", + "usage: minikube delete": "", + "usage: minikube delete --all": "", "usage: minikube profile [MINIKUBE_PROFILE_NAME]": "", "zsh completion failed": "", "{{.addonName}} was successfully enabled": "", @@ -464,4 +519,4 @@ "{{.prefix}}minikube {{.version}} on {{.platform}}": "{{.prefix}}minikube {{.version}} sur {{.platform}}", "{{.type}} is not yet a supported filesystem. We will try anyways!": "", "{{.url}} is not accessible: {{.error}}": "" -} \ No newline at end of file +} diff --git a/translations/ja.json b/translations/ja.json new file mode 100644 index 0000000000..a63609e260 --- /dev/null +++ b/translations/ja.json @@ -0,0 +1,518 @@ +{ + "\"{{.minikube_addon}}\" was successfully disabled": "", + "\"{{.name}}\" cluster does not exist. Proceeding ahead with cleanup.": "", + "\"{{.name}}\" profile does not exist": "「{{.name}}」プロファイルは存在しません", + "\"{{.profile_name}}\" VM does not exist, nothing to stop": "", + "\"{{.profile_name}}\" host does not exist, unable to show an IP": "", + "\"{{.profile_name}}\" stopped.": "", + "'none' driver does not support 'minikube docker-env' command": "", + "'none' driver does not support 'minikube mount' command": "", + "'none' driver does not support 'minikube ssh' command": "", + "A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "", + "A firewall is blocking Docker within the minikube VM from reaching the internet. You may need to configure it to use a proxy.": "", + "A firewall is interfering with minikube's ability to make outgoing HTTPS requests. You may need to change the value of the HTTPS_PROXY environment variable.": "", + "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.": "", + "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "Kubernetes 用に生成された証明書で使用される一連の API サーバー IP アドレス。マシンの外部から API サーバーを利用できるようにする場合に使用します。", + "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "Kubernetes 用に生成された証明書で使用される一連の API サーバー名。マシンの外部から API サーバーを利用できるようにする場合に使用します。", + "A set of key=value pairs that describe configuration that may be passed to different components.\nThe key should be '.' separated, and the first part before the dot is the component to apply the configuration to.\nValid components are: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy, scheduler\nValid kubeadm parameters:": "さまざまなコンポーネントに渡される可能性のある構成を記述する一連の key=value ペア。\nキーは「.」で区切る必要があり、このドットより前の部分は構成の適用先のコンポーネントを表します。\n有効なコンポーネントは、kubelet、kubeadm、apiserver、controller-manager、etcd、proxy、scheduler です。\n有効な kubeadm パラメータ:", + "A set of key=value pairs that describe feature gates for alpha/experimental features.": "アルファ版または試験運用版の機能の機能ゲートを記述する一連の key=value ペア。", + "Access the kubernetes dashboard running within the minikube cluster": "", + "Add an image to local cache.": "", + "Add machine IP to NO_PROXY environment variable": "", + "Add or delete an image from the local cache.": "", + "Additional help topics": "", + "Additional mount options, such as cache=fscache": "", + "Advanced Commands:": "", + "Aliases": "", + "Allow user prompts for more information": "", + "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \\\"auto\\\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers": "Docker イメージの pull 元の代替イメージ リポジトリ。これは、gcr.io へのアクセスが制限されている場合に使用できます。これを \\\"auto\\\" に設定すると、minikube によって自動的に指定されるようになります。中国本土のユーザーの場合、registry.cn-hangzhou.aliyuncs.com/google_containers などのローカル gcr.io ミラーを使用できます。", + "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "minikube VM に割り当てられた RAM 容量(形式: \u003cnumber\u003e[\u003cunit\u003e]、unit = b、k、m、g)", + "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "", + "Amount of time to wait for a service in seconds": "", + "Amount of time to wait for service in seconds": "", + "Available Commands": "", + "Basic Commands:": "", + "Cannot find directory {{.path}} for mount": "", + "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.": "", + "Check that your --kubernetes-version has a leading 'v'. For example: 'v1.1.14'": "", + "Check that your apiserver flags are valid, or run 'minikube delete'": "", + "Check your firewall rules for interference, and run 'virt-host-validate' to check for KVM configuration issues. If you are running minikube within a VM, consider using --vm-driver=none": "", + "Configuration and Management Commands:": "", + "Configure an external network switch following the official documentation, then add `--hyperv-virtual-switch=\u003cswitch-name\u003e` to `minikube start`": "", + "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list": "", + "Configuring local host environment ...": "", + "Confirm that you have a working internet connection and that your VM has not run out of resources by using: 'minikube logs'": "", + "Confirm that you have supplied the correct value to --hyperv-virtual-switch using the 'Get-VMSwitch' command": "", + "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn.": "使用するイメージミラーの国コード。グローバルのものを使用する場合は空のままにします。中国本土のユーザーの場合は、「cn」に設定します。", + "Created a new profile : {{.profile_name}}": "", + "Creating a new profile failed": "", + "Creating mount {{.name}} ...": "マウント {{.name}} を作成しています...", + "Creating {{.driver_name}} VM (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "", + "Default group id used for the mount": "", + "Default user id used for the mount": "", + "Delete an image from the local cache.": "", + "Deletes a local kubernetes cluster": "", + "Deletes a local kubernetes cluster. This command deletes the VM, and removes all\nassociated files.": "", + "Deletes a local kubernetes cluster. This command deletes the VM, and removes all associated files.": "ローカルの Kubernetes クラスタを削除します。このコマンドによって、VM とそれに関連付けられているすべてのファイルが削除されます。", + "Deleting \"{{.profile_name}}\" in {{.driver_name}} ...": "{{.driver_name}} の「{{.profile_name}}」を削除しています...", + "Disable checking for the availability of hardware virtualization before the vm is started (virtualbox driver only)": "VM が起動する前にハードウェアの仮想化の可用性チェックを無効にします(virtualbox ドライバのみ)", + "Disable dynamic memory in your VM manager, or pass in a larger --memory value": "", + "Disables the addon w/ADDON_NAME within minikube (example: minikube addons disable dashboard). For a list of available addons use: minikube addons list": "", + "Disables the filesystem mounts provided by the hypervisors": "ハイパーバイザによって指定されているファイル システム マウントを無効にします", + "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "minikube VM に割り当てられたディスクサイズ(形式: \u003cnumber\u003e[\u003cunit\u003e]、unit = b、k、m、g)", + "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "", + "Display dashboard URL instead of opening a browser": "", + "Display the kubernetes addons URL in the CLI instead of opening it in the default browser": "", + "Display the kubernetes service URL in the CLI instead of opening it in the default browser": "", + "Display values currently set in the minikube config file": "", + "Display values currently set in the minikube config file.": "", + "Docker inside the VM is unavailable. Try running 'minikube delete' to reset the VM.": "", + "Docs have been saved at - {{.path}}": "", + "Documentation: {{.url}}": "", + "Done! kubectl is now configured to use \"{{.name}}": "完了しました。kubectl が「{{.name}}」を使用するよう構成されました", + "Done! kubectl is now configured to use \"{{.name}}\"": "", + "Done! kubectl is now configured to use \"{{.name}}__1": "完了しました。kubectl が「{{.name}}」を使用するよう構成されました", + "Download complete!": "ダウンロードが完了しました。", + "Downloading VM boot image ...": "", + "Downloading driver {{.driver}}:": "", + "Downloading {{.name}} {{.version}}": "", + "ERROR creating `registry-creds-dpr` secret": "", + "ERROR creating `registry-creds-ecr` secret: {{.error}}": "", + "ERROR creating `registry-creds-gcr` secret: {{.error}}": "", + "Either systemctl is not installed, or Docker is broken. Run 'sudo systemctl start docker' and 'journalctl -u docker'": "", + "Enable experimental NVIDIA GPU support in minikube": "minikube での試験運用版 NVIDIA GPU の対応を有効にします", + "Enable host resolver for NAT DNS requests (virtualbox driver only)": "NAT DNS リクエスト用のホストリゾルバを有効にします(virtualbox ドライバのみ)", + "Enable proxy for NAT DNS requests (virtualbox driver only)": "NAT DNS リクエスト用のプロキシを有効にします(virtualbox ドライバのみ)", + "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\": "デフォルトの CNI プラグイン(/etc/cni/net.d/k8s.conf)を有効にします。\\\"--network-plugin=cni\\\" と組み合わせて使用されます。", + "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\\".": "", + "Enables the addon w/ADDON_NAME within minikube (example: minikube addons enable dashboard). For a list of available addons use: minikube addons list": "", + "Enabling dashboard ...": "", + "Environment variables to pass to the Docker daemon. (format: key=value)": "Docker デーモンに渡す環境変数(形式: Key=Value)", + "Error checking driver version: {{.error}}": "ドライバのバージョンの確認中にエラーが発生しました。{{.error}}", + "Error creating list template": "", + "Error creating minikube directory": "", + "Error creating status template": "", + "Error creating view template": "", + "Error executing list template": "", + "Error executing status template": "", + "Error executing template": "", + "Error executing view template": "", + "Error finding port for mount": "", + "Error getting IP": "", + "Error getting bootstrapper": "", + "Error getting client": "", + "Error getting client: {{.error}}": "", + "Error getting cluster": "", + "Error getting cluster bootstrapper": "", + "Error getting config": "", + "Error getting host": "", + "Error getting host status": "", + "Error getting machine logs": "", + "Error getting machine status": "", + "Error getting service status": "", + "Error getting service with namespace: {{.namespace}} and labels {{.labelName}}:{{.addonName}}: {{.error}}": "", + "Error getting the host IP address to use from within the VM": "", + "Error host driver ip status": "", + "Error killing mount process": "", + "Error loading api": "", + "Error loading profile config": "", + "Error loading profile config: {{.error}}": "", + "Error loading profile {{.name}}: {{.error}}": "プロファイル {{.name}} の読み込み中にエラーが発生しました。{{.error}}", + "Error opening service": "", + "Error parsing minikube version: {{.error}}": "minikube バージョンの解析中にエラーが発生しました。{{.error}}", + "Error parsing vmDriver version: {{.error}}": "vmDriver バージョンの解析中にエラーが発生しました。{{.error}}", + "Error reading {{.path}}: {{.error}}": "", + "Error restarting cluster": "", + "Error setting shell variables": "", + "Error starting cluster": "", + "Error starting mount": "", + "Error unsetting shell variables": "", + "Error while setting kubectl current context : {{.error}}": "", + "Error writing mount pid": "", + "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}\"": "", + "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}": "エラー: Kubernetes v{{.new}} が選択されましたが、使用しているプロファイルの既存クラスタで実行されているのは Kubernetes v{{.old}} です。非破壊的なダウングレードはサポートされていませんが、以下のいずれかの方法で続行できます。\n* Kubernetes v{{.new}} を使用してクラスタを再作成する: 「minikube delete {{.profile}}」を実行してから、「minikube start {{.profile}} --kubernetes-version={{.new}}」を実行します。\n* Kubernetes v{{.new}} を使用して 2 つ目のクラスタを作成する: 「minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}」を実行します。\n* Kubernetes v{{.old}} 以降を使用して既存のクラスタを再利用する: 「minikube start {{.profile}} --kubernetes-version={{.old}}」を実行します。", + "Error: [{{.id}}] {{.error}}": "", + "Examples": "", + "Exiting": "終了しています", + "Exiting due to driver incompatibility": "", + "Failed runtime": "", + "Failed to cache ISO": "", + "Failed to cache and load images": "", + "Failed to cache binaries": "", + "Failed to cache images": "", + "Failed to change permissions for {{.minikube_dir_path}}: {{.error}}": "{{.minikube_dir_path}} に対する権限を変更できませんでした。{{.error}}", + "Failed to check if machine exists": "", + "Failed to check main repository and mirrors for images for images": "", + "Failed to delete cluster: {{.error}}": "クラスタを削除できませんでした。{{.error}}", + "Failed to delete cluster: {{.error}}__1": "クラスタを削除できませんでした。{{.error}}", + "Failed to delete images": "", + "Failed to delete images from config": "", + "Failed to download kubectl": "", + "Failed to enable container runtime": "", + "Failed to generate config": "", + "Failed to get bootstrapper": "", + "Failed to get command runner": "", + "Failed to get driver URL": "", + "Failed to get image map": "", + "Failed to get machine client": "", + "Failed to get service URL: {{.error}}": "", + "Failed to kill mount process: {{.error}}": "マウント プロセスを強制終了できませんでした。{{.error}}", + "Failed to list cached images": "", + "Failed to remove profile": "", + "Failed to save config": "", + "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}": "NO_PROXY 環境変数を設定できませんでした。「export NO_PROXY=$NO_PROXY,{{.ip}}」を使用してください。", + "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}`.": "", + "Failed to setup certs": "", + "Failed to setup kubeconfig": "", + "Failed to update cluster": "", + "Failed to update config": "", + "Failed unmount: {{.error}}": "", + "File permissions used for the mount": "", + "Flags": "", + "Follow": "", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "最適な結果を得るには、kubectl を次のサイト https://kubernetes.io/docs/tasks/tools/install-kubectl/ からインストールしてください", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/__1": "最適な結果を得るには、kubectl を次のサイト https://kubernetes.io/docs/tasks/tools/install-kubectl/ からインストールしてください", + "For more information, see:": "詳細については、次をご覧ください。", + "Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect": "", + "Force minikube to perform possibly dangerous operations": "minikube で危険な可能性のある操作を強制的に実行します", + "Found network options:": "ネットワーク オプションが見つかりました。", + "Found {{.number}} invalid profile(s) !": "", + "Gets the kubernetes URL(s) for the specified service in your local cluster": "", + "Gets the kubernetes URL(s) for the specified service in your local cluster. In the case of multiple URLs they will be printed one at a time.": "", + "Gets the logs of the running instance, used for debugging minikube, not user code.": "", + "Gets the status of a local kubernetes cluster": "", + "Gets the status of a local kubernetes cluster.\n\tExit status contains the status of minikube's VM, cluster and kubernetes encoded on it's bits in this order from right to left.\n\tEg: 7 meaning: 1 (for minikube NOK) + 2 (for cluster NOK) + 4 (for kubernetes NOK)": "", + "Gets the value of PROPERTY_NAME from the minikube config file": "", + "Global Flags": "", + "Go template format string for the addon list output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd/config#AddonListTemplate": "", + "Go template format string for the cache list output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#CacheListTemplate": "", + "Go template format string for the config view output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd/config#ConfigViewTemplate": "", + "Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#Status": "", + "Group ID: {{.groupID}}": "", + "Have you set up libvirt correctly?": "", + "Hide the hypervisor signature from the guest in minikube (kvm2 driver only)": "minikube でゲストに対し、ハイパーバイザ署名を非表示にします(kvm2 ドライバのみ)", + "If the above advice does not help, please let us know:": "", + "If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --vm-driver=none.": "true の場合、現在のブートストラッパの Docker イメージをキャッシュに保存して、マシンに読み込みます。--vm-driver=none の場合は常に false です。", + "If true, only download and cache files for later use - don't install or start anything.": "true の場合、後で使用できるようにファイルのダウンロードとキャッシュ保存だけが行われます。インストールも起動も行われません。", + "If using the none driver, ensure that systemctl is installed": "", + "If you are running minikube within a VM, consider using --vm-driver=none:": "", + "Images Commands:": "", + "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "", + "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "Docker デーモンに渡す Docker レジストリが安全ではありません。デフォルトのサービス CIDR 範囲が自動的に追加されます。", + "Install VirtualBox, or select an alternative value for --vm-driver": "", + "Install the latest hyperkit binary, and run 'minikube delete'": "", + "Invalid size passed in argument: {{.error}}": "", + "IsEnabled failed": "", + "Kill the mount process spawned by minikube start": "", + "Kubernetes {{.version}} is not supported by this release of minikube": "", + "Launching Kubernetes ...": "Kubernetes を起動しています...", + "Launching proxy ...": "", + "List all available images from the local cache.": "", + "List of guest VSock ports that should be exposed as sockets on the host (hyperkit driver only)": "ホストでソケットとして公開する必要のあるゲスト VSock ポートのリスト(hyperkit ドライバのみ)", + "Lists all available minikube addons as well as their current statuses (enabled/disabled)": "", + "Lists all minikube profiles.": "", + "Lists all valid minikube profiles and detects all possible invalid profiles.": "", + "Lists the URLs for the services in your local cluster": "", + "Local folders to share with Guest via NFS mounts (hyperkit driver only)": "NFS マウントを介してゲストと共有するローカル フォルダ(hyperkit ドライバのみ)", + "Location of the VPNKit socket used for networking. If empty, disables Hyperkit VPNKitSock, if 'auto' uses Docker for Mac VPNKit connection, otherwise uses the specified VSock (hyperkit driver only)": "ネットワーキングに使用する VPNKit ソケットのロケーション。空の場合、Hyperkit VPNKitSock が無効になり、「auto」の場合、Mac VPNKit 接続に Docker が使用され、それ以外の場合、指定された VSock が使用されます(hyperkit ドライバのみ)", + "Location of the minikube iso": "minikube iso のロケーション", + "Location of the minikube iso.": "", + "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'": "", + "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'.": "", + "Message Size: {{.size}}": "", + "Minikube is a CLI tool that provisions and manages single-node Kubernetes clusters optimized for development workflows.": "", + "Minikube is a tool for managing local Kubernetes clusters.": "", + "Modify minikube config": "", + "Modify minikube's kubernetes addons": "", + "Mount type: {{.name}}": "", + "Mounting host path {{.sourcePath}} into VM as {{.destinationPath}} ...": "", + "Mounts the specified directory into minikube": "", + "Mounts the specified directory into minikube.": "", + "NOTE: This process must stay alive for the mount to be accessible ...": "", + "Networking and Connectivity Commands:": "", + "No minikube profile was found. You can create one using `minikube start`.": "", + "None of the known repositories in your location are accessible. Using {{.image_repository_name}} as fallback.": "使用しているロケーション内で既知のいずれのリポジトリにもアクセスできません。フォールバックとして {{.image_repository_name}} を使用します。", + "None of the known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag": "既知のいずれのリポジトリにもアクセスできません。--image-repository フラグとともに代替のイメージ リポジトリを指定することを検討してください。", + "Not passing {{.name}}={{.value}} to docker env.": "", + "Number of CPUs allocated to the minikube VM": "minikube VM に割り当てられた CPU の数", + "Number of CPUs allocated to the minikube VM.": "", + "Number of lines back to go within the log": "", + "OS release is {{.pretty_name}}": "", + "Open the addons URL with https instead of http": "", + "Open the service URL with https instead of http": "", + "Opening kubernetes service {{.namespace_name}}/{{.service_name}} in default browser...": "", + "Opening {{.url}} in your default browser...": "", + "Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list": "", + "Options: {{.options}}": "", + "Outputs minikube shell completion for the given shell (bash or zsh)": "", + "Outputs minikube shell completion for the given shell (bash or zsh)\n\n\tThis depends on the bash-completion binary. Example installation instructions:\n\tOS X:\n\t\t$ brew install bash-completion\n\t\t$ source $(brew --prefix)/etc/bash_completion\n\t\t$ minikube completion bash \u003e ~/.minikube-completion # for bash users\n\t\t$ minikube completion zsh \u003e ~/.minikube-completion # for zsh users\n\t\t$ source ~/.minikube-completion\n\tUbuntu:\n\t\t$ apt-get install bash-completion\n\t\t$ source /etc/bash-completion\n\t\t$ source \u003c(minikube completion bash) # for bash users\n\t\t$ source \u003c(minikube completion zsh) # for zsh users\n\n\tAdditionally, you may want to output the completion to a file and source in your .bashrc\n\n\tNote for zsh users: [1] zsh completions are only supported in versions of zsh \u003e= 5.2": "", + "Permissions: {{.octalMode}} ({{.writtenMode}})": "", + "Please enter a value:": "", + "Please install the minikube hyperkit VM driver, or select an alternative --vm-driver": "", + "Please install the minikube kvm2 VM driver, or select an alternative --vm-driver": "", + "Please make sure the service you are looking for is deployed or is in the correct namespace.": "", + "Please specify the directory to be mounted: \n\tminikube mount \u003csource directory\u003e:\u003ctarget directory\u003e (example: \"/host-home:/vm-home\")": "", + "Please upgrade the '{{.driver_executable}}'. {{.documentation_url}}": "「{{.driver_executable}}」をアップグレードしてください。{{.documentation_url}}", + "Populates the specified folder with documentation in markdown about minikube": "", + "Powering off \"{{.profile_name}}\" via SSH ...": "SSH 経由で「{{.profile_name}}」の電源をオフにしています...", + "Preparing Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}} ...": "{{.runtime}} {{.runtimeVersion}} で Kubernetes {{.k8sVersion}} を準備しています...", + "Print current and latest version number": "", + "Print the version of minikube": "", + "Print the version of minikube.": "", + "Problems detected in {{.entry}}:": "", + "Problems detected in {{.name}}:": "", + "Profile gets or sets the current minikube profile": "", + "Provide VM UUID to restore MAC address (hyperkit driver only)": "MAC アドレスを復元するための VM UUID を指定します(hyperkit ドライバのみ)", + "Pulling images ...": "", + "Reboot to complete VirtualBox installation, and verify that VirtualBox is not blocked by your system": "", + "Rebuild libvirt with virt-network support": "", + "Received {{.name}} signal": "", + "Registry mirrors to pass to the Docker daemon": "Docker デーモンに渡すレジストリ ミラー", + "Reinstall VirtualBox and reboot. Alternatively, try the kvm2 driver: https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/": "", + "Reinstall VirtualBox and verify that it is not blocked: System Preferences -\u003e Security \u0026 Privacy -\u003e General -\u003e Some system software was blocked from loading": "", + "Related issues:": "", + "Relaunching Kubernetes using {{.bootstrapper}} ...": "{{.bootstrapper}} を使用して Kubernetes を再起動しています...", + "Removing {{.directory}} ...": "{{.directory}} を削除しています...", + "Requested CPU count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}": "", + "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}": "リクエストされたディスクサイズ {{.requested_size}} が最小値 {{.minimum_size}} 未満です", + "Requested memory allocation ({{.memory}}MB) is less than the default memory allocation of {{.default_memorysize}}MB. Beware that minikube might not work correctly or crash unexpectedly.": "リクエストされたメモリ割り当て({{.memory}} MB)がデフォルトのメモリ割り当て {{.default_memorysize}} MB 未満です。minikube が正常に動作しないか、予期せずクラッシュする可能性があることに注意してください。", + "Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}": "リクエストされたメモリ割り当て {{.requested_size}} が許可される最小値 {{.minimum_size}} 未満です", + "Retriable failure: {{.error}}": "", + "Retrieve the ssh identity key path of the specified cluster": "", + "Retrieve the ssh identity key path of the specified cluster.": "", + "Retrieves the IP address of the running cluster": "", + "Retrieves the IP address of the running cluster, and writes it to STDOUT.": "", + "Retrieves the IP address of the running cluster, checks it\n\t\t\twith IP in kubeconfig, and corrects kubeconfig if incorrect.": "", + "Returns the value of PROPERTY_NAME from the minikube config file. Can be overwritten at runtime by flags or environmental variables.": "", + "Run 'kubectl describe pod coredns -n kube-system' and check for a firewall or DNS conflict": "", + "Run 'minikube delete' to delete the stale VM": "", + "Run kubectl": "", + "Run minikube from the C: drive.": "", + "Run the kubernetes client, download it if necessary.\nExamples:\nminikube kubectl -- --help\nkubectl get pods --namespace kube-system": "", + "Run the minikube command as an Administrator": "", + "Running on localhost (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "", + "Set failed": "", + "Sets an individual value in a minikube config file": "", + "Sets the PROPERTY_NAME config value to PROPERTY_VALUE\n\tThese values can be overwritten by flags or environment variables at runtime.": "", + "Sets up docker env variables; similar to '$(docker-machine env)'": "", + "Sets up docker env variables; similar to '$(docker-machine env)'.": "", + "Setting profile failed": "", + "Show only log entries which point to known problems": "", + "Show only the most recent journal entries, and continuously print new entries as they are appended to the journal.": "", + "Skipped switching kubectl context for {{.profile_name}} because --keep-context was set.": "", + "Sorry that minikube crashed. If this was unexpected, we would love to hear from you:": "", + "Sorry, Kubernetes {{.version}} is not supported by this release of minikube": "", + "Sorry, completion support is not yet implemented for {{.name}}": "", + "Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config": "申し訳ありません。現在、kubeadm.{{.parameter_name}} パラメータは --extra-config でサポートされていません", + "Sorry, the url provided with the --registry-mirror flag is invalid: {{.url}}": "申し訳ありません。--registry-mirror フラグとともに指定された URL は無効です。{{.url}}", + "Specified Kubernetes version {{.specified}} is less than the oldest supported version: {{.oldest}}": "", + "Specify --kubernetes-version in v\u003cmajor\u003e.\u003cminor.\u003cbuild\u003e form. example: 'v1.1.14'": "", + "Specify an alternate --host-only-cidr value, such as 172.16.0.1/24": "", + "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)": "Docker デーモンに渡す任意のフラグを指定します(形式: key=value)。", + "Specify the 9p version that the mount should use": "", + "Specify the ip that the mount should be setup on": "", + "Specify the mount filesystem type (supported types: 9p)": "", + "Starting existing {{.driver_name}} VM for \"{{.profile_name}}\" ...": "", + "Starts a local kubernetes cluster": "ローカルの Kubernetes クラスタを起動します", + "Stopping \"{{.profile_name}}\" in {{.driver_name}} ...": "", + "Stops a local kubernetes cluster running in Virtualbox. This command stops the VM\nitself, leaving all files intact. The cluster can be started again with the \"start\" command.": "", + "Stops a running local kubernetes cluster": "", + "Successfully mounted {{.sourcePath}} to {{.destinationPath}}": "", + "Suggestion: {{.advice}}": "", + "Target directory {{.path}} must be an absolute path": "", + "The \"{{.driver_name}}\" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}": "「{{.driver_name}}」ドライバにはルート権限が必要です。「sudo minikube --vm-driver={{.driver_name}}」を使用して minikube を実行してください", + "The \"{{.driver_name}}\" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}'.": "", + "The \"{{.driver_name}}\" driver should not be used with root privileges.": "", + "The \"{{.name}}\" cluster has been deleted.": "「{{.name}}」クラスタが削除されました。", + "The \"{{.name}}\" cluster has been deleted.__1": "「{{.name}}」クラスタが削除されました。", + "The 'none' driver does not support multiple profiles: https://minikube.sigs.k8s.io/docs/reference/drivers/none/": "", + "The 'none' driver provides limited isolation and may reduce system security and reliability.": "ドライバに「none」を指定すると、分離が制限され、システムのセキュリティと信頼性が低下する可能性があります。", + "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\\n\\n{{ .example }}\\n": "", + "The CIDR to be used for service cluster IPs.": "サービス クラスタ IP に使用される CIDR。", + "The CIDR to be used for the minikube VM (virtualbox driver only)": "minikube VM に使用される CIDR(virtualbox ドライバのみ)", + "The KVM QEMU connection URI. (kvm2 driver only)": "KVM QEMU 接続 URI(kvm2 ドライバのみ)", + "The KVM driver is unable to resurrect this old VM. Please run `minikube delete` to delete it and try again.": "", + "The KVM network name. (kvm2 driver only)": "KVM ネットワーク名(kvm2 ドライバのみ)", + "The VM driver crashed. Run 'minikube start --alsologtostderr -v=8' to see the VM driver error message": "", + "The VM driver exited with an error, and may be corrupt. Run 'minikube start' with --alsologtostderr -v=8 to see the error": "", + "The VM that minikube is configured for no longer exists. Run 'minikube delete'": "", + "The apiserver listening port": "API サーバー リスニング ポート", + "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "Kubernetes 用に生成された証明書で使用される API サーバー名。マシンの外部から API サーバーを利用できるようにする場合に使用します。", + "The argument to pass the minikube mount command on start": "起動時に minikube マウント コマンドを渡す引数", + "The argument to pass the minikube mount command on start.": "", + "The cluster dns domain name used in the kubernetes cluster": "Kubernetes クラスタで使用されるクラスタ DNS ドメイン名", + "The container runtime to be used (docker, crio, containerd)": "使用されるコンテナ ランタイム(docker、crio、containerd)", + "The container runtime to be used (docker, crio, containerd).": "", + "The cri socket path to be used": "使用される CRI ソケットパス", + "The cri socket path to be used.": "", + "The docker host is currently not running": "", + "The docker service is currently not active": "", + "The driver '{{.driver}}' is not supported on {{.os}}": "ドライバ「{{.driver}}」は、{{.os}} ではサポートされていません", + "The existing \"{{.profile_name}}\" VM that was created using the \"{{.old_driver}}\" driver, and is incompatible with the \"{{.driver}}\" driver.": "", + "The hyperv virtual switch name. Defaults to first found. (hyperv driver only)": "hyperv 仮想スイッチ名。最初に見つかったものにデフォルト設定されます(hyperv ドライバのみ)", + "The initial time interval for each check that wait performs in seconds": "", + "The kubernetes version that the minikube VM will use (ex: v1.2.3)": "minikube VM で使用される Kubernetes バージョン(例: v1.2.3)", + "The minikube VM is offline. Please run 'minikube start' to start it again.": "", + "The name of the network plugin": "ネットワーク プラグインの名前", + "The name of the network plugin.": "", + "The number of bytes to use for 9p packet payload": "", + "The path on the file system where the docs in markdown need to be saved": "", + "The service namespace": "", + "The services namespace": "", + "The time interval for each check that wait performs in seconds": "", + "The value passed to --format is invalid": "", + "The value passed to --format is invalid: {{.error}}": "", + "The vmwarefusion driver is deprecated and support for it will be removed in a future release.\n\t\t\tPlease consider switching to the new vmware unified driver, which is intended to replace the vmwarefusion driver.\n\t\t\tSee https://minikube.sigs.k8s.io/docs/reference/drivers/vmware/ for more information.\n\t\t\tTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "", + "The {{.driver_name}} driver should not be used with root privileges.": "{{.driver_name}} ドライバをルート権限で使用しないでください。", + "There appears to be another hypervisor conflicting with KVM. Please stop the other hypervisor, or use --vm-driver to switch to it.": "", + "There's a new version for '{{.driver_executable}}'. Please consider upgrading. {{.documentation_url}}": "「{{.driver_executable}}」の新しいバージョンがあります。アップグレードを検討してください。{{.documentation_url}}", + "These changes will take effect upon a minikube delete and then a minikube start": "", + "This addon does not have an endpoint defined for the 'addons open' command.\nYou can add one by annotating a service with the label {{.labelName}}:{{.addonName}}": "", + "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true": "これは環境変数 CHANGE_MINIKUBE_NONE_USER=true を設定して自動的に行うこともできます", + "This will keep the existing kubectl context and will create a minikube context.": "これにより既存の kubectl コンテキストが保持され、minikube コンテキストが作成されます。", + "This will start the mount daemon and automatically mount files into minikube": "これによりマウント デーモンが起動し、ファイルが minikube に自動的にマウントされます", + "This will start the mount daemon and automatically mount files into minikube.": "", + "Tip: To remove this root owned cluster, run: sudo {{.cmd}} delete": "ヒント: この root 所有のクラスタを削除するには、「sudo {{.cmd}} delete」を実行します", + "Tip: Use 'minikube start -p \u003cname\u003e' to create a new cluster, or 'minikube delete' to delete this one.": "", + "To connect to this cluster, use: kubectl --context={{.name}}": "このクラスタに接続するには、「kubectl --context={{.name}}」を使用します", + "To connect to this cluster, use: kubectl --context={{.name}}__1": "このクラスタに接続するには、「kubectl --context={{.name}}」を使用します", + "To connect to this cluster, use: kubectl --context={{.profile_name}}": "", + "To disable this notice, run: 'minikube config set WantUpdateNotification false'\\n": "", + "To proceed, either:\n 1) Delete the existing VM using: '{{.command}} delete'\n or\n 2) Restart with the existing driver: '{{.command}} start --vm-driver={{.old_driver}}'": "", + "To start minikube with HyperV Powershell must be in your PATH`": "", + "To use kubectl or minikube commands as your own user, you may need to relocate them. For example, to overwrite your own settings, run:": "kubectl か minikube コマンドを独自のユーザーとして使用するには、そのコマンドの再配置が必要な場合があります。たとえば、独自の設定を上書きするには、以下を実行します。", + "Troubleshooting Commands:": "", + "Unable to bind flags": "", + "Unable to enable dashboard": "", + "Unable to fetch latest version info": "", + "Unable to generate docs": "", + "Unable to generate the documentation. Please ensure that the path specified is a directory, exists \u0026 you have permission to write to it.": "", + "Unable to get VM IP address": "", + "Unable to get bootstrapper: {{.error}}": "ブートストラッパを取得できません。{{.error}}", + "Unable to get current user": "", + "Unable to get runtime": "", + "Unable to get the status of the cluster.": "", + "Unable to kill mount process: {{.error}}": "", + "Unable to load cached images from config file.": "キャッシュに保存されているイメージを構成ファイルから読み込むことができません。", + "Unable to load cached images: {{.error}}": "", + "Unable to load config: {{.error}}": "構成を読み込むことができません。{{.error}}", + "Unable to parse \"{{.kubernetes_version}}\": {{.error}}": "「{{.kubernetes_version}}」を解析できません。{{.error}}", + "Unable to parse oldest Kubernetes version from constants: {{.error}}": "", + "Unable to pull images, which may be OK: {{.error}}": "イメージを pull できませんが、問題ありません。{{.error}}", + "Unable to remove machine directory: %v": "", + "Unable to start VM": "", + "Unable to stop VM": "", + "Unable to update {{.driver}} driver: {{.error}}": "", + "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...": "{{.bootstrapper_name}} を使用して Kubernetes {{.kubernetes_version}} をアンインストールしています...", + "Unmounting {{.path}} ...": "", + "Unset the KUBECONFIG environment variable, or verify that it does not point to an empty or otherwise invalid path": "", + "Unset variables instead of setting them": "", + "Update server returned an empty list": "", + "Upgrade to QEMU v3.1.0+, run 'virt-host-validate', or ensure that you are not running in a nested VM environment.": "", + "Upgrading from Kubernetes {{.old}} to {{.new}}": "Kubernetes を {{.old}} から {{.new}} にアップグレードしています", + "Usage": "", + "Usage: minikube completion SHELL": "", + "Usage: minikube delete": "", + "Use \"{{.CommandPath}} [command] --help\" for more information about a command.": "", + "Use VirtualBox to remove the conflicting VM and/or network interfaces": "", + "Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'.": "", + "User ID: {{.userID}}": "", + "Userspace file server is shutdown": "", + "Userspace file server:": "", + "Using image repository {{.name}}": "イメージ リポジトリ {{.name}} を使用しています", + "Using the running {{.driver_name}} \"{{.profile_name}}\" VM ...": "", + "VM driver is one of: %v": "VM ドライバは次のいずれかです。%v", + "Verify that your HTTP_PROXY and HTTPS_PROXY environment variables are set correctly.": "", + "Verify the IP address of the running cluster in kubeconfig.": "", + "Verifying dashboard health ...": "", + "Verifying proxy health ...": "", + "Version: {{.version}}": "", + "VirtualBox and Hyper-V are having a conflict. Use '--vm-driver=hyperv' or disable Hyper-V using: 'bcdedit /set hypervisorlaunchtype off'": "", + "VirtualBox cannot create a network, probably because it conflicts with an existing network that minikube no longer knows about. Try running 'minikube delete'": "", + "VirtualBox is broken. Disable real-time anti-virus software, reboot, and reinstall VirtualBox if the problem continues.": "", + "VirtualBox is broken. Reinstall VirtualBox, reboot, and run 'minikube delete'.": "", + "Wait failed": "", + "Wait failed: {{.error}}": "", + "Wait until Kubernetes core services are healthy before exiting": "Kubernetes コアサービスが正常になるまで待機してから終了してください", + "Wait until Kubernetes core services are healthy before exiting.": "", + "Waiting for the host to be provisioned ...": "", + "Waiting for:": "", + "Where to root the NFS Shares, defaults to /nfsshares (hyperkit driver only)": "NFS 共有のルートに指定する場所。デフォルトは /nfsshares(hyperkit ドライバのみ)", + "You appear to be using a proxy, but your NO_PROXY environment does not include the minikube IP ({{.ip_address}}). Please see {{.documentation_url}} for more details": "プロキシを使用しようとしていますが、現在の NO_PROXY 環境に minikube IP({{.ip_address}})は含まれていません。詳細については、{{.documentation_url}} をご覧ください", + "You can delete them using the following command(s):": "", + "You may need to manually remove the \"{{.name}}\" VM from your hypervisor": "ハイパーバイザから「{{.name}}」VM を手動で削除することが必要な可能性があります", + "You must specify a service name": "", + "Your host does not support KVM virtualization. Ensure that qemu-kvm is installed, and run 'virt-host-validate' to debug the problem": "", + "Your host does not support virtualization. If you are running minikube within a VM, try '--vm-driver=none'. Otherwise, enable virtualization in your BIOS": "", + "Your host is failing to route packets to the minikube VM. If you have VPN software, try turning it off or configuring it so that it does not re-route traffic to the VM IP. If not, check your VM environment routing options.": "", + "Your minikube config refers to an unsupported driver. Erase ~/.minikube, and try again.": "", + "Your minikube vm is not running, try minikube start.": "", + "addon '{{.name}}' is currently not enabled.\nTo enable this addon run:\nminikube addons enable {{.name}}": "", + "addon '{{.name}}' is not a valid addon packaged with minikube.\nTo see the list of available addons run:\nminikube addons list": "", + "addon list failed": "", + "addons modifies minikube addons files using subcommands like \"minikube addons enable heapster\"": "", + "api load": "", + "bash completion failed": "", + "browser failed to open url: {{.error}}": "", + "call with cleanup=true to remove old tunnels": "", + "command runner": "", + "config modifies minikube config files using subcommands like \"minikube config set vm-driver kvm\"\nConfigurable fields:\\n\\n": "", + "config view failed": "", + "dashboard service is not running: {{.error}}": "", + "disable failed": "", + "enable failed": "", + "error creating clientset": "", + "error creating machine client": "", + "error getting driver": "", + "error parsing the input ip address for mount": "", + "error starting tunnel": "", + "failed to open browser: {{.error}}": "", + "if true, will embed the certs in kubeconfig.": "", + "kubectl and minikube configuration will be stored in {{.home_folder}}": "kubectl と minikube の構成は {{.home_folder}} に保存されます", + "kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "", + "kubectl proxy": "", + "logdir set failed": "", + "max time to wait per Kubernetes core services to be healthy.": "", + "minikube is not running, so the service cannot be accessed": "", + "minikube is unable to access the Google Container Registry. You may need to configure it to use a HTTP proxy.": "", + "minikube profile was successfully set to {{.profile_name}}": "", + "minikube {{.version}} is available! Download it: {{.url}}": "", + "mount argument \"{{.value}}\" must be in form: \u003csource directory\u003e:\u003ctarget directory\u003e": "", + "mount failed": "", + "not enough arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", + "profile sets the current minikube profile, or gets the current profile if no arguments are provided. This is used to run and manage multiple minikube instance. You can return to the default minikube profile by running `minikube profile default`": "", + "service {{.namespace_name}}/{{.service_name}} has no node port": "", + "stat failed": "", + "toom any arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", + "tunnel creates a route to services deployed with type LoadBalancer and sets their Ingress to their ClusterIP": "", + "tunnel makes services of type LoadBalancer accessible on localhost": "", + "unable to bind flags": "", + "unable to set logtostderr": "", + "unset failed": "", + "unset minikube profile": "", + "unsets PROPERTY_NAME from the minikube config file. Can be overwritten by flags or environmental variables": "", + "unsets an individual value in a minikube config file": "", + "unsupported driver: {{.name}}": "", + "update config": "", + "usage: minikube addons configure ADDON_NAME": "", + "usage: minikube addons disable ADDON_NAME": "", + "usage: minikube addons enable ADDON_NAME": "", + "usage: minikube addons list": "", + "usage: minikube addons open ADDON_NAME": "", + "usage: minikube config unset PROPERTY_NAME": "", + "usage: minikube profile [MINIKUBE_PROFILE_NAME]": "", + "zsh completion failed": "", + "{{.addonName}} was successfully enabled": "", + "{{.extra_option_component_name}}.{{.key}}={{.value}}": "", + "{{.machine}} IP has been updated to point at {{.ip}}": "", + "{{.machine}} IP was already correctly configured for {{.ip}}": "", + "{{.name}} cluster does not exist": "", + "{{.name}} has no available configuration options": "", + "{{.name}} was successfully configured": "", + "{{.prefix}}minikube {{.version}} on {{.platform}}": "{{.platform}} 上の {{.prefix}}minikube {{.version}}", + "{{.type}} is not yet a supported filesystem. We will try anyways!": "", + "{{.url}} is not accessible: {{.error}}": "" +} \ No newline at end of file diff --git a/translations/pl.json b/translations/pl.json new file mode 100644 index 0000000000..d5a35d30ca --- /dev/null +++ b/translations/pl.json @@ -0,0 +1,520 @@ +{ + "\"{{.minikube_addon}}\" was successfully disabled": "\"{{.minikube_addon}}\" został wyłaczony", + "\"{{.name}}\" cluster does not exist. Proceeding ahead with cleanup.": "", + "\"{{.name}}\" profile does not exist": "Profil \"{{.name}}\" nie istnieje", + "\"{{.profile_name}}\" VM does not exist, nothing to stop": "Maszyna wirtualna \"{{.profile_name}}\" nie istnieje. Nie można zatrzymać", + "\"{{.profile_name}}\" host does not exist, unable to show an IP": "Profil \"{{.profile_name}}\" nie istnieje. Nie można wyświetlić adresu IP ", + "\"{{.profile_name}}\" stopped.": "Zatrzymano \"{{.profile_name}}\"", + "'none' driver does not support 'minikube docker-env' command": "sterownik 'none' nie wspiera komendy 'minikube docker-env'", + "'none' driver does not support 'minikube mount' command": "sterownik 'none' nie wspiera komendy 'minikube mount'", + "'none' driver does not support 'minikube ssh' command": "sterownik 'none' nie wspiera komendy 'minikube ssh'", + "A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "", + "A firewall is blocking Docker within the minikube VM from reaching the internet. You may need to configure it to use a proxy.": "", + "A firewall is interfering with minikube's ability to make outgoing HTTPS requests. You may need to change the value of the HTTPS_PROXY environment variable.": "", + "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.": "", + "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "A set of key=value pairs that describe configuration that may be passed to different components.\nThe key should be '.' separated, and the first part before the dot is the component to apply the configuration to.\nValid components are: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy, scheduler\nValid kubeadm parameters:": "", + "A set of key=value pairs that describe feature gates for alpha/experimental features.": "", + "Access the kubernetes dashboard running within the minikube cluster": "Dostęp do dashboardu uruchomionego w klastrze kubernetesa w minikube", + "Add an image to local cache.": "", + "Add machine IP to NO_PROXY environment variable": "", + "Add or delete an image from the local cache.": "", + "Additional help topics": "Dodatkowe tematy pomocy", + "Additional mount options, such as cache=fscache": "Dodatkowe opcje montowania, jak na przykład cache=fscache", + "Advanced Commands:": "Zaawansowane komendy", + "Aliases": "Aliasy", + "Allow user prompts for more information": "", + "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \\\"auto\\\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers": "", + "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "Ilość zarezerwowanej pamieci RAM dla maszyny wirtualnej minikube (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or )", + "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "Ilość zarezerwowanej pamieci RAM dla maszyny wirtualnej minikube (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or )", + "Amount of time to wait for a service in seconds": "Czas oczekiwania na serwis w sekundach", + "Amount of time to wait for service in seconds": "Czas oczekiwania na servis w sekundach", + "Available Commands": "Dostępne polecenia", + "Basic Commands:": "Podstawowe polecenia", + "Cannot find directory {{.path}} for mount": "Nie można odnoleść folderu {{.path}} do zamontowania", + "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.": "Upewnij się że minikube zpstało uruchomione i że podano poprawną przestrzeń nazw(-n flag) celem zamontowania", + "Check that your --kubernetes-version has a leading 'v'. For example: 'v1.1.14'": "Upewnij się że --kubernetes-version ma 'v' z przodu. Na przykład `v1.1.14`", + "Check that your apiserver flags are valid, or run 'minikube delete'": "", + "Check your firewall rules for interference, and run 'virt-host-validate' to check for KVM configuration issues. If you are running minikube within a VM, consider using --vm-driver=none": "", + "Configuration and Management Commands:": "Polecenia konfiguracji i zarządzania", + "Configure an external network switch following the official documentation, then add `--hyperv-virtual-switch=\u003cswitch-name\u003e` to `minikube start`": "", + "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list": "", + "Configuring environment for Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}}": "Konfiguruje środowisko dla: Kubernetesa w wersji {{.k8sVersion}} na {{.runtime}} {{.runtimeVersion}}", + "Configuring local host environment ...": "Konfiguruje lokalne środowisko hosta", + "Confirm that you have a working internet connection and that your VM has not run out of resources by using: 'minikube logs'": "", + "Confirm that you have supplied the correct value to --hyperv-virtual-switch using the 'Get-VMSwitch' command": "", + "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn.": "", + "Created a new profile : {{.profile_name}}": "Stworzono nowy profil : {{.profile_name}}", + "Creating a new profile failed": "Tworzenie nowego profilu nie powiodło się", + "Creating mount {{.name}} ...": "", + "Creating {{.driver_name}} VM (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "Tworzenie {{.driver_name}} (CPUs={{.number_of_cpus}}, Pamięć={{.memory_size}}MB, Dysk={{.disk_size}}MB)...", + "Default group id used for the mount": "Domyślne id groupy użyte dla montowania", + "Default user id used for the mount": "Domyślne id użytkownia użyte dla montowania ", + "Delete an image from the local cache.": "", + "Deletes a local kubernetes cluster": "Usuwa lokalny klaster kubernetesa", + "Deletes a local kubernetes cluster. This command deletes the VM, and removes all\nassociated files.": "Usuwa lokalny klaster kubernetesa. Ta komenda usuwa maszynę wirtualna i wszystkie powiązane pliki.", + "Deletes a local kubernetes cluster. This command deletes the VM, and removes all associated files.": "Usuwa lokalny klaster kubernetesa. Ta komenda usuwa maszynę wirtualna i wszystkie powiązane pliki.", + "Deleting \"{{.profile_name}}\" in {{.driver_name}} ...": "Usuwanie \"{{.profile_name}}\" - {{.driver_name}}...", + "Disable checking for the availability of hardware virtualization before the vm is started (virtualbox driver only)": "", + "Disable dynamic memory in your VM manager, or pass in a larger --memory value": "", + "Disables the addon w/ADDON_NAME within minikube (example: minikube addons disable dashboard). For a list of available addons use: minikube addons list": "", + "Disables the filesystem mounts provided by the hypervisors": "", + "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "", + "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "", + "Display dashboard URL instead of opening a browser": "", + "Display the kubernetes addons URL in the CLI instead of opening it in the default browser": "", + "Display the kubernetes service URL in the CLI instead of opening it in the default browser": "", + "Display values currently set in the minikube config file": "Wyświetl wartości z obecnej konfiguracji minikube", + "Display values currently set in the minikube config file.": "Wyświetl wartości z obecnej konfiguracji minikube", + "Docker inside the VM is unavailable. Try running 'minikube delete' to reset the VM.": "", + "Docs have been saved at - {{.path}}": "Dokumentacja została zapisana w {{.path}}", + "Documentation: {{.url}}": "Dokumentacja", + "Done! kubectl is now configured to use \"{{.name}}": "Gotowe! kubectl jest skonfigurowany do użycia z \"{{.name}}\".", + "Done! kubectl is now configured to use \"{{.name}}\"": "Gotowe! kubectl jest skonfigurowany do użycia z \"{{.name}}\".", + "Download complete!": "Pobieranie zakończone!", + "Downloading VM boot image ...": "Pobieranie obrazu maszyny wirtualnej ...", + "Downloading driver {{.driver}}:": "", + "Downloading {{.name}} {{.version}}": "Pobieranie {{.name}} {{.version}}", + "ERROR creating `registry-creds-dpr` secret": "", + "ERROR creating `registry-creds-ecr` secret: {{.error}}": "", + "ERROR creating `registry-creds-gcr` secret: {{.error}}": "", + "Either systemctl is not installed, or Docker is broken. Run 'sudo systemctl start docker' and 'journalctl -u docker'": "", + "Enable experimental NVIDIA GPU support in minikube": "aktywuj eksperymentalne wsparcie minikube dla NVIDIA GPU", + "Enable host resolver for NAT DNS requests (virtualbox driver only)": "", + "Enable proxy for NAT DNS requests (virtualbox driver only)": "", + "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\": "", + "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\\".": "", + "Enables the addon w/ADDON_NAME within minikube (example: minikube addons enable dashboard). For a list of available addons use: minikube addons list": "", + "Enabling dashboard ...": "", + "Environment variables to pass to the Docker daemon. (format: key=value)": "Zmienne środowiskowe do przekazania do demona docker (format: klucz-wartość)", + "Error checking driver version: {{.error}}": "Błąd podczas sprawdzania wersij sterownika : {{.error}}", + "Error creating list template": "", + "Error creating minikube directory": "", + "Error creating status template": "", + "Error creating view template": "", + "Error executing list template": "", + "Error executing status template": "", + "Error executing template": "", + "Error executing view template": "", + "Error finding port for mount": "", + "Error getting IP": "", + "Error getting bootstrapper": "", + "Error getting client": "", + "Error getting client: {{.error}}": "", + "Error getting cluster": "", + "Error getting cluster bootstrapper": "", + "Error getting config": "", + "Error getting host": "", + "Error getting host status": "", + "Error getting machine logs": "", + "Error getting machine status": "", + "Error getting service status": "", + "Error getting service with namespace: {{.namespace}} and labels {{.labelName}}:{{.addonName}}: {{.error}}": "", + "Error getting the host IP address to use from within the VM": "", + "Error host driver ip status": "", + "Error killing mount process": "", + "Error loading api": "", + "Error loading profile config": "", + "Error loading profile config: {{.error}}": "", + "Error loading profile {{.name}}: {{.error}}": "", + "Error opening service": "", + "Error parsing minikube version: {{.error}}": "Bład parsowania wersji minikube: {{.error}}", + "Error parsing vmDriver version: {{.error}}": "Błąd parsowania wersji vmDriver: {{.error}}", + "Error reading {{.path}}: {{.error}}": "Błąd odczytu {{.path}} {{.error}}", + "Error restarting cluster": "Błąd podczas restartowania klastra", + "Error setting shell variables": "Błąd podczas ustawiania zmiennych powłoki(shell)", + "Error starting cluster": "Błąd podczas uruchamiania klastra", + "Error starting mount": "", + "Error unsetting shell variables": "", + "Error while setting kubectl current context : {{.error}}": "Błąd podczas ustawiania kontekstu kubectl: {{.error}}", + "Error writing mount pid": "", + "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}\"": "", + "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}": "Erreur : Vous avez sélectionné Kubernetes v{{.new}}, mais le cluster existent pour votre profil exécute Kubernetes v{{.old}}. Les rétrogradations non-destructives ne sont pas compatibles. Toutefois, vous pouvez poursuivre le processus en réalisant l'une des trois actions suivantes :\n* Créer à nouveau le cluster en utilisant Kubernetes v{{.new}} – exécutez \"minikube delete {{.profile}}\", puis \"minikube start {{.profile}} --kubernetes-version={{.new}}\".\n* Créer un second cluster avec Kubernetes v{{.new}} – exécutez \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\".\n* Réutiliser le cluster existent avec Kubernetes v{{.old}} ou version ultérieure – exécutez \"minikube start {{.profile}} --kubernetes-version={{.old}}\".", + "Error: [{{.id}}] {{.error}}": "", + "Examples": "Przykłady", + "Exiting": "", + "Exiting due to driver incompatibility": "", + "Failed runtime": "", + "Failed to cache ISO": "", + "Failed to cache and load images": "", + "Failed to cache binaries": "", + "Failed to cache images": "", + "Failed to change permissions for {{.minikube_dir_path}}: {{.error}}": "Nie udało się zmienić uprawnień pliku {{.minikube_dir_path}}: {{.error}}", + "Failed to check if machine exists": "", + "Failed to check main repository and mirrors for images for images": "", + "Failed to delete cluster: {{.error}}": "", + "Failed to delete cluster: {{.error}}__1": "", + "Failed to delete images": "", + "Failed to delete images from config": "", + "Failed to download kubectl": "Pobieranie kubectl nie powiodło się", + "Failed to enable container runtime": "", + "Failed to generate config": "", + "Failed to get bootstrapper": "", + "Failed to get command runner": "", + "Failed to get driver URL": "", + "Failed to get image map": "", + "Failed to get machine client": "", + "Failed to get service URL: {{.error}}": "", + "Failed to kill mount process: {{.error}}": "Zabicie procesu nie powiodło się: {{.error}}", + "Failed to list cached images": "", + "Failed to remove profile": "Usunięcie profilu nie powiodło się", + "Failed to save config": "Zapisywanie konfiguracji nie powiodło się", + "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}": "", + "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}`.": "", + "Failed to setup certs": "Konfiguracja certyfikatów nie powiodła się", + "Failed to setup kubeconfig": "Konfiguracja kubeconfig nie powiodła się", + "Failed to update cluster": "Aktualizacja klastra nie powiodła się", + "Failed to update config": "Aktualizacja konfiguracji nie powiodła się", + "Failed unmount: {{.error}}": "", + "File permissions used for the mount": "", + "Flags": "", + "Follow": "", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/__1": "", + "For more information, see:": "", + "Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect": "", + "Force minikube to perform possibly dangerous operations": "Wymuś wykonanie potencjalnie niebezpiecznych operacji", + "Found network options:": "Wykryto opcje sieciowe:", + "Found {{.number}} invalid profile(s) !": "Wykryto {{.number}} nieprawidłowych profili ! ", + "Gets the kubernetes URL(s) for the specified service in your local cluster": "", + "Gets the kubernetes URL(s) for the specified service in your local cluster. In the case of multiple URLs they will be printed one at a time.": "", + "Gets the logs of the running instance, used for debugging minikube, not user code.": "Pobiera logi z aktualnie uruchomionej instancji. Przydatne do debugowania kodu który nie należy do aplikacji użytkownika", + "Gets the status of a local kubernetes cluster": "Pobiera aktualny status klastra kubernetesa", + "Gets the status of a local kubernetes cluster.\n\tExit status contains the status of minikube's VM, cluster and kubernetes encoded on it's bits in this order from right to left.\n\tEg: 7 meaning: 1 (for minikube NOK) + 2 (for cluster NOK) + 4 (for kubernetes NOK)": "", + "Gets the value of PROPERTY_NAME from the minikube config file": "", + "Global Flags": "", + "Go template format string for the addon list output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd/config#AddonListTemplate": "", + "Go template format string for the cache list output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#CacheListTemplate": "", + "Go template format string for the config view output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd/config#ConfigViewTemplate": "", + "Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#Status": "", + "Group ID: {{.groupID}}": "", + "Have you set up libvirt correctly?": "Czy napewno skonfigurowano libvirt w sposób prawidłowy?", + "Hide the hypervisor signature from the guest in minikube (kvm2 driver only)": "", + "If the above advice does not help, please let us know:": "", + "If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --vm-driver=none.": "", + "If true, only download and cache files for later use - don't install or start anything.": "", + "If using the none driver, ensure that systemctl is installed": "Jeśli użyto sterownika 'none', upewnij się że systemctl jest zainstalowany", + "If you are running minikube within a VM, consider using --vm-driver=none:": "", + "Images Commands:": "", + "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "", + "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "", + "Install VirtualBox, or select an alternative value for --vm-driver": "", + "Install the latest hyperkit binary, and run 'minikube delete'": "", + "Invalid size passed in argument: {{.error}}": "Nieprawidłowy rozmiar przekazany w argumencie: {{.error}}", + "IsEnabled failed": "", + "Kill the mount process spawned by minikube start": "", + "Kubernetes {{.version}} is not supported by this release of minikube": "", + "Launching Kubernetes ...": "Uruchamianie Kubernetesa...", + "Launching proxy ...": "", + "List all available images from the local cache.": "", + "List of guest VSock ports that should be exposed as sockets on the host (hyperkit driver only)": "", + "Lists all available minikube addons as well as their current statuses (enabled/disabled)": "", + "Lists all minikube profiles.": "Wylistuj wszystkie profile minikube", + "Lists all valid minikube profiles and detects all possible invalid profiles.": "", + "Lists the URLs for the services in your local cluster": "", + "Local folders to share with Guest via NFS mounts (hyperkit driver only)": "", + "Location of the VPNKit socket used for networking. If empty, disables Hyperkit VPNKitSock, if 'auto' uses Docker for Mac VPNKit connection, otherwise uses the specified VSock (hyperkit driver only)": "", + "Location of the minikube iso": "Ścieżka do obrazu iso minikube", + "Location of the minikube iso.": "Ścieżka do obrazu iso minikube", + "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'": "Zaloguj się i wykonaj polecenie w maszynie za pomocą ssh. Podobne do 'docker-machine ssh'", + "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'.": "Zaloguj się i wykonaj polecenie w maszynie za pomocą ssh. Podobne do 'docker-machine ssh'", + "Message Size: {{.size}}": "", + "Minikube is a CLI tool that provisions and manages single-node Kubernetes clusters optimized for development workflows.": "", + "Minikube is a tool for managing local Kubernetes clusters.": "", + "Modify minikube config": "", + "Modify minikube's kubernetes addons": "", + "Mount type: {{.name}}": "", + "Mounting host path {{.sourcePath}} into VM as {{.destinationPath}} ...": "", + "Mounts the specified directory into minikube": "Montuje podany katalog wewnątrz minikube", + "Mounts the specified directory into minikube.": "Montuje podany katalog wewnątrz minikube", + "NOTE: This process must stay alive for the mount to be accessible ...": "", + "Networking and Connectivity Commands:": "", + "No minikube profile was found. You can create one using `minikube start`.": "", + "None of the known repositories in your location are accessible. Using {{.image_repository_name}} as fallback.": "", + "None of the known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag": "", + "Not passing {{.name}}={{.value}} to docker env.": "", + "Number of CPUs allocated to the minikube VM": "Liczba procesorów przypisana do maszyny wirtualnej minikube", + "Number of CPUs allocated to the minikube VM.": "Liczba procesorów przypisana do maszyny wirtualnej minikube", + "Number of lines back to go within the log": "", + "OS release is {{.pretty_name}}": "", + "Open the addons URL with https instead of http": "", + "Open the service URL with https instead of http": "", + "Opening kubernetes service {{.namespace_name}}/{{.service_name}} in default browser...": "", + "Opening {{.url}} in your default browser...": "", + "Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list": "", + "Options: {{.options}}": "", + "Outputs minikube shell completion for the given shell (bash or zsh)": "Zwraca autouzupełnianie poleceń minikube dla powłoki system(bash, zsh)", + "Outputs minikube shell completion for the given shell (bash or zsh)\n\n\tThis depends on the bash-completion binary. Example installation instructions:\n\tOS X:\n\t\t$ brew install bash-completion\n\t\t$ source $(brew --prefix)/etc/bash_completion\n\t\t$ minikube completion bash \u003e ~/.minikube-completion # for bash users\n\t\t$ minikube completion zsh \u003e ~/.minikube-completion # for zsh users\n\t\t$ source ~/.minikube-completion\n\tUbuntu:\n\t\t$ apt-get install bash-completion\n\t\t$ source /etc/bash-completion\n\t\t$ source \u003c(minikube completion bash) # for bash users\n\t\t$ source \u003c(minikube completion zsh) # for zsh users\n\n\tAdditionally, you may want to output the completion to a file and source in your .bashrc\n\n\tNote for zsh users: [1] zsh completions are only supported in versions of zsh \u003e= 5.2": "", + "Permissions: {{.octalMode}} ({{.writtenMode}})": "", + "Please enter a value:": "Wprowadź wartość", + "Please install the minikube hyperkit VM driver, or select an alternative --vm-driver": "", + "Please install the minikube kvm2 VM driver, or select an alternative --vm-driver": "", + "Please make sure the service you are looking for is deployed or is in the correct namespace.": "Proszę upewnij się, że serwis którego szukasz znajduje się w prawidłowej przestrzeni nazw", + "Please specify the directory to be mounted: \n\tminikube mount \u003csource directory\u003e:\u003ctarget directory\u003e (example: \"/host-home:/vm-home\")": "", + "Please upgrade the '{{.driver_executable}}'. {{.documentation_url}}": "Proszę zaktualizować '{{.driver_executable}}'. {{.documentation_url}}", + "Populates the specified folder with documentation in markdown about minikube": "", + "Powering off \"{{.profile_name}}\" via SSH ...": "", + "Preparing Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}} ...": "przygowowywanie Kubernetesa {{.k8sVersion}} na {{.runtime}} {{.runtimeVersion}}...", + "Print current and latest version number": "Wyświetl aktualna i najnowszą wersję", + "Print the version of minikube": "Wyświetl wersję minikube", + "Print the version of minikube.": "Wyświetl wersję minikube", + "Problems detected in {{.entry}}:": "Wykryto problem w {{.name}}", + "Problems detected in {{.name}}:": "Wykryto problem w {{.name}}", + "Profile gets or sets the current minikube profile": "Pobiera lub ustawia aktywny profil minikube", + "Provide VM UUID to restore MAC address (hyperkit driver only)": "", + "Pulling images ...": "", + "Reboot to complete VirtualBox installation, and verify that VirtualBox is not blocked by your system": "Uruchom ponownie komputer aby zakończyć instalacje VirtualBox'a i upewnij się że nie jest on blokowany przez twój system", + "Rebuild libvirt with virt-network support": "", + "Received {{.name}} signal": "", + "Registry mirrors to pass to the Docker daemon": "", + "Reinstall VirtualBox and reboot. Alternatively, try the kvm2 driver: https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/": "", + "Reinstall VirtualBox and verify that it is not blocked: System Preferences -\u003e Security \u0026 Privacy -\u003e General -\u003e Some system software was blocked from loading": "", + "Related issues:": "Powiązane problemy", + "Relaunching Kubernetes using {{.bootstrapper}} ...": "", + "Removing {{.directory}} ...": "", + "Requested cpu count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}": "", + "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}": "", + "Requested memory allocation ({{.memory}}MB) is less than the default memory allocation of {{.default_memorysize}}MB. Beware that minikube might not work correctly or crash unexpectedly.": "", + "Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}": "", + "Retriable failure: {{.error}}": "", + "Retrieve the ssh identity key path of the specified cluster": "Pozyskuje ścieżkę do klucza ssh dla wyspecyfikowanego klastra", + "Retrieve the ssh identity key path of the specified cluster.": "Pozyskuje ścieżkę do klucza ssh dla wyspecyfikowanego klastra.", + "Retrieves the IP address of the running cluster": "Pobiera adres IP aktualnie uruchomionego klastra", + "Retrieves the IP address of the running cluster, and writes it to STDOUT.": "Pobiera adres IP aktualnie uruchomionego klastra i wypisuje go do STDOUT", + "Retrieves the IP address of the running cluster, checks it\n\t\t\twith IP in kubeconfig, and corrects kubeconfig if incorrect.": "", + "Returns the value of PROPERTY_NAME from the minikube config file. Can be overwritten at runtime by flags or environmental variables.": "", + "Run 'kubectl describe pod coredns -n kube-system' and check for a firewall or DNS conflict": "", + "Run 'minikube delete' to delete the stale VM": "", + "Run kubectl": "Uruchamia kubectl", + "Run minikube from the C: drive.": "", + "Run the kubernetes client, download it if necessary.\nExamples:\nminikube kubectl -- --help\nkubectl get pods --namespace kube-system": "", + "Run the minikube command as an Administrator": "", + "Running on localhost (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "", + "Set failed": "", + "Sets an individual value in a minikube config file": "", + "Sets the PROPERTY_NAME config value to PROPERTY_VALUE\n\tThese values can be overwritten by flags or environment variables at runtime.": "", + "Sets up docker env variables; similar to '$(docker-machine env)'": "Ustawia zmienne środowiskowe dockera. Podobne do `(docker-machine env)`", + "Sets up docker env variables; similar to '$(docker-machine env)'.": "Ustawia zmienne środowiskowe dockera. Podobne do `(docker-machine env)`", + "Setting profile failed": "Ustawianie profilu nie powiodło się", + "Show only log entries which point to known problems": "Pokaż logi które wskazują na znane problemy", + "Show only the most recent journal entries, and continuously print new entries as they are appended to the journal.": "", + "Skipped switching kubectl context for {{.profile_name}} because --keep-context was set.": "Zignorowano zmianę kontekstu kubectl ponieważ --keep-context zostało przekazane", + "Sorry that minikube crashed. If this was unexpected, we would love to hear from you:": "", + "Sorry, Kubernetes {{.version}} is not supported by this release of minikube": "", + "Sorry, completion support is not yet implemented for {{.name}}": "", + "Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config": "", + "Sorry, the url provided with the --registry-mirror flag is invalid: {{.url}}": "", + "Specified Kubernetes version {{.specified}} is less than the oldest supported version: {{.oldest}}": "", + "Specify --kubernetes-version in v\u003cmajor\u003e.\u003cminor.\u003cbuild\u003e form. example: 'v1.1.14'": "", + "Specify an alternate --host-only-cidr value, such as 172.16.0.1/24": "", + "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)": "", + "Specify the 9p version that the mount should use": "", + "Specify the ip that the mount should be setup on": "", + "Specify the mount filesystem type (supported types: 9p)": "", + "Starting existing {{.driver_name}} VM for \"{{.profile_name}}\" ...": "", + "Starts a local kubernetes cluster": "Uruchamianie lokalnego klastra kubernetesa", + "Stopping \"{{.profile_name}}\" in {{.driver_name}} ...": "Zatrzymywanie \"{{.profile_name}}\" - {{.driver_name}}...", + "Stops a local kubernetes cluster running in Virtualbox. This command stops the VM\nitself, leaving all files intact. The cluster can be started again with the \"start\" command.": "", + "Stops a running local kubernetes cluster": "Zatrzymuje lokalny klaster kubernetesa", + "Successfully mounted {{.sourcePath}} to {{.destinationPath}}": "Pomyślnie zamontowano {{.sourcePath}} do {{.destinationPath}}", + "Suggestion: {{.advice}}": "Sugestia: {{.advice}}", + "Target directory {{.path}} must be an absolute path": "", + "The \"{{.cluster_name}}\" cluster has been deleted.": "Klaster \"{{.cluster_name}}\" został usunięty", + "The \"{{.driver_name}}\" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}'.": "Sterownik \"{{.driver_name}}\" wymaga uprawnień root'a. Użyj 'sudo minikube --vm-driver={{.driver_name}}'", + "The \"{{.driver_name}}\" driver should not be used with root privileges.": "", + "The \"{{.name}}\" cluster has been deleted.": "Klaster \"{{.name}}\" został usunięty", + "The 'none' driver does not support multiple profiles: https://minikube.sigs.k8s.io/docs/reference/drivers/none/": "", + "The 'none' driver provides limited isolation and may reduce system security and reliability.": "", + "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\\n\\n{{ .example }}\\n": "", + "The CIDR to be used for service cluster IPs.": "", + "The CIDR to be used for the minikube VM (virtualbox driver only)": "", + "The KVM QEMU connection URI. (kvm2 driver only)": "", + "The KVM driver is unable to resurrect this old VM. Please run `minikube delete` to delete it and try again.": "", + "The KVM network name. (kvm2 driver only)": "Nazwa sieci KVM(wspierane tylko przez kvm2)", + "The VM driver crashed. Run 'minikube start --alsologtostderr -v=8' to see the VM driver error message": "", + "The VM driver exited with an error, and may be corrupt. Run 'minikube start' with --alsologtostderr -v=8 to see the error": "", + "The VM that minikube is configured for no longer exists. Run 'minikube delete'": "", + "The apiserver listening port": "API nasłuchuje na porcie:", + "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "The argument to pass the minikube mount command on start": "", + "The argument to pass the minikube mount command on start.": "", + "The cluster dns domain name used in the kubernetes cluster": "Domena dns clastra użyta przez kubernetesa", + "The container runtime to be used (docker, crio, containerd)": "Runtime konteneryzacji (docker, crio, containerd).", + "The container runtime to be used (docker, crio, containerd).": "", + "The cri socket path to be used": "", + "The cri socket path to be used.": "", + "The docker host is currently not running": "", + "The docker service is currently not active": "Serwis docker jest nieaktywny", + "The driver '{{.driver}}' is not supported on {{.os}}": "Sterownik '{{.driver}} jest niewspierany przez system {{.os}}", + "The existing \"{{.profile_name}}\" VM that was created using the \"{{.old_driver}}\" driver, and is incompatible with the \"{{.driver}}\" driver.": "", + "The hyperv virtual switch name. Defaults to first found. (hyperv driver only)": "", + "The initial time interval for each check that wait performs in seconds": "", + "The kubernetes version that the minikube VM will use (ex: v1.2.3)": "Wersja kubernetesa która zostanie użyta przez wirtualną maszyna minikube (np. v1.2.3)", + "The minikube VM is offline. Please run 'minikube start' to start it again.": "", + "The name of the network plugin": "Nazwa pluginu sieciowego", + "The name of the network plugin.": "Nazwa pluginu sieciowego", + "The number of bytes to use for 9p packet payload": "", + "The path on the file system where the docs in markdown need to be saved": "", + "The service namespace": "", + "The services namespace": "", + "The time interval for each check that wait performs in seconds": "", + "The value passed to --format is invalid": "Wartość przekazana do --format jest nieprawidłowa", + "The value passed to --format is invalid: {{.error}}": "Wartość przekazana do --format jest nieprawidłowa: {{.error}}", + "The vmwarefusion driver is deprecated and support for it will be removed in a future release.\n\t\t\tPlease consider switching to the new vmware unified driver, which is intended to replace the vmwarefusion driver.\n\t\t\tSee https://minikube.sigs.k8s.io/docs/reference/drivers/vmware/ for more information.\n\t\t\tTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "", + "The {{.driver_name}} driver should not be used with root privileges.": "{{.driver_name}} nie powinien byc używany z przywilejami root'a.", + "There appears to be another hypervisor conflicting with KVM. Please stop the other hypervisor, or use --vm-driver to switch to it.": "", + "There's a new version for '{{.driver_executable}}'. Please consider upgrading. {{.documentation_url}}": "", + "These changes will take effect upon a minikube delete and then a minikube start": "", + "This addon does not have an endpoint defined for the 'addons open' command.\nYou can add one by annotating a service with the label {{.labelName}}:{{.addonName}}": "", + "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true": "", + "This will keep the existing kubectl context and will create a minikube context.": "", + "This will start the mount daemon and automatically mount files into minikube": "", + "This will start the mount daemon and automatically mount files into minikube.": "", + "Tip: To remove this root owned cluster, run: sudo {{.cmd}} delete": "", + "Tip: Use 'minikube start -p \u003cname\u003e' to create a new cluster, or 'minikube delete' to delete this one.": "", + "To connect to this cluster, use: kubectl --context={{.name}}": "Aby połączyć się z klastrem użyj: kubectl --context={{.name}}", + "To connect to this cluster, use: kubectl --context={{.profile_name}}": "Aby połaczyć się z klastem uzyj: kubectl --context={{.profile_name}}", + "To disable this notice, run: 'minikube config set WantUpdateNotification false'": "Aby wyłączyć te notyfikację, użyj: 'minikube config set WantUpdateNotification false'", + "To disable this notice, run: 'minikube config set WantUpdateNotification false'\\n": "", + "To proceed, either:\n 1) Delete the existing VM using: '{{.command}} delete'\n or\n 2) Restart with the existing driver: '{{.command}} start --vm-driver={{.old_driver}}'": "", + "To start minikube with HyperV Powershell must be in your PATH`": "Aby uruchomić minikube z HyperV Powershell musi znajdować się w zmiennej PATH", + "To use kubectl or minikube commands as your own user, you may need to relocate them. For example, to overwrite your own settings, run:": "", + "Troubleshooting Commands:": "", + "Unable to bind flags": "", + "Unable to enable dashboard": "", + "Unable to fetch latest version info": "", + "Unable to generate docs": "", + "Unable to generate the documentation. Please ensure that the path specified is a directory, exists \u0026 you have permission to write to it.": "", + "Unable to get VM IP address": "", + "Unable to get bootstrapper: {{.error}}": "", + "Unable to get current user": "", + "Unable to get runtime": "", + "Unable to get the status of the cluster.": "", + "Unable to kill mount process: {{.error}}": "", + "Unable to load cached images from config file.": "", + "Unable to load cached images: {{.error}}": "", + "Unable to load config: {{.error}}": "", + "Unable to parse \"{{.kubernetes_version}}\": {{.error}}": "", + "Unable to parse oldest Kubernetes version from constants: {{.error}}": "", + "Unable to pull images, which may be OK: {{.error}}": "", + "Unable to remove machine directory: %v": "", + "Unable to start VM": "Nie można uruchomić maszyny wirtualnej", + "Unable to stop VM": "Nie można zatrzymać maszyny wirtualnej", + "Unable to update {{.driver}} driver: {{.error}}": "", + "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...": "", + "Unmounting {{.path}} ...": "", + "Unset the KUBECONFIG environment variable, or verify that it does not point to an empty or otherwise invalid path": "", + "Unset variables instead of setting them": "", + "Update server returned an empty list": "", + "Upgrade to QEMU v3.1.0+, run 'virt-host-validate', or ensure that you are not running in a nested VM environment.": "", + "Upgrading from Kubernetes {{.old}} to {{.new}}": "", + "Usage": "", + "Usage: minikube completion SHELL": "", + "Usage: minikube delete": "", + "Use \"{{.CommandPath}} [command] --help\" for more information about a command.": "", + "Use VirtualBox to remove the conflicting VM and/or network interfaces": "", + "Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'.": "", + "User ID: {{.userID}}": "", + "Userspace file server is shutdown": "", + "Userspace file server:": "", + "Using image repository {{.name}}": "", + "Using the running {{.driver_name}} \"{{.profile_name}}\" VM ...": "", + "VM driver is one of: %v": "Sterownik wirtualnej maszyny to jeden z: %v", + "Verify that your HTTP_PROXY and HTTPS_PROXY environment variables are set correctly.": "Weryfikuję czy zmienne HTTP_PROXY i HTTPS_PROXY sa ustawione poprawnie", + "Verify the IP address of the running cluster in kubeconfig.": "Weryfikuję adres IP działającego klastra w kubeconfig", + "Verifying dashboard health ...": "Weryfikuję status dashboardu", + "Verifying proxy health ...": "Weryfukuje status proxy", + "Verifying:": "Weryfikuje :", + "Version: {{.version}}": "", + "VirtualBox and Hyper-V are having a conflict. Use '--vm-driver=hyperv' or disable Hyper-V using: 'bcdedit /set hypervisorlaunchtype off'": "", + "VirtualBox cannot create a network, probably because it conflicts with an existing network that minikube no longer knows about. Try running 'minikube delete'": "", + "VirtualBox is broken. Disable real-time anti-virus software, reboot, and reinstall VirtualBox if the problem continues.": "", + "VirtualBox is broken. Reinstall VirtualBox, reboot, and run 'minikube delete'.": "", + "Virtualization support is disabled on your computer. If you are running minikube within a VM, try '--vm-driver=none'. Otherwise, consult your systems BIOS manual for how to enable virtualization.": "", + "Wait failed": "", + "Wait failed: {{.error}}": "", + "Wait until Kubernetes core services are healthy before exiting": "", + "Wait until Kubernetes core services are healthy before exiting.": "", + "Waiting for SSH access ...": "Oczekiwanie na połaczenie SSH...", + "Waiting for the host to be provisioned ...": "", + "Waiting for:": "Oczekiwanie na :", + "Where to root the NFS Shares, defaults to /nfsshares (hyperkit driver only)": "", + "You appear to be using a proxy, but your NO_PROXY environment does not include the minikube IP ({{.ip_address}}). Please see {{.documentation_url}} for more details": "", + "You can delete them using the following command(s):": "", + "You may need to manually remove the \"{{.name}}\" VM from your hypervisor": "", + "You must specify a service name": "Musisz podać nazwę serwisu", + "Your host does not support KVM virtualization. Ensure that qemu-kvm is installed, and run 'virt-host-validate' to debug the problem": "Twoje środowisko nie wspiera virtualizacji KVM. Upewnij się że qemu-kvm jest zainstalowane i uruchom 'virt-host-validate' aby rozwiązać problem.", + "Your host does not support virtualization. If you are running minikube within a VM, try '--vm-driver=none'. Otherwise, enable virtualization in your BIOS": "", + "Your host is failing to route packets to the minikube VM. If you have VPN software, try turning it off or configuring it so that it does not re-route traffic to the VM IP. If not, check your VM environment routing options.": "", + "Your minikube config refers to an unsupported driver. Erase ~/.minikube, and try again.": "", + "Your minikube vm is not running, try minikube start.": "", + "addon '{{.name}}' is currently not enabled.\nTo enable this addon run:\nminikube addons enable {{.name}}": "", + "addon '{{.name}}' is not a valid addon packaged with minikube.\nTo see the list of available addons run:\nminikube addons list": "", + "addon list failed": "", + "addons modifies minikube addons files using subcommands like \"minikube addons enable heapster\"": "", + "api load": "", + "bash completion failed": "", + "browser failed to open url: {{.error}}": "", + "call with cleanup=true to remove old tunnels": "", + "command runner": "", + "config modifies minikube config files using subcommands like \"minikube config set vm-driver kvm\"\nConfigurable fields:\\n\\n": "", + "config view failed": "", + "dashboard service is not running: {{.error}}": "", + "disable failed": "", + "enable failed": "", + "error creating clientset": "", + "error creating machine client": "", + "error getting driver": "", + "error parsing the input ip address for mount": "", + "error starting tunnel": "", + "failed to open browser: {{.error}}": "Nie udało się otworzyć przeglądarki: {{.error}}", + "if true, will embed the certs in kubeconfig.": "", + "kubectl and minikube configuration will be stored in {{.home_folder}}": "konfiguracja minikube i kubectl będzie przechowywana w katalogu {{.home_dir}}", + "kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "kubectl nie zostało odnaleźione w zmiennej środowiskowej ${PATH}. Instrukcja instalacji: https://kubernetes.io/docs/tasks/tools/install-kubectl/", + "kubectl proxy": "", + "logdir set failed": "", + "max time to wait per Kubernetes core services to be healthy.": "", + "minikube is not running, so the service cannot be accessed": "", + "minikube is unable to access the Google Container Registry. You may need to configure it to use a HTTP proxy.": "", + "minikube profile was successfully set to {{.profile_name}}": "", + "minikube {{.version}} is available! Download it: {{.url}}": "minikube {{.version}} jest dostępne! Pobierz je z: {{.url}}", + "mount argument \"{{.value}}\" must be in form: \u003csource directory\u003e:\u003ctarget directory\u003e": "", + "mount failed": "Montowanie się nie powiodło", + "not enough arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", + "profile sets the current minikube profile, or gets the current profile if no arguments are provided. This is used to run and manage multiple minikube instance. You can return to the default minikube profile by running `minikube profile default`": "", + "service {{.namespace_name}}/{{.service_name}} has no node port": "", + "stat failed": "", + "toom any arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", + "tunnel creates a route to services deployed with type LoadBalancer and sets their Ingress to their ClusterIP": "", + "tunnel makes services of type LoadBalancer accessible on localhost": "", + "unable to bind flags": "", + "unable to set logtostderr": "", + "unset failed": "", + "unset minikube profile": "", + "unsets PROPERTY_NAME from the minikube config file. Can be overwritten by flags or environmental variables": "", + "unsets an individual value in a minikube config file": "", + "unsupported driver: {{.name}}": "nie wspierany sterownik: {{.name}}", + "update config": "", + "usage: minikube addons configure ADDON_NAME": "", + "usage: minikube addons disable ADDON_NAME": "", + "usage: minikube addons enable ADDON_NAME": "", + "usage: minikube addons list": "", + "usage: minikube addons open ADDON_NAME": "", + "usage: minikube config unset PROPERTY_NAME": "", + "usage: minikube profile [MINIKUBE_PROFILE_NAME]": "", + "zsh completion failed": "", + "{{.addonName}} was successfully enabled": "{{.addonName}} został aktywowany pomyślnie", + "{{.extra_option_component_name}}.{{.key}}={{.value}}": "", + "{{.machine}} IP has been updated to point at {{.ip}}": "", + "{{.machine}} IP was already correctly configured for {{.ip}}": "", + "{{.name}} cluster does not exist": "Klaster {{.name}} nie istnieje", + "{{.name}} has no available configuration options": "{{.name}} nie posiada opcji configuracji", + "{{.name}} was successfully configured": "{{.name}} skonfigurowano pomyślnie", + "{{.prefix}}minikube {{.version}} on {{.platform}}": "{{.prefix}}minikube {{.version}} na {{.platform}}", + "{{.type}} is not yet a supported filesystem. We will try anyways!": "{{.type}} nie jest wspierany przez system plików. I tak spróbujemy!", + "{{.url}} is not accessible: {{.error}}": "" +} diff --git a/translations/zh-CN.json b/translations/zh-CN.json index d752c783c8..64141d7925 100644 --- a/translations/zh-CN.json +++ b/translations/zh-CN.json @@ -1,173 +1,188 @@ { - "\n\tOutputs minikube shell completion for the given shell (bash or zsh)\n\n\tThis depends on the bash-completion binary. Example installation instructions:\n\tOS X:\n\t\t$ brew install bash-completion\n\t\t$ source $(brew --prefix)/etc/bash_completion\n\t\t$ minikube completion bash \u003e ~/.minikube-completion # for bash users\n\t\t$ minikube completion zsh \u003e ~/.minikube-completion # for zsh users\n\t\t$ source ~/.minikube-completion\n\tUbuntu:\n\t\t$ apt-get install bash-completion\n\t\t$ source /etc/bash-completion\n\t\t$ source \u003c(minikube completion bash) # for bash users\n\t\t$ source \u003c(minikube completion zsh) # for zsh users\n\n\tAdditionally, you may want to output the completion to a file and source in your .bashrc\n\n\tNote for zsh users: [1] zsh completions are only supported in versions of zsh \u003e= 5.2\n": "", - "\"{{.minikube_addon}}\" was successfully disabled": "", - "\"{{.name}}\" cluster does not exist": "", - "\"{{.name}}\" profile does not exist": "", - "\"{{.profile_name}}\" VM does not exist, nothing to stop": "", - "\"{{.profile_name}}\" host does not exist, unable to show an IP": "", - "\"{{.profile_name}}\" stopped.": "", - "'none' driver does not support 'minikube docker-env' command": "", - "'none' driver does not support 'minikube mount' command": "", - "'none' driver does not support 'minikube ssh' command": "", - "A firewall is blocking Docker within the minikube VM from reaching the internet. You may need to configure it to use a proxy.": "", - "A firewall is interfering with minikube's ability to make outgoing HTTPS requests. You may need to change the value of the HTTPS_PROXY environment variable.": "", - "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.": "", - "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", - "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", - "A set of key=value pairs that describe feature gates for alpha/experimental features.": "", - "Access the kubernetes dashboard running within the minikube cluster": "", - "Add an image to local cache.": "", - "Add machine IP to NO_PROXY environment variable": "", - "Add or delete an image from the local cache.": "", - "Additional help topics": "", - "Additional mount options, such as cache=fscache": "", - "Advanced Commands:": "", - "Aliases": "", - "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \\\"auto\\\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers": "", - "Alternatively, you may delete the existing VM using `minikube delete -p {{.profile_name}}`": "", - "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "", - "Amount of time to wait for a service in seconds": "", - "Amount of time to wait for service in seconds": "", - "Available Commands": "", - "Basic Commands:": "", - "Cannot find directory {{.path}} for mount": "", - "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.": "", - "Check that your --kubernetes-version has a leading 'v'. For example: 'v1.1.14'": "", - "Configuration and Management Commands:": "", - "Configure an external network switch following the official documentation, then add `--hyperv-virtual-switch=\u003cswitch-name\u003e` to `minikube start`": "", - "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list ": "", + "\"{{.minikube_addon}}\" was successfully disabled": "已成功禁用 \"{{.minikube_addon}}\"", + "\"{{.name}}\" cluster does not exist. Proceeding ahead with cleanup.": "\"{{.name}}\" 集群不存在,将继续清理", + "\"{{.name}}\" profile does not exist": "“{{.name}}”配置文件不存在", + "\"{{.profile_name}}\" VM does not exist, nothing to stop": "\"{{.profile_name}}\" 虚拟机不存在,没有什么可供停止的", + "\"{{.profile_name}}\" host does not exist, unable to show an IP": "\"{{.profile_name}}\" 主机不存在,无法显示其IP", + "\"{{.profile_name}}\" stopped.": "\"{{.profile_name}}\" 已停止", + "'none' driver does not support 'minikube docker-env' command": "'none' 驱动不支持 'minikube docker-env' 命令", + "'none' driver does not support 'minikube mount' command": "'none' 驱动不支持 'minikube mount' 命令", + "'none' driver does not support 'minikube ssh' command": "'none' 驱动不支持 'minikube ssh' 命令", + "A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "VPN 或者防火墙正在干扰对 minikube 虚拟机的 HTTP 访问。或者,您可以使用其它的虚拟机驱动:https://minikube.sigs.k8s.io/docs/start/", + "A firewall is blocking Docker within the minikube VM from reaching the internet. You may need to configure it to use a proxy.": "防火墙正在阻止 minikube 虚拟机中的 Docker 访问互联网,您可能需要对其进行配置为使用代理", + "A firewall is interfering with minikube's ability to make outgoing HTTPS requests. You may need to change the value of the HTTPS_PROXY environment variable.": "防火墙正在干扰 minikube 发送 HTTPS 请求的能力,您可能需要改变 HTTPS_PROXY 环境变量的值", + "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.": "防火墙可能会阻止 minikube 访问互联网。您可能需要将 minikube 配置为使用", + "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "一组在为 kubernetes 生成的证书中使用的 apiserver IP 地址。如果您希望将此 apiserver 设置为可从机器外部访问,则可以使用这组 apiserver IP 地址", + "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "一组在为 kubernetes 生成的证书中使用的 apiserver IP 地址。如果您希望将此 apiserver 设置为可从机器外部访问,则可以使用这组 apiserver IP 地址", + "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "一组在为 kubernetes 生成的证书中使用的 apiserver 名称。如果您希望将此 apiserver 设置为可从机器外部访问,则可以使用这组 apiserver 名称", + "A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "一组在为 kubernetes 生成的证书中使用的 apiserver 名称。如果您希望将此 apiserver 设置为可从机器外部访问,则可以使用这组 apiserver 名称", + "A set of key=value pairs that describe configuration that may be passed to different components.\nThe key should be '.' separated, and the first part before the dot is the component to apply the configuration to.\nValid components are: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy, scheduler\nValid kubeadm parameters:": "一组用于描述可传递给不同组件的配置的键值对。\n其中键应以英文句点“.”分隔,英文句点前面的第一个部分是应用该配置的组件。\n有效组件包括:kubelet、kubeadm、apiserver、controller-manager、etcd、proxy、scheduler\n有效 kubeadm 参数包括:", + "A set of key=value pairs that describe feature gates for alpha/experimental features.": "一组用于描述 alpha 版功能/实验性功能的功能限制的键值对。", + "Access the kubernetes dashboard running within the minikube cluster": "访问在 minikube 集群中运行的 kubernetes dashboard", + "Add an image to local cache.": "将 image 添加到本地缓存。", + "Add machine IP to NO_PROXY environment variable": "将机器IP添加到环境变量 NO_PROXY 中", + "Add or delete an image from the local cache.": "在本地缓存中添加或删除 image。", + "Additional help topics": "其他帮助", + "Additional mount options, such as cache=fscache": "其他挂载选项,例如:cache=fscache", + "Advanced Commands:": "高级命令:", + "Aliases": "别名", + "Allow user prompts for more information": "允许用户提示以获取更多信息", + "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \\\"auto\\\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers": "用于从中拉取 docker 映像的备选映像存储库。如果您对 gcr.io 的访问受到限制,则可以使用该映像存储库。将映像存储库设置为“auto”可让 minikube 为您选择一个存储库。对于中国大陆用户,您可以使用本地 gcr.io 镜像,例如 registry.cn-hangzhou.aliyuncs.com/google_containers", + "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "为 minikube 虚拟机分配的 RAM 容量(格式:\u003c数字\u003e[\u003c单位\u003e],其中单位 = b、k、m 或 g)", + "Amount of RAM allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "为 minikube 虚拟机分配的 RAM 容量(格式:\u003c数字\u003e[\u003c单位\u003e],其中单位 = b、k、m 或 g)。", + "Amount of time to wait for a service in seconds": "等待服务的时间(单位秒)", + "Amount of time to wait for service in seconds": "等待服务的时间(单位秒)", + "Available Commands": "可用命令", + "Basic Commands:": "基本命令:", + "Cannot find directory {{.path}} for mount": "找不到用来挂载的 {{.path}} 目录", + "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.": "检测 minikube 是否正在运行,以及是否根据需要指定了正确的 namespace (-n 标志)", + "Check that your --kubernetes-version has a leading 'v'. For example: 'v1.1.14'": "检测您的 --kubernetes-version 前面是否有 'v', 例如:'v1.1.14", + "Check that your apiserver flags are valid, or run 'minikube delete'": "请检查您的 apiserver 标志是否有效,或者允许 'minikube delete'", + "Check your firewall rules for interference, and run 'virt-host-validate' to check for KVM configuration issues. If you are running minikube within a VM, consider using --vm-driver=none": "检查您的防火墙规则是否存在干扰,然后运行 'virt-host-validate' 以检查 KVM 配置问题,如果在虚拟机中运行minikube,请考虑使用 --vm-driver=none", + "Configuration and Management Commands:": "配置和管理命令:", + "Configure an external network switch following the official documentation, then add `--hyperv-virtual-switch=\u003cswitch-name\u003e` to `minikube start`": "根据官方文档配置外部网络交换机,然后添加 `--hyperv-virtual-switch=\u003cswitch-name\u003e` 到 `minikube start`", + "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list": "在 minikube 中配置插件 w/ADDON_NAME(例如:minikube addons configure registry-creds)。查看相关可用的插件列表,请使用:minikube addons list", "Configuring environment for Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}}": "开始为Kubernetes {{.k8sVersion}},{{.runtime}} {{.runtimeVersion}} 配置环境变量", - "Configuring local host environment ...": "", - "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn.": "", - "Created a new profile : {{.profile_name}}": "", - "Creating %s VM (CPUs=%d, Memory=%dMB, Disk=%dMB) ...": "正在创建%s虚拟机(CPU=%d,内存=%dMB,磁盘=%dMB)...", - "Creating a new profile failed": "", - "Creating mount {{.name}} ...": "", - "Creating {{.driver_name}} VM (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "", - "Default group id used for the mount": "", - "Default user id used for the mount": "", - "Delete an image from the local cache.": "", - "Deletes a local kubernetes cluster": "", - "Deletes a local kubernetes cluster. This command deletes the VM, and removes all\nassociated files.": "", - "Deleting \"{{.profile_name}}\" in {{.driver_name}} ...": "", - "Disable Hyper-V when you want to run VirtualBox to boot the VM": "", - "Disable checking for the availability of hardware virtualization before the vm is started (virtualbox driver only)": "", - "Disable dynamic memory in your VM manager, or pass in a larger --memory value": "", - "Disable real-time anti-virus software, reboot, and reinstall VirtualBox if the problem continues.": "", - "Disables the addon w/ADDON_NAME within minikube (example: minikube addons disable dashboard). For a list of available addons use: minikube addons list ": "", - "Disables the filesystem mounts provided by the hypervisors": "", - "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "", - "Display dashboard URL instead of opening a browser": "", - "Display the kubernetes addons URL in the CLI instead of opening it in the default browser": "", - "Display the kubernetes service URL in the CLI instead of opening it in the default browser": "", - "Display values currently set in the minikube config file": "", - "Display values currently set in the minikube config file.": "", - "Docker inside the VM is unavailable. Try running 'minikube delete' to reset the VM.": "", - "Docs have been saved at - {{.path}}": "", - "Documentation: {{.url}}": "", - "Done! kubectl is now configured to use \"{{.name}}\"": "", + "Configuring local host environment ...": "开始配置本地主机环境...", + "Confirm that you have a working internet connection and that your VM has not run out of resources by using: 'minikube logs'": "使用 'minikube logs' 确认您的互联网连接正常,并且您的虚拟机没有耗尽资源", + "Confirm that you have supplied the correct value to --hyperv-virtual-switch using the 'Get-VMSwitch' command": "使用 'Get-VMSwitch' 命令确认已经为 --hyperv-virtual-switch 提供了正确的值", + "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn.": "需要使用的映像镜像的国家/地区代码。留空以使用全球代码。对于中国大陆用户,请将其设置为 cn。", + "Created a new profile : {{.profile_name}}": "创建了新的配置文件:{{.profile_name}}", + "Creating a new profile failed": "创建新的配置文件失败", + "Creating mount {{.name}} ...": "正在创建装载 {{.name}}…", + "Creating {{.driver_name}} VM (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "正在创建 {{.driver_name}} 虚拟机(CPUs={{.number_of_cpus}},Memory={{.memory_size}}MB, Disk={{.disk_size}}MB)...", + "Default group id used for the mount": "用于挂载默认的 group id", + "Default user id used for the mount": "用于挂载默认的 user id", + "Delete an image from the local cache.": "从本地缓存中删除 image。", + "Deletes a local kubernetes cluster": "删除本地的 kubernetes 集群", + "Deletes a local kubernetes cluster. This command deletes the VM, and removes all\nassociated files.": "删除本地的 kubernetes 集群。此命令还将删除虚拟机,并删除所有的\n相关文件", + "Deletes a local kubernetes cluster. This command deletes the VM, and removes all associated files.": "删除本地 kubernetes 集群。此命令会删除虚拟机并移除所有关联的文件。", + "Deleting \"{{.profile_name}}\" in {{.driver_name}} ...": "正在删除 {{.driver_name}} 中的“{{.profile_name}}”…", + "Disable checking for the availability of hardware virtualization before the vm is started (virtualbox driver only)": "禁用在启动虚拟机之前检查硬件虚拟化的可用性(仅限 virtualbox 驱动程序)", + "Disable dynamic memory in your VM manager, or pass in a larger --memory value": "禁用虚拟机管理器中的动态内存,或者使用 --memory 传入更大的值", + "Disables the addon w/ADDON_NAME within minikube (example: minikube addons disable dashboard). For a list of available addons use: minikube addons list": "在 minikube 中禁用插件 w/ADDON_NAME(例如:minikube addons disable dashboard)。查看相关可用的插件列表,请使用:minikube addons list", + "Disables the filesystem mounts provided by the hypervisors": "停用由管理程序提供的文件系统装载", + "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g)": "分配给 minikube 虚拟机的磁盘大小(格式:\u003c数字\u003e[\u003c单位\u003e],其中单位 = b、k、m 或 g)", + "Disk size allocated to the minikube VM (format: \u003cnumber\u003e[\u003cunit\u003e], where unit = b, k, m or g).": "分配给 minikube 虚拟机的磁盘大小(格式:\u003c数字\u003e[\u003c单位\u003e],其中单位 = b、k、m 或 g)。", + "Display dashboard URL instead of opening a browser": "显示 dashboard URL,而不是打开浏览器", + "Display the kubernetes addons URL in the CLI instead of opening it in the default browser": "在终端中显示 kubernetes addons URL,而不是在默认浏览器中打开它", + "Display the kubernetes service URL in the CLI instead of opening it in the default browser": "在终端中显示 kubernetes service URL,而不是在默认浏览器中打开它", + "Display values currently set in the minikube config file": "显示当前在 minikube 配置文件中设置的值", + "Display values currently set in the minikube config file.": "显示当前在 minikube 配置文件中设置的值。", + "Docker inside the VM is unavailable. Try running 'minikube delete' to reset the VM.": "虚拟机中的 Docker 不可用,尝试运行 'minikube delete' 来重置虚拟机。", + "Docs have been saved at - {{.path}}": "文档已保存在 - {{.path}}", + "Documentation: {{.url}}": "文档:{{.url}}", + "Done! kubectl is now configured to use \"{{.name}}\"": "完成!kubectl 已经配置至 \"{{.name}}\"", "Done! kubectl is now configured to use {{.name}}": "完成!kubectl已经配置至{{.name}}", - "Download complete!": "", - "Downloading VM boot image ...": "", - "Downloading {{.name}} {{.version}}": "", - "ERROR creating `registry-creds-dpr` secret": "", - "ERROR creating `registry-creds-ecr` secret: {{.error}}": "", - "ERROR creating `registry-creds-gcr` secret: {{.error}}": "", - "Enable experimental NVIDIA GPU support in minikube": "", - "Enable host resolver for NAT DNS requests (virtualbox driver only)": "", - "Enable proxy for NAT DNS requests (virtualbox driver only)": "", - "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\\".": "", - "Enables the addon w/ADDON_NAME within minikube (example: minikube addons enable dashboard). For a list of available addons use: minikube addons list ": "", - "Enabling dashboard ...": "", - "Environment variables to pass to the Docker daemon. (format: key=value)": "", - "Error checking driver version: {{.error}}": "", - "Error creating list template": "", - "Error creating minikube directory": "", - "Error creating status template": "", - "Error creating view template": "", - "Error executing list template": "", - "Error executing status template": "", - "Error executing template": "", - "Error executing view template": "", - "Error finding port for mount": "", - "Error getting IP": "", - "Error getting bootstrapper": "", - "Error getting client": "", - "Error getting client: {{.error}}": "", - "Error getting cluster": "", - "Error getting cluster bootstrapper": "", - "Error getting config": "", - "Error getting host": "", - "Error getting host status": "", - "Error getting machine logs": "", - "Error getting machine status": "", - "Error getting service status": "", - "Error getting service with namespace: {{.namespace}} and labels {{.labelName}}:{{.addonName}}: {{.error}}": "", - "Error getting the host IP address to use from within the VM": "", + "Download complete!": "下载完成!", + "Downloading VM boot image ...": "正在下载 VM boot image...", + "Downloading driver {{.driver}}:": "正在下载驱动 {{.driver}}:", + "Downloading {{.name}} {{.version}}": "正在下载 {{.name}} {{.version}}", + "ERROR creating `registry-creds-dpr` secret": "创建 `registry-creds-dpr` secret 时出错", + "ERROR creating `registry-creds-ecr` secret: {{.error}}": "创建 `registry-creds-ecr` secret 时出错:{{.error}}", + "ERROR creating `registry-creds-gcr` secret: {{.error}}": "创建 `registry-creds-gcr` secret 时出错:{{.error}}", + "Either systemctl is not installed, or Docker is broken. Run 'sudo systemctl start docker' and 'journalctl -u docker'": "未安装 systemctl 或者 Docker 损坏。请运行 'sudo systemctl start docker' 和 'journalctl -u docker'", + "Enable experimental NVIDIA GPU support in minikube": "在 minikube 中启用实验性 NVIDIA GPU 支持", + "Enable host resolver for NAT DNS requests (virtualbox driver only)": "为 NAT DNS 请求启用主机解析器(仅限 virtualbox 驱动程序)", + "Enable proxy for NAT DNS requests (virtualbox driver only)": "为 NAT DNS 请求启用代理(仅限 virtualbox 驱动程序)", + "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\": "启用默认 CNI 插件 (/etc/cni/net.d/k8s.conf)。与“--network-plugin=cni”结合使用", + "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \\\"--network-plugin=cni\\\".": "启用默认 CNI 插件 (/etc/cni/net.d/k8s.conf)。与“--network-plugin=cni”结合使用。", + "Enables the addon w/ADDON_NAME within minikube (example: minikube addons enable dashboard). For a list of available addons use: minikube addons list": "启动 minikube 插件 w/ADDON_NAME(例如:minikube addons enable dashboard)。查看相关可用的插件列表,请使用:minikube addons list", + "Enabling dashboard ...": "正在开启 dashboard ...", + "Environment variables to pass to the Docker daemon. (format: key=value)": "传递给 Docker 守护进程的环境变量。(格式:键值对)", + "Error checking driver version: {{.error}}": "检查驱动程序版本时出错:{{.error}}", + "Error creating list template": "创建 list template 时出错", + "Error creating minikube directory": "创建 minikube 目录时出错", + "Error creating status template": "创建 status template 时出错", + "Error creating view template": "创建 view template 时出错", + "Error executing list template": "执行 list template 时出错", + "Error executing status template": "执行 status template 时出错", + "Error executing template": "执行 template 时出错", + "Error executing view template": "执行 view template 时出错", + "Error finding port for mount": "查找 mount 端口时出错", + "Error getting IP": "获取 IP 时出错", + "Error getting bootstrapper": "获取 bootstrapper 时出错", + "Error getting client": "获取 client 时出错", + "Error getting client: {{.error}}": "获取 client 时出错:{{.error}}", + "Error getting cluster": "获取 cluster 时出错", + "Error getting cluster bootstrapper": "获取 cluster bootstrapper 时出错", + "Error getting config": "获取 config 时出错", + "Error getting host": "获取 host 时出错", + "Error getting host status": "获取 host status 时出错", + "Error getting machine logs": "获取 machine logs 时出错", + "Error getting machine status": "获取 machine status 时出错", + "Error getting service status": "获取 service status 时出错", + "Error getting service with namespace: {{.namespace}} and labels {{.labelName}}:{{.addonName}}: {{.error}}": "使用 namespace: {{.namespace}} 和 labels {{.labelName}}:{{.addonName}} 获取 service 时出错:{{.error}}", + "Error getting the host IP address to use from within the VM": "从虚拟机中获取 host IP 地址时出错", "Error host driver ip status": "", - "Error killing mount process": "", - "Error loading api": "", - "Error loading profile config": "", - "Error loading profile config: {{.error}}": "", - "Error loading profile {{.name}}: {{.error}}": "", - "Error opening service": "", - "Error parsing minikube version: {{.error}}": "", - "Error parsing vmDriver version: {{.error}}": "", - "Error reading {{.path}}: {{.error}}": "", - "Error restarting cluster": "", - "Error setting shell variables": "", - "Error starting cluster": "", - "Error starting mount": "", - "Error unsetting shell variables": "", - "Error while setting kubectl current context : {{.error}}": "", - "Error writing mount pid": "", - "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}\"": "", - "Error: [{{.id}}] {{.error}}": "", - "Examples": "", - "Exiting": "", + "Error killing mount process": "杀死 mount 进程时出错", + "Error loading api": "加载 api 时出错", + "Error loading profile config": "加载配置文件的配置时出错", + "Error loading profile config: {{.error}}": "加载配置文件的配置时出错:{{.error}}", + "Error loading profile {{.name}}: {{.error}}": "加载配置文件 {{.name}} 时出错:{{.error}}", + "Error opening service": "开启 service 时出错", + "Error parsing minikube version: {{.error}}": "解析 minikube 版本时出错:{{.error}}", + "Error parsing vmDriver version: {{.error}}": "解析 vmDriver 版本时出错:{{.error}}", + "Error reading {{.path}}: {{.error}}": "读取 {{.path}} 时出错:{{.error}}", + "Error restarting cluster": "重启 cluster 时出错", + "Error setting shell variables": "设置 shell 变量时出错", + "Error starting cluster": "开启 cluster 时出错", + "Error starting mount": "开启 mount 时出错", + "Error unsetting shell variables": "取消设置 shell 变量时出错", + "Error while setting kubectl current context : {{.error}}": "设置 kubectl 上下文时出错 :{{.error}}", + "Error writing mount pid": "写入 mount pid 时出错", + "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}\"": "错误:您已选择 Kubernetes v{{.new}},但您的配置文件的现有集群正在运行 Kubernetes v{{.old}}。非破坏性降级不受支持,但若要继续操作,您可以执行以下选项之一:\n\n* 使用 Kubernetes v{{.new}} 重新创建现有集群:运行“minikube delete {{.profile}}”,然后运行“minikube start {{.profile}} --kubernetes-version={{.new}}”\n* 使用 Kubernetes v{{.new}} 再创建一个集群:运行“minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}”\n* 通过 Kubernetes v{{.old}} 或更高版本重复使用现有集群:运行“minikube start {{.profile}} --kubernetes-version={{.old}}”", + "Error: You have selected Kubernetes v{{.new}}, but the existing cluster for your profile is running Kubernetes v{{.old}}. Non-destructive downgrades are not supported, but you can proceed by performing one of the following options:\n* Recreate the cluster using Kubernetes v{{.new}}: Run \"minikube delete {{.profile}}\", then \"minikube start {{.profile}} --kubernetes-version={{.new}}\"\n* Create a second cluster with Kubernetes v{{.new}}: Run \"minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}\"\n* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run \"minikube start {{.profile}} --kubernetes-version={{.old}}": "错误:您已选择 Kubernetes v{{.new}},但您的配置文件的现有集群正在运行 Kubernetes v{{.old}}。非破坏性降级不受支持,但若要继续操作,您可以执行以下选项之一:\n* 使用 Kubernetes v{{.new}} 重新创建现有集群:运行“minikube delete {{.profile}}”,然后运行“minikube start {{.profile}} --kubernetes-version={{.new}}”\n* 使用 Kubernetes v{{.new}} 再创建一个集群:运行“minikube start -p \u003cnew name\u003e --kubernetes-version={{.new}}”\n* 通过 Kubernetes v{{.old}} 或更高版本重复使用现有集群:运行“minikube start {{.profile}} --kubernetes-version={{.old}}”", + "Error: [{{.id}}] {{.error}}": "错误:[{{.id}}] {{.error}}", + "Examples": "示例", + "Exiting": "正在退出", + "Exiting due to driver incompatibility": "由于驱动程序不兼容而退出", "Failed runtime": "", - "Failed to cache ISO": "", - "Failed to cache and load images": "", - "Failed to cache binaries": "", - "Failed to cache images": "", - "Failed to change permissions for {{.minikube_dir_path}}: {{.error}}": "", - "Failed to check if machine exists": "", - "Failed to check main repository and mirrors for images for images": "", - "Failed to delete cluster: {{.error}}": "", - "Failed to delete images": "", - "Failed to delete images from config": "", - "Failed to download kubectl": "", + "Failed to cache ISO": "缓存ISO 时失败", + "Failed to cache and load images": "无法加载 cache 和 images", + "Failed to cache binaries": "缓存二进制文件失败", + "Failed to cache images": "缓存 images 时失败", + "Failed to change permissions for {{.minikube_dir_path}}: {{.error}}": "未能更改 {{.minikube_dir_path}} 的权限:{{.error}}", + "Failed to check if machine exists": "无法检测机器是否存在", + "Failed to check main repository and mirrors for images for images": "无法检测主 repository 和 mirrors images 中的 images", + "Failed to delete cluster: {{.error}}": "未能删除集群:{{.error}}", + "Failed to delete cluster: {{.error}}__1": "未能删除集群:{{.error}}", + "Failed to delete images": "删除 images 时失败", + "Failed to delete images from config": "无法从 config 里删除 images", + "Failed to download kubectl": "下载 kubectl 失败", "Failed to enable container runtime": "", - "Failed to generate config": "", - "Failed to get bootstrapper": "", + "Failed to generate config": "无法生成 config", + "Failed to get bootstrapper": "获取 bootstrapper 失败", "Failed to get command runner": "", - "Failed to get driver URL": "", + "Failed to get driver URL": "获取 driver URL 失败", "Failed to get image map": "", "Failed to get machine client": "", - "Failed to get service URL: {{.error}}": "", - "Failed to kill mount process: {{.error}}": "", - "Failed to list cached images": "", - "Failed to remove profile": "", - "Failed to save config": "", - "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}`.": "", - "Failed to setup certs": "", - "Failed to setup kubeconfig": "", - "Failed to update cluster": "", - "Failed to update config": "", - "Failed unmount: {{.error}}": "", - "File permissions used for the mount": "", - "Flags": "", - "Follow": "", - "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "", - "For more information, see:": "", - "Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect": "", - "Force minikube to perform possibly dangerous operations": "", - "Found network options:": "", - "Found {{.number}} invalid profile(s) ! ": "", - "Gets the kubernetes URL(s) for the specified service in your local cluster": "", - "Gets the kubernetes URL(s) for the specified service in your local cluster. In the case of multiple URLs they will be printed one at a time.": "", - "Gets the logs of the running instance, used for debugging minikube, not user code.": "", - "Gets the status of a local kubernetes cluster": "", + "Failed to get service URL: {{.error}}": "获取 service URL 失败:{{.error}}", + "Failed to kill mount process: {{.error}}": "未能终止装载进程:{{.error}}", + "Failed to list cached images": "无法列出缓存镜像", + "Failed to remove profile": "无法删除配置文件", + "Failed to save config": "无法保存配置", + "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}": "未能设置 NO_PROXY 环境变量。请使用“export NO_PROXY=$NO_PROXY,{{.ip}}”", + "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,{{.ip}}`.": "未能设置 NO_PROXY 环境变量。请使用“export NO_PROXY=$NO_PROXY,{{.ip}}”。", + "Failed to setup certs": "设置 certs 失败", + "Failed to setup kubeconfig": "设置 kubeconfig 失败", + "Failed to update cluster": "更新 cluster 失败", + "Failed to update config": "更新 config 失败", + "Failed unmount: {{.error}}": "unmount 失败:{{.error}}", + "File permissions used for the mount": "用于 mount 的文件权限", + "Flags": "标志", + "Follow": "跟踪", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "为获得最佳结果,请安装 kubectl:https://kubernetes.io/docs/tasks/tools/install-kubectl/", + "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/__1": "为获得最佳结果,请安装 kubectl:https://kubernetes.io/docs/tasks/tools/install-kubectl/", + "For more information, see:": "如需了解详情,请参阅:", + "Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect": "强制为指定的 shell 配置环境:[fish, cmd, powershell, tcsh, bash, zsh],默认为 auto-detect", + "Force minikube to perform possibly dangerous operations": "强制 minikube 执行可能有风险的操作", + "Found network options:": "找到的网络选项:", + "Found {{.number}} invalid profile(s) !": "找到 {{.number}} 个无效的配置文件!", + "Gets the kubernetes URL(s) for the specified service in your local cluster": "获取本地集群中指定服务的 kubernetes URL", + "Gets the kubernetes URL(s) for the specified service in your local cluster. In the case of multiple URLs they will be printed one at a time.": "获取本地集群中指定服务的 kubernetes URL。如果有多个 URL,他们将一次打印一个", + "Gets the logs of the running instance, used for debugging minikube, not user code.": "获取正在运行的实例日志,用于调试 minikube,不是用户代码", + "Gets the status of a local kubernetes cluster": "获取本地 kubernetes 集群状态", "Gets the status of a local kubernetes cluster.\n\tExit status contains the status of minikube's VM, cluster and kubernetes encoded on it's bits in this order from right to left.\n\tEg: 7 meaning: 1 (for minikube NOK) + 2 (for cluster NOK) + 4 (for kubernetes NOK)": "", "Gets the value of PROPERTY_NAME from the minikube config file": "", "Global Flags": "", @@ -177,31 +192,33 @@ "Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/\nFor the list accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#Status": "", "Group ID: {{.groupID}}": "", "Have you set up libvirt correctly?": "", - "Hide the hypervisor signature from the guest in minikube (kvm2 driver only)": "", - "If the above advice does not help, please let us know: ": "", - "If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --vm-driver=none.": "", - "If true, only download and cache files for later use - don't install or start anything.": "", + "Hide the hypervisor signature from the guest in minikube (kvm2 driver only)": "向 minikube 中的访客隐藏管理程序签名(仅限 kvm2 驱动程序)", + "If the above advice does not help, please let us know:": "", + "If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --vm-driver=none.": "如果为 true,请缓存当前引导程序的 docker 映像并将其加载到机器中。在 --vm-driver=none 情况下始终为 false。", + "If true, only download and cache files for later use - don't install or start anything.": "如果为 true,仅会下载和缓存文件以备后用 - 不会安装或启动任何项。", "If using the none driver, ensure that systemctl is installed": "", - "Ignoring --vm-driver={{.driver_name}}, as the existing \"{{.profile_name}}\" VM was created using the {{.driver_name2}} driver.": "", + "If you are running minikube within a VM, consider using --vm-driver=none:": "", "Images Commands:": "", - "In some environments, this message is incorrect. Try 'minikube start --no-vtx-check'": "", "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "", - "Install VirtualBox, ensure that VBoxManage is executable and in path, or select an alternative value for --vm-driver": "", - "Install the latest kvm2 driver and run 'virt-host-validate'": "", - "Install the latest minikube hyperkit driver, and run 'minikube delete'": "", + "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.": "传递给 Docker 守护进程的不安全 Docker 注册表。系统会自动添加默认服务 CIDR 范围。", + "Install VirtualBox, or select an alternative value for --vm-driver": "", + "Install the latest hyperkit binary, and run 'minikube delete'": "", "Invalid size passed in argument: {{.error}}": "", "IsEnabled failed": "", "Kill the mount process spawned by minikube start": "", + "Kubernetes {{.version}} is not supported by this release of minikube": "", + "Launching Kubernetes ...": "", "Launching Kubernetes ... ": "正在启动 Kubernetes ... ", "Launching proxy ...": "", "List all available images from the local cache.": "", - "List of guest VSock ports that should be exposed as sockets on the host (hyperkit driver only)": "", + "List of guest VSock ports that should be exposed as sockets on the host (hyperkit driver only)": "应在主机上公开为套接字的访客 VSock 端口列表(仅限 hyperkit 驱动程序)", "Lists all available minikube addons as well as their current statuses (enabled/disabled)": "", "Lists all minikube profiles.": "", "Lists all valid minikube profiles and detects all possible invalid profiles.": "", "Lists the URLs for the services in your local cluster": "", - "Local folders to share with Guest via NFS mounts (hyperkit driver only)": "", - "Location of the VPNKit socket used for networking. If empty, disables Hyperkit VPNKitSock, if 'auto' uses Docker for Mac VPNKit connection, otherwise uses the specified VSock (hyperkit driver only)": "", + "Local folders to share with Guest via NFS mounts (hyperkit driver only)": "通过 NFS 装载与访客共享的本地文件夹(仅限 hyperkit 驱动程序)", + "Location of the VPNKit socket used for networking. If empty, disables Hyperkit VPNKitSock, if 'auto' uses Docker for Mac VPNKit connection, otherwise uses the specified VSock (hyperkit driver only)": "用于网络连接的 VPNKit 套接字的位置。如果为空,则停用 Hyperkit VPNKitSock;如果为“auto”,则将 Docker 用于 Mac VPNKit 连接;否则使用指定的 VSock(仅限 hyperkit 驱动程序)", + "Location of the minikube iso": "minikube iso 的位置", "Location of the minikube iso.": "", "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'": "", "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'.": "", @@ -217,8 +234,10 @@ "NOTE: This process must stay alive for the mount to be accessible ...": "", "Networking and Connectivity Commands:": "", "No minikube profile was found. You can create one using `minikube start`.": "", - "None of the known repositories in your location are accessible. Using {{.image_repository_name}} as fallback.": "", - "None of the known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag": "", + "None of the known repositories in your location are accessible. Using {{.image_repository_name}} as fallback.": "您所在位置的已知存储库都无法访问。正在将 {{.image_repository_name}} 用作后备存储库。", + "None of the known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag": "已知存储库都无法访问。请考虑使用 --image-repository 标志指定备选映像存储库", + "Not passing {{.name}}={{.value}} to docker env.": "", + "Number of CPUs allocated to the minikube VM": "分配给 minikube 虚拟机的 CPU 的数量", "Number of CPUs allocated to the minikube VM.": "", "Number of lines back to go within the log": "", "OS release is {{.pretty_name}}": "", @@ -226,52 +245,54 @@ "Open the service URL with https instead of http": "", "Opening kubernetes service {{.namespace_name}}/{{.service_name}} in default browser...": "", "Opening {{.url}} in your default browser...": "", - "Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list ": "", + "Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list": "", "Options: {{.options}}": "", "Outputs minikube shell completion for the given shell (bash or zsh)": "", + "Outputs minikube shell completion for the given shell (bash or zsh)\n\n\tThis depends on the bash-completion binary. Example installation instructions:\n\tOS X:\n\t\t$ brew install bash-completion\n\t\t$ source $(brew --prefix)/etc/bash_completion\n\t\t$ minikube completion bash \u003e ~/.minikube-completion # for bash users\n\t\t$ minikube completion zsh \u003e ~/.minikube-completion # for zsh users\n\t\t$ source ~/.minikube-completion\n\tUbuntu:\n\t\t$ apt-get install bash-completion\n\t\t$ source /etc/bash-completion\n\t\t$ source \u003c(minikube completion bash) # for bash users\n\t\t$ source \u003c(minikube completion zsh) # for zsh users\n\n\tAdditionally, you may want to output the completion to a file and source in your .bashrc\n\n\tNote for zsh users: [1] zsh completions are only supported in versions of zsh \u003e= 5.2": "", "Permissions: {{.octalMode}} ({{.writtenMode}})": "", - "Please check your BIOS, and ensure that you are running without HyperV or other nested virtualization that may interfere": "", "Please enter a value:": "", "Please install the minikube hyperkit VM driver, or select an alternative --vm-driver": "", "Please install the minikube kvm2 VM driver, or select an alternative --vm-driver": "", "Please make sure the service you are looking for is deployed or is in the correct namespace.": "", "Please specify the directory to be mounted: \n\tminikube mount \u003csource directory\u003e:\u003ctarget directory\u003e (example: \"/host-home:/vm-home\")": "", - "Please upgrade the '{{.driver_executable}}'. {{.documentation_url}}": "", + "Please upgrade the '{{.driver_executable}}'. {{.documentation_url}}": "请升级“{{.driver_executable}}”。{{.documentation_url}}", "Populates the specified folder with documentation in markdown about minikube": "", - "Powering off \"{{.profile_name}}\" via SSH ...": "", - "Preparing Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}} ...": "", + "Powering off \"{{.profile_name}}\" via SSH ...": "正在通过 SSH 关闭“{{.profile_name}}”…", + "Preparing Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}} ...": "正在 {{.runtime}} {{.runtimeVersion}} 中准备 Kubernetes {{.k8sVersion}}…", "Print current and latest version number": "", "Print the version of minikube": "", "Print the version of minikube.": "", "Problems detected in {{.entry}}:": "", "Problems detected in {{.name}}:": "", "Profile gets or sets the current minikube profile": "", - "Provide VM UUID to restore MAC address (hyperkit driver only)": "", + "Provide VM UUID to restore MAC address (hyperkit driver only)": "提供虚拟机 UUID 以恢复 MAC 地址(仅限 hyperkit 驱动程序)", "Pulling images ...": "拉取镜像 ...", - "Re-run 'minikube start' with --alsologtostderr -v=8 to see the VM driver error message": "", "Reboot to complete VirtualBox installation, and verify that VirtualBox is not blocked by your system": "", "Rebuild libvirt with virt-network support": "", "Received {{.name}} signal": "", - "Registry mirrors to pass to the Docker daemon": "", + "Registry mirrors to pass to the Docker daemon": "传递给 Docker 守护进程的注册表镜像", + "Reinstall VirtualBox and reboot. Alternatively, try the kvm2 driver: https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/": "", "Reinstall VirtualBox and verify that it is not blocked: System Preferences -\u003e Security \u0026 Privacy -\u003e General -\u003e Some system software was blocked from loading": "", "Related issues:": "", - "Relaunching Kubernetes using {{.bootstrapper}} ... ": "", - "Removing {{.directory}} ...": "", - "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}": "", - "Requested memory allocation ({{.memory}}MB) is less than the default memory allocation of {{.default_memorysize}}MB. Beware that minikube might not work correctly or crash unexpectedly.": "", - "Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}": "", + "Relaunching Kubernetes using {{.bootstrapper}} ...": "正在使用 {{.bootstrapper}} 重新启动 Kubernetes…", + "Removing {{.directory}} ...": "正在移除 {{.directory}}…", + "Requested CPU count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}": "", + "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}": "请求的磁盘大小 {{.requested_size}} 小于 {{.minimum_size}} 的最小值", + "Requested memory allocation ({{.memory}}MB) is less than the default memory allocation of {{.default_memorysize}}MB. Beware that minikube might not work correctly or crash unexpectedly.": "请求的内存分配 ({{.memory}}MB) 小于默认内存分配 {{.default_memorysize}}MB。请注意 minikube 可能无法正常运行或可能会意外崩溃。", + "Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}": "请求的内存分配 {{.requested_size}} 小于允许的 {{.minimum_size}} 最小值", + "Retriable failure: {{.error}}": "", "Retrieve the ssh identity key path of the specified cluster": "", "Retrieve the ssh identity key path of the specified cluster.": "", "Retrieves the IP address of the running cluster": "", "Retrieves the IP address of the running cluster, and writes it to STDOUT.": "", "Retrieves the IP address of the running cluster, checks it\n\t\t\twith IP in kubeconfig, and corrects kubeconfig if incorrect.": "", "Returns the value of PROPERTY_NAME from the minikube config file. Can be overwritten at runtime by flags or environmental variables.": "", + "Run 'kubectl describe pod coredns -n kube-system' and check for a firewall or DNS conflict": "", "Run 'minikube delete' to delete the stale VM": "", - "Run 'minikube delete'. If the problem persists, check your proxy or firewall configuration": "", - "Run 'sudo modprobe vboxdrv' and reinstall VirtualBox if it fails.": "", "Run kubectl": "", "Run minikube from the C: drive.": "", "Run the kubernetes client, download it if necessary.\nExamples:\nminikube kubectl -- --help\nkubectl get pods --namespace kube-system": "", + "Run the minikube command as an Administrator": "", "Running on localhost (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...": "", "Set failed": "", "Sets an individual value in a minikube config file": "", @@ -283,45 +304,60 @@ "Show only the most recent journal entries, and continuously print new entries as they are appended to the journal.": "", "Skipped switching kubectl context for {{.profile_name}} because --keep-context was set.": "", "Sorry that minikube crashed. If this was unexpected, we would love to hear from you:": "", + "Sorry, Kubernetes {{.version}} is not supported by this release of minikube": "", "Sorry, completion support is not yet implemented for {{.name}}": "", - "Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config": "", - "Sorry, the url provided with the --registry-mirror flag is invalid: {{.url}}": "", + "Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config": "抱歉,--extra-config 目前不支持 kubeadm.{{.parameter_name}} 参数", + "Sorry, the url provided with the --registry-mirror flag is invalid: {{.url}}": "抱歉,通过 --registry-mirror 标志提供的网址无效:{{.url}}", + "Specified Kubernetes version {{.specified}} is less than the oldest supported version: {{.oldest}}": "", "Specify --kubernetes-version in v\u003cmajor\u003e.\u003cminor.\u003cbuild\u003e form. example: 'v1.1.14'": "", "Specify an alternate --host-only-cidr value, such as 172.16.0.1/24": "", - "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)": "", + "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)": "指定要传递给 Docker 守护进程的任意标志。(格式:key=value)", "Specify the 9p version that the mount should use": "", "Specify the ip that the mount should be setup on": "", "Specify the mount filesystem type (supported types: 9p)": "", "Starting existing {{.driver_name}} VM for \"{{.profile_name}}\" ...": "", - "Starts a local kubernetes cluster": "", + "Starts a local kubernetes cluster": "启动本地 kubernetes 集群", "Stopping \"{{.profile_name}}\" in {{.driver_name}} ...": "", "Stops a local kubernetes cluster running in Virtualbox. This command stops the VM\nitself, leaving all files intact. The cluster can be started again with the \"start\" command.": "", "Stops a running local kubernetes cluster": "", "Successfully mounted {{.sourcePath}} to {{.destinationPath}}": "", "Suggestion: {{.advice}}": "", "Target directory {{.path}} must be an absolute path": "", + "The \"{{.driver_name}}\" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}": "“{{.driver_name}}”驱动程序需要根权限。请使用“sudo minikube --vm-driver={{.driver_name}}”运行 minikube", "The \"{{.driver_name}}\" driver requires root privileges. Please run minikube using 'sudo minikube --vm-driver={{.driver_name}}'.": "", - "The \"{{.name}}\" cluster has been deleted.": "", - "The 'none' driver provides limited isolation and may reduce system security and reliability.": "", - "The CIDR to be used for service cluster IPs.": "", - "The CIDR to be used for the minikube VM (virtualbox driver only)": "", - "The KVM QEMU connection URI. (kvm2 driver only)": "", + "The \"{{.driver_name}}\" driver should not be used with root privileges.": "", + "The \"{{.name}}\" cluster has been deleted.": "“{{.name}}”集群已删除。", + "The \"{{.name}}\" cluster has been deleted.__1": "“{{.name}}”集群已删除。", + "The 'none' driver does not support multiple profiles: https://minikube.sigs.k8s.io/docs/reference/drivers/none/": "", + "The 'none' driver provides limited isolation and may reduce system security and reliability.": "“none”驱动程序提供有限的隔离功能,并且可能会降低系统安全性和可靠性。", + "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\\n\\n{{ .example }}\\n": "", + "The CIDR to be used for service cluster IPs.": "需要用于服务集群 IP 的 CIDR。", + "The CIDR to be used for the minikube VM (virtualbox driver only)": "需要用于 minikube 虚拟机的 CIDR(仅限 virtualbox 驱动程序)", + "The KVM QEMU connection URI. (kvm2 driver only)": "KVM QEMU 连接 URI。(仅限 kvm2 驱动程序)", "The KVM driver is unable to resurrect this old VM. Please run `minikube delete` to delete it and try again.": "", - "The KVM network name. (kvm2 driver only)": "", + "The KVM network name. (kvm2 driver only)": "KVM 网络名称。(仅限 kvm2 驱动程序)", + "The VM driver crashed. Run 'minikube start --alsologtostderr -v=8' to see the VM driver error message": "", "The VM driver exited with an error, and may be corrupt. Run 'minikube start' with --alsologtostderr -v=8 to see the error": "", - "The apiserver listening port": "", + "The VM that minikube is configured for no longer exists. Run 'minikube delete'": "", + "The apiserver listening port": "apiserver 侦听端口", "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "", + "The apiserver name which is used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine": "在为 kubernetes 生成的证书中使用的 apiserver 名称。如果您希望将此 apiserver 设置为可从机器外部访问,则可以使用这组 apiserver 名称", + "The argument to pass the minikube mount command on start": "用于在启动时传递 minikube 装载命令的参数", "The argument to pass the minikube mount command on start.": "", - "The cluster dns domain name used in the kubernetes cluster": "", + "The cluster dns domain name used in the kubernetes cluster": "kubernetes 集群中使用的集群 dns 域名", + "The container runtime to be used (docker, crio, containerd)": "需要使用的容器运行时(docker、crio、containerd)", "The container runtime to be used (docker, crio, containerd).": "", + "The cri socket path to be used": "需要使用的 cri 套接字路径", "The cri socket path to be used.": "", "The docker host is currently not running": "", "The docker service is currently not active": "", - "The driver '{{.driver}}' is not supported on {{.os}}": "", - "The hyperv virtual switch name. Defaults to first found. (hyperv driver only)": "", + "The driver '{{.driver}}' is not supported on {{.os}}": "{{.os}} 不支持驱动程序“{{.driver}}”", + "The existing \"{{.profile_name}}\" VM that was created using the \"{{.old_driver}}\" driver, and is incompatible with the \"{{.driver}}\" driver.": "", + "The hyperv virtual switch name. Defaults to first found. (hyperv driver only)": "hyperv 虚拟交换机名称。默认为找到的第一个 hyperv 虚拟交换机。(仅限 hyperv 驱动程序)", "The initial time interval for each check that wait performs in seconds": "", - "The kubernetes version that the minikube VM will use (ex: v1.2.3)": "", + "The kubernetes version that the minikube VM will use (ex: v1.2.3)": "minikube 虚拟机将使用的 kubernetes 版本(例如 v1.2.3)", "The minikube VM is offline. Please run 'minikube start' to start it again.": "", + "The name of the network plugin": "网络插件的名称", "The name of the network plugin.": "", "The number of bytes to use for 9p packet payload": "", "The path on the file system where the docs in markdown need to be saved": "", @@ -331,21 +367,24 @@ "The value passed to --format is invalid": "", "The value passed to --format is invalid: {{.error}}": "", "The vmwarefusion driver is deprecated and support for it will be removed in a future release.\n\t\t\tPlease consider switching to the new vmware unified driver, which is intended to replace the vmwarefusion driver.\n\t\t\tSee https://minikube.sigs.k8s.io/docs/reference/drivers/vmware/ for more information.\n\t\t\tTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "", - "The {{.driver_name}} driver should not be used with root privileges.": "", - "There's a new version for '{{.driver_executable}}'. Please consider upgrading. {{.documentation_url}}": "", + "The {{.driver_name}} driver should not be used with root privileges.": "不应以根权限使用 {{.driver_name}} 驱动程序。", + "There appears to be another hypervisor conflicting with KVM. Please stop the other hypervisor, or use --vm-driver to switch to it.": "", + "There's a new version for '{{.driver_executable}}'. Please consider upgrading. {{.documentation_url}}": "“{{.driver_executable}}”有一个新版本。请考虑升级。{{.documentation_url}}", "These changes will take effect upon a minikube delete and then a minikube start": "", "This addon does not have an endpoint defined for the 'addons open' command.\nYou can add one by annotating a service with the label {{.labelName}}:{{.addonName}}": "", - "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true": "", - "This will keep the existing kubectl context and will create a minikube context.": "", + "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true": "此操作还可通过设置环境变量 CHANGE_MINIKUBE_NONE_USER=true 自动完成", + "This will keep the existing kubectl context and will create a minikube context.": "这将保留现有 kubectl 上下文并创建 minikube 上下文。", + "This will start the mount daemon and automatically mount files into minikube": "这将启动装载守护进程并将文件自动装载到 minikube 中", "This will start the mount daemon and automatically mount files into minikube.": "", - "Tip: To remove this root owned cluster, run: sudo {{.cmd}} delete": "", + "Tip: To remove this root owned cluster, run: sudo {{.cmd}} delete": "提示:要移除这个由根用户拥有的集群,请运行 sudo {{.cmd}} delete", "Tip: Use 'minikube start -p \u003cname\u003e' to create a new cluster, or 'minikube delete' to delete this one.": "", - "To connect to this cluster, use: kubectl --context={{.name}}": "", + "To connect to this cluster, use: kubectl --context={{.name}}": "如需连接到此集群,请使用 kubectl --context={{.name}}", + "To connect to this cluster, use: kubectl --context={{.name}}__1": "如需连接到此集群,请使用 kubectl --context={{.name}}", "To connect to this cluster, use: kubectl --context={{.profile_name}}": "", - "To disable this notice, run: 'minikube config set WantUpdateNotification false'": "", + "To disable this notice, run: 'minikube config set WantUpdateNotification false'\\n": "", + "To proceed, either:\n 1) Delete the existing VM using: '{{.command}} delete'\n or\n 2) Restart with the existing driver: '{{.command}} start --vm-driver={{.old_driver}}'": "", "To start minikube with HyperV Powershell must be in your PATH`": "", - "To switch drivers, you may create a new VM using `minikube start -p \u003cname\u003e --vm-driver={{.driver_name}}`": "", - "To use kubectl or minikube commands as your own user, you may need to relocate them. For example, to overwrite your own settings, run:": "", + "To use kubectl or minikube commands as your own user, you may need to relocate them. For example, to overwrite your own settings, run:": "如需以您自己的用户身份使用 kubectl 或 minikube 命令,您可能需要重新定位该命令。例如,如需覆盖您的自定义设置,请运行:", "Troubleshooting Commands:": "", "Unable to bind flags": "", "Unable to enable dashboard": "", @@ -353,50 +392,65 @@ "Unable to generate docs": "", "Unable to generate the documentation. Please ensure that the path specified is a directory, exists \u0026 you have permission to write to it.": "", "Unable to get VM IP address": "", - "Unable to get bootstrapper: {{.error}}": "", + "Unable to get bootstrapper: {{.error}}": "无法获取引导程序:{{.error}}", + "Unable to get current user": "", "Unable to get runtime": "", + "Unable to get the status of the cluster.": "", "Unable to kill mount process: {{.error}}": "", - "Unable to load cached images from config file.": "", + "Unable to load cached images from config file.": "无法从配置文件中加载缓存的映像。", "Unable to load cached images: {{.error}}": "", - "Unable to load config: {{.error}}": "", - "Unable to parse \"{{.kubernetes_version}}\": {{.error}}": "", - "Unable to pull images, which may be OK: {{.error}}": "", + "Unable to load config: {{.error}}": "无法加载配置:{{.error}}", + "Unable to parse \"{{.kubernetes_version}}\": {{.error}}": "无法解析“{{.kubernetes_version}}”:{{.error}}", + "Unable to parse oldest Kubernetes version from constants: {{.error}}": "", + "Unable to pull images, which may be OK: {{.error}}": "无法拉取映像,有可能是正常状况:{{.error}}", "Unable to remove machine directory: %v": "", "Unable to start VM": "", "Unable to stop VM": "", - "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...": "", + "Unable to update {{.driver}} driver: {{.error}}": "", + "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...": "正在使用 {{.bootstrapper_name}} 卸载 Kubernetes {{.kubernetes_version}}…", "Unmounting {{.path}} ...": "", + "Unset the KUBECONFIG environment variable, or verify that it does not point to an empty or otherwise invalid path": "", "Unset variables instead of setting them": "", "Update server returned an empty list": "", "Upgrade to QEMU v3.1.0+, run 'virt-host-validate', or ensure that you are not running in a nested VM environment.": "", - "Upgrading from Kubernetes {{.old}} to {{.new}}": "", + "Upgrading from Kubernetes {{.old}} to {{.new}}": "正在从 Kubernetes {{.old}} 升级到 {{.new}}", "Usage": "", "Usage: minikube completion SHELL": "", "Usage: minikube delete": "", "Use \"{{.CommandPath}} [command] --help\" for more information about a command.": "", + "Use VirtualBox to remove the conflicting VM and/or network interfaces": "", + "Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'.": "", "User ID: {{.userID}}": "", "Userspace file server is shutdown": "", - "Userspace file server: ": "", - "Using image repository {{.name}}": "", + "Userspace file server:": "", + "Using image repository {{.name}}": "正在使用映像存储库 {{.name}}", "Using the running {{.driver_name}} \"{{.profile_name}}\" VM ...": "", + "VM driver is one of: %v": "虚拟机驱动程序是以下项之一:%v", "Verify that your HTTP_PROXY and HTTPS_PROXY environment variables are set correctly.": "", "Verify the IP address of the running cluster in kubeconfig.": "", "Verifying dashboard health ...": "", "Verifying proxy health ...": "", "Verifying:": "正在验证:", "Version: {{.version}}": "", + "VirtualBox and Hyper-V are having a conflict. Use '--vm-driver=hyperv' or disable Hyper-V using: 'bcdedit /set hypervisorlaunchtype off'": "", + "VirtualBox cannot create a network, probably because it conflicts with an existing network that minikube no longer knows about. Try running 'minikube delete'": "", + "VirtualBox is broken. Disable real-time anti-virus software, reboot, and reinstall VirtualBox if the problem continues.": "", + "VirtualBox is broken. Reinstall VirtualBox, reboot, and run 'minikube delete'.": "", "Wait failed": "", "Wait failed: {{.error}}": "", + "Wait until Kubernetes core services are healthy before exiting": "等到 Kubernetes 核心服务正常运行再退出", "Wait until Kubernetes core services are healthy before exiting.": "", "Waiting for the host to be provisioned ...": "", "Waiting for:": "", - "Where to root the NFS Shares, defaults to /nfsshares (hyperkit driver only)": "", - "You appear to be using a proxy, but your NO_PROXY environment does not include the minikube IP ({{.ip_address}}). Please see {{.documentation_url}} for more details": "", - "You can delete them using the following command(s): ": "", - "You may need to manually remove the \"{{.name}}\" VM from your hypervisor": "", + "Where to root the NFS Shares, defaults to /nfsshares (hyperkit driver only)": "NFS 共享的根目录位置,默认为 /nfsshares(仅限 hyperkit 驱动程序)", + "You appear to be using a proxy, but your NO_PROXY environment does not include the minikube IP ({{.ip_address}}). Please see {{.documentation_url}} for more details": "您似乎正在使用代理,但您的 NO_PROXY 环境不包含 minikube IP ({{.ip_address}})。如需了解详情,请参阅 {{.documentation_url}}", + "You can delete them using the following command(s):": "", + "You may need to manually remove the \"{{.name}}\" VM from your hypervisor": "您可能需要从管理程序中手动移除“{{.name}}”虚拟机", "You must specify a service name": "", "Your host does not support KVM virtualization. Ensure that qemu-kvm is installed, and run 'virt-host-validate' to debug the problem": "", + "Your host does not support virtualization. If you are running minikube within a VM, try '--vm-driver=none'. Otherwise, enable virtualization in your BIOS": "", "Your host is failing to route packets to the minikube VM. If you have VPN software, try turning it off or configuring it so that it does not re-route traffic to the VM IP. If not, check your VM environment routing options.": "", + "Your minikube config refers to an unsupported driver. Erase ~/.minikube, and try again.": "", "Your minikube vm is not running, try minikube start.": "", "addon '{{.name}}' is currently not enabled.\nTo enable this addon run:\nminikube addons enable {{.name}}": "", "addon '{{.name}}' is not a valid addon packaged with minikube.\nTo see the list of available addons run:\nminikube addons list": "", @@ -407,7 +461,7 @@ "browser failed to open url: {{.error}}": "", "call with cleanup=true to remove old tunnels": "", "command runner": "", - "config modifies minikube config files using subcommands like \"minikube config set vm-driver kvm\"\nConfigurable fields: \\n\\n": "", + "config modifies minikube config files using subcommands like \"minikube config set vm-driver kvm\"\nConfigurable fields:\\n\\n": "", "config view failed": "", "dashboard service is not running: {{.error}}": "", "disable failed": "", @@ -419,7 +473,7 @@ "error starting tunnel": "", "failed to open browser: {{.error}}": "", "if true, will embed the certs in kubeconfig.": "", - "kubectl and minikube configuration will be stored in {{.home_folder}}": "", + "kubectl and minikube configuration will be stored in {{.home_folder}}": "kubectl 和 minikube 配置将存储在 {{.home_folder}} 中", "kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "", "kubectl proxy": "", "logdir set failed": "", @@ -428,12 +482,13 @@ "minikube is unable to access the Google Container Registry. You may need to configure it to use a HTTP proxy.": "", "minikube profile was successfully set to {{.profile_name}}": "", "minikube {{.version}} is available! Download it: {{.url}}": "", - "minikube {{.version}} on {{.os}} ({{.arch}})": "您正在使用minikube {{.version}}, 运行平台:{{.os}} ({{.arch}})", "mount argument \"{{.value}}\" must be in form: \u003csource directory\u003e:\u003ctarget directory\u003e": "", "mount failed": "", + "not enough arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", "profile sets the current minikube profile, or gets the current profile if no arguments are provided. This is used to run and manage multiple minikube instance. You can return to the default minikube profile by running `minikube profile default`": "", "service {{.namespace_name}}/{{.service_name}} has no node port": "", "stat failed": "", + "toom any arguments ({{.ArgCount}}).\\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", "tunnel creates a route to services deployed with type LoadBalancer and sets their Ingress to their ClusterIP": "", "tunnel makes services of type LoadBalancer accessible on localhost": "", "unable to bind flags": "", @@ -449,8 +504,9 @@ "usage: minikube addons enable ADDON_NAME": "", "usage: minikube addons list": "", "usage: minikube addons open ADDON_NAME": "", - "usage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "", "usage: minikube config unset PROPERTY_NAME": "", + "usage: minikube delete": "", + "usage: minikube delete --all": "", "usage: minikube profile [MINIKUBE_PROFILE_NAME]": "", "zsh completion failed": "", "{{.addonName}} was successfully enabled": "", @@ -460,7 +516,7 @@ "{{.name}} cluster does not exist": "", "{{.name}} has no available configuration options": "", "{{.name}} was successfully configured": "", - "{{.prefix}}minikube {{.version}} on {{.platform}}": "", + "{{.prefix}}minikube {{.version}} on {{.platform}}": "{{.platform}} 上的 {{.prefix}}minikube {{.version}}", "{{.type}} is not yet a supported filesystem. We will try anyways!": "", "{{.url}} is not accessible: {{.error}}": "" -} \ No newline at end of file +}