From eaa01716ead5cb4e71b396c3ebe3bd2651152edc Mon Sep 17 00:00:00 2001 From: Medya Gh Date: Wed, 2 Oct 2019 15:56:21 -0700 Subject: [PATCH] move unit tests from util to command_runner --- pkg/minikube/command/ssh_runner.go | 42 +++++++++++++++++ pkg/minikube/command/ssh_runner_test.go | 63 +++++++++++++++++++++++++ pkg/util/utils_test.go | 41 ---------------- 3 files changed, 105 insertions(+), 41 deletions(-) create mode 100644 pkg/minikube/command/ssh_runner_test.go diff --git a/pkg/minikube/command/ssh_runner.go b/pkg/minikube/command/ssh_runner.go index 9117d7cabf..537ad6c663 100644 --- a/pkg/minikube/command/ssh_runner.go +++ b/pkg/minikube/command/ssh_runner.go @@ -21,8 +21,11 @@ import ( "bytes" "fmt" "io" + "os/exec" "path" + "strings" "sync" + "time" "github.com/golang/glog" "github.com/pkg/errors" @@ -98,6 +101,45 @@ func teeSSH(s *ssh.Session, cmd string, outB io.Writer, errB io.Writer) error { return err } +// RunCmd implements the Command Runner interface to run a exec.Cmd object +func (s *SSHRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) { + rr := &RunResult{Args: cmd.Args} + glog.Infof("(SSHRunner) Run: %v", rr.Command()) + + var outb, errb bytes.Buffer + cmd.Stdout, rr.Stdout = &outb, &outb + cmd.Stderr, rr.Stderr = &errb, &errb + start := time.Now() + + sess, err := s.c.NewSession() + if err != nil { + return rr, errors.Wrap(err, "NewSession") + } + + defer func() { + if err := sess.Close(); err != nil { + if err != io.EOF { + glog.Errorf("session close: %v", err) + } + } + }() + + elapsed := time.Since(start) + err = teeSSH(sess, strings.Join(cmd.Args, " "), &outb, &errb) + if err == nil { + // Reduce log spam + if elapsed > (1 * time.Second) { + glog.Infof("(SSHRunner) Done: %v: (%s)", rr.Command(), elapsed) + } + } else { + if exitError, ok := err.(*exec.ExitError); ok { + rr.ExitCode = exitError.ExitCode() + } + glog.Infof("(SSHRunner) Non-zero exit: %v: %v (%s)\n%s", rr.Command(), err, elapsed, rr.Output()) + } + return rr, err +} + // Run starts a command on the remote and waits for it to return. func (s *SSHRunner) Run(cmd string) error { glog.Infof("SSH: %s", cmd) diff --git a/pkg/minikube/command/ssh_runner_test.go b/pkg/minikube/command/ssh_runner_test.go new file mode 100644 index 0000000000..df9e7d509e --- /dev/null +++ b/pkg/minikube/command/ssh_runner_test.go @@ -0,0 +1,63 @@ +/* +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 command + +import ( + "bytes" + "fmt" + "strings" + "sync" + "testing" +) + +func TestTeePrefix(t *testing.T) { + var in bytes.Buffer + var out bytes.Buffer + var logged strings.Builder + + logSink := func(format string, args ...interface{}) { + logged.WriteString("(" + fmt.Sprintf(format, args...) + ")") + } + + // Simulate the primary use case: tee in the background. This also helps avoid I/O races. + var wg sync.WaitGroup + wg.Add(1) + go func() { + if err := teePrefix(":", &in, &out, logSink); err != nil { + t.Errorf("teePrefix: %v", err) + } + wg.Done() + }() + + in.Write([]byte("goo")) + in.Write([]byte("\n")) + in.Write([]byte("g\r\n\r\n")) + in.Write([]byte("le")) + wg.Wait() + + gotBytes := out.Bytes() + wantBytes := []byte("goo\ng\r\n\r\nle") + if !bytes.Equal(gotBytes, wantBytes) { + t.Errorf("output=%q, want: %q", gotBytes, wantBytes) + } + + gotLog := logged.String() + wantLog := "(:goo)(:g)(:le)" + if gotLog != wantLog { + t.Errorf("log=%q, want: %q", gotLog, wantLog) + } +} diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go index 4fc94ce7bc..26a10724a4 100644 --- a/pkg/util/utils_test.go +++ b/pkg/util/utils_test.go @@ -17,10 +17,7 @@ limitations under the License. package util import ( - "bytes" - "fmt" "strings" - "sync" "testing" ) @@ -44,44 +41,6 @@ func TestGetBinaryDownloadURL(t *testing.T) { } -func TestTeePrefix(t *testing.T) { - var in bytes.Buffer - var out bytes.Buffer - var logged strings.Builder - - logSink := func(format string, args ...interface{}) { - logged.WriteString("(" + fmt.Sprintf(format, args...) + ")") - } - - // Simulate the primary use case: tee in the background. This also helps avoid I/O races. - var wg sync.WaitGroup - wg.Add(1) - go func() { - if err := TeePrefix(":", &in, &out, logSink); err != nil { - t.Errorf("TeePrefix: %v", err) - } - wg.Done() - }() - - in.Write([]byte("goo")) - in.Write([]byte("\n")) - in.Write([]byte("g\r\n\r\n")) - in.Write([]byte("le")) - wg.Wait() - - gotBytes := out.Bytes() - wantBytes := []byte("goo\ng\r\n\r\nle") - if !bytes.Equal(gotBytes, wantBytes) { - t.Errorf("output=%q, want: %q", gotBytes, wantBytes) - } - - gotLog := logged.String() - wantLog := "(:goo)(:g)(:le)" - if gotLog != wantLog { - t.Errorf("log=%q, want: %q", gotLog, wantLog) - } -} - func TestReplaceChars(t *testing.T) { testData := []struct { src []string