diff --git a/.travis.yml b/.travis.yml index 39089ee1503c..123568d3cb9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,12 +9,11 @@ env: - GOPROXY=https://proxy.golang.org matrix: include: - - language: python - name: Check Boilerplate + - language: go + name: Check Boilerplate + go: 1.12.12 env: - TESTSUITE=boilerplate - before_install: - - pip install flake8 && flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics script: make test - language: go @@ -36,6 +35,8 @@ matrix: script: make test after_success: - bash <(curl -s https://codecov.io/bash) +travisBuddy: + regex: (FAIL:|\.go:\d+:|^panic:|failed$) notifications: webhooks: urls: diff --git a/CHANGELOG.md b/CHANGELOG.md index 350f30fd8e66..c0b16d2b4b51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Release Notes +## 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) diff --git a/Makefile b/Makefile index f1c0296142f1..cc2907f480f6 100755 --- a/Makefile +++ b/Makefile @@ -15,12 +15,12 @@ # Bump these on release - and please check ISO_VERSION for correctness. VERSION_MAJOR ?= 1 VERSION_MINOR ?= 5 -VERSION_BUILD ?= 0-beta.0 +VERSION_BUILD ?= 0 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-beta.0 +ISO_VERSION ?= v$(VERSION_MAJOR).$(VERSION_MINOR).0 # Dashes are valid in semver, but not Linux packaging. Use ~ to delimit alpha/beta DEB_VERSION ?= $(subst -,~,$(RAW_VERSION)) RPM_VERSION ?= $(DEB_VERSION) @@ -51,7 +51,7 @@ MINIKUBE_RELEASES_URL=https://github.com/kubernetes/minikube/releases/download KERNEL_VERSION ?= 4.19.76 # latest from https://github.com/golangci/golangci-lint/releases -GOLINT_VERSION ?= v1.20.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 @@ -59,9 +59,11 @@ GOLINT_GOGC ?= 100 # options for lint (golangci-lint) GOLINT_OPTIONS = --timeout 4m \ --build-tags "${MINIKUBE_INTEGRATION_BUILD_TAGS}" \ - --enable goimports,gocritic,golint,gocyclo,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 @@ -480,11 +482,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 diff --git a/cmd/minikube/cmd/config/config.go b/cmd/minikube/cmd/config/config.go index d76fe8a9f99f..dc572acf8990 100644 --- a/cmd/minikube/cmd/config/config.go +++ b/cmd/minikube/cmd/config/config.go @@ -201,12 +201,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, diff --git a/cmd/minikube/cmd/root.go b/cmd/minikube/cmd/root.go index 4eded97c99c2..38f84246d9ed 100644 --- a/cmd/minikube/cmd/root.go +++ b/cmd/minikube/cmd/root.go @@ -235,9 +235,11 @@ func initConfig() { 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() } diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index 876175fdfa9e..84a97c5438f8 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -63,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" @@ -194,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 %s)", driver.SupportedDrivers(), driver.Default())) + 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 @@ -289,6 +290,7 @@ func runStart(cmd *cobra.Command, args []string) { } driverName := selectDriver(oldConfig) + glog.Infof("selected: %v", driverName) err = autoSetDriverOptions(cmd, driverName) if err != nil { glog.Errorf("Error autoSetOptions : %v", err) @@ -297,11 +299,14 @@ func runStart(cmd *cobra.Command, args []string) { 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 := 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}) + // No need to install a driver in download-only mode + if !viper.GetBool(downloadOnly) { + 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}) + } } k8sVersion, isUpgrade := getKubernetesVersion(oldConfig) @@ -541,17 +546,41 @@ func showKubectlInfo(kcs *kubeconfig.Settings, k8sVersion string) error { func selectDriver(oldConfig *cfg.Config) string { name := viper.GetString("vm-driver") - // By default, the driver is whatever we used last time + glog.Infof("selectDriver: flag=%q, old=%v", name, oldConfig) if name == "" { - name = driver.Default() + // By default, the driver is whatever we used last time if oldConfig != nil { 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 !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 { diff --git a/deploy/addons/gvisor/gvisor-pod.yaml.tmpl b/deploy/addons/gvisor/gvisor-pod.yaml.tmpl index 652dbb681087..1b7d69cd84af 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/layouts/gvisor/single.html b/deploy/addons/layouts/gvisor/single.html new file mode 100644 index 000000000000..620f9d82b608 --- /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 000000000000..620f9d82b608 --- /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 000000000000..27fcd101d0d6 --- /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 000000000000..620f9d82b608 --- /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/gvisor/Dockerfile b/deploy/gvisor/Dockerfile index 9dacfa546644..9d62239fc359 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/minikube/releases.json b/deploy/minikube/releases.json index 33573e46b1c6..d9f6f0ea3804 100644 --- a/deploy/minikube/releases.json +++ b/deploy/minikube/releases.json @@ -1,4 +1,12 @@ [ + { + "name": "v1.5.0", + "checksums": { + "darwin": "eb716c176f404bb555966ff3947d5d9c5fb63eb902d11c83839fda492ff4b1fc", + "linux": "ca50dcc7c83d4dde484d650a5a1934ea1bef692340af3aa831d34c6e847b2770", + "windows": "bdd61e446f49570428848ad15337264edfecc55d1dd4aed4499d559f9c8383b9" + } + }, { "name": "v1.4.0", "checksums": { diff --git a/hack/boilerplate/boilerplate.go b/hack/boilerplate/boilerplate.go new file mode 100644 index 000000000000..1318711f710b --- /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 35fc425706cf..000000000000 --- 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 4d01cb312c1c..b2b4c98b84a0 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 ccd921616037..fb4ae49338b4 100755 --- a/hack/jenkins/common.sh +++ b/hack/jenkins/common.sh @@ -165,7 +165,7 @@ if type -P vboxmanage; then vboxmanage unregistervm "${guid}" || true done - ifaces=$(vboxmanage list hostonlyifs | grep -E "^Name:" | awk '{ printf $2 }') + ifaces=$(vboxmanage list hostonlyifs | grep -E "^Name:" | awk '{ print $2 }') for if in $ifaces; do vboxmanage hostonlyif remove "${if}" || true done @@ -245,22 +245,25 @@ 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 echo "" echo ">> Starting ${E2E_BIN} at $(date)" +set -x ${SUDO_PREFIX}${E2E_BIN} \ -minikube-start-args="--vm-driver=${VM_DRIVER} ${EXTRA_START_ARGS}" \ + -expected-default-driver="${EXPECTED_DEFAULT_DRIVER}" \ -test.timeout=60m \ -test.parallel=${PARALLEL_COUNT} \ -binary="${MINIKUBE_BIN}" && result=$? || result=$? +set +x echo ">> ${E2E_BIN} exited with ${result} at $(date)" echo "" diff --git a/hack/jenkins/linux_integration_tests_kvm.sh b/hack/jenkins/linux_integration_tests_kvm.sh index 5af0e48b3039..fe40576f65e4 100755 --- a/hack/jenkins/linux_integration_tests_kvm.sh +++ b/hack/jenkins/linux_integration_tests_kvm.sh @@ -29,6 +29,7 @@ OS_ARCH="linux-amd64" VM_DRIVER="kvm2" JOB_NAME="KVM_Linux" PARALLEL_COUNT=4 +EXPECTED_DEFAULT_DRIVER="kvm2" # 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 6721d15e2455..767e6867a09b 100755 --- a/hack/jenkins/linux_integration_tests_none.sh +++ b/hack/jenkins/linux_integration_tests_none.sh @@ -31,6 +31,7 @@ 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" diff --git a/hack/jenkins/linux_integration_tests_virtualbox.sh b/hack/jenkins/linux_integration_tests_virtualbox.sh index bd413c158687..6f624eeead0a 100755 --- a/hack/jenkins/linux_integration_tests_virtualbox.sh +++ b/hack/jenkins/linux_integration_tests_virtualbox.sh @@ -29,6 +29,7 @@ OS_ARCH="linux-amd64" VM_DRIVER="virtualbox" JOB_NAME="VirtualBox_Linux" PARALLEL_COUNT=4 +EXPECTED_DEFAULT_DRIVER="kvm2" # Download files and set permissions source ./common.sh diff --git a/hack/jenkins/osx_integration_tests_hyperkit.sh b/hack/jenkins/osx_integration_tests_hyperkit.sh index 7091d6512b0f..aa9d5d69d104 100755 --- a/hack/jenkins/osx_integration_tests_hyperkit.sh +++ b/hack/jenkins/osx_integration_tests_hyperkit.sh @@ -32,6 +32,8 @@ JOB_NAME="HyperKit_macOS" EXTRA_ARGS="--bootstrapper=kubeadm" EXTRA_START_ARGS="" PARALLEL_COUNT=3 +EXPECTED_DEFAULT_DRIVER="hyperkit" + # 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 cb48a389ed2a..8f7c6e3dd0b8 100755 --- a/hack/jenkins/osx_integration_tests_virtualbox.sh +++ b/hack/jenkins/osx_integration_tests_virtualbox.sh @@ -30,6 +30,10 @@ 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" + # Download files and set permissions source common.sh diff --git a/hack/jenkins/windows_integration_test_hyperv.ps1 b/hack/jenkins/windows_integration_test_hyperv.ps1 index fb42370c4977..995b17506c52 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 559a6e220e1f..86dfde120a2c 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/netlify.toml b/netlify.toml index 6084cd18c02d..d335d812befb 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/gvisor/enable.go b/pkg/gvisor/enable.go index 2a5f9e315a29..b2905574b878 100644 --- a/pkg/gvisor/enable.go +++ b/pkg/gvisor/enable.go @@ -157,7 +157,7 @@ func copyConfigFiles() error { 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...") + 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") } @@ -171,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()) @@ -182,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) @@ -193,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/bootstrapper/kubeadm/kubeadm_test.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go index f6d696816dfb..e638c32c4532 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go @@ -66,7 +66,7 @@ Wants=crio.service [Service] ExecStart= -ExecStart=/var/lib/minikube/binaries/v1.16.1/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 --node-ip=192.168.1.100 --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 --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,7 @@ Wants=containerd.service [Service] ExecStart= -ExecStart=/var/lib/minikube/binaries/v1.16.1/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 --node-ip=192.168.1.100 --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 --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] `, @@ -109,7 +109,7 @@ Wants=containerd.service [Service] ExecStart= -ExecStart=/var/lib/minikube/binaries/v1.16.1/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 --node-ip=192.168.1.200 --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 --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] `, @@ -128,7 +128,7 @@ Wants=docker.socket [Service] ExecStart= -ExecStart=/var/lib/minikube/binaries/v1.16.1/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 --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 +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 --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] `, diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index f976b297de8d..3e1a4683f02a 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -433,15 +433,11 @@ 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/missing 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) } - - dd := def.ConfigCreator(config) + dd := def.Config(config) data, err := json.Marshal(dd) if err != nil { return nil, errors.Wrap(err, "marshal") diff --git a/pkg/minikube/cluster/cluster_test.go b/pkg/minikube/cluster/cluster_test.go index 62d7fd3f483f..ae2ee8c5b897 100644 --- a/pkg/minikube/cluster/cluster_test.go +++ b/pkg/minikube/cluster/cluster_test.go @@ -22,8 +22,8 @@ import ( "testing" "time" - // Register drivers - _ "k8s.io/minikube/pkg/minikube/registry/drvs" + // Driver used by testdata + _ "k8s.io/minikube/pkg/minikube/registry/drvs/virtualbox" "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/host" @@ -47,18 +47,13 @@ func createMockDriverHost(c config.MachineConfig) interface{} { func RegisterMockDriver(t *testing.T) { t.Helper() - _, err := registry.Driver(driver.Mock) - // Already registered - if err == nil { + if !registry.Driver(driver.Mock).Empty() { return } - err = registry.Register(registry.DriverDef{ - Name: driver.Mock, - 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) @@ -103,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 @@ -111,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()) } } diff --git a/pkg/minikube/config/config_test.go b/pkg/minikube/config/config_test.go index 9eef5a7a61cb..19671ef61788 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/driver" ) 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": driver.KVM2, + "vm-driver": "test-driver", "cpus": 4, "disk-size": "20g", "v": 5, @@ -132,7 +130,7 @@ func TestReadConfig(t *testing.T) { } expectedConfig := map[string]interface{}{ - "vm-driver": driver.KVM2, + "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": driver.KVM2, + "vm-driver": "test-driver", "cpus": 4, "disk-size": "20g", "show-libmachine-logs": true, diff --git a/pkg/minikube/config/profile.go b/pkg/minikube/config/profile.go index 55b933bc34a1..f07aad446b48 100644 --- a/pkg/minikube/config/profile.go +++ b/pkg/minikube/config/profile.go @@ -44,7 +44,7 @@ func (p *Profile) IsValid() bool { return true } -// check if the profile is an internal keywords +// ProfileNameInReservedKeywords checks if the profile is an internal keywords func ProfileNameInReservedKeywords(name string) bool { for _, v := range keywords { if strings.EqualFold(v, name) { diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index af0244a8d43d..ba0aeacf106e 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -65,10 +65,10 @@ 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.1" +var DefaultKubernetesVersion = "v1.16.2" // NewestKubernetesVersion is the newest Kubernetes version to test against -var NewestKubernetesVersion = "v1.16.1" +var NewestKubernetesVersion = "v1.16.2" // OldestKubernetesVersion is the oldest Kubernetes version to test against var OldestKubernetesVersion = "v1.11.10" diff --git a/pkg/minikube/driver/driver.go b/pkg/minikube/driver/driver.go index 7d6712d8ea55..a87d7a607120 100644 --- a/pkg/minikube/driver/driver.go +++ b/pkg/minikube/driver/driver.go @@ -19,6 +19,10 @@ package driver import ( "fmt" "os" + "sort" + + "github.com/golang/glog" + "k8s.io/minikube/pkg/minikube/registry" ) const ( @@ -33,6 +37,11 @@ const ( 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 @@ -62,14 +71,12 @@ type FlagHints struct { // FlagDefaults returns suggested defaults based on a driver func FlagDefaults(name string) FlagHints { if name != None { - return FlagHints{} + return FlagHints{CacheImages: true} } - // for more info see: https://github.com/kubernetes/minikube/issues/3511 - f := "/run/systemd/resolve/resolv.conf" extraOpts := "" - if _, err := os.Stat(f); err == nil { - extraOpts = fmt.Sprintf("kubelet.resolv-conf=%s", f) + if _, err := os.Stat(systemdResolvConf); err == nil { + extraOpts = fmt.Sprintf("kubelet.resolv-conf=%s", systemdResolvConf) } return FlagHints{ ExtraOptions: extraOpts, @@ -77,7 +84,50 @@ func FlagDefaults(name string) FlagHints { } } -// Default returns the default driver on this hos -func Default() string { - return VirtualBox +// 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/driver/driver_test.go b/pkg/minikube/driver/driver_test.go new file mode 100644 index 000000000000..cd35e8d77598 --- /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/driver/install_test.go b/pkg/minikube/driver/install_test.go new file mode 100644 index 000000000000..f57e1f541e3b --- /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/machine/client.go b/pkg/minikube/machine/client.go index 2c6464b430fb..dd10ee31c9b6 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" @@ -81,17 +82,15 @@ type LocalClient struct { // NewHost creates a new Host func (api *LocalClient) NewHost(drvName string, rawDriver []byte) (*host.Host, error) { - var def registry.DriverDef - var err error - if def, err = registry.Driver(drvName); err != nil { - return nil, err - } else if !def.Builtin || def.DriverCreator == nil { + def := registry.Driver(drvName) + if def.Empty() { + return nil, fmt.Errorf("driver %q does not exist", drvName) + } + if def.Init == nil { return api.legacyClient.NewHost(drvName, rawDriver) } - - d := def.DriverCreator() - - err = json.Unmarshal(rawDriver, d) + d := def.Init() + err := json.Unmarshal(rawDriver, d) if err != nil { return nil, errors.Wrapf(err, "Error getting driver %s", string(rawDriver)) } @@ -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) } @@ -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) } @@ -271,12 +272,9 @@ func (cg *CertGenerator) ValidateCertificate(addr string, authOptions *auth.Opti } func registerDriver(drvName string) { - def, err := registry.Driver(drvName) - if err != nil { - if err == registry.ErrDriverNotFound { - exit.UsageT("unsupported or missing driver: {{.name}}", out.V{"name": drvName}) - } - exit.WithError("error getting driver", err) + 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/out/style.go b/pkg/minikube/out/style.go index 4791787413b6..0af52ed464d1 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 890c54f6b001..0bc8ac182279 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/registry/drvs/hyperkit/driver.go b/pkg/minikube/registry/drvs/hyperkit/hyperkit.go similarity index 68% rename from pkg/minikube/registry/drvs/hyperkit/driver.go rename to pkg/minikube/registry/drvs/hyperkit/hyperkit.go index c9c2f9369841..e42b4207dafe 100644 --- a/pkg/minikube/registry/drvs/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/driver" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/registry" - "k8s.io/minikube/pkg/minikube/driver" +) + +const ( + docURL = "https://minikube.sigs.k8s.io/docs/reference/drivers/hyperkit/" ) func init() { if err := registry.Register(registry.DriverDef{ - Name: driver.HyperKit, - 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 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/registry/drvs/hyperv/driver.go b/pkg/minikube/registry/drvs/hyperv/hyperv.go similarity index 55% rename from pkg/minikube/registry/drvs/hyperv/driver.go rename to pkg/minikube/registry/drvs/hyperv/hyperv.go index 50dc236607bd..e0216f12cfe8 100644 --- a/pkg/minikube/registry/drvs/hyperv/driver.go +++ b/pkg/minikube/registry/drvs/hyperv/hyperv.go @@ -19,28 +19,37 @@ 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/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: driver.HyperV, - 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 @@ -48,6 +57,19 @@ func createHypervHost(config cfg.MachineConfig) interface{} { 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/kvm2/driver.go b/pkg/minikube/registry/drvs/kvm2/kvm2.go similarity index 59% rename from pkg/minikube/registry/drvs/kvm2/driver.go rename to pkg/minikube/registry/drvs/kvm2/kvm2.go index 7d14ed952ea7..cdd61f20945e 100644 --- a/pkg/minikube/registry/drvs/kvm2/driver.go +++ b/pkg/minikube/registry/drvs/kvm2/kvm2.go @@ -20,27 +20,34 @@ 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, - Builtin: false, - ConfigCreator: createKVM2Host, + Name: driver.KVM2, + Config: configure, + Status: status, + Priority: registry.Preferred, }); 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 +// This is duplicate of kvm.Driver. Avoids importing the kvm2 driver, which requires cgo & libvirt. type kvmDriver struct { *drivers.BaseDriver @@ -57,9 +64,9 @@ type kvmDriver struct { ConnectionURI string } -func createKVM2Host(mc config.MachineConfig) interface{} { +func configure(mc config.MachineConfig) interface{} { name := config.GetMachineName() - return &kvmDriver{ + return kvmDriver{ BaseDriver: &drivers.BaseDriver{ MachineName: name, StorePath: localpath.MiniPath(), @@ -78,3 +85,33 @@ func createKVM2Host(mc config.MachineConfig) interface{} { 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/registry/drvs/none/driver.go b/pkg/minikube/registry/drvs/none/none.go similarity index 67% rename from pkg/minikube/registry/drvs/none/driver.go rename to pkg/minikube/registry/drvs/none/none.go index 119a7439374a..fa094f9aeaa8 100644 --- a/pkg/minikube/registry/drvs/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,6 +20,7 @@ package none import ( "fmt" + "os/exec" "github.com/docker/machine/libmachine/drivers" "k8s.io/minikube/pkg/drivers/none" @@ -29,22 +32,28 @@ import ( func init() { if err := registry.Register(registry.DriverDef{ - Name: driver.None, - 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(mc config.MachineConfig) interface{} { +func configure(mc config.MachineConfig) interface{} { return none.NewDriver(none.Config{ MachineName: config.GetMachineName(), StorePath: localpath.MiniPath(), ContainerRuntime: mc.ContainerRuntime, }) } + +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 registry.State{Installed: true, Healthy: true} +} diff --git a/pkg/minikube/registry/drvs/parallels/driver.go b/pkg/minikube/registry/drvs/parallels/parallels.go similarity index 69% rename from pkg/minikube/registry/drvs/parallels/driver.go rename to pkg/minikube/registry/drvs/parallels/parallels.go index f0648fb6a821..193ea54da9d9 100644 --- a/pkg/minikube/registry/drvs/parallels/driver.go +++ b/pkg/minikube/registry/drvs/parallels/parallels.go @@ -20,6 +20,7 @@ package parallels import ( "fmt" + "os/exec" parallels "github.com/Parallels/docker-machine-parallels" "github.com/docker/machine/libmachine/drivers" @@ -31,12 +32,11 @@ import ( func init() { err := registry.Register(registry.DriverDef{ - Name: driver.Parallels, - 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/doc.go b/pkg/minikube/registry/drvs/virtualbox/doc.go deleted file mode 100644 index 0e6eff9e8ca1..000000000000 --- a/pkg/minikube/registry/drvs/virtualbox/doc.go +++ /dev/null @@ -1,17 +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 diff --git a/pkg/minikube/registry/drvs/virtualbox/driver.go b/pkg/minikube/registry/drvs/virtualbox/virtualbox.go similarity index 57% rename from pkg/minikube/registry/drvs/virtualbox/driver.go rename to pkg/minikube/registry/drvs/virtualbox/virtualbox.go index 5677ec614631..fa4483cf0b3c 100644 --- a/pkg/minikube/registry/drvs/virtualbox/driver.go +++ b/pkg/minikube/registry/drvs/virtualbox/virtualbox.go @@ -18,34 +18,38 @@ 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" +const ( + defaultVirtualboxNicType = "virtio" + docURL = "https://minikube.sigs.k8s.io/docs/reference/drivers/virtualbox/" +) func init() { err := registry.Register(registry.DriverDef{ - Name: driver.VirtualBox, - Builtin: true, - ConfigCreator: createVirtualboxHost, - DriverCreator: func() drivers.Driver { - return virtualbox.NewDriver("", "") - }, + 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 createVirtualboxHost(mc config.MachineConfig) interface{} { +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 @@ -57,6 +61,31 @@ func createVirtualboxHost(mc config.MachineConfig) interface{} { 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/registry/drvs/vmware/driver.go b/pkg/minikube/registry/drvs/vmware/vmware.go similarity index 67% rename from pkg/minikube/registry/drvs/vmware/driver.go rename to pkg/minikube/registry/drvs/vmware/vmware.go index 10b53ca390bc..82b1126d1838 100644 --- a/pkg/minikube/registry/drvs/vmware/driver.go +++ b/pkg/minikube/registry/drvs/vmware/vmware.go @@ -18,6 +18,7 @@ package vmware import ( "fmt" + "os/exec" vmwcfg "github.com/machine-drivers/docker-machine-driver-vmware/pkg/drivers/vmware/config" "k8s.io/minikube/pkg/minikube/config" @@ -28,16 +29,17 @@ import ( func init() { err := registry.Register(registry.DriverDef{ - Name: driver.VMware, - 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(mc config.MachineConfig) interface{} { +func configure(mc config.MachineConfig) interface{} { d := vmwcfg.NewConfig(config.GetMachineName(), localpath.MiniPath()) d.Boot2DockerURL = mc.Downloader.GetISOFileURI(mc.MinikubeISO) d.Memory = mc.Memory @@ -49,3 +51,15 @@ func createVMwareHost(mc config.MachineConfig) interface{} { 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/registry/drvs/vmwarefusion/driver.go b/pkg/minikube/registry/drvs/vmwarefusion/vmwarefusion.go similarity index 70% rename from pkg/minikube/registry/drvs/vmwarefusion/driver.go rename to pkg/minikube/registry/drvs/vmwarefusion/vmwarefusion.go index 7cb0893818f1..cbff0022ed0f 100644 --- a/pkg/minikube/registry/drvs/vmwarefusion/driver.go +++ b/pkg/minikube/registry/drvs/vmwarefusion/vmwarefusion.go @@ -20,30 +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/driver" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/registry" - "k8s.io/minikube/pkg/minikube/driver" - ) func init() { if err := registry.Register(registry.DriverDef{ - Name: driver.VMwareFusion, - 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 @@ -55,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 000000000000..97882296a540 --- /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 000000000000..1ccb418a9145 --- /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{ + 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 15b7b15e41e9..9fd491ea9e97 100644 --- a/pkg/minikube/registry/registry.go +++ b/pkg/minikube/registry/registry.go @@ -21,18 +21,21 @@ 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") - - // ErrDriverNotFound is the error returned when driver of a given name does - // not exist in registry - ErrDriverNotFound = errors.New("registry: driver not found") +// Priority is how we determine what driver to default to +type Priority int + +const ( + Unknown Priority = iota + Discouraged + Deprecated + Fallback + Default + Preferred + StronglyPreferred ) // Registry contains all the supported driver definitions on the host @@ -47,31 +50,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{} + +// Loader is a function that loads a byte stream and creates a driver. +type Loader func() drivers.Driver -// DriverFactory is a function that loads a byte stream and creates a driver. -type DriverFactory func() drivers.Driver +// 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 a machine driver metadata. It tells minikube how to initialize -// and load drivers. +// 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 generates 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 +100,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 +133,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 8d44019a08a8..e1d84b25efae 100644 --- a/pkg/minikube/registry/registry_test.go +++ b/pkg/minikube/registry/registry_test.go @@ -19,107 +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 testDriver(name string) DriverDef { - return DriverDef{ - Name: name, - 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) } -} -func TestRegistry1(t *testing.T) { - foo := testDriver("foo") - bar := testDriver("bar") + d := r.Driver("foo") + if d.Empty() { + t.Errorf("driver.Empty = true, expected false") + } - 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)) - } - }) + d = r.Driver("bar") + if !d.Empty() { + t.Errorf("driver.Empty = false, expected true") + } } -func TestRegistry2(t *testing.T) { - foo := testDriver("foo") - bar := testDriver("bar") - - registry := createRegistry() - if err := registry.Register(foo); err != nil { - t.Skipf("error not expect but got: %v", err) - } - if err := registry.Register(bar); err != nil { - t.Skipf("error not expect but got: %v", err) +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) } - t.Run("Driver", func(t *testing.T) { - name := "foo" - d, err := registry.Driver(name) - if err != nil { - t.Fatalf("expect nil for registering foo driver, but got: %v", err) - } - if d.Name != name { - t.Fatalf("expect registry.Driver(%s) returns registered driver, but got: %s", name, d.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") + + 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/site/config.toml b/site/config.toml index e49df4aedd17..bdebbf905173 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.0" privacy_policy = "" diff --git a/site/content/en/docs/Examples/_index.md b/site/content/en/docs/Examples/_index.md index 84092b2cb4dd..a72b1c0ade47 100755 --- a/site/content/en/docs/Examples/_index.md +++ b/site/content/en/docs/Examples/_index.md @@ -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/Drivers/includes/virtualbox_usage.inc b/site/content/en/docs/Reference/Drivers/includes/virtualbox_usage.inc index bb67f88d293d..6bce73731acf 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/Tasks/addons.md b/site/content/en/docs/Tasks/addons.md index c454360cbcc0..b29499a8fbac 100644 --- a/site/content/en/docs/Tasks/addons.md +++ b/site/content/en/docs/Tasks/addons.md @@ -20,10 +20,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) -* [helm-tiller](../deploy/addons/helm-tiller/README.md) -* [ingress-dns](../deploy/addons/ingress-dns/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 diff --git a/test.sh b/test.sh index c48b4a1ef055..846ed3e5752c 100755 --- a/test.sh +++ b/test.sh @@ -33,9 +33,11 @@ fi if [[ "$TESTSUITE" = "boilerplate" ]] || [[ "$TESTSUITE" = "all" ]] then 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)" + 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" @@ -43,6 +45,7 @@ then else echo "ok" fi + popd >/dev/null fi diff --git a/test/integration/README.md b/test/integration/README.md index 1a554492f678..7af031f6eab7 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/Untitled-1 b/test/integration/Untitled-1 deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/test/integration/a_serial_tests.go b/test/integration/a_serial_test.go similarity index 52% rename from test/integration/a_serial_tests.go rename to test/integration/a_serial_test.go index 7e4b604efff2..ae931662d707 100644 --- a/test/integration/a_serial_tests.go +++ b/test/integration/a_serial_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "encoding/json" "fmt" "os" "os/exec" @@ -29,15 +30,20 @@ import ( "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 TestDownloadAndDeleteAll(t *testing.T) { +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, @@ -46,22 +52,28 @@ func TestDownloadAndDeleteAll(t *testing.T) { } 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...)) + // 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) } - // 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) - } + 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) } } @@ -75,8 +87,40 @@ func TestDownloadAndDeleteAll(t *testing.T) { } }) } + + // 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) @@ -84,6 +128,9 @@ func TestDownloadAndDeleteAll(t *testing.T) { }) // 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/gvisor_addon_test.go b/test/integration/gvisor_addon_test.go index c5e2790bb6b0..b75aa0bbb1b2 100644 --- a/test/integration/gvisor_addon_test.go +++ b/test/integration/gvisor_addon_test.go @@ -35,6 +35,13 @@ func TestGvisorAddon(t *testing.T) { profile := UniqueProfileName("gvisor") ctx, cancel := context.WithTimeout(context.Background(), 30*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) }() @@ -44,10 +51,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/main.go b/test/integration/main.go index 02ce476cf9db..dbd5830c156b 100644 --- a/test/integration/main.go +++ b/test/integration/main.go @@ -27,6 +27,7 @@ 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") @@ -65,3 +66,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 +}