diff --git a/.travis.yml b/.travis.yml index acdc98cf0e..39089ee150 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ os: linux language: go go: - - 1.12.9 + - 1.12.12 env: global: - GOPROXY=https://proxy.golang.org @@ -19,7 +19,7 @@ matrix: - language: go name: Code Lint - go: 1.12.9 + go: 1.12.12 env: - TESTSUITE=lint before_install: @@ -28,7 +28,7 @@ matrix: - language: go name: Unit Test - go: 1.12.9 + go: 1.12.12 env: - TESTSUITE=unittest before_install: diff --git a/Makefile b/Makefile index f4efa1dfdb..0b159ac3ee 100755 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ DEB_VERSION ?= $(subst -,~,$(RAW_VERSION)) RPM_VERSION ?= $(DEB_VERSION) # used by hack/jenkins/release_build_and_upload.sh and KVM_BUILD_IMAGE, see also BUILD_IMAGE below -GO_VERSION ?= 1.12.9 +GO_VERSION ?= 1.12.12 INSTALL_SIZE ?= $(shell du out/minikube-windows-amd64.exe | cut -f1) BUILDROOT_BRANCH ?= 2019.02.6 @@ -127,17 +127,30 @@ endif ifeq ($(GOOS),windows) IS_EXE = .exe + DIRSEP_ = \\ + DIRSEP = $(strip $(DIRSEP_)) + PATHSEP = ; +else + DIRSEP = / + PATHSEP = : endif +out/minikube-linux-x86_64: out/minikube-linux-amd64 + cp $< $@ + +out/minikube-linux-aarch64: out/minikube-linux-arm64 + cp $< $@ + out/minikube$(IS_EXE): out/minikube-$(GOOS)-$(GOARCH)$(IS_EXE) cp $< $@ out/minikube-windows-amd64.exe: out/minikube-windows-amd64 cp $< $@ -.PHONY: minikube-linux-amd64 minikube-darwin-amd64 minikube-windows-amd64.exe +.PHONY: minikube-linux-amd64 minikube-linux-arm64 minikube-darwin-amd64 minikube-windows-amd64.exe minikube-linux-amd64: out/minikube-linux-amd64 +minikube-linux-arm64: out/minikube-linux-arm64 minikube-darwin-amd64: out/minikube-darwin-amd64 minikube-windows-amd64.exe: out/minikube-windows-amd64.exe @@ -239,33 +252,25 @@ extract: pkg/minikube/assets/assets.go: $(shell find "deploy/addons" -type f) ifeq ($(MINIKUBE_BUILD_IN_DOCKER),y) $(call DOCKER,$(BUILD_IMAGE),/usr/bin/make $@) -else ifeq ($(GOOS),windows) - which go-bindata || GO111MODULE=off GOBIN="$(GOPATH)\bin" go get github.com/jteeuwen/go-bindata/... - PATH="$(PATH);$(GOPATH)\bin" go-bindata -nomemcopy -o $@ -pkg assets deploy/addons/... - -gofmt -s -w $@ -else - which go-bindata || GO111MODULE=off GOBIN=$(GOPATH)/bin go get github.com/jteeuwen/go-bindata/... - PATH="$(PATH):$(GOPATH)/bin" go-bindata -nomemcopy -o $@ -pkg assets deploy/addons/... - -gofmt -s -w $@ endif + which go-bindata || GO111MODULE=off GOBIN="$(GOPATH)$(DIRSEP)bin" go get github.com/jteeuwen/go-bindata/... + PATH="$(PATH)$(PATHSEP)$(GOPATH)$(DIRSEP)bin" go-bindata -nomemcopy -o $@ -pkg assets deploy/addons/... + -gofmt -s -w $@ + @#golint: Dns should be DNS (compat sed) + @sed -i -e 's/Dns/DNS/g' $@ && rm -f ./-e pkg/minikube/translate/translations.go: $(shell find "translations/" -type f) ifeq ($(MINIKUBE_BUILD_IN_DOCKER),y) $(call DOCKER,$(BUILD_IMAGE),/usr/bin/make $@) -else ifeq ($(GOOS),windows) - which go-bindata || GO111MODULE=off GOBIN="$(GOPATH)\bin" go get github.com/jteeuwen/go-bindata/... - PATH="$(PATH);$(GOPATH)\bin" go-bindata -nomemcopy -o $@ -pkg translate translations/... - -gofmt -s -w $@ -else - which go-bindata || GO111MODULE=off GOBIN=$(GOPATH)/bin go get github.com/jteeuwen/go-bindata/... - PATH="$(PATH):$(GOPATH)/bin" go-bindata -nomemcopy -o $@ -pkg translate translations/... - -gofmt -s -w $@ endif + which go-bindata || GO111MODULE=off GOBIN="$(GOPATH)$(DIRSEP)bin" go get github.com/jteeuwen/go-bindata/... + PATH="$(PATH)$(PATHSEP)$(GOPATH)$(DIRSEP)bin" go-bindata -nomemcopy -o $@ -pkg translate translations/... + -gofmt -s -w $@ @#golint: Json should be JSON (compat sed) @sed -i -e 's/Json/JSON/' $@ && rm -f ./-e .PHONY: cross -cross: minikube-linux-amd64 minikube-darwin-amd64 minikube-windows-amd64.exe +cross: minikube-linux-amd64 minikube-linux-arm64 minikube-darwin-amd64 minikube-windows-amd64.exe .PHONY: windows windows: minikube-windows-amd64.exe @@ -281,7 +286,8 @@ e2e-cross: e2e-linux-amd64 e2e-darwin-amd64 e2e-windows-amd64.exe .PHONY: checksum checksum: - for f in out/minikube-linux-amd64 out/minikube-darwin-amd64 out/minikube-windows-amd64.exe out/minikube.iso \ + for f in out/minikube.iso out/minikube-linux-amd64 minikube-linux-arm64 \ + out/minikube-darwin-amd64 out/minikube-windows-amd64.exe \ out/docker-machine-driver-kvm2 out/docker-machine-driver-hyperkit; do \ if [ -f "$${f}" ]; then \ openssl sha256 "$${f}" | awk '{print $$2}' > "$${f}.sha256" ; \ @@ -312,7 +318,7 @@ vet: @go vet $(SOURCE_PACKAGES) .PHONY: golint -golint: +golint: pkg/minikube/assets/assets.go pkg/minikube/translate/translations.go @golint -set_exit_status $(SOURCE_PACKAGES) .PHONY: gocyclo @@ -348,21 +354,29 @@ mdlint: out/docs/minikube.md: $(shell find "cmd") $(shell find "pkg/minikube/constants") pkg/minikube/assets/assets.go pkg/minikube/translate/translations.go go run -ldflags="$(MINIKUBE_LDFLAGS)" -tags gendocs hack/help_text/gen_help_text.go -out/minikube_$(DEB_VERSION).deb: out/minikube-linux-amd64 +out/minikube_$(DEB_VERSION).deb: out/minikube_$(DEB_VERSION)-0_amd64.deb + cp $< $@ + +out/minikube_$(DEB_VERSION)-0_%.deb: out/minikube-linux-% cp -r installers/linux/deb/minikube_deb_template out/minikube_$(DEB_VERSION) chmod 0755 out/minikube_$(DEB_VERSION)/DEBIAN sed -E -i 's/--VERSION--/'$(DEB_VERSION)'/g' out/minikube_$(DEB_VERSION)/DEBIAN/control + sed -E -i 's/--ARCH--/'$*'/g' out/minikube_$(DEB_VERSION)/DEBIAN/control mkdir -p out/minikube_$(DEB_VERSION)/usr/bin - cp out/minikube-linux-amd64 out/minikube_$(DEB_VERSION)/usr/bin/minikube - fakeroot dpkg-deb --build out/minikube_$(DEB_VERSION) + cp $< out/minikube_$(DEB_VERSION)/usr/bin/minikube + fakeroot dpkg-deb --build out/minikube_$(DEB_VERSION) $@ rm -rf out/minikube_$(DEB_VERSION) -out/minikube-$(RPM_VERSION).rpm: out/minikube-linux-amd64 +out/minikube-$(RPM_VERSION).rpm: out/minikube-$(RPM_VERSION)-0.x86_64.rpm + cp $< $@ + +out/minikube-$(RPM_VERSION)-0.%.rpm: out/minikube-linux-% cp -r installers/linux/rpm/minikube_rpm_template out/minikube-$(RPM_VERSION) sed -E -i 's/--VERSION--/'$(RPM_VERSION)'/g' out/minikube-$(RPM_VERSION)/minikube.spec sed -E -i 's|--OUT--|'$(PWD)/out'|g' out/minikube-$(RPM_VERSION)/minikube.spec - rpmbuild -bb -D "_rpmdir $(PWD)/out" -D "_rpmfilename minikube-$(RPM_VERSION).rpm" \ + rpmbuild -bb -D "_rpmdir $(PWD)/out" --target $* \ out/minikube-$(RPM_VERSION)/minikube.spec + @mv out/$*/minikube-$(RPM_VERSION)-0.$*.rpm out/ && rmdir out/$* rm -rf out/minikube-$(RPM_VERSION) .PHONY: apt @@ -380,14 +394,16 @@ out/repodata/repomd.xml: out/minikube-$(RPM_VERSION).rpm -u "$(MINIKUBE_RELEASES_URL)/$(VERSION)/" out .SECONDEXPANSION: -TAR_TARGETS_linux := out/minikube-linux-amd64 out/docker-machine-driver-kvm2 -TAR_TARGETS_darwin := out/minikube-darwin-amd64 out/docker-machine-driver-hyperkit -TAR_TARGETS_windows := out/minikube-windows-amd64.exe -out/minikube-%-amd64.tar.gz: $$(TAR_TARGETS_$$*) +TAR_TARGETS_linux-amd64 := out/minikube-linux-amd64 out/docker-machine-driver-kvm2 +TAR_TARGETS_linux-arm64 := out/minikube-linux-arm64 +TAR_TARGETS_darwin-amd64 := out/minikube-darwin-amd64 out/docker-machine-driver-hyperkit +TAR_TARGETS_windows-amd64 := out/minikube-windows-amd64.exe +out/minikube-%.tar.gz: $$(TAR_TARGETS_$$*) tar -cvzf $@ $^ .PHONY: cross-tars -cross-tars: out/minikube-windows-amd64.tar.gz out/minikube-linux-amd64.tar.gz out/minikube-darwin-amd64.tar.gz +cross-tars: out/minikube-linux-amd64.tar.gz out/minikube-linux-arm64.tar.gz \ + out/minikube-windows-amd64.tar.gz out/minikube-darwin-amd64.tar.gz -cd out && $(SHA512SUM) *.tar.gz > SHA512SUM out/minikube-installer.exe: out/minikube-windows-amd64.exe @@ -551,3 +567,7 @@ site: site/themes/docsy/assets/vendor/bootstrap/package.js out/hugo/hugo --navigateToChanged \ --ignoreCache \ --buildFuture) + +.PHONY: out/mkcmp +out/mkcmp: + GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $@ cmd/performance/main.go diff --git a/cmd/minikube/cmd/config/addons_list.go b/cmd/minikube/cmd/config/addons_list.go index 72aadadc22..08492a3678 100644 --- a/cmd/minikube/cmd/config/addons_list.go +++ b/cmd/minikube/cmd/config/addons_list.go @@ -17,18 +17,23 @@ limitations under the License. package config import ( + "encoding/json" + "fmt" "os" "sort" + "strings" "text/template" "github.com/spf13/cobra" "k8s.io/minikube/pkg/minikube/assets" "k8s.io/minikube/pkg/minikube/exit" + "k8s.io/minikube/pkg/minikube/out" ) const defaultAddonListFormat = "- {{.AddonName}}: {{.AddonStatus}}\n" var addonListFormat string +var addonListOutput string // AddonListTemplate represents the addon list template type AddonListTemplate struct { @@ -44,28 +49,49 @@ var addonsListCmd = &cobra.Command{ if len(args) != 0 { exit.UsageT("usage: minikube addons list") } - err := addonList() - if err != nil { - exit.WithError("addon list failed", err) + + if addonListOutput != "list" && addonListFormat != defaultAddonListFormat { + exit.UsageT("Cannot use both --output and --format options") + } + + switch strings.ToLower(addonListOutput) { + case "list": + printAddonsList() + case "json": + printAddonsJSON() + default: + exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'list', 'json'", addonListOutput)) } }, } func init() { - AddonsCmd.Flags().StringVar(&addonListFormat, "format", defaultAddonListFormat, + addonsListCmd.Flags().StringVarP( + &addonListFormat, + "format", + "f", + defaultAddonListFormat, `Go template format string for the addon list output. The format for Go templates can be found here: https://golang.org/pkg/text/template/ For the list of accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd/config#AddonListTemplate`) + + addonsListCmd.Flags().StringVarP( + &addonListOutput, + "output", + "o", + "list", + `minikube addons list --output OUTPUT. json, list`) + AddonsCmd.AddCommand(addonsListCmd) } -func stringFromStatus(addonStatus bool) string { +var stringFromStatus = func(addonStatus bool) string { if addonStatus { return "enabled" } return "disabled" } -func addonList() error { +var printAddonsList = func() { addonNames := make([]string, 0, len(assets.Addons)) for addonName := range assets.Addons { addonNames = append(addonNames, addonName) @@ -76,7 +102,7 @@ func addonList() error { addonBundle := assets.Addons[addonName] addonStatus, err := addonBundle.IsEnabled() if err != nil { - return err + exit.WithError("Error getting addons status", err) } tmpl, err := template.New("list").Parse(addonListFormat) if err != nil { @@ -88,5 +114,30 @@ func addonList() error { exit.WithError("Error executing list template", err) } } - return nil +} + +var printAddonsJSON = func() { + addonNames := make([]string, 0, len(assets.Addons)) + for addonName := range assets.Addons { + addonNames = append(addonNames, addonName) + } + sort.Strings(addonNames) + + addonsMap := map[string]map[string]interface{}{} + + for _, addonName := range addonNames { + addonBundle := assets.Addons[addonName] + + addonStatus, err := addonBundle.IsEnabled() + if err != nil { + exit.WithError("Error getting addons status", err) + } + + addonsMap[addonName] = map[string]interface{}{ + "Status": stringFromStatus(addonStatus), + } + } + jsonString, _ := json.Marshal(addonsMap) + + out.String(string(jsonString)) } diff --git a/cmd/minikube/cmd/config/disable_test.go b/cmd/minikube/cmd/config/disable_test.go index 3141605972..dbde3850c7 100644 --- a/cmd/minikube/cmd/config/disable_test.go +++ b/cmd/minikube/cmd/config/disable_test.go @@ -16,10 +16,24 @@ limitations under the License. package config -import "testing" +import ( + "testing" + + "gotest.tools/assert" + pkgConfig "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/localpath" +) func TestDisableUnknownAddon(t *testing.T) { if err := Set("InvalidAddon", "false"); err == nil { t.Fatalf("Disable did not return error for unknown addon") } } + +func TestDisableAddon(t *testing.T) { + if err := Set("dashboard", "false"); err != nil { + t.Fatalf("Disable returned unexpected error: " + err.Error()) + } + config, _ := pkgConfig.ReadConfig(localpath.ConfigFile) + assert.Equal(t, config["dashboard"], false) +} diff --git a/cmd/minikube/cmd/config/enable_test.go b/cmd/minikube/cmd/config/enable_test.go index 007d59516d..28556dc1d8 100644 --- a/cmd/minikube/cmd/config/enable_test.go +++ b/cmd/minikube/cmd/config/enable_test.go @@ -16,10 +16,24 @@ limitations under the License. package config -import "testing" +import ( + "testing" + + "gotest.tools/assert" + pkgConfig "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/localpath" +) func TestEnableUnknownAddon(t *testing.T) { if err := Set("InvalidAddon", "false"); err == nil { t.Fatalf("Enable did not return error for unknown addon") } } + +func TestEnableAddon(t *testing.T) { + if err := Set("ingress", "true"); err != nil { + t.Fatalf("Enable returned unexpected error: " + err.Error()) + } + config, _ := pkgConfig.ReadConfig(localpath.ConfigFile) + assert.Equal(t, config["ingress"], true) +} diff --git a/cmd/minikube/cmd/config/open.go b/cmd/minikube/cmd/config/open.go index b8becfbfea..8a852729ce 100644 --- a/cmd/minikube/cmd/config/open.go +++ b/cmd/minikube/cmd/config/open.go @@ -17,6 +17,7 @@ limitations under the License. package config import ( + "os" "text/template" "github.com/spf13/cobra" @@ -62,7 +63,9 @@ var addonsOpenCmd = &cobra.Command{ } defer api.Close() - cluster.EnsureMinikubeRunningOrExit(api, 1) + if !cluster.IsMinikubeRunning(api) { + os.Exit(1) + } addon, ok := assets.Addons[addonName] // validate addon input if !ok { exit.WithCodeT(exit.Data, `addon '{{.name}}' is not a valid addon packaged with minikube. diff --git a/cmd/minikube/cmd/config/profile.go b/cmd/minikube/cmd/config/profile.go index cbf6f1fe6d..64f0f372f5 100644 --- a/cmd/minikube/cmd/config/profile.go +++ b/cmd/minikube/cmd/config/profile.go @@ -45,6 +45,15 @@ var ProfileCmd = &cobra.Command{ } profile := args[0] + /** + we need to add code over here to check whether the profile + name is in the list of reserved keywords + */ + if pkgConfig.ProfileNameInReservedKeywords(profile) { + out.ErrT(out.FailureType, `Profile name "{{.profilename}}" is minikube keyword. To delete profile use command minikube delete -p `, out.V{"profilename": profile}) + os.Exit(0) + } + if profile == "default" { profile = "minikube" } else { diff --git a/cmd/minikube/cmd/config/profile_list.go b/cmd/minikube/cmd/config/profile_list.go index b6aa8b91e7..b79bf162cc 100644 --- a/cmd/minikube/cmd/config/profile_list.go +++ b/cmd/minikube/cmd/config/profile_list.go @@ -17,9 +17,11 @@ limitations under the License. package config import ( + "encoding/json" "fmt" "os" "strconv" + "strings" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/exit" @@ -29,48 +31,101 @@ import ( "github.com/spf13/cobra" ) +var ( + output string +) + var profileListCmd = &cobra.Command{ Use: "list", Short: "Lists all minikube profiles.", Long: "Lists all valid minikube profiles and detects all possible invalid profiles.", Run: func(cmd *cobra.Command, args []string) { - var validData [][]string - - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"Profile", "VM Driver", "NodeIP", "Node Port", "Kubernetes Version"}) - table.SetAutoFormatHeaders(false) - table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true}) - table.SetCenterSeparator("|") - validProfiles, invalidProfiles, err := config.ListProfiles() - - if len(validProfiles) == 0 || err != nil { - exit.UsageT("No minikube profile was found. You can create one using `minikube start`.") - } - for _, p := range validProfiles { - validData = append(validData, []string{p.Name, p.Config.MachineConfig.VMDriver, p.Config.KubernetesConfig.NodeIP, strconv.Itoa(p.Config.KubernetesConfig.NodePort), p.Config.KubernetesConfig.KubernetesVersion}) + switch strings.ToLower(output) { + case "json": + printProfilesJSON() + case "table": + printProfilesTable() + default: + exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'table', 'json'", output)) } - table.AppendBulk(validData) - table.Render() - - if invalidProfiles != nil { - out.T(out.WarningType, "Found {{.number}} invalid profile(s) ! ", out.V{"number": len(invalidProfiles)}) - for _, p := range invalidProfiles { - out.T(out.Empty, "\t "+p.Name) - } - out.T(out.Tip, "You can delete them using the following command(s): ") - for _, p := range invalidProfiles { - out.String(fmt.Sprintf("\t $ minikube delete -p %s \n", p.Name)) - } - - } - if err != nil { - exit.WithCodeT(exit.Config, fmt.Sprintf("error loading profiles: %v", err)) - } }, } +var printProfilesTable = func() { + + var validData [][]string + + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Profile", "VM Driver", "NodeIP", "Node Port", "Kubernetes Version"}) + table.SetAutoFormatHeaders(false) + table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true}) + table.SetCenterSeparator("|") + validProfiles, invalidProfiles, err := config.ListProfiles() + + if len(validProfiles) == 0 || err != nil { + exit.UsageT("No minikube profile was found. You can create one using `minikube start`.") + } + for _, p := range validProfiles { + validData = append(validData, []string{p.Name, p.Config.MachineConfig.VMDriver, p.Config.KubernetesConfig.NodeIP, strconv.Itoa(p.Config.KubernetesConfig.NodePort), p.Config.KubernetesConfig.KubernetesVersion}) + } + + table.AppendBulk(validData) + table.Render() + + if invalidProfiles != nil { + out.T(out.WarningType, "Found {{.number}} invalid profile(s) ! ", out.V{"number": len(invalidProfiles)}) + for _, p := range invalidProfiles { + out.T(out.Empty, "\t "+p.Name) + } + out.T(out.Tip, "You can delete them using the following command(s): ") + for _, p := range invalidProfiles { + out.String(fmt.Sprintf("\t $ minikube delete -p %s \n", p.Name)) + } + + } + + if err != nil { + exit.WithCodeT(exit.Config, fmt.Sprintf("error loading profiles: %v", err)) + } + +} + +var printProfilesJSON = func() { + validProfiles, invalidProfiles, err := config.ListProfiles() + + var valid []*config.Profile + var invalid []*config.Profile + + if validProfiles != nil { + valid = validProfiles + } else { + valid = []*config.Profile{} + } + + if invalidProfiles != nil { + invalid = invalidProfiles + } else { + invalid = []*config.Profile{} + } + + var body = map[string]interface{}{} + + if err == nil { + body["valid"] = valid + body["invalid"] = invalid + jsonString, _ := json.Marshal(body) + out.String(string(jsonString)) + } else { + body["error"] = err + jsonString, _ := json.Marshal(body) + out.String(string(jsonString)) + os.Exit(exit.Failure) + } +} + func init() { + profileListCmd.Flags().StringVarP(&output, "output", "o", "table", "The output format. One of 'json', 'table'") ProfileCmd.AddCommand(profileListCmd) } diff --git a/cmd/minikube/cmd/config/prompt.go b/cmd/minikube/cmd/config/prompt.go index 1577afc10c..10c00a7f0f 100644 --- a/cmd/minikube/cmd/config/prompt.go +++ b/cmd/minikube/cmd/config/prompt.go @@ -151,5 +151,5 @@ func posString(slice []string, element string) int { // containsString returns true if slice contains element func containsString(slice []string, element string) bool { - return !(posString(slice, element) == -1) + return posString(slice, element) != -1 } diff --git a/cmd/minikube/cmd/config/util.go b/cmd/minikube/cmd/config/util.go index 10be06c4f5..cb26c7349c 100644 --- a/cmd/minikube/cmd/config/util.go +++ b/cmd/minikube/cmd/config/util.go @@ -111,6 +111,18 @@ func EnableOrDisableAddon(name string, val string) error { if err != nil { return errors.Wrapf(err, "parsing bool: %s", name) } + addon := assets.Addons[name] + + // check addon status before enabling/disabling it + alreadySet, err := isAddonAlreadySet(addon, enable) + if err != nil { + out.ErrT(out.Conflict, "{{.error}}", out.V{"error": err}) + return err + } + //if addon is already enabled or disabled, do nothing + if alreadySet { + return nil + } // TODO(r2d4): config package should not reference API, pull this out api, err := machine.NewAPIClient() @@ -118,13 +130,18 @@ func EnableOrDisableAddon(name string, val string) error { return errors.Wrap(err, "machine client") } defer api.Close() - cluster.EnsureMinikubeRunningOrExit(api, 0) - addon := assets.Addons[name] + //if minikube is not running, we return and simply update the value in the addon + //config and rewrite the file + if !cluster.IsMinikubeRunning(api) { + return nil + } + host, err := cluster.CheckIfHostExistsAndLoad(api, config.GetMachineName()) if err != nil { return errors.Wrap(err, "getting host") } + cmd, err := machine.CommandRunner(host) if err != nil { return errors.Wrap(err, "command runner") @@ -139,30 +156,24 @@ func EnableOrDisableAddon(name string, val string) error { return enableOrDisableAddonInternal(addon, cmd, data, enable) } -func isAddonAlreadySet(addon *assets.Addon, enable bool) error { - +func isAddonAlreadySet(addon *assets.Addon, enable bool) (bool, error) { addonStatus, err := addon.IsEnabled() if err != nil { - return errors.Wrap(err, "get the addon status") + return false, errors.Wrap(err, "get the addon status") } if addonStatus && enable { - return fmt.Errorf("addon %s was already enabled", addon.Name()) + return true, nil } else if !addonStatus && !enable { - return fmt.Errorf("addon %s was already disabled", addon.Name()) + return true, nil } - return nil + return false, nil } func enableOrDisableAddonInternal(addon *assets.Addon, cmd command.Runner, data interface{}, enable bool) error { var err error - // check addon status before enabling/disabling it - if err := isAddonAlreadySet(addon, enable); err != nil { - out.ErrT(out.Conflict, "{{.error}}", out.V{"error": err}) - os.Exit(0) - } if enable { for _, addon := range addon.Assets { diff --git a/cmd/minikube/cmd/config/util_test.go b/cmd/minikube/cmd/config/util_test.go index 11845ba6c4..46bd62d8de 100644 --- a/cmd/minikube/cmd/config/util_test.go +++ b/cmd/minikube/cmd/config/util_test.go @@ -86,15 +86,12 @@ func TestSetBool(t *testing.T) { func TestIsAddonAlreadySet(t *testing.T) { testCases := []struct { addonName string - expectErr string }{ { addonName: "ingress", - expectErr: "addon ingress was already ", }, { addonName: "heapster", - expectErr: "addon heapster was already ", }, } @@ -102,13 +99,16 @@ func TestIsAddonAlreadySet(t *testing.T) { addon := assets.Addons[test.addonName] addonStatus, _ := addon.IsEnabled() - expectMsg := test.expectErr + "enabled" - if !addonStatus { - expectMsg = test.expectErr + "disabled" + alreadySet, err := isAddonAlreadySet(addon, addonStatus) + if !alreadySet { + if addonStatus { + t.Errorf("Did not get expected status, \n\n expected %+v already enabled", test.addonName) + } else { + t.Errorf("Did not get expected status, \n\n expected %+v already disabled", test.addonName) + } } - err := isAddonAlreadySet(addon, addonStatus) - if err.Error() != expectMsg { - t.Errorf("Did not get expected error, \n\n expected: %+v \n\n actual: %+v", expectMsg, err) + if err != nil { + t.Errorf("Got unexpected error: %+v", err) } } } diff --git a/cmd/minikube/cmd/dashboard.go b/cmd/minikube/cmd/dashboard.go index 4908e319f1..27a472d80b 100644 --- a/cmd/minikube/cmd/dashboard.go +++ b/cmd/minikube/cmd/dashboard.go @@ -93,7 +93,9 @@ var dashboardCmd = &cobra.Command{ exit.WithCodeT(exit.NoInput, "kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/") } - cluster.EnsureMinikubeRunningOrExit(api, 1) + if !cluster.IsMinikubeRunning(api) { + os.Exit(1) + } // Check dashboard status before enabling it dashboardAddon := assets.Addons["dashboard"] diff --git a/cmd/minikube/cmd/delete.go b/cmd/minikube/cmd/delete.go index 6aac39c98f..ffba41d4a7 100644 --- a/cmd/minikube/cmd/delete.go +++ b/cmd/minikube/cmd/delete.go @@ -25,7 +25,7 @@ import ( "github.com/docker/machine/libmachine/mcnerror" "github.com/golang/glog" - ps "github.com/mitchellh/go-ps" + "github.com/mitchellh/go-ps" "github.com/pkg/errors" "github.com/docker/machine/libmachine" @@ -43,6 +43,7 @@ import ( ) var deleteAll bool +var purge bool // deleteCmd represents the delete command var deleteCmd = &cobra.Command{ @@ -56,11 +57,15 @@ associated files.`, type typeOfError int const ( - Fatal typeOfError = 0 + // Fatal is a type of DeletionError + Fatal typeOfError = 0 + // MissingProfile is a type of DeletionError MissingProfile typeOfError = 1 + // MissingCluster is a type of DeletionError MissingCluster typeOfError = 2 ) +// DeletionError can be returned from DeleteProfiles type DeletionError struct { Err error Errtype typeOfError @@ -70,6 +75,16 @@ func (error DeletionError) Error() string { return error.Err.Error() } +func init() { + deleteCmd.Flags().BoolVar(&deleteAll, "all", false, "Set flag to delete all profiles") + deleteCmd.Flags().BoolVar(&purge, "purge", false, "Set this flag to delete the '.minikube' folder from your user directory.") + + if err := viper.BindPFlags(deleteCmd.Flags()); err != nil { + exit.WithError("unable to bind flags", err) + } + RootCmd.AddCommand(deleteCmd) +} + // runDelete handles the executes the flow of "minikube delete" func runDelete(cmd *cobra.Command, args []string) { if len(args) > 0 { @@ -80,14 +95,23 @@ func runDelete(cmd *cobra.Command, args []string) { exit.WithError("Could not get profile flag", err) } + validProfiles, invalidProfiles, err := pkg_config.ListProfiles() + profilesToDelete := append(validProfiles, invalidProfiles...) + + // If the purge flag is set, go ahead and delete the .minikube directory. + if purge && len(profilesToDelete) > 1 && !deleteAll { + out.ErrT(out.Notice, "Multiple minikube profiles were found - ") + for _, p := range profilesToDelete { + out.T(out.Notice, " - {{.profile}}", out.V{"profile": p.Name}) + } + exit.UsageT("Usage: minikube delete --all --purge") + } + if deleteAll { if profileFlag != constants.DefaultMachineName { exit.UsageT("usage: minikube delete --all") } - validProfiles, invalidProfiles, err := pkg_config.ListProfiles() - profilesToDelete := append(validProfiles, invalidProfiles...) - if err != nil { exit.WithError("Error getting profiles to delete", err) } @@ -116,9 +140,18 @@ func runDelete(cmd *cobra.Command, args []string) { out.T(out.DeletingHost, "Successfully deleted profile \"{{.name}}\"", out.V{"name": profileName}) } } + + // If the purge flag is set, go ahead and delete the .minikube directory. + if purge { + glog.Infof("Purging the '.minikube' directory located at %s", localpath.MiniPath()) + if err := os.RemoveAll(localpath.MiniPath()); err != nil { + exit.WithError("unable to delete minikube config folder", err) + } + out.T(out.Crushed, "Successfully purged minikube directory located at - [{{.minikubeDirectory}}]", out.V{"minikubeDirectory": localpath.MiniPath()}) + } } -// Deletes one or more profiles +// DeleteProfiles deletes one or more profiles func DeleteProfiles(profiles []*pkg_config.Profile) []error { var errs []error for _, profile := range profiles { @@ -177,7 +210,7 @@ func deleteProfile(profile *pkg_config.Profile) error { if err = cluster.DeleteHost(api); err != nil { switch errors.Cause(err).(type) { case mcnerror.ErrHostDoesNotExist: - out.T(out.Meh, `"{{.name}}" cluster does not exist. Proceeding ahead with cleanup.`, out.V{"name": profile}) + out.T(out.Meh, `"{{.name}}" cluster does not exist. Proceeding ahead with cleanup.`, out.V{"name": profile.Name}) default: out.T(out.FailureType, "Failed to delete cluster: {{.error}}", out.V{"error": err}) out.T(out.Notice, `You may need to manually remove the "{{.name}}" VM from your hypervisor`, out.V{"name": profile.Name}) @@ -246,7 +279,7 @@ func uninstallKubernetes(api libmachine.API, kc pkg_config.KubernetesConfig, bsN return nil } -// Handles deletion error from DeleteProfiles +// HandleDeletionErrors handles deletion errors from DeleteProfiles func HandleDeletionErrors(errors []error) { if len(errors) == 1 { handleSingleDeletionError(errors[0]) @@ -346,8 +379,3 @@ func killMountProcess() error { } return nil } - -func init() { - deleteCmd.Flags().BoolVar(&deleteAll, "all", false, "Set flag to delete all profiles") - RootCmd.AddCommand(deleteCmd) -} diff --git a/cmd/minikube/cmd/delete_test.go b/cmd/minikube/cmd/delete_test.go index 042b86dd4c..69a37be735 100644 --- a/cmd/minikube/cmd/delete_test.go +++ b/cmd/minikube/cmd/delete_test.go @@ -22,450 +22,171 @@ import ( "path/filepath" "testing" + "github.com/google/go-cmp/cmp" + "github.com/otiai10/copy" "github.com/spf13/viper" + "k8s.io/minikube/pkg/minikube/cluster" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/localpath" ) -func TestDeleteProfileWithValidConfig(t *testing.T) { - testMinikubeDir := "../../../pkg/minikube/config/testdata/delete-single/.minikube" - miniDir, err := filepath.Abs(testMinikubeDir) - - if err != nil { - t.Errorf("error getting dir path for %s : %v", testMinikubeDir, err) +// except returns a list of strings, minus the excluded ones +func exclude(vals []string, exclude []string) []string { + result := []string{} + for _, v := range vals { + excluded := false + for _, e := range exclude { + if e == v { + excluded = true + continue + } + } + if !excluded { + result = append(result, v) + } } - - err = os.Setenv(localpath.MinikubeHome, miniDir) - if err != nil { - t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome) - } - - files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")) - numberOfProfileDirs := len(files) - - files, _ = ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) - numberOfMachineDirs := len(files) - - profileToDelete := "p1" - profile, _ := config.LoadProfile(profileToDelete) - - errs := DeleteProfiles([]*config.Profile{profile}) - - if len(errs) > 0 { - HandleDeletionErrors(errs) - t.Fatal("Errors while deleting profiles") - } - - pathToProfile := config.ProfileFolderPath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")); len(files) != (numberOfProfileDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")); len(files) != (numberOfMachineDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - viper.Set(config.MachineProfile, "") + return result } -func TestDeleteProfileWithEmptyProfileConfig(t *testing.T) { - testMinikubeDir := "../../../pkg/minikube/config/testdata/delete-single/.minikube" - miniDir, err := filepath.Abs(testMinikubeDir) - +func fileNames(path string) ([]string, error) { + result := []string{} + fis, err := ioutil.ReadDir(path) if err != nil { - t.Errorf("error getting dir path for %s : %v", testMinikubeDir, err) + return result, err } - - err = os.Setenv(localpath.MinikubeHome, miniDir) - if err != nil { - t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome) + for _, fi := range fis { + result = append(result, fi.Name()) } - - files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")) - numberOfProfileDirs := len(files) - - files, _ = ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) - numberOfMachineDirs := len(files) - - profileToDelete := "p2_empty_profile_config" - profile, _ := config.LoadProfile(profileToDelete) - - errs := DeleteProfiles([]*config.Profile{profile}) - - if len(errs) > 0 { - HandleDeletionErrors(errs) - t.Fatal("Errors while deleting profiles") - } - - pathToProfile := config.ProfileFolderPath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")); len(files) != (numberOfProfileDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")); len(files) != (numberOfMachineDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - viper.Set(config.MachineProfile, "") + return result, nil } -func TestDeleteProfileWithInvalidProfileConfig(t *testing.T) { - testMinikubeDir := "../../../pkg/minikube/config/testdata/delete-single/.minikube" - miniDir, err := filepath.Abs(testMinikubeDir) - +func TestDeleteProfile(t *testing.T) { + td, err := ioutil.TempDir("", "single") if err != nil { - t.Errorf("error getting dir path for %s : %v", testMinikubeDir, err) + t.Fatalf("tempdir: %v", err) } - - err = os.Setenv(localpath.MinikubeHome, miniDir) + err = copy.Copy("../../../pkg/minikube/config/testdata/delete-single", td) if err != nil { - t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome) + t.Fatalf("copy: %v", err) } - files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")) - numberOfProfileDirs := len(files) - - files, _ = ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) - numberOfMachineDirs := len(files) - - profileToDelete := "p3_invalid_profile_config" - profile, _ := config.LoadProfile(profileToDelete) - - errs := DeleteProfiles([]*config.Profile{profile}) - - if len(errs) > 0 { - HandleDeletionErrors(errs) - t.Fatal("Errors while deleting profiles") + tests := []struct { + name string + profile string + expected []string + }{ + {"normal", "p1", []string{"p1"}}, + {"empty-profile", "p2_empty_profile_config", []string{"p2_empty_profile_config"}}, + {"invalid-profile", "p3_invalid_profile_config", []string{"p3_invalid_profile_config"}}, + {"partial-profile", "p4_partial_profile_config", []string{"p4_partial_profile_config"}}, + {"missing-mach", "p5_missing_machine_config", []string{"p5_missing_machine_config"}}, + {"empty-mach", "p6_empty_machine_config", []string{"p6_empty_machine_config"}}, + {"invalid-mach", "p7_invalid_machine_config", []string{"p7_invalid_machine_config"}}, + {"partial-mach", "p8_partial_machine_config", []string{"p8_partial_machine_config"}}, } - pathToProfile := config.ProfileFolderPath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err = os.Setenv(localpath.MinikubeHome, td) + if err != nil { + t.Errorf("setenv: %v", err) + } + + beforeProfiles, err := fileNames(filepath.Join(localpath.MiniPath(), "profiles")) + if err != nil { + t.Errorf("readdir: %v", err) + } + beforeMachines, err := fileNames(filepath.Join(localpath.MiniPath(), "machines")) + if err != nil { + t.Errorf("readdir: %v", err) + } + + profile, err := config.LoadProfile(tt.profile) + if err != nil { + t.Logf("load failure: %v", err) + } + + errs := DeleteProfiles([]*config.Profile{profile}) + if len(errs) > 0 { + HandleDeletionErrors(errs) + t.Errorf("Errors while deleting profiles: %v", errs) + } + pathToProfile := config.ProfileFolderPath(profile.Name, localpath.MiniPath()) + if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) { + t.Errorf("Profile folder of profile \"%s\" was not deleted", profile.Name) + } + + pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath()) + if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) { + t.Errorf("Profile folder of profile \"%s\" was not deleted", profile.Name) + } + + afterProfiles, err := fileNames(filepath.Join(localpath.MiniPath(), "profiles")) + if err != nil { + t.Errorf("readdir profiles: %v", err) + } + + afterMachines, err := fileNames(filepath.Join(localpath.MiniPath(), "machines")) + if err != nil { + t.Errorf("readdir machines: %v", err) + } + + expectedProfiles := exclude(beforeProfiles, tt.expected) + if diff := cmp.Diff(expectedProfiles, afterProfiles); diff != "" { + t.Errorf("profiles mismatch (-want +got):\n%s", diff) + } + + expectedMachines := exclude(beforeMachines, tt.expected) + if diff := cmp.Diff(expectedMachines, afterMachines); diff != "" { + t.Errorf("machines mismatch (-want +got):\n%s", diff) + } + + viper.Set(config.MachineProfile, "") + }) } - - pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")); len(files) != (numberOfProfileDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")); len(files) != (numberOfMachineDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - viper.Set(config.MachineProfile, "") -} - -func TestDeleteProfileWithPartialProfileConfig(t *testing.T) { - testMinikubeDir := "../../../pkg/minikube/config/testdata/delete-single/.minikube" - miniDir, err := filepath.Abs(testMinikubeDir) - - if err != nil { - t.Errorf("error getting dir path for %s : %v", testMinikubeDir, err) - } - - err = os.Setenv(localpath.MinikubeHome, miniDir) - if err != nil { - t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome) - } - - files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")) - numberOfProfileDirs := len(files) - - files, _ = ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) - numberOfMachineDirs := len(files) - - profileToDelete := "p4_partial_profile_config" - profile, _ := config.LoadProfile(profileToDelete) - - errs := DeleteProfiles([]*config.Profile{profile}) - - if len(errs) > 0 { - HandleDeletionErrors(errs) - t.Fatal("Errors while deleting profiles") - } - - pathToProfile := config.ProfileFolderPath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")); len(files) != (numberOfProfileDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")); len(files) != (numberOfMachineDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - viper.Set(config.MachineProfile, "") -} - -func TestDeleteProfileWithMissingMachineConfig(t *testing.T) { - testMinikubeDir := "../../../pkg/minikube/config/testdata/delete-single/.minikube" - miniDir, err := filepath.Abs(testMinikubeDir) - - if err != nil { - t.Errorf("error getting dir path for %s : %v", testMinikubeDir, err) - } - - err = os.Setenv(localpath.MinikubeHome, miniDir) - if err != nil { - t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome) - } - - files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")) - numberOfProfileDirs := len(files) - - files, _ = ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) - numberOfMachineDirs := len(files) - - profileToDelete := "p5_missing_machine_config" - profile, _ := config.LoadProfile(profileToDelete) - - errs := DeleteProfiles([]*config.Profile{profile}) - - if len(errs) > 0 { - HandleDeletionErrors(errs) - t.Fatal("Errors while deleting profiles") - } - - pathToProfile := config.ProfileFolderPath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")); len(files) != (numberOfProfileDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")); len(files) != numberOfMachineDirs { - t.Fatal("Deleted a machine config when it should not") - } - - viper.Set(config.MachineProfile, "") -} - -func TestDeleteProfileWithEmptyMachineConfig(t *testing.T) { - testMinikubeDir := "../../../pkg/minikube/config/testdata/delete-single/.minikube" - miniDir, err := filepath.Abs(testMinikubeDir) - - if err != nil { - t.Errorf("error getting dir path for %s : %v", testMinikubeDir, err) - } - - err = os.Setenv(localpath.MinikubeHome, miniDir) - if err != nil { - t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome) - } - - files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")) - numberOfProfileDirs := len(files) - - files, _ = ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) - numberOfMachineDirs := len(files) - - profileToDelete := "p6_empty_machine_config" - profile, _ := config.LoadProfile(profileToDelete) - - errs := DeleteProfiles([]*config.Profile{profile}) - - if len(errs) > 0 { - HandleDeletionErrors(errs) - t.Fatal("Errors while deleting profiles") - } - - pathToProfile := config.ProfileFolderPath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")); len(files) != (numberOfProfileDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")); len(files) != (numberOfMachineDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - viper.Set(config.MachineProfile, "") -} - -func TestDeleteProfileWithInvalidMachineConfig(t *testing.T) { - testMinikubeDir := "../../../pkg/minikube/config/testdata/delete-single/.minikube" - miniDir, err := filepath.Abs(testMinikubeDir) - - if err != nil { - t.Errorf("error getting dir path for %s : %v", testMinikubeDir, err) - } - - err = os.Setenv(localpath.MinikubeHome, miniDir) - if err != nil { - t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome) - } - - files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")) - numberOfProfileDirs := len(files) - - files, _ = ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) - numberOfMachineDirs := len(files) - - profileToDelete := "p7_invalid_machine_config" - profile, _ := config.LoadProfile(profileToDelete) - - errs := DeleteProfiles([]*config.Profile{profile}) - - if len(errs) > 0 { - HandleDeletionErrors(errs) - t.Fatal("Errors while deleting profiles") - } - - pathToProfile := config.ProfileFolderPath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")); len(files) != (numberOfProfileDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")); len(files) != (numberOfMachineDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - viper.Set(config.MachineProfile, "") -} - -func TestDeleteProfileWithPartialMachineConfig(t *testing.T) { - testMinikubeDir := "../../../pkg/minikube/config/testdata/delete-single/.minikube" - miniDir, err := filepath.Abs(testMinikubeDir) - - if err != nil { - t.Errorf("error getting dir path for %s : %v", testMinikubeDir, err) - } - - err = os.Setenv(localpath.MinikubeHome, miniDir) - if err != nil { - t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome) - } - - files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")) - numberOfProfileDirs := len(files) - - files, _ = ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) - numberOfMachineDirs := len(files) - - profileToDelete := "p8_partial_machine_config" - profile, _ := config.LoadProfile(profileToDelete) - - errs := DeleteProfiles([]*config.Profile{profile}) - - if len(errs) > 0 { - HandleDeletionErrors(errs) - t.Fatal("Errors while deleting profiles") - } - - pathToProfile := config.ProfileFolderPath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath()) - if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) { - t.Fatalf("Profile folder of profile \"%s\" was not deleted", profile.Name) - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")); len(files) != (numberOfProfileDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - if files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")); len(files) != (numberOfMachineDirs - 1) { - t.Fatal("Did not delete exactly one profile") - } - - viper.Set(config.MachineProfile, "") } func TestDeleteAllProfiles(t *testing.T) { - const numberOfTotalProfileDirs = 8 - const numberOfTotalMachineDirs = 7 - - testMinikubeDir := "../../../pkg/minikube/config/testdata/delete-all/.minikube" - miniDir, err := filepath.Abs(testMinikubeDir) - + td, err := ioutil.TempDir("", "all") if err != nil { - t.Errorf("error getting dir path for %s : %v", testMinikubeDir, err) + t.Fatalf("tempdir: %v", err) + } + err = copy.Copy("../../../pkg/minikube/config/testdata/delete-all", td) + if err != nil { + t.Fatalf("copy: %v", err) } - err = os.Setenv(localpath.MinikubeHome, miniDir) + err = os.Setenv(localpath.MinikubeHome, td) if err != nil { t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome) } - files, _ := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")) - numberOfProfileDirs := len(files) - - files, _ = ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) - numberOfMachineDirs := len(files) - - if numberOfTotalProfileDirs != numberOfProfileDirs { - t.Error("invalid testdata") + pFiles, err := fileNames(filepath.Join(localpath.MiniPath(), "profiles")) + if err != nil { + t.Errorf("filenames: %v", err) + } + mFiles, err := fileNames(filepath.Join(localpath.MiniPath(), "machines")) + if err != nil { + t.Errorf("filenames: %v", err) } - if numberOfTotalMachineDirs != numberOfMachineDirs { - t.Error("invalid testdata") + const numberOfTotalProfileDirs = 8 + if numberOfTotalProfileDirs != len(pFiles) { + t.Errorf("got %d test profiles, expected %d: %s", len(pFiles), numberOfTotalProfileDirs, pFiles) + } + const numberOfTotalMachineDirs = 7 + if numberOfTotalMachineDirs != len(mFiles) { + t.Errorf("got %d test machines, expected %d: %s", len(mFiles), numberOfTotalMachineDirs, mFiles) } validProfiles, inValidProfiles, err := config.ListProfiles() - if err != nil { t.Error(err) } if numberOfTotalProfileDirs != len(validProfiles)+len(inValidProfiles) { - t.Error("invalid testdata") + t.Errorf("ListProfiles length = %d, expected %d\nvalid: %v\ninvalid: %v\n", len(validProfiles)+len(inValidProfiles), numberOfTotalProfileDirs, validProfiles, inValidProfiles) } profiles := append(validProfiles, inValidProfiles...) @@ -475,18 +196,20 @@ func TestDeleteAllProfiles(t *testing.T) { t.Errorf("errors while deleting all profiles: %v", errs) } - files, _ = ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "profiles")) - numberOfProfileDirs = len(files) - - files, _ = ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) - numberOfMachineDirs = len(files) - - if numberOfProfileDirs != 0 { - t.Errorf("Did not delete all profiles: still %d profiles left", numberOfProfileDirs) + afterProfiles, err := fileNames(filepath.Join(localpath.MiniPath(), "profiles")) + if err != nil { + t.Errorf("profiles: %v", err) + } + afterMachines, err := ioutil.ReadDir(filepath.Join(localpath.MiniPath(), "machines")) + if err != nil { + t.Errorf("machines: %v", err) + } + if len(afterProfiles) != 0 { + t.Errorf("Did not delete all profiles, remaining: %v", afterProfiles) } - if numberOfMachineDirs != 0 { - t.Errorf("Did not delete all profiles: still %d machines left", numberOfMachineDirs) + if len(afterMachines) != 0 { + t.Errorf("Did not delete all machines, remaining: %v", afterMachines) } viper.Set(config.MachineProfile, "") diff --git a/cmd/minikube/cmd/service.go b/cmd/minikube/cmd/service.go index d550c736a7..d0068a23da 100644 --- a/cmd/minikube/cmd/service.go +++ b/cmd/minikube/cmd/service.go @@ -17,6 +17,7 @@ limitations under the License. package cmd import ( + "os" "text/template" "github.com/spf13/cobra" @@ -64,7 +65,9 @@ var serviceCmd = &cobra.Command{ } defer api.Close() - cluster.EnsureMinikubeRunningOrExit(api, 1) + if !cluster.IsMinikubeRunning(api) { + os.Exit(1) + } err = service.WaitAndMaybeOpenService(api, namespace, svc, serviceURLTemplate, serviceURLMode, https, wait, interval) if err != nil { diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index 21b522eddb..50c05feaf2 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -17,7 +17,9 @@ limitations under the License. package cmd import ( + "encoding/json" "fmt" + "math" "net" "net/url" "os" @@ -117,6 +119,7 @@ const ( minimumMemorySize = "1024mb" minimumCPUS = 2 minimumDiskSize = "2000mb" + autoUpdate = "auto-update-drivers" ) var ( @@ -130,6 +133,18 @@ var ( extraOptions cfg.ExtraOptionSlice ) +type kubectlversion struct { + CVersion VersionInfo `json:"clientVersion"` + SVersion VersionInfo `json:"serverVersion"` +} + +// VersionInfo holds version information +type VersionInfo struct { + Major string `json:"major"` + Minor string `json:"minor"` + GitVersion string `json:"gitVersion"` +} + func init() { initMinikubeFlags() initKubernetesFlags() @@ -170,6 +185,7 @@ func initMinikubeFlags() { startCmd.Flags().Bool(waitUntilHealthy, true, "Wait until Kubernetes core services are healthy before exiting.") startCmd.Flags().Duration(waitTimeout, 6*time.Minute, "max time to wait per Kubernetes core services to be healthy.") startCmd.Flags().Bool(nativeSSH, true, "Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'.") + startCmd.Flags().Bool(autoUpdate, true, "If set, automatically updates drivers to the latest version. Defaults to true.") } // initKubernetesFlags inits the commandline flags for kubernetes related options @@ -293,7 +309,13 @@ func runStart(cmd *cobra.Command, args []string) { validateFlags(driver) validateUser(driver) - _ = getMinikubeVersion(driver) + v, err := version.GetSemverVersion() + if err != nil { + out.WarningT("Error parsing minikube version: {{.error}}", out.V{"error": err}) + } else if err := drivers.InstallOrUpdate(driver, localpath.MakeMiniPath("bin"), v, viper.GetBool(interactive), viper.GetBool(autoUpdate)); err != nil { + out.WarningT("Unable to update {{.driver}} driver: {{.error}}", out.V{"driver": driver, "error": err}) + } + k8sVersion, isUpgrade := getKubernetesVersion(oldConfig) config, err := generateCfgFromFlags(cmd, k8sVersion, driver) if err != nil { @@ -446,8 +468,12 @@ func startMachine(config *cfg.Config) (runner command.Runner, preExists bool, ma exit.WithError("Failed to get machine client", err) } host, preExists = startHost(m, config.MachineConfig) + runner, err = machine.CommandRunner(host) + if err != nil { + exit.WithError("Failed to get command runner", err) + } - ip := validateNetwork(host) + ip := validateNetwork(host, runner) // Bypass proxy for minikube's vm host ip err = proxy.ExcludeIP(ip) if err != nil { @@ -458,10 +484,6 @@ func startMachine(config *cfg.Config) (runner command.Runner, preExists bool, ma if err := saveConfig(config); err != nil { exit.WithError("Failed to save config", err) } - runner, err = machine.CommandRunner(host) - if err != nil { - exit.WithError("Failed to get command runner", err) - } return runner, preExists, m, host } @@ -477,15 +499,49 @@ func showVersionInfo(k8sVersion string, cr cruntime.Manager) { } } +/** +Function to check for kubectl. The checking is to compare +the version reported by both the client and server. Checking +is based on what is outlined in Kubernetes document +https://kubernetes.io/docs/setup/release/version-skew-policy/#kubectl +*/ func showKubectlConnectInfo(kcs *kubeconfig.Settings) { + var output []byte + clientVersion := kubectlversion{} + if kcs.KeepContext { out.T(out.Kubectl, "To connect to this cluster, use: kubectl --context={{.name}}", out.V{"name": kcs.ClusterName}) } else { out.T(out.Ready, `Done! kubectl is now configured to use "{{.name}}"`, out.V{"name": cfg.GetMachineName()}) } - _, err := exec.LookPath("kubectl") + + path, err := exec.LookPath("kubectl") + // ...not found just print and return if err != nil { out.T(out.Tip, "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/") + return + } + + output, err = exec.Command(path, "version", "--output=json").Output() + if err != nil { + return + } + glog.Infof("Received output from kubectl %s", output) + + // unmarshal the json + output = []byte("Nanik") + clientjsonErr := json.Unmarshal(output, &clientVersion) + if clientjsonErr != nil { + glog.Infof("There was an error processing kubectl json output.") + return + } + // obtain the minor version for both client & server + serverMinor, _ := strconv.Atoi(clientVersion.SVersion.Minor) + clientMinor, _ := strconv.Atoi(clientVersion.CVersion.Minor) + + if math.Abs(float64(clientMinor-serverMinor)) > 1 { + out.T(out.Tip, "{{.path}} is version {{.clientMinor}}, and is incompatible with your specified Kubernetes version. You will need to update {{.path}} or use 'minikube kubectl' to connect with this cluster", + out.V{"path": path, "clientMinor": clientMinor}) } } @@ -764,7 +820,7 @@ func generateCfgFromFlags(cmd *cobra.Command, k8sVersion string, driver string) repository = autoSelectedRepository } - if repository != "" { + if cmd.Flags().Changed(imageRepository) { out.T(out.SuccessType, "Using image repository {{.name}}", out.V{"name": repository}) } @@ -905,7 +961,7 @@ func startHost(api libmachine.API, mc cfg.MachineConfig) (*host.Host, bool) { } // validateNetwork tries to catch network problems as soon as possible -func validateNetwork(h *host.Host) string { +func validateNetwork(h *host.Host, r command.Runner) string { ip, err := h.Driver.GetIP() if err != nil { exit.WithError("Unable to get VM IP address", err) @@ -929,19 +985,52 @@ func validateNetwork(h *host.Host) string { } } - // Here is where we should be checking connectivity to/from the VM - return ip -} + // none driver does not need ssh access + if h.Driver.DriverName() != constants.DriverNone { + sshAddr := fmt.Sprintf("%s:22", ip) + conn, err := net.Dial("tcp", sshAddr) + if err != nil { + exit.WithCodeT(exit.IO, `minikube is unable to connect to the VM: {{.error}} -// getMinikubeVersion ensures that the driver binary is up to date -func getMinikubeVersion(driver string) string { - v, err := version.GetSemverVersion() - if err != nil { - out.WarningT("Error parsing minikube version: {{.error}}", out.V{"error": err}) - } else if err := drivers.InstallOrUpdate(driver, localpath.MakeMiniPath("bin"), v, viper.GetBool(interactive)); err != nil { - out.WarningT("Unable to update {{.driver}} driver: {{.error}}", out.V{"driver": driver, "error": err}) +This is likely due to one of two reasons: + +- VPN or firewall interference +- {{.hypervisor}} network configuration issue + +Suggested workarounds: + +- Disable your local VPN or firewall software +- Configure your local VPN or firewall to allow access to {{.ip}} +- Restart or reinstall {{.hypervisor}} +- Use an alternative --vm-driver`, out.V{"error": err, "hypervisor": h.Driver.DriverName(), "ip": ip}) + } + defer conn.Close() } - return v.String() + + if err := r.Run("nslookup kubernetes.io"); err != nil { + out.WarningT("VM is unable to resolve DNS hosts: {[.error}}", out.V{"error": err}) + } + + // Try both UDP and ICMP to assert basic external connectivity + if err := r.Run("nslookup k8s.io 8.8.8.8 || nslookup k8s.io 1.1.1.1 || ping -c1 8.8.8.8"); err != nil { + out.WarningT("VM is unable to directly connect to the internet: {{.error}}", out.V{"error": err}) + } + + // Try an HTTPS connection to the + proxy := os.Getenv("HTTPS_PROXY") + opts := "-sS" + if proxy != "" && !strings.HasPrefix(proxy, "localhost") && !strings.HasPrefix(proxy, "127.0") { + opts = fmt.Sprintf("-x %s %s", proxy, opts) + } + + repo := viper.GetString(imageRepository) + if repo == "" { + repo = images.DefaultImageRepo + } + if err := r.Run(fmt.Sprintf("curl %s https://%s/", opts, repo)); err != nil { + out.WarningT("VM is unable to connect to the selected image repository: {{.error}}", out.V{"error": err}) + } + return ip } // getKubernetesVersion ensures that the requested version is reasonable diff --git a/cmd/minikube/cmd/update-context.go b/cmd/minikube/cmd/update-context.go index 3a6eeda815..9474bf7ab5 100644 --- a/cmd/minikube/cmd/update-context.go +++ b/cmd/minikube/cmd/update-context.go @@ -44,7 +44,7 @@ var updateContextCmd = &cobra.Command{ if err != nil { exit.WithError("Error host driver ip status", err) } - updated, err := kubeconfig.UpdateIP(ip, constants.KubeconfigPath, machineName) + updated, err := kubeconfig.UpdateIP(ip, machineName, constants.KubeconfigPath) if err != nil { exit.WithError("update config", err) } diff --git a/cmd/performance/cmd/mkcmp.go b/cmd/performance/cmd/mkcmp.go new file mode 100644 index 0000000000..f1a26fe953 --- /dev/null +++ b/cmd/performance/cmd/mkcmp.go @@ -0,0 +1,49 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "errors" + "fmt" + "os" + + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: "mkcmp [path to first binary] [path to second binary]", + Short: "mkcmp is used to compare performance of two minikube binaries", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + return validateArgs(args) + }, + Run: func(cmd *cobra.Command, args []string) {}, +} + +func validateArgs(args []string) error { + if len(args) != 2 { + return errors.New("mkcmp requires two minikube binaries to compare: mkcmp [path to first binary] [path to second binary]") + } + return nil +} + +// Execute runs the mkcmp command +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/cmd/performance/main.go b/cmd/performance/main.go new file mode 100644 index 0000000000..82c0efb0ac --- /dev/null +++ b/cmd/performance/main.go @@ -0,0 +1,23 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import "k8s.io/minikube/cmd/performance/cmd" + +func main() { + cmd.Execute() +} diff --git a/deploy/iso/minikube-iso/board/coreos/minikube/isolinux.cfg b/deploy/iso/minikube-iso/board/coreos/minikube/isolinux.cfg index ecb2f1d708..9857e0bb2a 100644 --- a/deploy/iso/minikube-iso/board/coreos/minikube/isolinux.cfg +++ b/deploy/iso/minikube-iso/board/coreos/minikube/isolinux.cfg @@ -2,4 +2,4 @@ default 1 label 1 kernel /boot/bzImage initrd /boot/initrd - append root=/dev/sr0 loglevel=3 console=ttyS0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes + append root=/dev/sr0 loglevel=3 console=ttyS0 noembed nomodeset norestore waitusb=10 random.trust_cpu=on hw_rng_model=virtio systemd.legacy_systemd_cgroup_controller=yes diff --git a/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/usr/bin/toolbox b/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/usr/bin/toolbox index bdfad1d3e9..2f597cd80b 100644 --- a/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/usr/bin/toolbox +++ b/deploy/iso/minikube-iso/board/coreos/minikube/rootfs-overlay/usr/bin/toolbox @@ -8,11 +8,11 @@ machine=$(uname -m) case ${machine} in aarch64 ) TOOLBOX_DOCKER_IMAGE=arm64v8/fedora - TOOLBOX_DOCKER_TAG=29 + TOOLBOX_DOCKER_TAG=latest ;; x86_64 ) TOOLBOX_DOCKER_IMAGE=fedora - TOOLBOX_DOCKER_TAG=29 + TOOLBOX_DOCKER_TAG=latest ;; * ) echo "Warning: Unknown machine type ${machine}" >&2 @@ -69,10 +69,9 @@ if [ "x${1-}" == x-c ]; then set /bin/sh "$@" fi -sudo systemd-nspawn \ +sudo SYSTEMD_NSPAWN_SHARE_SYSTEM=1 systemd-nspawn \ --directory="${machinepath}" \ --capability=all \ - --share-system \ ${TOOLBOX_BIND} \ ${TOOLBOX_ENV} \ --user="${TOOLBOX_USER}" "$@" diff --git a/deploy/iso/minikube-iso/package/gluster/gluster.mk b/deploy/iso/minikube-iso/package/gluster/gluster.mk index aedf078124..35a761afd1 100644 --- a/deploy/iso/minikube-iso/package/gluster/gluster.mk +++ b/deploy/iso/minikube-iso/package/gluster/gluster.mk @@ -5,7 +5,10 @@ ################################################################################ GLUSTER_VERSION = 4.1.5 -GLUSTER_SITE = https://download.gluster.org/pub/gluster/glusterfs/4.1/$(GLUSTER_VERSION) +# Official gluster site has SSL problems +# https://bugzilla.redhat.com/show_bug.cgi?id=1572944 +# GLUSTER_SITE = https://download.gluster.org/pub/gluster/glusterfs/4.1/$(GLUSTER_VERSION) +GLUSTER_SITE = http://download.openpkg.org/components/cache/glusterfs GLUSTER_SOURCE = glusterfs-$(GLUSTER_VERSION).tar.gz GLUSTER_CONF_OPTS = --disable-tiering --disable-ec-dynamic --disable-xmltest --disable-crypt-xlator --disable-georeplication --disable-ibverbs --disable-glupy --disable-gnfs --disable-cmocka --without-server GLUSTER_INSTALL_TARGET_OPTS = DESTDIR=$(TARGET_DIR) install diff --git a/go.mod b/go.mod index 80bd0d2055..ebb2c83a49 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,6 @@ require ( github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/google/go-cmp v0.3.0 - github.com/google/go-github/v25 v25.0.2 github.com/gorilla/mux v1.7.1 // indirect github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce // indirect github.com/hashicorp/go-getter v1.3.0 @@ -50,6 +49,7 @@ require ( github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936 github.com/moby/hyperkit v0.0.0-20171020124204-a12cd7250bcd github.com/olekukonko/tablewriter v0.0.0-20160923125401-bdcc175572fd + github.com/otiai10/copy v1.0.2 github.com/pborman/uuid v1.2.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/pkg/browser v0.0.0-20160118053552-9302be274faa @@ -69,13 +69,13 @@ require ( github.com/zchee/go-vmnet v0.0.0-20161021174912-97ebf9174097 golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 - golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a golang.org/x/sync v0.0.0-20190423024810-112230192c58 golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb golang.org/x/text v0.3.2 gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect + gotest.tools v2.2.0+incompatible k8s.io/api v0.0.0 k8s.io/apimachinery v0.0.0 k8s.io/client-go v0.0.0 diff --git a/go.sum b/go.sum index cae562ed96..03c9eb6c9c 100644 --- a/go.sum +++ b/go.sum @@ -195,8 +195,6 @@ github.com/google/go-containerregistry v0.0.0-20180731221751-697ee0b3d46e h1:Hm0 github.com/google/go-containerregistry v0.0.0-20180731221751-697ee0b3d46e/go.mod h1:yZAFP63pRshzrEYLXLGPmUt0Ay+2zdjmMN1loCnRLUk= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-github/v25 v25.0.2 h1:MqXE7nOlIF91NJ/PXAcvS2dC+XXCDbY7RvJzjyEPAoU= -github.com/google/go-github/v25 v25.0.2/go.mod h1:6z5pC69qHtrPJ0sXPsj4BLnd82b+r6sLB7qcBoRZqpw= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= @@ -383,6 +381,11 @@ github.com/opencontainers/runc v0.0.0-20181113202123-f000fe11ece1/go.mod h1:qT5X github.com/opencontainers/runtime-spec v1.0.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v0.0.0-20170621221121-4a2974bf1ee9/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc= +github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -558,7 +561,6 @@ golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5Tlb golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/hack/.gitignore b/hack/.gitignore new file mode 100644 index 0000000000..6168f032c4 --- /dev/null +++ b/hack/.gitignore @@ -0,0 +1 @@ +release-notes diff --git a/hack/jenkins/common.sh b/hack/jenkins/common.sh index 96a6658b37..ccd9216160 100755 --- a/hack/jenkins/common.sh +++ b/hack/jenkins/common.sh @@ -37,10 +37,26 @@ echo "job: ${JOB_NAME}" echo "test home: ${TEST_HOME}" echo "sudo: ${SUDO_PREFIX}" echo "kernel: $(uname -v)" +echo "uptime: $(uptime)" # Setting KUBECONFIG prevents the version ceck from erroring out due to permission issues echo "kubectl: $(env KUBECONFIG=${TEST_HOME} kubectl version --client --short=true)" echo "docker: $(docker version --format '{{ .Client.Version }}')" +readonly LOAD=$(uptime | egrep -o "load average.*: [0-9]" | cut -d" " -f3) +if [[ "${LOAD}" -gt 2 ]]; then + echo "" + echo "********************** LOAD WARNING ********************************" + echo "Load average is very high (${LOAD}), which may cause failures. Top:" + if [[ "$(uname)" == "Darwin" ]]; then + # Two samples, macOS does not calculate CPU usage on the first one + top -l 2 -o cpu -n 5 | tail -n 15 + else + top -b -n1 | head -n 15 + fi + echo "********************** LOAD WARNING ********************************" + echo "" +fi + case "${VM_DRIVER}" in kvm2) echo "virsh: $(virsh --version)" @@ -99,29 +115,29 @@ mkdir -p "${TEST_ROOT}" echo "" echo ">> Cleaning up after previous test runs ..." for entry in $(ls ${TEST_ROOT}); do - echo "* Cleaning stale test path: ${entry}" - for tunnel in $(find ${entry} -name tunnels.json -type f); do + test_path="${TEST_ROOT}/${entry}" + ls -lad "${test_path}" || continue + + echo "* Cleaning stale test path: ${test_path}" + for tunnel in $(find ${test_path} -name tunnels.json -type f); do env MINIKUBE_HOME="$(dirname ${tunnel})" ${MINIKUBE_BIN} tunnel --cleanup || true done - for home in $(find ${entry} -name .minikube -type d); do - env MINIKUBE_HOME="$(dirname ${home})" ${MINIKUBE_BIN} delete || true + for home in $(find ${test_path} -name .minikube -type d); do + env MINIKUBE_HOME="$(dirname ${home})" ${MINIKUBE_BIN} delete --all || true sudo rm -Rf "${home}" done - ${MINIKUBE_BIN} delete --all || true - - for kconfig in $(find ${entry} -name kubeconfig -type f); do + for kconfig in $(find ${test_path} -name kubeconfig -type f); do sudo rm -f "${kconfig}" done # Be very specific to avoid accidentally deleting other items, like wildcards or devices - if [[ -d "${entry}" ]]; then - rm -Rf "${entry}" || true - elif [[ -f "${entry}" ]]; then - rm -f "${entry}" || true + if [[ -d "${test_path}" ]]; then + rm -Rf "${test_path}" || true + elif [[ -f "${test_path}" ]]; then + rm -f "${test_path}" || true fi - done # sometimes tests left over zombie procs that won't exit @@ -143,19 +159,21 @@ if type -P virsh; then fi if type -P vboxmanage; then - for guid in $(vboxmanage list vms | egrep -Eo '\{[-a-Z0-9]+\}'); do + for guid in $(vboxmanage list vms | grep -Eo '\{[a-zA-Z0-9-]+\}'); do echo "- Removing stale VirtualBox VM: $guid" - vboxmanage startvm $guid --type emergencystop || true - vboxmanage unregistervm $guid || true + vboxmanage startvm "${guid}" --type emergencystop || true + vboxmanage unregistervm "${guid}" || true done - vboxmanage list hostonlyifs \ - | grep "^Name:" \ - | awk '{ print $2 }' \ - | xargs -n1 vboxmanage hostonlyif remove || true + ifaces=$(vboxmanage list hostonlyifs | grep -E "^Name:" | awk '{ printf $2 }') + for if in $ifaces; do + vboxmanage hostonlyif remove "${if}" || true + done echo ">> VirtualBox VM list after clean up (should be empty):" vboxmanage list vms || true + echo ">> VirtualBox interface list after clean up (should be empty):" + vboxmanage list hostonlyifs || true fi diff --git a/hack/release_notes.sh b/hack/release_notes.sh index 0cc0733243..ca0acc9fc4 100755 --- a/hack/release_notes.sh +++ b/hack/release_notes.sh @@ -18,7 +18,23 @@ set -eux -o pipefail DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -go run "${DIR}/release_notes/listpullreqs.go" +install_release_notes_helper() { + release_notes_workdir="$(mktemp -d)" + trap 'rm -rf -- ${release_notes_workdir}' RETURN + + # See https://stackoverflow.com/questions/56842385/using-go-get-to-download-binaries-without-adding-them-to-go-mod for this workaround + cd "${release_notes_workdir}" + go mod init release-notes + GOBIN="$DIR" go get github.com/corneliusweig/release-notes + cd - +} + +if ! [[ -x "${DIR}/release-notes" ]]; then + echo >&2 'Installing release-notes' + install_release_notes_helper +fi + +"${DIR}/release-notes" kubernetes minikube echo "Huge thank you for this release towards our contributors: " git log "$(git describe --abbrev=0)".. --format="%aN" --reverse | sort | uniq | awk '{printf "- %s\n", $0 }' diff --git a/hack/release_notes/listpullreqs.go b/hack/release_notes/listpullreqs.go deleted file mode 100644 index 64ea8fc3d9..0000000000 --- a/hack/release_notes/listpullreqs.go +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// listpullreqs.go lists pull requests since the last release. -package main - -import ( - "context" - "fmt" - - "github.com/golang/glog" - "github.com/google/go-github/v25/github" - "github.com/spf13/cobra" - "golang.org/x/oauth2" -) - -var ( - token string - fromTag string - toTag string -) - -var rootCmd = &cobra.Command{ - Use: "listpullreqs fromTag toTag", - Short: "Lists pull requests between two versions in our changelog markdown format", - ArgAliases: []string{"fromTag", "toTag"}, - Run: func(cmd *cobra.Command, args []string) { - printPullRequests() - }, -} - -const org = "kubernetes" -const repo = "minikube" - -func main() { - rootCmd.Flags().StringVar(&token, "token", "", "Specify personal Github Token if you are hitting a rate limit anonymously. https://github.com/settings/tokens") - rootCmd.Flags().StringVar(&fromTag, "fromTag", "", "comparison of commits is based on this tag (defaults to the latest tag in the repo)") - rootCmd.Flags().StringVar(&toTag, "toTag", "master", "this is the commit that is compared with fromTag") - if err := rootCmd.Execute(); err != nil { - glog.Fatalf("Failed to execute: %v", err) - } -} - -func printPullRequests() { - client := getClient() - - releases, _, err := client.Repositories.ListReleases(context.Background(), org, repo, &github.ListOptions{}) - if err != nil { - glog.Fatalf("Failed to list releases: %v", err) - } - lastReleaseTime := *releases[0].PublishedAt - fmt.Println(fmt.Sprintf("Collecting pull request that were merged since the last release: %s (%s)", *releases[0].TagName, lastReleaseTime)) - - listSize := 1 - for page := 1; listSize > 0; page++ { - pullRequests, _, err := client.PullRequests.List(context.Background(), org, repo, &github.PullRequestListOptions{ - State: "closed", - Sort: "updated", - Direction: "desc", - ListOptions: github.ListOptions{ - PerPage: 100, - Page: page, - }, - }) - if err != nil { - glog.Fatalf("Failed to list pull requests: %v", err) - } - - seen := 0 - for idx := range pullRequests { - pr := pullRequests[idx] - if pr.MergedAt != nil { - if pr.GetMergedAt().After(lastReleaseTime.Time) { - fmt.Printf("* %s [#%d](https://github.com/%s/%s/pull/%d)\n", pr.GetTitle(), *pr.Number, org, repo, *pr.Number) - seen++ - } - } - } - if seen == 0 { - break - } - listSize = len(pullRequests) - } -} - -func getClient() *github.Client { - if len(token) == 0 { - return github.NewClient(nil) - } - ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - tc := oauth2.NewClient(ctx, ts) - return github.NewClient(tc) -} diff --git a/installers/linux/deb/minikube_deb_template/DEBIAN/control b/installers/linux/deb/minikube_deb_template/DEBIAN/control index 98a93a25b8..ad9d368ba0 100644 --- a/installers/linux/deb/minikube_deb_template/DEBIAN/control +++ b/installers/linux/deb/minikube_deb_template/DEBIAN/control @@ -2,7 +2,7 @@ Package: minikube Version: --VERSION-- Section: base Priority: optional -Architecture: amd64 +Architecture: --ARCH-- Recommends: virtualbox Maintainer: Thomas Strömberg Description: Minikube diff --git a/installers/linux/rpm/minikube_rpm_template/minikube.spec b/installers/linux/rpm/minikube_rpm_template/minikube.spec index 16adf43729..01868349ed 100644 --- a/installers/linux/rpm/minikube_rpm_template/minikube.spec +++ b/installers/linux/rpm/minikube_rpm_template/minikube.spec @@ -18,12 +18,12 @@ day-to-day. %prep mkdir -p %{name}-%{version} cd %{name}-%{version} -cp --OUT--/minikube-linux-amd64 . +cp --OUT--/minikube-linux-%{_arch} minikube %install cd %{name}-%{version} mkdir -p %{buildroot}%{_bindir} -install -m 755 minikube-linux-amd64 %{buildroot}%{_bindir}/%{name} +install -m 755 minikube %{buildroot}%{_bindir}/%{name} %files %{_bindir}/%{name} diff --git a/pkg/drivers/drivers.go b/pkg/drivers/drivers.go index da7edaf2d8..1a18a2a8eb 100644 --- a/pkg/drivers/drivers.go +++ b/pkg/drivers/drivers.go @@ -149,14 +149,15 @@ func fixMachinePermissions(path string) error { } // InstallOrUpdate downloads driver if it is not present, or updates it if there's a newer version -func InstallOrUpdate(driver string, directory string, v semver.Version, interactive bool) error { +func InstallOrUpdate(driver string, directory string, v semver.Version, interactive bool, autoUpdate bool) error { if driver != constants.DriverKvm2 && driver != constants.DriverHyperkit { return nil } executable := fmt.Sprintf("docker-machine-driver-%s", driver) + exists := driverExists(executable) path, err := validateDriver(executable, v) - if err != nil { + if !exists || (err != nil && autoUpdate) { glog.Warningf("%s: %v", executable, err) path = filepath.Join(directory, executable) derr := download(executable, path, v) @@ -240,6 +241,11 @@ func validateDriver(driver string, v semver.Version) (string, error) { return path, nil } +func driverExists(driver string) bool { + _, err := exec.LookPath(driver) + return err == nil +} + func driverWithChecksumURL(driver string, v semver.Version) string { base := fmt.Sprintf("https://github.com/kubernetes/minikube/releases/download/v%s/%s", v, driver) return fmt.Sprintf("%s?checksum=file:%s.sha256", base, base) diff --git a/pkg/drivers/kvm/network_test.go b/pkg/drivers/kvm/network_test.go index 985ac7f26e..905816165a 100644 --- a/pkg/drivers/kvm/network_test.go +++ b/pkg/drivers/kvm/network_test.go @@ -46,7 +46,7 @@ var ( ]`) ) -func Test_parseStatusAndReturnIp(t *testing.T) { +func TestParseStatusAndReturnIp(t *testing.T) { type args struct { mac string statuses []byte diff --git a/pkg/minikube/assets/addons.go b/pkg/minikube/assets/addons.go index 18cfd22251..82bc8c1bd0 100644 --- a/pkg/minikube/assets/addons.go +++ b/pkg/minikube/assets/addons.go @@ -420,7 +420,7 @@ func GenerateTemplateData(cfg config.KubernetesConfig) interface{} { // for less common architectures blank suffix for amd64 ea := "" if runtime.GOARCH != "amd64" { - ea = runtime.GOARCH + ea = "-" + runtime.GOARCH } opts := struct { Arch string diff --git a/pkg/minikube/bootstrapper/images/images.go b/pkg/minikube/bootstrapper/images/images.go index 8d4d723ca8..b69d1160bd 100644 --- a/pkg/minikube/bootstrapper/images/images.go +++ b/pkg/minikube/bootstrapper/images/images.go @@ -25,10 +25,17 @@ import ( minikubeVersion "k8s.io/minikube/pkg/version" ) +const ( + // DefaultImageRepo is the default repository for images + DefaultImageRepo = "k8s.gcr.io" + // DefaultMinikubeRepo is the default repository for minikube + DefaultMinikubeRepo = "gcr.io/k8s-minikube" +) + // getImageRepositories returns either the k8s image registry on GCR or a mirror if specified func getImageRepository(imageRepository string) string { if imageRepository == "" { - imageRepository = "k8s.gcr.io" + imageRepository = DefaultImageRepo } if !strings.HasSuffix(imageRepository, "/") { imageRepository += "/" @@ -41,7 +48,7 @@ func getImageRepository(imageRepository string) string { func getMinikubeRepository(imageRepository string) string { minikubeRepository := imageRepository if minikubeRepository == "" { - minikubeRepository = "gcr.io/k8s-minikube" + minikubeRepository = DefaultMinikubeRepo } if !strings.HasSuffix(minikubeRepository, "/") { minikubeRepository += "/" diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index 96f8e45b7a..4f3e3bf99d 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -278,8 +278,10 @@ func DeleteHost(api libmachine.API) error { // Get the status of the host. Ensure that it exists before proceeding ahead. status, err := GetHostStatus(api) if err != nil { - exit.WithCodeT(exit.Failure, "Unable to get the status of the cluster.") + // Warn, but proceed + out.WarningT("Unable to get the status of the {{.name}} cluster.", out.V{"name": cfg.GetMachineName()}) } + if status == state.None.String() { return mcnerror.ErrHostDoesNotExist{Name: host.Name} } @@ -289,6 +291,7 @@ func DeleteHost(api libmachine.API) error { if err := trySSHPowerOff(host); err != nil { glog.Infof("Unable to power off minikube because the host was not found.") } + out.T(out.DeletingHost, "Successfully powered off Hyper-V. minikube driver -- {{.driver}}", out.V{"driver": host.Driver.DriverName()}) } out.T(out.DeletingHost, `Deleting "{{.profile_name}}" in {{.driver_name}} ...`, out.V{"profile_name": cfg.GetMachineName(), "driver_name": host.DriverName}) @@ -602,14 +605,15 @@ func CreateSSHShell(api libmachine.API, args []string) error { return client.Shell(args...) } -// EnsureMinikubeRunningOrExit checks that minikube has a status available and that -// the status is `Running`, otherwise it will exit -func EnsureMinikubeRunningOrExit(api libmachine.API, exitStatus int) { +// IsMinikubeRunning checks that minikube has a status available and that +// the status is `Running` +func IsMinikubeRunning(api libmachine.API) bool { s, err := GetHostStatus(api) if err != nil { - exit.WithError("Error getting machine status", err) + return false } if s != state.Running.String() { - exit.WithCodeT(exit.Unavailable, "minikube is not running, so the service cannot be accessed") + return false } + return true } diff --git a/pkg/minikube/cluster/machine.go b/pkg/minikube/cluster/machine.go index 8a24b04e3c..bb8e880389 100644 --- a/pkg/minikube/cluster/machine.go +++ b/pkg/minikube/cluster/machine.go @@ -26,6 +26,7 @@ import ( "k8s.io/minikube/pkg/minikube/machine" ) +// Machine contains information about a machine type Machine struct { *host.Host } @@ -58,7 +59,7 @@ func (h *Machine) IsValid() bool { return true } -// ListsMachines return all valid and invalid machines +// ListMachines return all valid and invalid machines // If a machine is valid or invalid is determined by the cluster.IsValid function func ListMachines(miniHome ...string) (validMachines []*Machine, inValidMachines []*Machine, err error) { pDirs, err := machineDirs(miniHome...) @@ -80,7 +81,7 @@ func ListMachines(miniHome ...string) (validMachines []*Machine, inValidMachines return validMachines, inValidMachines, nil } -// Loads a machine or throws an error if the machine could not be loadedG +// LoadMachine loads a machine or throws an error if the machine could not be loadedG func LoadMachine(name string) (*Machine, error) { api, err := machine.NewAPIClient() if err != nil { diff --git a/pkg/minikube/config/config_test.go b/pkg/minikube/config/config_test.go index b2671628df..20334aaa54 100644 --- a/pkg/minikube/config/config_test.go +++ b/pkg/minikube/config/config_test.go @@ -174,7 +174,7 @@ func TestWriteConfig(t *testing.T) { } } -func Test_encode(t *testing.T) { +func TestEncode(t *testing.T) { var b bytes.Buffer for _, tt := range configTestCases { err := encode(&b, tt.config) diff --git a/pkg/minikube/config/profile.go b/pkg/minikube/config/profile.go index 2c09f4b446..55b933bc34 100644 --- a/pkg/minikube/config/profile.go +++ b/pkg/minikube/config/profile.go @@ -21,18 +21,20 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "github.com/golang/glog" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/util/lock" ) +var keywords = []string{"start", "stop", "status", "delete", "config", "open", "profile", "addons", "cache", "logs"} + // IsValid checks if the profile has the essential info needed for a profile func (p *Profile) IsValid() bool { if p.Config == nil { return false } - if p.Config.MachineConfig.VMDriver == "" { return false } @@ -42,6 +44,16 @@ func (p *Profile) IsValid() bool { return true } +// check if the profile is an internal keywords +func ProfileNameInReservedKeywords(name string) bool { + for _, v := range keywords { + if strings.EqualFold(v, name) { + return true + } + } + return false +} + // ProfileExists returns true if there is a profile config (regardless of being valid) func ProfileExists(name string, miniHome ...string) bool { miniPath := localpath.MiniPath() @@ -136,7 +148,7 @@ func ListProfiles(miniHome ...string) (validPs []*Profile, inValidPs []*Profile, return validPs, inValidPs, nil } -// loadProfile loads type Profile based on its name +// LoadProfile loads type Profile based on its name func LoadProfile(name string, miniHome ...string) (*Profile, error) { cfg, err := DefaultLoader.LoadConfigFromFile(name, miniHome...) p := &Profile{ diff --git a/pkg/minikube/config/profile_test.go b/pkg/minikube/config/profile_test.go index 2cd674a8ef..8852ea9b6f 100644 --- a/pkg/minikube/config/profile_test.go +++ b/pkg/minikube/config/profile_test.go @@ -72,6 +72,32 @@ func TestListProfiles(t *testing.T) { } } +func TestProfileNameInReservedKeywords(t *testing.T) { + var testCases = []struct { + name string + expected bool + }{ + {"start", true}, + {"stop", true}, + {"status", true}, + {"delete", true}, + {"config", true}, + {"open", true}, + {"profile", true}, + {"addons", true}, + {"cache", true}, + {"logs", true}, + {"myprofile", false}, + {"log", false}, + } + for _, tt := range testCases { + got := ProfileNameInReservedKeywords(tt.name) + if got != tt.expected { + t.Errorf("expected ProfileNameInReservedKeywords(%s)=%t but got %t ", tt.name, tt.expected, got) + } + } +} + func TestProfileExists(t *testing.T) { miniDir, err := filepath.Abs("./testdata/.minikube2") if err != nil { diff --git a/pkg/minikube/drivers/hyperkit/driver.go b/pkg/minikube/drivers/hyperkit/driver.go index 0db642f25b..fe44da80ea 100644 --- a/pkg/minikube/drivers/hyperkit/driver.go +++ b/pkg/minikube/drivers/hyperkit/driver.go @@ -61,6 +61,6 @@ func createHyperkitHost(config cfg.MachineConfig) interface{} { UUID: uuID, VpnKitSock: config.HyperkitVpnKitSock, VSockPorts: config.HyperkitVSockPorts, - Cmdline: "loglevel=3 user=docker console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes base host=" + cfg.GetMachineName(), + Cmdline: "loglevel=3 console=ttyS0 console=tty0 noembed nomodeset norestore waitusb=10 systemd.legacy_systemd_cgroup_controller=yes random.trust_cpu=on hw_rng_model=virtio base host=" + cfg.GetMachineName(), } } diff --git a/pkg/minikube/drivers/hyperv/driver.go b/pkg/minikube/drivers/hyperv/driver.go index 00b7543351..8070737a3c 100644 --- a/pkg/minikube/drivers/hyperv/driver.go +++ b/pkg/minikube/drivers/hyperv/driver.go @@ -28,7 +28,7 @@ import ( ) func init() { - registry.Register(registry.DriverDef{ + _ = registry.Register(registry.DriverDef{ Name: constants.DriverHyperv, Builtin: true, ConfigCreator: createHypervHost, @@ -45,7 +45,7 @@ func createHypervHost(config cfg.MachineConfig) interface{} { d.VSwitch = config.HypervVirtualSwitch d.MemSize = config.Memory d.CPU = config.CPUs - d.DiskSize = int(config.DiskSize) + d.DiskSize = config.DiskSize d.SSHUser = "docker" d.DisableDynamicMemory = true // default to disable dynamic memory as minikube is unlikely to work properly with dynamic memory diff --git a/pkg/minikube/machine/cache_binaries_test.go b/pkg/minikube/machine/cache_binaries_test.go new file mode 100644 index 0000000000..19bbb1fa2e --- /dev/null +++ b/pkg/minikube/machine/cache_binaries_test.go @@ -0,0 +1,203 @@ +/* +Copyright 2019 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package machine + +import ( + "fmt" + "io/ioutil" + "os" + "runtime" + "testing" + + "k8s.io/minikube/pkg/minikube/assets" + "k8s.io/minikube/pkg/minikube/bootstrapper" + "k8s.io/minikube/pkg/minikube/command" +) + +type copyFailRunner struct { + command.Runner +} + +func (copyFailRunner) Copy(a assets.CopyableFile) error { + return fmt.Errorf("test error during copy file") +} + +func newFakeCommandRunnerCopyFail() command.Runner { + return copyFailRunner{command.NewFakeCommandRunner()} +} + +func TestCopyBinary(t *testing.T) { + var tc = []struct { + lastUpdateCheckFilePath string + src, dst, desc string + err bool + runner command.Runner + }{ + { + desc: "not existing src", + dst: "/tmp/testCopyBinary1", + src: "/tmp/testCopyBinary2", + err: true, + runner: command.NewFakeCommandRunner(), + }, + { + desc: "src /etc/hosts", + dst: "/tmp/testCopyBinary1", + src: "/etc/hosts", + err: false, + runner: command.NewFakeCommandRunner(), + }, + { + desc: "existing src, copy fail", + dst: "/etc/passwd", + src: "/etc/hosts", + err: true, + runner: newFakeCommandRunnerCopyFail(), + }, + } + for _, test := range tc { + t.Run(test.desc, func(t *testing.T) { + err := CopyBinary(test.runner, test.src, test.dst) + if err != nil && !test.err { + t.Fatalf("Error %v expected but not occurred", err) + } + if err == nil && test.err { + t.Fatal("Unexpected error") + } + }) + } +} + +func TestCacheBinariesForBootstrapper(t *testing.T) { + var tc = []struct { + version, clusterBootstrapper string + err bool + }{ + { + version: "v1.16.0", + clusterBootstrapper: bootstrapper.BootstrapperTypeKubeadm, + }, + { + version: "invalid version", + clusterBootstrapper: bootstrapper.BootstrapperTypeKubeadm, + err: true, + }, + } + for _, test := range tc { + t.Run(test.version, func(t *testing.T) { + err := CacheBinariesForBootstrapper(test.version, test.clusterBootstrapper) + if err != nil && !test.err { + t.Fatalf("Got unexpected error %v", err) + } + if err == nil && test.err { + t.Fatalf("Expected error but got %v", err) + } + }) + } +} +func TestCacheBinary(t *testing.T) { + oldMinikubeHome := os.Getenv("MINIKUBE_HOME") + defer os.Setenv("MINIKUBE_HOME", oldMinikubeHome) + + minikubeHome, err := ioutil.TempDir("/tmp", "") + if err != nil { + t.Fatalf("error during creating tmp dir: %v", err) + } + defer os.RemoveAll(minikubeHome) + noWritePermDir, err := ioutil.TempDir("/tmp", "") + if err != nil { + t.Fatalf("error during creating tmp dir: %v", err) + } + defer os.RemoveAll(noWritePermDir) + err = os.Chmod(noWritePermDir, 0000) + if err != nil { + t.Fatalf("error (%v) during changing permissions of dir %v", err, noWritePermDir) + } + + var tc = []struct { + desc, version, osName, archName string + minikubeHome, binary, description string + err bool + }{ + { + desc: "ok kubeadm", + version: "v1.16.0", + osName: "linux", + archName: runtime.GOARCH, + binary: "kubeadm", + err: false, + minikubeHome: minikubeHome, + }, + { + desc: "minikube home in dir without perms and arm runtime", + version: "v1.16.0", + osName: runtime.GOOS, + archName: "arm", + binary: "kubectl", + err: true, + minikubeHome: noWritePermDir, + }, + { + desc: "minikube home in dir without perms", + version: "v1.16.0", + osName: runtime.GOOS, + archName: runtime.GOARCH, + binary: "kubectl", + err: true, + minikubeHome: noWritePermDir, + }, + { + desc: "binary foo", + version: "v1.16.0", + osName: runtime.GOOS, + archName: runtime.GOARCH, + binary: "foo", + err: true, + minikubeHome: minikubeHome, + }, + { + desc: "version 9000", + version: "v9000", + osName: runtime.GOOS, + archName: runtime.GOARCH, + binary: "foo", + err: true, + minikubeHome: minikubeHome, + }, + { + desc: "bad os", + version: "v1.16.0", + osName: "no-such-os", + archName: runtime.GOARCH, + binary: "kubectl", + err: true, + minikubeHome: minikubeHome, + }, + } + for _, test := range tc { + t.Run(test.desc, func(t *testing.T) { + os.Setenv("MINIKUBE_HOME", test.minikubeHome) + _, err := CacheBinary(test.binary, test.version, test.osName, test.archName) + if err != nil && !test.err { + t.Fatalf("Got unexpected error %v", err) + } + if err == nil && test.err { + t.Fatalf("Expected error but got %v", err) + } + }) + } +} diff --git a/pkg/minikube/service/service.go b/pkg/minikube/service/service.go index 710ca0ebfe..58a7264731 100644 --- a/pkg/minikube/service/service.go +++ b/pkg/minikube/service/service.go @@ -275,7 +275,7 @@ func WaitAndMaybeOpenService(api libmachine.API, namespace string, service strin chkSVC := func() error { return CheckService(namespace, service) } if err := retry.Expo(chkSVC, time.Duration(interval)*time.Second, time.Duration(wait)*time.Second); err != nil { - return errors.Wrapf(err, "Could not find finalized endpoint being pointed to by %s", service) + return errors.Wrapf(err, "Service %s was not found in %q namespace. You may select another namespace by using 'minikube service %s -n ", service, namespace, service) } serviceURL, err := GetServiceURLsForService(api, namespace, service, urlTemplate) diff --git a/pkg/minikube/service/service_test.go b/pkg/minikube/service/service_test.go index 20b9f30fc0..0d4d13fe57 100644 --- a/pkg/minikube/service/service_test.go +++ b/pkg/minikube/service/service_test.go @@ -97,6 +97,27 @@ var serviceNamespaces = map[string]typed_core.ServiceInterface{ "default": defaultNamespaceServiceInterface, } +var serviceNamespaceOther = map[string]typed_core.ServiceInterface{ + "default": nondefaultNamespaceServiceInterface, +} + +var nondefaultNamespaceServiceInterface = &MockServiceInterface{ + ServiceList: &core.ServiceList{ + Items: []core.Service{ + { + ObjectMeta: meta.ObjectMeta{ + Name: "non-namespace-dashboard-no-ports", + Namespace: "cannot_be_found_namespace", + Labels: map[string]string{"mock": "mock"}, + }, + Spec: core.ServiceSpec{ + Ports: []core.ServicePort{}, + }, + }, + }, + }, +} + var defaultNamespaceServiceInterface = &MockServiceInterface{ ServiceList: &core.ServiceList{ Items: []core.Service{ @@ -893,3 +914,54 @@ func TestWaitAndMaybeOpenService(t *testing.T) { }) } } + +func TestWaitAndMaybeOpenServiceForNotDefaultNamspace(t *testing.T) { + defaultAPI := &tests.MockAPI{ + FakeStore: tests.FakeStore{ + Hosts: map[string]*host.Host{ + config.GetMachineName(): { + Name: config.GetMachineName(), + Driver: &tests.MockDriver{}, + }, + }, + }, + } + defaultTemplate := template.Must(template.New("svc-template").Parse("http://{{.IP}}:{{.Port}}")) + + var tests = []struct { + description string + api libmachine.API + namespace string + service string + expected []string + urlMode bool + https bool + err bool + }{ + { + description: "correctly return empty serviceURLs", + namespace: "default", + service: "non-namespace-dashboard-no-ports", + api: defaultAPI, + expected: []string{}, + err: true, + }, + } + defer revertK8sClient(K8s) + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + K8s = &MockClientGetter{ + servicesMap: serviceNamespaceOther, + endpointsMap: endpointNamespaces, + } + err := WaitAndMaybeOpenService(test.api, test.namespace, test.service, defaultTemplate, test.urlMode, test.https, 1, 0) + if test.err && err == nil { + t.Fatalf("WaitAndMaybeOpenService expected to fail for test: %v", test) + } + if !test.err && err != nil { + t.Fatalf("WaitAndMaybeOpenService not expected to fail but got err: %v", err) + } + + }) + } +} diff --git a/pkg/minikube/translate/translate_test.go b/pkg/minikube/translate/translate_test.go index 00c4411810..fd7a73cf64 100644 --- a/pkg/minikube/translate/translate_test.go +++ b/pkg/minikube/translate/translate_test.go @@ -51,3 +51,53 @@ func TestSetPreferredLanguage(t *testing.T) { } } + +func TestT(t *testing.T) { + var tests = []struct { + description, input, expected string + langDef, langPref language.Tag + translations map[string]interface{} + }{ + { + description: "empty string not default language", + input: "", + expected: "", + langPref: language.English, + langDef: language.Lithuanian, + }, + { + description: "empty string and default language", + input: "", + expected: "", + langPref: language.English, + langDef: language.English, + }, + { + description: "existing translation", + input: "cat", + expected: "kot", + langPref: language.Lithuanian, + langDef: language.English, + translations: map[string]interface{}{"cat": "kot"}, + }, + { + description: "not existing translation", + input: "cat", + expected: "cat", + langPref: language.Lithuanian, + langDef: language.English, + translations: map[string]interface{}{"dog": "pies"}, + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + defaultLanguage = test.langDef + preferredLanguage = test.langPref + Translations = test.translations + got := T(test.input) + if test.expected != got { + t.Errorf("T(%v) shoud return %v, but got: %v", test.input, test.expected, got) + } + }) + } +} diff --git a/pkg/minikube/tunnel/route_windows.go b/pkg/minikube/tunnel/route_windows.go index 757074f190..0d561b3c1c 100644 --- a/pkg/minikube/tunnel/route_windows.go +++ b/pkg/minikube/tunnel/route_windows.go @@ -93,7 +93,7 @@ func (router *osRouter) parseTable(table []byte) routingTable { }, line: line, } - glog.V(4).Infof("adding line %s", tableLine) + glog.V(4).Infof("adding line %v", tableLine) t = append(t, tableLine) } } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index c549ca4ebe..ec2d8f6faf 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -34,13 +34,13 @@ import ( "k8s.io/minikube/pkg/minikube/out" ) -// ErrPrefix notes an error -const ErrPrefix = "! " - -// OutPrefix notes output -const OutPrefix = "> " - const ( + // ErrPrefix notes an error + ErrPrefix = "! " + + // OutPrefix notes output + OutPrefix = "> " + downloadURL = "https://storage.googleapis.com/minikube/releases/%s/minikube-%s-amd64%s" ) diff --git a/site/config.toml b/site/config.toml index 85c597f291..e49df4aedd 100644 --- a/site/config.toml +++ b/site/config.toml @@ -115,6 +115,11 @@ no = 'Sorry to hear that. Please