github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/cmd/juju/scp_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "bytes" 8 "fmt" 9 "io/ioutil" 10 "net/url" 11 "path/filepath" 12 "strings" 13 14 "github.com/juju/charm" 15 charmtesting "github.com/juju/charm/testing" 16 jc "github.com/juju/testing/checkers" 17 gc "launchpad.net/gocheck" 18 19 "github.com/juju/juju/network" 20 coretesting "github.com/juju/juju/testing" 21 ) 22 23 var _ = gc.Suite(&SCPSuite{}) 24 var _ = gc.Suite(&expandArgsSuite{}) 25 26 type SCPSuite struct { 27 SSHCommonSuite 28 } 29 30 type expandArgsSuite struct{} 31 32 var scpTests = []struct { 33 about string 34 args []string 35 result string 36 proxy bool 37 error string 38 }{ 39 { 40 about: "scp from machine 0 to current dir", 41 args: []string{"0:foo", "."}, 42 result: commonArgsNoProxy + "ubuntu@dummyenv-0.dns:foo .\n", 43 }, { 44 about: "scp from machine 0 to current dir with extra args", 45 args: []string{"0:foo", ".", "-rv", "-o", "SomeOption"}, 46 result: commonArgsNoProxy + "ubuntu@dummyenv-0.dns:foo . -rv -o SomeOption\n", 47 }, { 48 about: "scp from current dir to machine 0", 49 args: []string{"foo", "0:"}, 50 result: commonArgsNoProxy + "foo ubuntu@dummyenv-0.dns:\n", 51 }, { 52 about: "scp from current dir to machine 0 with extra args", 53 args: []string{"foo", "0:", "-r", "-v"}, 54 result: commonArgsNoProxy + "foo ubuntu@dummyenv-0.dns: -r -v\n", 55 }, { 56 about: "scp from machine 0 to unit mysql/0", 57 args: []string{"0:foo", "mysql/0:/foo"}, 58 result: commonArgsNoProxy + "ubuntu@dummyenv-0.dns:foo ubuntu@dummyenv-0.dns:/foo\n", 59 }, { 60 about: "scp from machine 0 to unit mysql/0 and extra args", 61 args: []string{"0:foo", "mysql/0:/foo", "-q"}, 62 result: commonArgsNoProxy + "ubuntu@dummyenv-0.dns:foo ubuntu@dummyenv-0.dns:/foo -q\n", 63 }, { 64 about: "scp from machine 0 to unit mysql/0 and extra args before", 65 args: []string{"-q", "-r", "0:foo", "mysql/0:/foo"}, 66 result: commonArgsNoProxy + "-q -r ubuntu@dummyenv-0.dns:foo ubuntu@dummyenv-0.dns:/foo\n", 67 }, { 68 about: "scp two local files to unit mysql/0", 69 args: []string{"file1", "file2", "mysql/0:/foo/"}, 70 result: commonArgsNoProxy + "file1 file2 ubuntu@dummyenv-0.dns:/foo/\n", 71 }, { 72 about: "scp from unit mongodb/1 to unit mongodb/0 and multiple extra args", 73 args: []string{"mongodb/1:foo", "mongodb/0:", "-r", "-v", "-q", "-l5"}, 74 result: commonArgsNoProxy + "ubuntu@dummyenv-2.dns:foo ubuntu@dummyenv-1.dns: -r -v -q -l5\n", 75 }, { 76 about: "scp works with IPv6 addresses", 77 args: []string{"ipv6-svc/0:foo", "bar"}, 78 result: commonArgsNoProxy + `ubuntu@\[2001:db8::\]:foo bar` + "\n", 79 }, { 80 about: "scp from machine 0 to unit mysql/0 with proxy", 81 args: []string{"0:foo", "mysql/0:/foo"}, 82 result: commonArgs + "ubuntu@dummyenv-0.internal:foo ubuntu@dummyenv-0.internal:/foo\n", 83 proxy: true, 84 }, { 85 args: []string{"0:foo", ".", "-rv", "-o", "SomeOption"}, 86 result: commonArgsNoProxy + "ubuntu@dummyenv-0.dns:foo . -rv -o SomeOption\n", 87 }, { 88 args: []string{"foo", "0:", "-r", "-v"}, 89 result: commonArgsNoProxy + "foo ubuntu@dummyenv-0.dns: -r -v\n", 90 }, { 91 args: []string{"mongodb/1:foo", "mongodb/0:", "-r", "-v", "-q", "-l5"}, 92 result: commonArgsNoProxy + "ubuntu@dummyenv-2.dns:foo ubuntu@dummyenv-1.dns: -r -v -q -l5\n", 93 }, { 94 about: "scp from unit mongodb/1 to unit mongodb/0 with a --", 95 args: []string{"--", "-r", "-v", "mongodb/1:foo", "mongodb/0:", "-q", "-l5"}, 96 result: commonArgsNoProxy + "-- -r -v ubuntu@dummyenv-2.dns:foo ubuntu@dummyenv-1.dns: -q -l5\n", 97 }, { 98 about: "scp with no such machine", 99 args: []string{"5:foo", "bar"}, 100 error: "machine 5 not found", 101 }, 102 } 103 104 func (s *SCPSuite) TestSCPCommand(c *gc.C) { 105 m := s.makeMachines(4, c, true) 106 ch := charmtesting.Charms.Dir("dummy") 107 curl := charm.MustParseURL( 108 fmt.Sprintf("local:quantal/%s-%d", ch.Meta().Name, ch.Revision()), 109 ) 110 bundleURL, err := url.Parse("http://bundles.testing.invalid/dummy-1") 111 c.Assert(err, gc.IsNil) 112 dummyCharm, err := s.State.AddCharm(ch, curl, bundleURL, "dummy-1-sha256") 113 c.Assert(err, gc.IsNil) 114 srv := s.AddTestingService(c, "mysql", dummyCharm) 115 s.addUnit(srv, m[0], c) 116 117 srv = s.AddTestingService(c, "mongodb", dummyCharm) 118 s.addUnit(srv, m[1], c) 119 s.addUnit(srv, m[2], c) 120 srv = s.AddTestingService(c, "ipv6-svc", dummyCharm) 121 s.addUnit(srv, m[3], c) 122 // TODO(dimitern) This is a horrible hack and needs to 123 // be fixed as we implement proper #IPv6 support. 124 // Simulate machine 3 has a public IPv6 address. 125 ipv6Addr := network.Address{ 126 Value: "2001:db8::", 127 Type: network.IPv4Address, // ..because SelectPublicAddress ignores IPv6 addresses 128 Scope: network.ScopePublic, 129 } 130 err = m[3].SetAddresses(ipv6Addr) 131 c.Assert(err, gc.IsNil) 132 133 for i, t := range scpTests { 134 c.Logf("test %d: %s -> %s\n", i, t.about, t.args) 135 ctx := coretesting.Context(c) 136 scpcmd := &SCPCommand{} 137 scpcmd.proxy = t.proxy 138 139 err := scpcmd.Init(t.args) 140 c.Check(err, gc.IsNil) 141 err = scpcmd.Run(ctx) 142 if t.error != "" { 143 c.Check(err, gc.ErrorMatches, t.error) 144 c.Check(t.result, gc.Equals, "") 145 } else { 146 c.Check(err, gc.IsNil) 147 // we suppress stdout from scp 148 c.Check(ctx.Stderr.(*bytes.Buffer).String(), gc.Equals, "") 149 c.Check(ctx.Stdout.(*bytes.Buffer).String(), gc.Equals, "") 150 data, err := ioutil.ReadFile(filepath.Join(s.bin, "scp.args")) 151 c.Check(err, gc.IsNil) 152 c.Check(string(data), gc.Equals, t.result) 153 } 154 } 155 } 156 157 var hostsFromTargets = map[string]string{ 158 "0": "dummyenv-0.dns", 159 "mysql/0": "dummyenv-0.dns", 160 "mongodb/0": "dummyenv-1.dns", 161 "mongodb/1": "dummyenv-2.dns", 162 "ipv6-svc/0": "2001:db8::", 163 } 164 165 func dummyHostsFromTarget(target string) (string, error) { 166 if res, ok := hostsFromTargets[target]; ok { 167 return res, nil 168 } 169 return target, nil 170 } 171 172 func (s *expandArgsSuite) TestSCPExpandArgs(c *gc.C) { 173 for i, t := range scpTests { 174 if t.error != "" { 175 // We are just running a focused set of tests on 176 // expandArgs, we aren't implementing the full 177 // hostsFromTargets to actually trigger errors 178 continue 179 } 180 c.Logf("test %d: %s -> %s\n", i, t.about, t.args) 181 // expandArgs doesn't add the commonArgs prefix, so strip it 182 // off, along with the trailing '\n' 183 var argString string 184 if t.proxy { 185 c.Check(strings.HasPrefix(t.result, commonArgs), jc.IsTrue) 186 argString = t.result[len(commonArgs):] 187 } else { 188 c.Check(strings.HasPrefix(t.result, commonArgsNoProxy), jc.IsTrue) 189 argString = t.result[len(commonArgsNoProxy):] 190 } 191 c.Check(strings.HasSuffix(argString, "\n"), jc.IsTrue) 192 argString = argString[:len(argString)-1] 193 args := strings.Split(argString, " ") 194 expanded, err := expandArgs(t.args, func(target string) (string, error) { 195 if res, ok := hostsFromTargets[target]; ok { 196 if t.proxy { 197 res = strings.Replace(res, ".dns", ".internal", 1) 198 } 199 return res, nil 200 } 201 return target, nil 202 }) 203 c.Check(err, gc.IsNil) 204 c.Check(expanded, gc.DeepEquals, args) 205 } 206 } 207 208 var expandTests = []struct { 209 about string 210 args []string 211 result []string 212 }{ 213 { 214 "don't expand params that start with '-'", 215 []string{"-0:stuff", "0:foo", "."}, 216 []string{"-0:stuff", "ubuntu@dummyenv-0.dns:foo", "."}, 217 }, 218 } 219 220 func (s *expandArgsSuite) TestExpandArgs(c *gc.C) { 221 for i, t := range expandTests { 222 c.Logf("test %d: %s -> %s\n", i, t.about, t.args) 223 expanded, err := expandArgs(t.args, dummyHostsFromTarget) 224 c.Check(err, gc.IsNil) 225 c.Check(expanded, gc.DeepEquals, t.result) 226 } 227 } 228 229 func (s *expandArgsSuite) TestExpandArgsPropagatesErrors(c *gc.C) { 230 erroringHostFromTargets := func(string) (string, error) { 231 return "", fmt.Errorf("this is my error") 232 } 233 expanded, err := expandArgs([]string{"foo:1", "bar"}, erroringHostFromTargets) 234 c.Assert(err, gc.ErrorMatches, "this is my error") 235 c.Check(expanded, gc.IsNil) 236 }