move unit tests from util to command_runner
parent
88183240ca
commit
eaa01716ea
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue