1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package ssh_test
import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"time"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/ssh"
)
const (
shortWait = 50 * time.Millisecond
longWait = 10 * time.Second
)
type ExecuteSSHCommandSuite struct {
testing.IsolationSuite
originalPath string
testbin string
fakessh string
}
var _ = gc.Suite(&ExecuteSSHCommandSuite{})
func (s *ExecuteSSHCommandSuite) SetUpSuite(c *gc.C) {
s.originalPath = os.Getenv("PATH")
s.IsolationSuite.SetUpSuite(c)
}
func (s *ExecuteSSHCommandSuite) SetUpTest(c *gc.C) {
if runtime.GOOS == "windows" {
c.Skip("issue 1403084: Tests use OpenSSH only")
}
s.IsolationSuite.SetUpTest(c)
err := os.Setenv("PATH", s.originalPath)
c.Assert(err, jc.ErrorIsNil)
s.testbin = c.MkDir()
s.fakessh = filepath.Join(s.testbin, "ssh")
s.PatchEnvPathPrepend(s.testbin)
}
func (s *ExecuteSSHCommandSuite) fakeSSH(c *gc.C, cmd string) {
err := ioutil.WriteFile(s.fakessh, []byte(cmd), 0755)
c.Assert(err, jc.ErrorIsNil)
}
func (s *ExecuteSSHCommandSuite) TestCaptureOutput(c *gc.C) {
s.fakeSSH(c, echoSSH)
response, err := ssh.ExecuteCommandOnMachine(ssh.ExecParams{
Host: "hostname",
Command: "sudo apt-get update\nsudo apt-get upgrade",
Timeout: longWait,
})
c.Assert(err, jc.ErrorIsNil)
c.Assert(response.Code, gc.Equals, 0)
c.Assert(string(response.Stdout), gc.Equals, "sudo apt-get update\nsudo apt-get upgrade\n")
c.Assert(string(response.Stderr), gc.Equals,
"-o PasswordAuthentication no -o ServerAliveInterval 30 hostname /bin/bash -s\n")
}
func (s *ExecuteSSHCommandSuite) TestIdentityFile(c *gc.C) {
s.fakeSSH(c, echoSSH)
response, err := ssh.ExecuteCommandOnMachine(ssh.ExecParams{
IdentityFile: "identity-file",
Host: "hostname",
Timeout: longWait,
})
c.Assert(err, jc.ErrorIsNil)
c.Assert(string(response.Stderr), jc.Contains, " -i identity-file ")
}
func (s *ExecuteSSHCommandSuite) TestTimoutCaptureOutput(c *gc.C) {
s.fakeSSH(c, slowSSH)
response, err := ssh.ExecuteCommandOnMachine(ssh.ExecParams{
IdentityFile: "identity-file",
Host: "hostname",
Command: "ignored",
Timeout: shortWait,
})
c.Check(err, gc.ErrorMatches, "command timed out")
c.Assert(response.Code, gc.Equals, 0)
c.Assert(string(response.Stdout), gc.Equals, "stdout\n")
c.Assert(string(response.Stderr), gc.Equals, "stderr\n")
}
func (s *ExecuteSSHCommandSuite) TestCapturesReturnCode(c *gc.C) {
s.fakeSSH(c, passthroughSSH)
response, err := ssh.ExecuteCommandOnMachine(ssh.ExecParams{
IdentityFile: "identity-file",
Host: "hostname",
Command: "echo stdout; exit 42",
Timeout: longWait,
})
c.Check(err, jc.ErrorIsNil)
c.Assert(response.Code, gc.Equals, 42)
c.Assert(string(response.Stdout), gc.Equals, "stdout\n")
c.Assert(string(response.Stderr), gc.Equals, "")
}
// echoSSH outputs the command args to stderr, and copies stdin to stdout
var echoSSH = `#!/bin/bash
# Write the args to stderr
echo "$*" >&2
cat /dev/stdin
`
// slowSSH sleeps for a while after outputting some text to stdout and stderr
var slowSSH = `#!/bin/bash
echo "stderr" >&2
echo "stdout"
sleep 5s
`
// passthroughSSH creates an ssh that executes stdin.
var passthroughSSH = `#!/bin/bash -s`
|