From 6639b2d91b950e37263651cd5754580f4df641ae Mon Sep 17 00:00:00 2001 From: Aaron Prindle Date: Wed, 14 Jun 2017 14:27:01 -0700 Subject: [PATCH] Removed systemd dependency from minikube, updated none driver to reflect this --- cmd/minikube/cmd/start.go | 10 +++- cmd/util/util.go | 32 ++++++++++++ pkg/minikube/assets/vm_assets.go | 25 +-------- pkg/minikube/cluster/cluster.go | 22 ++++---- pkg/minikube/cluster/commands.go | 64 ++++++++++++++++++++--- pkg/minikube/kubeconfig/config.go | 5 ++ pkg/minikube/machine/drivers/none/none.go | 52 +++++++++++------- 7 files changed, 151 insertions(+), 59 deletions(-) diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index bc27e9b86bb6..368815bc7913 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -241,8 +241,10 @@ func runStart(cmd *cobra.Command, args []string) { fmt.Println(`=================== WARNING: IT IS RECOMMENDED NOT TO RUN THE NONE DRIVER ON PERSONAL WORKSTATIONS The 'none' driver will run an insecure kubernetes apiserver as root that may leave the host vulnerable to CSRF attacks +`) -When using the none driver, the kubectl config and credentials generated will be root owned and will appear in the root home directory. + if os.Getenv("CHANGE_MINIKUBE_NONE_USER") != "" { + fmt.Println(`When using the none driver, the kubectl config and credentials generated will be root owned and will appear in the root home directory. You will need to move the files to the appropriate location and then set the correct permissions. An example of this is below: sudo mv /root/.kube $HOME/.kube # this will overwrite any config you have. You may have to append the file contents manually sudo chown -R $USER $HOME/.kube @@ -252,6 +254,12 @@ You will need to move the files to the appropriate location and then set the cor sudo chown -R $USER $HOME/.minikube sudo chgrp -R $USER $HOME/.minikube This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true`) + } + if err := cmdUtil.MaybeChownDirRecursiveToMinikubeUser(constants.GetMinipath()); err != nil { + glog.Errorf("Error recursively changing ownership of directory %s: %s", + constants.GetMinipath(), err) + cmdUtil.MaybeReportErrorAndExit(err) + } } } diff --git a/cmd/util/util.go b/cmd/util/util.go index fa046ab89dd9..cd169c277b23 100644 --- a/cmd/util/util.go +++ b/cmd/util/util.go @@ -27,6 +27,7 @@ import ( "net/http" "os" "os/exec" + "os/user" "path/filepath" "runtime" "strings" @@ -246,3 +247,34 @@ func KillMountProcess() error { } return mountProc.Kill() } + +func MaybeChownDirRecursiveToMinikubeUser(dir string) error { + if os.Getenv("CHANGE_MINIKUBE_NONE_USER") != "" && os.Getenv("SUDO_USER") != "" { + username := os.Getenv("SUDO_USER") + usr, err := user.Lookup(username) + if err != nil { + return errors.Wrap(err, "Error looking up user") + } + uid, err := strconv.Atoi(usr.Uid) + if err != nil { + return errors.Wrapf(err, "Error parsing uid for user: %s", username) + } + gid, err := strconv.Atoi(usr.Gid) + if err != nil { + return errors.Wrapf(err, "Error parsing gid for user: %s", username) + } + if err := ChownR(dir, uid, gid); err != nil { + return errors.Wrapf(err, "Error changing ownership for: %s", dir) + } + } + return nil +} + +func ChownR(path string, uid, gid int) error { + return filepath.Walk(path, func(name string, info os.FileInfo, err error) error { + if err == nil { + err = os.Chown(name, uid, gid) + } + return err + }) +} diff --git a/pkg/minikube/assets/vm_assets.go b/pkg/minikube/assets/vm_assets.go index e4f53c9c5143..9a53b4437308 100644 --- a/pkg/minikube/assets/vm_assets.go +++ b/pkg/minikube/assets/vm_assets.go @@ -20,7 +20,6 @@ import ( "bytes" "io" "os" - "os/user" "path/filepath" "strconv" @@ -157,29 +156,7 @@ func CopyFileLocal(f CopyableFile) error { _, err = io.Copy(target, f) if err != nil { - return errors.Wrap(err, "Error copying file to target location") - } - - if os.Getenv("CHANGE_MINIKUBE_NONE_USER") != "" { - username := os.Getenv("SUDO_USER") - if username == "" { - return nil - } - usr, err := user.Lookup(username) - if err != nil { - return errors.Wrap(err, "Error looking up user") - } - uid, err := strconv.Atoi(usr.Uid) - if err != nil { - return errors.Wrapf(err, "Error parsing uid for user: %s", username) - } - gid, err := strconv.Atoi(usr.Gid) - if err != nil { - return errors.Wrapf(err, "Error parsing gid for user: %s", username) - } - if err := os.Chown(targetPath, uid, gid); err != nil { - return errors.Wrapf(err, "Error changing ownership for: %s", targetPath) - } + return errors.Wrap(err, "Error copying file to target location, do you have the correct permissions?") } return nil } diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index 18a2fd1b4b99..884f0253476f 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -153,10 +153,7 @@ func GetLocalkubeStatus(api libmachine.API) (string, error) { if err != nil { return "", err } - - statusCmd := localkubeStatusCommand - - s, err := RunCommand(h, statusCmd, false) + s, err := RunCommand(h, localkubeStatusCommand, false) if err != nil { return "", err } @@ -446,7 +443,7 @@ func GetHostLogs(api libmachine.API, follow bool) (string, error) { s, err := RunCommand(h, logsCommand, false) if err != nil { - return "", err + return s, err } return s, nil } @@ -577,15 +574,20 @@ func EnsureMinikubeRunningOrExit(api libmachine.API, exitStatus int) { // RunCommand executes commands for both the local and driver implementations func RunCommand(h *host.Host, command string, sudo bool) (string, error) { if h.Driver.DriverName() == "none" { - cmd := exec.Command("/bin/sh", "-c", command) + cmd := exec.Command("/bin/bash", "-c", command) if sudo { - cmd = exec.Command("sudo", "/bin/sh", "-c", command) + cmd = exec.Command("sudo", "/bin/bash", "-c", command) } out, err := cmd.CombinedOutput() if err != nil { - return "", err + return "", errors.Wrap(err, string(out)) } - return string(out), nil + return string(out), err } - return h.RunSSHCommand(command) + out, err := h.RunSSHCommand(command) + if err != nil { + return "", errors.Wrap(err, string(out)) + } + return string(out), err + } diff --git a/pkg/minikube/cluster/commands.go b/pkg/minikube/cluster/commands.go index 45401bfef986..b19f18191731 100644 --- a/pkg/minikube/cluster/commands.go +++ b/pkg/minikube/cluster/commands.go @@ -23,13 +23,20 @@ import ( "net" "strings" - "k8s.io/minikube/pkg/minikube/constants" "text/template" + + "k8s.io/minikube/pkg/minikube/constants" ) // Kill any running instances. + var localkubeStartCmdTemplate = "/usr/local/bin/localkube {{.Flags}} --generate-certs=false --logtostderr=true --enable-dns=false" +var startCommandNoSystemdTemplate = ` +# Run with nohup so it stays up. Redirect logs to useful places. +sudo sh -c 'PATH=/usr/local/sbin:$PATH nohup {{.LocalkubeStartCmd}} > {{.Stdout}} 2> {{.Stderr}} < /dev/null & echo $! > {{.Pidfile}} &' +` + var localkubeSystemdTmpl = `[Unit] Description=Localkube Documentation=https://github.com/kubernetes/minikube/tree/master/pkg/localkube @@ -47,11 +54,15 @@ ExecReload=/bin/kill -s HUP $MAINPID WantedBy=multi-user.target ` -var startCommandTemplate = ` +var startCommandTemplate = "if [[ `systemctl` =~ -\\.mount ]] &>/dev/null;" + `then {{.StartCommandSystemd}} sudo systemctl daemon-reload sudo systemctl enable localkube.service sudo systemctl restart localkube.service || true +else + sudo killall localkube || true + {{.StartCommandNoSystemd}} +fi ` func GetStartCommand(kubernetesConfig KubernetesConfig) (string, error) { @@ -59,6 +70,10 @@ func GetStartCommand(kubernetesConfig KubernetesConfig) (string, error) { if err != nil { return "", err } + startCommandNoSystemd, err := GetStartCommandNoSystemd(kubernetesConfig, localkubeStartCommand) + if err != nil { + return "", err + } startCommandSystemd, err := GetStartCommandSystemd(kubernetesConfig, localkubeStartCommand) if err != nil { return "", err @@ -66,9 +81,31 @@ func GetStartCommand(kubernetesConfig KubernetesConfig) (string, error) { t := template.Must(template.New("startCommand").Parse(startCommandTemplate)) buf := bytes.Buffer{} data := struct { - StartCommandSystemd string + StartCommandNoSystemd string + StartCommandSystemd string }{ - StartCommandSystemd: startCommandSystemd, + StartCommandNoSystemd: startCommandNoSystemd, + StartCommandSystemd: startCommandSystemd, + } + if err := t.Execute(&buf, data); err != nil { + return "", err + } + return buf.String(), nil +} + +func GetStartCommandNoSystemd(kubernetesConfig KubernetesConfig, localkubeStartCmd string) (string, error) { + t := template.Must(template.New("startCommand").Parse(startCommandNoSystemdTemplate)) + buf := bytes.Buffer{} + data := struct { + LocalkubeStartCmd string + Stdout string + Stderr string + Pidfile string + }{ + LocalkubeStartCmd: localkubeStartCmd, + Stdout: constants.RemoteLocalKubeOutPath, + Stderr: constants.RemoteLocalKubeErrPath, + Pidfile: constants.LocalkubePIDPath, } if err := t.Execute(&buf, data); err != nil { return "", err @@ -130,6 +167,7 @@ func GenLocalkubeStartCmd(kubernetesConfig KubernetesConfig) (string, error) { flagVals = append(flagVals, fmt.Sprintf("--extra-config=%s", e.String())) } flags := strings.Join(flagVals, " ") + t := template.Must(template.New("localkubeStartCmd").Parse(localkubeStartCmdTemplate)) buf := bytes.Buffer{} data := struct { @@ -145,7 +183,12 @@ func GenLocalkubeStartCmd(kubernetesConfig KubernetesConfig) (string, error) { return buf.String(), nil } -const logsTemplate = "sudo journalctl {{.Flags}} -u localkube" +const logsTemplate = "if [[ `systemctl` =~ -\\.mount ]] &>/dev/null; " + `then + sudo journalctl {{.Flags}} -u localkube +else + tail -n +1 {{.Flags}} {{.RemoteLocalkubeErrPath}} {{.RemoteLocalkubeOutPath}} +fi +` func GetLogsCommand(follow bool) (string, error) { t, err := template.New("logsTemplate").Parse(logsTemplate) @@ -173,7 +216,16 @@ func GetLogsCommand(follow bool) (string, error) { return buf.String(), nil } -var localkubeStatusCommand = `sudo systemctl is-active localkube 2>&1 1>/dev/null && echo "Running" || echo "Stopped"` +var localkubeStatusCommand = fmt.Sprintf("if [[ `systemctl` =~ -\\.mount ]] &>/dev/null; "+`then + sudo systemctl is-active localkube &>/dev/null && echo "Running" || echo "Stopped" +else + if ps $(cat %s) &>/dev/null; then + echo "Running" + else + echo "Stopped" + fi +fi +`, constants.LocalkubePIDPath) func GetMountCleanupCommand(path string) string { return fmt.Sprintf("sudo umount %s;", path) diff --git a/pkg/minikube/kubeconfig/config.go b/pkg/minikube/kubeconfig/config.go index 32c8d98d0316..cdb5986f8a36 100644 --- a/pkg/minikube/kubeconfig/config.go +++ b/pkg/minikube/kubeconfig/config.go @@ -31,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/clientcmd/api" "k8s.io/client-go/tools/clientcmd/api/latest" + cmdUtil "k8s.io/minikube/cmd/util" cfg "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" ) @@ -166,6 +167,10 @@ func WriteConfig(config *api.Config, filename string) error { if err := ioutil.WriteFile(filename, data, 0600); err != nil { return errors.Wrapf(err, "Error writing file %s", filename) } + if err := cmdUtil.MaybeChownDirRecursiveToMinikubeUser(dir); err != nil { + return errors.Wrapf(err, "Error recursively changing ownership for dir: %s", dir) + } + return nil } diff --git a/pkg/minikube/machine/drivers/none/none.go b/pkg/minikube/machine/drivers/none/none.go index 55611d2ff3e6..288396473a74 100644 --- a/pkg/minikube/machine/drivers/none/none.go +++ b/pkg/minikube/machine/drivers/none/none.go @@ -17,17 +17,15 @@ limitations under the License. package none import ( - "errors" "fmt" - "io/ioutil" "os" "os/exec" - "path/filepath" "strings" "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/mcnflag" "github.com/docker/machine/libmachine/state" + "github.com/pkg/errors" "k8s.io/minikube/pkg/minikube/constants" ) @@ -50,10 +48,6 @@ func NewDriver(hostName, storePath string) *Driver { // PreCreateCheck checks that VBoxManage exists and works func (d *Driver) PreCreateCheck() error { - // check that systemd is installed as it is a requirement - if _, err := exec.LookPath("systemctl"); err != nil { - return errors.New("systemd is a requirement in order to use the none driver") - } return nil } @@ -96,17 +90,22 @@ func (d *Driver) GetURL() (string, error) { } func (d *Driver) GetState() (state.State, error) { - command := `sudo systemctl is-active localkube 2>&1 1>/dev/null && echo "Running" || echo "Stopped"` - - path := filepath.Join(constants.GetMinipath(), "tmp-cmd") - ioutil.WriteFile(filepath.Join(constants.GetMinipath(), "tmp-cmd"), []byte(command), os.FileMode(0644)) - defer os.Remove(path) - cmd := exec.Command("sudo", "/bin/sh", path) - out, err := cmd.CombinedOutput() + var statuscmd = fmt.Sprintf("if [[ `systemctl` =~ -\\.mount ]] &>/dev/null; "+`then + sudo systemctl is-active localkube &>/dev/null && echo "Running" || echo "Stopped" +else + if ps $(cat %s) &>/dev/null; then + echo "Running" + else + echo "Stopped" + fi +fi +`, constants.LocalkubePIDPath) + + out, err := runCommand(statuscmd, true) if err != nil { return state.None, err } - s := strings.TrimSpace(string(out)) + s := strings.TrimSpace(out) if state.Running.String() == s { return state.Running, nil } else if state.Stopped.String() == s { @@ -162,11 +161,16 @@ func (d *Driver) Start() error { } func (d *Driver) Stop() error { - cmd := exec.Command("sudo", "systemctl", "stop", "localkube.service") - if err := cmd.Start(); err != nil { + var stopcmd = fmt.Sprintf("if [[ `systemctl` =~ -\\.mount ]] &>/dev/null; "+`then + sudo systemctl stop localkube.service +else + sudo kill $(cat %s) +fi +`, constants.LocalkubePIDPath) + _, err := runCommand(stopcmd, true) + if err != nil { return err } - for { s, err := d.GetState() if err != nil { @@ -182,3 +186,15 @@ func (d *Driver) Stop() error { func (d *Driver) RunSSHCommandFromDriver() error { return fmt.Errorf("driver does not support ssh commands") } + +func runCommand(command string, sudo bool) (string, error) { + cmd := exec.Command("/bin/bash", "-c", command) + if sudo { + cmd = exec.Command("sudo", "/bin/bash", "-c", command) + } + out, err := cmd.CombinedOutput() + if err != nil { + return "", errors.Wrap(err, string(out)) + } + return string(out), nil +}