github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/utils/ssh/ssh_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // +build !windows
     5  
     6  package ssh_test
     7  
     8  import (
     9  	"bytes"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  
    16  	"github.com/juju/cmd"
    17  	"github.com/juju/testing"
    18  	jc "github.com/juju/testing/checkers"
    19  	gc "gopkg.in/check.v1"
    20  
    21  	"github.com/juju/juju/utils/ssh"
    22  )
    23  
    24  const (
    25  	echoCommand = "/bin/echo"
    26  	echoScript  = "#!/bin/sh\n" + echoCommand + " $0 \"$@\" | /usr/bin/tee $0.args"
    27  )
    28  
    29  type SSHCommandSuite struct {
    30  	testing.IsolationSuite
    31  	originalPath string
    32  	testbin      string
    33  	fakessh      string
    34  	fakescp      string
    35  	client       ssh.Client
    36  }
    37  
    38  var _ = gc.Suite(&SSHCommandSuite{})
    39  
    40  func (s *SSHCommandSuite) SetUpTest(c *gc.C) {
    41  	s.IsolationSuite.SetUpTest(c)
    42  	s.testbin = c.MkDir()
    43  	s.fakessh = filepath.Join(s.testbin, "ssh")
    44  	s.fakescp = filepath.Join(s.testbin, "scp")
    45  	err := ioutil.WriteFile(s.fakessh, []byte(echoScript), 0755)
    46  	c.Assert(err, jc.ErrorIsNil)
    47  	err = ioutil.WriteFile(s.fakescp, []byte(echoScript), 0755)
    48  	c.Assert(err, jc.ErrorIsNil)
    49  	s.PatchEnvPathPrepend(s.testbin)
    50  	s.client, err = ssh.NewOpenSSHClient()
    51  	c.Assert(err, jc.ErrorIsNil)
    52  	s.PatchValue(ssh.DefaultIdentities, nil)
    53  }
    54  
    55  func (s *SSHCommandSuite) command(args ...string) *ssh.Cmd {
    56  	return s.commandOptions(args, nil)
    57  }
    58  
    59  func (s *SSHCommandSuite) commandOptions(args []string, opts *ssh.Options) *ssh.Cmd {
    60  	return s.client.Command("localhost", args, opts)
    61  }
    62  
    63  func (s *SSHCommandSuite) assertCommandArgs(c *gc.C, cmd *ssh.Cmd, expected string) {
    64  	out, err := cmd.Output()
    65  	c.Assert(err, jc.ErrorIsNil)
    66  	c.Assert(strings.TrimSpace(string(out)), gc.Equals, expected)
    67  }
    68  
    69  func (s *SSHCommandSuite) TestDefaultClient(c *gc.C) {
    70  	ssh.InitDefaultClient()
    71  	c.Assert(ssh.DefaultClient, gc.FitsTypeOf, &ssh.OpenSSHClient{})
    72  	s.PatchEnvironment("PATH", "")
    73  	ssh.InitDefaultClient()
    74  	c.Assert(ssh.DefaultClient, gc.FitsTypeOf, &ssh.GoCryptoClient{})
    75  }
    76  
    77  func (s *SSHCommandSuite) TestCommandSSHPass(c *gc.C) {
    78  	// First create a fake sshpass, but don't set $SSHPASS
    79  	fakesshpass := filepath.Join(s.testbin, "sshpass")
    80  	err := ioutil.WriteFile(fakesshpass, []byte(echoScript), 0755)
    81  	s.assertCommandArgs(c, s.command(echoCommand, "123"),
    82  		fmt.Sprintf("%s -o StrictHostKeyChecking no -o PasswordAuthentication no -o ServerAliveInterval 30 localhost %s 123",
    83  			s.fakessh, echoCommand),
    84  	)
    85  	// Now set $SSHPASS.
    86  	s.PatchEnvironment("SSHPASS", "anyoldthing")
    87  	s.assertCommandArgs(c, s.command(echoCommand, "123"),
    88  		fmt.Sprintf("%s -e ssh -o StrictHostKeyChecking no -o PasswordAuthentication no -o ServerAliveInterval 30 localhost %s 123",
    89  			fakesshpass, echoCommand),
    90  	)
    91  	// Finally, remove sshpass from $PATH.
    92  	err = os.Remove(fakesshpass)
    93  	c.Assert(err, jc.ErrorIsNil)
    94  	s.assertCommandArgs(c, s.command(echoCommand, "123"),
    95  		fmt.Sprintf("%s -o StrictHostKeyChecking no -o PasswordAuthentication no -o ServerAliveInterval 30 localhost %s 123",
    96  			s.fakessh, echoCommand),
    97  	)
    98  }
    99  
   100  func (s *SSHCommandSuite) TestCommand(c *gc.C) {
   101  	s.assertCommandArgs(c, s.command(echoCommand, "123"),
   102  		fmt.Sprintf("%s -o StrictHostKeyChecking no -o PasswordAuthentication no -o ServerAliveInterval 30 localhost %s 123",
   103  			s.fakessh, echoCommand),
   104  	)
   105  }
   106  
   107  func (s *SSHCommandSuite) TestCommandEnablePTY(c *gc.C) {
   108  	var opts ssh.Options
   109  	opts.EnablePTY()
   110  	s.assertCommandArgs(c, s.commandOptions([]string{echoCommand, "123"}, &opts),
   111  		fmt.Sprintf("%s -o StrictHostKeyChecking no -o PasswordAuthentication no -o ServerAliveInterval 30 -t -t localhost %s 123",
   112  			s.fakessh, echoCommand),
   113  	)
   114  }
   115  
   116  func (s *SSHCommandSuite) TestCommandSetKnownHostsFile(c *gc.C) {
   117  	var opts ssh.Options
   118  	opts.SetKnownHostsFile("/tmp/known hosts")
   119  	s.assertCommandArgs(c, s.commandOptions([]string{echoCommand, "123"}, &opts),
   120  		fmt.Sprintf("%s -o StrictHostKeyChecking no -o PasswordAuthentication no -o ServerAliveInterval 30 -o UserKnownHostsFile \"/tmp/known hosts\" localhost %s 123",
   121  			s.fakessh, echoCommand),
   122  	)
   123  }
   124  
   125  func (s *SSHCommandSuite) TestCommandAllowPasswordAuthentication(c *gc.C) {
   126  	var opts ssh.Options
   127  	opts.AllowPasswordAuthentication()
   128  	s.assertCommandArgs(c, s.commandOptions([]string{echoCommand, "123"}, &opts),
   129  		fmt.Sprintf("%s -o StrictHostKeyChecking no -o ServerAliveInterval 30 localhost %s 123",
   130  			s.fakessh, echoCommand),
   131  	)
   132  }
   133  
   134  func (s *SSHCommandSuite) TestCommandIdentities(c *gc.C) {
   135  	var opts ssh.Options
   136  	opts.SetIdentities("x", "y")
   137  	s.assertCommandArgs(c, s.commandOptions([]string{echoCommand, "123"}, &opts),
   138  		fmt.Sprintf("%s -o StrictHostKeyChecking no -o PasswordAuthentication no -o ServerAliveInterval 30 -i x -i y localhost %s 123",
   139  			s.fakessh, echoCommand),
   140  	)
   141  }
   142  
   143  func (s *SSHCommandSuite) TestCommandPort(c *gc.C) {
   144  	var opts ssh.Options
   145  	opts.SetPort(2022)
   146  	s.assertCommandArgs(c, s.commandOptions([]string{echoCommand, "123"}, &opts),
   147  		fmt.Sprintf("%s -o StrictHostKeyChecking no -o PasswordAuthentication no -o ServerAliveInterval 30 -p 2022 localhost %s 123",
   148  			s.fakessh, echoCommand),
   149  	)
   150  }
   151  
   152  func (s *SSHCommandSuite) TestCopy(c *gc.C) {
   153  	var opts ssh.Options
   154  	opts.EnablePTY()
   155  	opts.AllowPasswordAuthentication()
   156  	opts.SetIdentities("x", "y")
   157  	opts.SetPort(2022)
   158  	err := s.client.Copy([]string{"/tmp/blah", "foo@bar.com:baz"}, &opts)
   159  	c.Assert(err, jc.ErrorIsNil)
   160  	out, err := ioutil.ReadFile(s.fakescp + ".args")
   161  	c.Assert(err, jc.ErrorIsNil)
   162  	// EnablePTY has no effect for Copy
   163  	c.Assert(string(out), gc.Equals, s.fakescp+" -o StrictHostKeyChecking no -o ServerAliveInterval 30 -i x -i y -P 2022 /tmp/blah foo@bar.com:baz\n")
   164  
   165  	// Try passing extra args
   166  	err = s.client.Copy([]string{"/tmp/blah", "foo@bar.com:baz", "-r", "-v"}, &opts)
   167  	c.Assert(err, jc.ErrorIsNil)
   168  	out, err = ioutil.ReadFile(s.fakescp + ".args")
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	c.Assert(string(out), gc.Equals, s.fakescp+" -o StrictHostKeyChecking no -o ServerAliveInterval 30 -i x -i y -P 2022 /tmp/blah foo@bar.com:baz -r -v\n")
   171  
   172  	// Try interspersing extra args
   173  	err = s.client.Copy([]string{"-r", "/tmp/blah", "-v", "foo@bar.com:baz"}, &opts)
   174  	c.Assert(err, jc.ErrorIsNil)
   175  	out, err = ioutil.ReadFile(s.fakescp + ".args")
   176  	c.Assert(err, jc.ErrorIsNil)
   177  	c.Assert(string(out), gc.Equals, s.fakescp+" -o StrictHostKeyChecking no -o ServerAliveInterval 30 -i x -i y -P 2022 -r /tmp/blah -v foo@bar.com:baz\n")
   178  }
   179  
   180  func (s *SSHCommandSuite) TestCommandClientKeys(c *gc.C) {
   181  	defer overrideGenerateKey(c).Restore()
   182  	clientKeysDir := c.MkDir()
   183  	defer ssh.ClearClientKeys()
   184  	err := ssh.LoadClientKeys(clientKeysDir)
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	ck := filepath.Join(clientKeysDir, "juju_id_rsa")
   187  	var opts ssh.Options
   188  	opts.SetIdentities("x", "y")
   189  	s.assertCommandArgs(c, s.commandOptions([]string{echoCommand, "123"}, &opts),
   190  		fmt.Sprintf("%s -o StrictHostKeyChecking no -o PasswordAuthentication no -o ServerAliveInterval 30 -i x -i y -i %s localhost %s 123",
   191  			s.fakessh, ck, echoCommand),
   192  	)
   193  }
   194  
   195  func (s *SSHCommandSuite) TestCommandError(c *gc.C) {
   196  	var opts ssh.Options
   197  	err := ioutil.WriteFile(s.fakessh, []byte("#!/bin/sh\nexit 42"), 0755)
   198  	c.Assert(err, jc.ErrorIsNil)
   199  	command := s.client.Command("ignored", []string{echoCommand, "foo"}, &opts)
   200  	err = command.Run()
   201  	c.Assert(cmd.IsRcPassthroughError(err), jc.IsTrue)
   202  }
   203  
   204  func (s *SSHCommandSuite) TestCommandDefaultIdentities(c *gc.C) {
   205  	var opts ssh.Options
   206  	tempdir := c.MkDir()
   207  	def1 := filepath.Join(tempdir, "def1")
   208  	def2 := filepath.Join(tempdir, "def2")
   209  	s.PatchValue(ssh.DefaultIdentities, []string{def1, def2})
   210  	// If no identities are specified, then the defaults aren't added.
   211  	s.assertCommandArgs(c, s.commandOptions([]string{echoCommand, "123"}, &opts),
   212  		fmt.Sprintf("%s -o StrictHostKeyChecking no -o PasswordAuthentication no -o ServerAliveInterval 30 localhost %s 123",
   213  			s.fakessh, echoCommand),
   214  	)
   215  	// If identities are specified, then the defaults are must added.
   216  	// Only the defaults that exist on disk will be added.
   217  	err := ioutil.WriteFile(def2, nil, 0644)
   218  	c.Assert(err, jc.ErrorIsNil)
   219  	opts.SetIdentities("x", "y")
   220  	s.assertCommandArgs(c, s.commandOptions([]string{echoCommand, "123"}, &opts),
   221  		fmt.Sprintf("%s -o StrictHostKeyChecking no -o PasswordAuthentication no -o ServerAliveInterval 30 -i x -i y -i %s localhost %s 123",
   222  			s.fakessh, def2, echoCommand),
   223  	)
   224  }
   225  
   226  func (s *SSHCommandSuite) TestCopyReader(c *gc.C) {
   227  	client := &fakeClient{}
   228  	r := bytes.NewBufferString("<data>")
   229  
   230  	err := ssh.TestCopyReader(client, "foo@bar.com:baz", "/tmp/blah", r, nil)
   231  	c.Assert(err, jc.ErrorIsNil)
   232  
   233  	client.checkCalls(c, "foo@bar.com:baz", []string{"cat - > /tmp/blah"}, nil, nil, "Command")
   234  	client.impl.checkCalls(c, r, nil, nil, "SetStdio", "Start", "Wait")
   235  }