diff --git a/pkg/minikube/bootstrapper/exec_runner.go b/pkg/minikube/bootstrapper/exec_runner.go index af7b19b670..3b4a2ac742 100644 --- a/pkg/minikube/bootstrapper/exec_runner.go +++ b/pkg/minikube/bootstrapper/exec_runner.go @@ -28,8 +28,12 @@ import ( "k8s.io/minikube/pkg/minikube/assets" ) +// ExecRunner runs commands using the os/exec package. +// +// It implements the CommandRunner interface. type ExecRunner struct{} +// Run starts the specified command in a bash shell and waits for it to complete. func (*ExecRunner) Run(cmd string) error { glog.Infoln("Run:", cmd) c := exec.Command("/bin/bash", "-c", cmd) @@ -39,6 +43,8 @@ func (*ExecRunner) Run(cmd string) error { return nil } +// CombinedOutput runs the command in a bash shell and returns its +// combined standard output and standard error. func (*ExecRunner) CombinedOutput(cmd string) (string, error) { glog.Infoln("Run with output:", cmd) c := exec.Command("/bin/bash", "-c", cmd) @@ -49,6 +55,7 @@ func (*ExecRunner) CombinedOutput(cmd string) (string, error) { return string(out), nil } +// Copy copies a file and its permissions func (*ExecRunner) Copy(f assets.CopyableFile) error { if err := os.MkdirAll(f.GetTargetDir(), os.ModePerm); err != nil { return errors.Wrapf(err, "error making dirs for %s", f.GetTargetDir()) @@ -74,12 +81,13 @@ func (*ExecRunner) Copy(f assets.CopyableFile) error { if _, err = io.Copy(target, f); err != nil { return errors.Wrapf(err, `error copying file %s to target location: -do you have the correct permissions? The none driver requires sudo for the "start" command`, +do you have the correct permissions?`, targetPath) } return target.Close() } +// Remove removes a file func (e *ExecRunner) Remove(f assets.CopyableFile) error { cmd := getDeleteFileCommand(f) return e.Run(cmd) diff --git a/pkg/minikube/bootstrapper/fake_runner.go b/pkg/minikube/bootstrapper/fake_runner.go index 4fd816e9fd..9405171bc9 100644 --- a/pkg/minikube/bootstrapper/fake_runner.go +++ b/pkg/minikube/bootstrapper/fake_runner.go @@ -27,6 +27,9 @@ import ( "k8s.io/minikube/pkg/minikube/assets" ) +// FakeCommandRunner mocks command output without running the Commands +// +// It implements the CommandRunner interface and is used for testing. type FakeCommandRunner struct { commandToOutput atomic.Value cmdMap map[string]string @@ -35,6 +38,9 @@ type FakeCommandRunner struct { fileMap map[string]string } +// NewFakeCommandRunner returns a new FakeCommandRunner +// +// The expected output of commands should be set with SetCommandToOutput func NewFakeCommandRunner() *FakeCommandRunner { f := &FakeCommandRunner{ cmdMap: make(map[string]string), @@ -46,19 +52,18 @@ func NewFakeCommandRunner() *FakeCommandRunner { return f } +// Run returns nil if output has been set for the given command text. func (f *FakeCommandRunner) Run(cmd string) error { _, err := f.GetCommandToOutput(cmd) return err } +// CombinedOutput returns the set output for a given command text. func (f *FakeCommandRunner) CombinedOutput(cmd string) (string, error) { return f.GetCommandToOutput(cmd) } -func (f *FakeCommandRunner) Shell(cmd string) error { - return f.Run(cmd) -} - +// Copy adds the filename, file contents key value pair to the stored map. func (f *FakeCommandRunner) Copy(file assets.CopyableFile) error { fileMap := f.fileToContents.Load().(map[string]string) var b bytes.Buffer @@ -71,6 +76,7 @@ func (f *FakeCommandRunner) Copy(file assets.CopyableFile) error { return nil } +// Remove removes the filename, file contents key value pair from the stored map func (f *FakeCommandRunner) Remove(file assets.CopyableFile) error { fileMap := f.fileToContents.Load().(map[string]string) delete(fileMap, file.GetAssetName()) @@ -78,6 +84,7 @@ func (f *FakeCommandRunner) Remove(file assets.CopyableFile) error { return nil } +// GetCommandToOutput retrieves the stored output for a given command from the stored command map func (f *FakeCommandRunner) GetCommandToOutput(cmd string) (string, error) { cmdMap := f.commandToOutput.Load().(map[string]string) val, ok := cmdMap[cmd] @@ -87,10 +94,12 @@ func (f *FakeCommandRunner) GetCommandToOutput(cmd string) (string, error) { return val, nil } +// SetCommandToOutput stores the command to output map for the FakeCommandRunner func (f *FakeCommandRunner) SetCommandToOutput(cmdToOutput map[string]string) { f.commandToOutput.Store(cmdToOutput) } +// GetFileToContents returns the value for the stored filename key func (f *FakeCommandRunner) GetFileToContents(fpath string) (string, error) { fileMap := f.fileToContents.Load().(map[string]string) val, ok := fileMap[fpath] @@ -100,10 +109,12 @@ func (f *FakeCommandRunner) GetFileToContents(fpath string) (string, error) { return val, nil } +// SetFileToContents stores the file to contents map for the FakeCommandRunner func (f *FakeCommandRunner) SetFileToContents(fileToContents map[string]string) { f.fileToContents.Store(fileToContents) } +// DumpMaps prints out the list of stored commands and stored filenames. func (f *FakeCommandRunner) DumpMaps(w io.Writer) { fmt.Fprint(w, "Commands: \n", f.cmdMap) fmt.Fprintln(w, "Filenames: ") diff --git a/pkg/minikube/bootstrapper/runner.go b/pkg/minikube/bootstrapper/runner.go index 675dc7b7a1..cb01924327 100644 --- a/pkg/minikube/bootstrapper/runner.go +++ b/pkg/minikube/bootstrapper/runner.go @@ -23,11 +23,19 @@ import ( "k8s.io/minikube/pkg/minikube/assets" ) +// CommandRunner represents an interface to run commands. type CommandRunner interface { + // Run starts the specified command and waits for it to complete. Run(cmd string) error + + // CombinedOutput runs the command and returns its combined standard + // output and standard error. CombinedOutput(cmd string) (string, error) + // Copy is a convenience method that runs a command to copy a file Copy(assets.CopyableFile) error + + //Remove is a convenience method that runs a command to remove a file Remove(assets.CopyableFile) error } diff --git a/pkg/minikube/bootstrapper/ssh_runner.go b/pkg/minikube/bootstrapper/ssh_runner.go index 18ed04e4e7..7a6f8dacb6 100644 --- a/pkg/minikube/bootstrapper/ssh_runner.go +++ b/pkg/minikube/bootstrapper/ssh_runner.go @@ -28,14 +28,20 @@ import ( "k8s.io/minikube/pkg/minikube/assets" ) +// SSHRunner runs commands through SSH. +// +// It implements the CommandRunner interface. type SSHRunner struct { c *ssh.Client } +// NewSSHRunner returns a new SSHRunner that will run commands +// through the ssh.Client provided. func NewSSHRunner(c *ssh.Client) *SSHRunner { return &SSHRunner{c} } +// Remove runs a command to delete a file on the remote. func (s *SSHRunner) Remove(f assets.CopyableFile) error { sess, err := s.c.NewSession() if err != nil { @@ -46,6 +52,7 @@ func (s *SSHRunner) Remove(f assets.CopyableFile) error { return sess.Run(cmd) } +// Run starts a command on the remote and waits for it to return. func (s *SSHRunner) Run(cmd string) error { glog.Infoln("Run:", cmd) sess, err := s.c.NewSession() @@ -56,6 +63,8 @@ func (s *SSHRunner) Run(cmd string) error { return sess.Run(cmd) } +// CombinedOutput runs the command on the remote and returns its combined +// standard output and standard error. func (s *SSHRunner) CombinedOutput(cmd string) (string, error) { glog.Infoln("Run with output:", cmd) sess, err := s.c.NewSession() @@ -70,6 +79,7 @@ func (s *SSHRunner) CombinedOutput(cmd string) (string, error) { return string(out), nil } +// Copy copies a file to the remote over SSH. func (s *SSHRunner) Copy(f assets.CopyableFile) error { deleteCmd := fmt.Sprintf("sudo rm -f %s", filepath.Join(f.GetTargetDir(), f.GetTargetName())) mkdirCmd := fmt.Sprintf("sudo mkdir -p %s", f.GetTargetDir())