Overhaul how we handle kubectl, as it isn't actually required except for the dashboard

pull/3680/head
Thomas Stromberg 2019-02-14 17:10:03 -08:00
parent 35449f685e
commit 71eafef5ce
7 changed files with 21 additions and 164 deletions

View File

@ -52,6 +52,11 @@ var dashboardCmd = &cobra.Command{
Short: "Access the kubernetes dashboard running within the minikube cluster", Short: "Access the kubernetes dashboard running within the minikube cluster",
Long: `Access the kubernetes dashboard running within the minikube cluster`, Long: `Access the kubernetes dashboard running within the minikube cluster`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
kubectl, err := exec.LookPath("kubectl")
if err != nil {
exit.WithCode(exit.NoInput, "kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/")
}
api, err := machine.NewAPIClient() api, err := machine.NewAPIClient()
defer func() { defer func() {
err := api.Close() err := api.Close()
@ -81,7 +86,7 @@ var dashboardCmd = &cobra.Command{
} }
console.ErrStyle("launch", "Launching proxy ...") console.ErrStyle("launch", "Launching proxy ...")
p, hostPort, err := kubectlProxy() p, hostPort, err := kubectlProxy(kubectl)
if err != nil { if err != nil {
exit.WithError("kubectl proxy", err) exit.WithError("kubectl proxy", err)
} }
@ -109,12 +114,7 @@ var dashboardCmd = &cobra.Command{
} }
// kubectlProxy runs "kubectl proxy", returning host:port // kubectlProxy runs "kubectl proxy", returning host:port
func kubectlProxy() (*exec.Cmd, string, error) { func kubectlProxy(path string) (*exec.Cmd, string, error) {
path, err := exec.LookPath("kubectl")
if err != nil {
return nil, "", errors.Wrap(err, "kubectl not found in PATH")
}
// port=0 picks a random system port // port=0 picks a random system port
// config.GetMachineName() respects the -p (profile) flag // config.GetMachineName() respects the -p (profile) flag
cmd := exec.Command(path, "--context", config.GetMachineName(), "proxy", "--port=0") cmd := exec.Command(path, "--context", config.GetMachineName(), "proxy", "--port=0")

View File

@ -21,7 +21,6 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"runtime"
"strings" "strings"
"github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine"
@ -32,7 +31,6 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/spf13/viper" "github.com/spf13/viper"
configCmd "k8s.io/minikube/cmd/minikube/cmd/config" configCmd "k8s.io/minikube/cmd/minikube/cmd/config"
"k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/bootstrapper" "k8s.io/minikube/pkg/minikube/bootstrapper"
"k8s.io/minikube/pkg/minikube/bootstrapper/kubeadm" "k8s.io/minikube/pkg/minikube/bootstrapper/kubeadm"
"k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/config"
@ -94,7 +92,6 @@ var RootCmd = &cobra.Command{
if enableUpdateNotification { if enableUpdateNotification {
notify.MaybePrintUpdateTextFromGithub(os.Stderr) notify.MaybePrintUpdateTextFromGithub(os.Stderr)
} }
util.MaybePrintKubectlDownloadMsg(runtime.GOOS, os.Stderr)
}, },
} }

View File

@ -211,6 +211,10 @@ func runStart(cmd *cobra.Command, args []string) {
} else { } else {
console.OutStyle("kubectl", "kubectl is now configured to use %q", cfg.GetMachineName()) console.OutStyle("kubectl", "kubectl is now configured to use %q", cfg.GetMachineName())
} }
_, err = exec.LookPath("kubectl")
if err != nil {
console.OutStyle("tip", "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/")
}
console.OutStyle("ready", "Done! Thank you for using minikube!") console.OutStyle("ready", "Done! Thank you for using minikube!")
} }
@ -305,17 +309,21 @@ func prepareNone() {
if viper.GetBool(cfg.WantNoneDriverWarning) { if viper.GetBool(cfg.WantNoneDriverWarning) {
console.OutLn("") console.OutLn("")
console.Warning("The 'none' driver provides limited isolation and may reduce system security and reliability.") console.Warning("The 'none' driver provides limited isolation and may reduce system security and reliability.")
console.Warning("For more information, see:")
console.OutStyle("url", "https://github.com/kubernetes/minikube/blob/master/docs/vmdriver-none.md") console.OutStyle("url", "https://github.com/kubernetes/minikube/blob/master/docs/vmdriver-none.md")
console.OutLn("") console.OutLn("")
} }
if os.Getenv("CHANGE_MINIKUBE_NONE_USER") == "" { if os.Getenv("CHANGE_MINIKUBE_NONE_USER") == "" {
console.Warning("kubectl and minikube configuration will be stored in %s", os.Getenv("HOME")) home := os.Getenv("HOME")
console.Warning("kubectl and minikube configuration will be stored in %s", home)
console.Warning("To use kubectl or minikube commands as your own user, you may") console.Warning("To use kubectl or minikube commands as your own user, you may")
console.Warning("need to relocate them. For example, to overwrite your own settings:") console.Warning("need to relocate them. For example, to overwrite your own settings:")
console.OutStyle("command", "sudo mv %s/.kube %s/.minikube $HOME", os.Getenv("HOME")) console.OutLn("")
console.OutStyle("command", "sudo chown -R $USER %s/.kube %s/.minikube", os.Getenv("HOME")) console.OutStyle("command", "sudo mv %s/.kube %s/.minikube $HOME", home, home)
console.OutStyle("command", "sudo chown -R $USER %s/.kube %s/.minikube", home, home)
console.OutLn("")
console.OutStyle("tip", "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true") console.OutStyle("tip", "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true")
} }

View File

@ -18,70 +18,16 @@ limitations under the License.
package util package util
import ( import (
"fmt"
"io"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"runtime"
"strconv" "strconv"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/constants"
) )
type ServiceContext struct {
Service string `json:"service"`
Version string `json:"version"`
}
type LookPath func(filename string) (string, error)
var lookPath LookPath
func init() {
lookPath = exec.LookPath
}
func MaybePrintKubectlDownloadMsg(goos string, out io.Writer) {
if !viper.GetBool(config.WantKubectlDownloadMsg) {
return
}
verb := "run"
installInstructions := "curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/%s/bin/%s/%s/kubectl && chmod +x kubectl && sudo cp kubectl /usr/local/bin/ && rm kubectl"
if goos == "windows" {
verb = "do"
installInstructions = `download kubectl from:
https://storage.googleapis.com/kubernetes-release/release/%s/bin/%s/%s/kubectl.exe
Add kubectl to your system PATH`
}
_, err := lookPath("kubectl")
if err != nil && goos == "windows" {
_, err = lookPath("kubectl.exe")
}
if err != nil {
fmt.Fprintf(out,
`========================================
kubectl could not be found on your path. kubectl is a requirement for using minikube
To install kubectl, please %s the following:
%s
To disable this message, run the following:
minikube config set WantKubectlDownloadMsg false
========================================
`,
verb, fmt.Sprintf(installInstructions, constants.DefaultKubernetesVersion, goos, runtime.GOARCH))
}
}
// Ask the kernel for a free open port that is ready to use // Ask the kernel for a free open port that is ready to use
func GetPort() (string, error) { func GetPort() (string, error) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0") addr, err := net.ResolveTCPAddr("tcp", "localhost:0")

View File

@ -17,108 +17,12 @@ limitations under the License.
package util package util
import ( import (
"bytes"
"fmt"
"net/http"
"net/http/httptest"
"os" "os"
"strings"
"testing" "testing"
"github.com/pkg/errors"
"github.com/spf13/viper"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
"k8s.io/minikube/pkg/minikube/config"
) )
func startTestHTTPServer(returnError bool, response string) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if returnError {
http.Error(w, response, 400)
} else {
fmt.Fprintf(w, response)
}
}))
}
func revertLookPath(l LookPath) {
lookPath = l
}
func fakeLookPathFound(string) (string, error) { return "/usr/local/bin/kubectl", nil }
func fakeLookPathError(string) (string, error) { return "", errors.New("") }
func TestKubectlDownloadMsg(t *testing.T) {
var tests = []struct {
description string
lp LookPath
goos string
matches string
noOutput bool
warningEnabled bool
}{
{
description: "No output when binary is found windows",
goos: "windows",
lp: fakeLookPathFound,
noOutput: true,
warningEnabled: true,
},
{
description: "No output when binary is found darwin",
goos: "darwin",
lp: fakeLookPathFound,
noOutput: true,
warningEnabled: true,
},
{
description: "windows kubectl not found, has .exe in output",
goos: "windows",
lp: fakeLookPathError,
matches: ".exe",
warningEnabled: true,
},
{
description: "linux kubectl not found",
goos: "linux",
lp: fakeLookPathError,
matches: "WantKubectlDownloadMsg",
warningEnabled: true,
},
{
description: "warning disabled",
goos: "linux",
lp: fakeLookPathError,
noOutput: true,
warningEnabled: false,
},
}
for _, test := range tests {
test := test
t.Run(test.description, func(t *testing.T) {
defer revertLookPath(lookPath)
// Remember the original config value and revert to it.
origConfig := viper.GetBool(config.WantKubectlDownloadMsg)
defer func() {
viper.Set(config.WantKubectlDownloadMsg, origConfig)
}()
viper.Set(config.WantKubectlDownloadMsg, test.warningEnabled)
lookPath = test.lp
var b bytes.Buffer
MaybePrintKubectlDownloadMsg(test.goos, &b)
actual := b.String()
if actual != "" && test.noOutput {
t.Errorf("Got output, but kubectl binary was found")
}
if !strings.Contains(actual, test.matches) {
t.Errorf("Output did not contain substring expected got output %s", actual)
}
})
}
}
func TestGetKubeConfigPath(t *testing.T) { func TestGetKubeConfigPath(t *testing.T) {
var tests = []struct { var tests = []struct {
input string input string

View File

@ -255,7 +255,8 @@ func (k *KubeadmBootstrapper) DeleteCluster(k8s config.KubernetesConfig) error {
// PullImages downloads images that will be used by RestartCluster // PullImages downloads images that will be used by RestartCluster
func (k *KubeadmBootstrapper) PullImages(k8s config.KubernetesConfig) error { func (k *KubeadmBootstrapper) PullImages(k8s config.KubernetesConfig) error {
cmd := fmt.Sprintf("sudo kubeadm config images pull --config %s", constants.KubeadmConfigFile) cmd := fmt.Sprintf("sudo kubeadm config images pull --config %s", constants.KubeadmConfigFile)
if err := k.c.Run(cmd); err != nil { // Use CombinedOutput instead of Run to avoid spamming stdout/stderr
if _, err := k.c.CombinedOutput(cmd); err != nil {
return errors.Wrapf(err, "running cmd: %s", cmd) return errors.Wrapf(err, "running cmd: %s", cmd)
} }
return nil return nil

View File

@ -49,6 +49,7 @@ var styles = map[string]style{
"launch": {Prefix: "🚀 "}, "launch": {Prefix: "🚀 "},
"thumbs-up": {Prefix: "👍 "}, "thumbs-up": {Prefix: "👍 "},
"option": {Prefix: " ▪ "}, // Indented bullet "option": {Prefix: " ▪ "}, // Indented bullet
"command": {Prefix: " ▪ "}, // Indented bullet
"log-entry": {Prefix: " "}, // Indent "log-entry": {Prefix: " "}, // Indent
"crushed": {Prefix: "💔 "}, "crushed": {Prefix: "💔 "},
"running": {Prefix: "🏃 "}, "running": {Prefix: "🏃 "},