github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/utils/ssh/ssh_test.go (about)

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