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 }