github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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 jc "github.com/juju/testing/checkers" 15 gc "launchpad.net/gocheck" 16 17 "github.com/juju/juju/charm" 18 charmtesting "github.com/juju/juju/charm/testing" 19 "github.com/juju/juju/instance" 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 // Simulate machine 3 has a public IPv6 address. 123 ipv6Addr := instance.Address{ 124 Value: "2001:db8::", 125 Type: instance.Ipv4Address, // ..because SelectPublicAddress ignores IPv6 addresses 126 NetworkScope: instance.NetworkPublic, 127 } 128 err = m[3].SetAddresses(ipv6Addr) 129 c.Assert(err, gc.IsNil) 130 131 for i, t := range scpTests { 132 c.Logf("test %d: %s -> %s\n", i, t.about, t.args) 133 ctx := coretesting.Context(c) 134 scpcmd := &SCPCommand{} 135 scpcmd.proxy = t.proxy 136 137 err := scpcmd.Init(t.args) 138 c.Check(err, gc.IsNil) 139 err = scpcmd.Run(ctx) 140 if t.error != "" { 141 c.Check(err, gc.ErrorMatches, t.error) 142 c.Check(t.result, gc.Equals, "") 143 } else { 144 c.Check(err, gc.IsNil) 145 // we suppress stdout from scp 146 c.Check(ctx.Stderr.(*bytes.Buffer).String(), gc.Equals, "") 147 c.Check(ctx.Stdout.(*bytes.Buffer).String(), gc.Equals, "") 148 data, err := ioutil.ReadFile(filepath.Join(s.bin, "scp.args")) 149 c.Check(err, gc.IsNil) 150 c.Check(string(data), gc.Equals, t.result) 151 } 152 } 153 } 154 155 var hostsFromTargets = map[string]string{ 156 "0": "dummyenv-0.dns", 157 "mysql/0": "dummyenv-0.dns", 158 "mongodb/0": "dummyenv-1.dns", 159 "mongodb/1": "dummyenv-2.dns", 160 "ipv6-svc/0": "2001:db8::", 161 } 162 163 func dummyHostsFromTarget(target string) (string, error) { 164 if res, ok := hostsFromTargets[target]; ok { 165 return res, nil 166 } 167 return target, nil 168 } 169 170 func (s *expandArgsSuite) TestSCPExpandArgs(c *gc.C) { 171 for i, t := range scpTests { 172 if t.error != "" { 173 // We are just running a focused set of tests on 174 // expandArgs, we aren't implementing the full 175 // hostsFromTargets to actually trigger errors 176 continue 177 } 178 c.Logf("test %d: %s -> %s\n", i, t.about, t.args) 179 // expandArgs doesn't add the commonArgs prefix, so strip it 180 // off, along with the trailing '\n' 181 var argString string 182 if t.proxy { 183 c.Check(strings.HasPrefix(t.result, commonArgs), jc.IsTrue) 184 argString = t.result[len(commonArgs):] 185 } else { 186 c.Check(strings.HasPrefix(t.result, commonArgsNoProxy), jc.IsTrue) 187 argString = t.result[len(commonArgsNoProxy):] 188 } 189 c.Check(strings.HasSuffix(argString, "\n"), jc.IsTrue) 190 argString = argString[:len(argString)-1] 191 args := strings.Split(argString, " ") 192 expanded, err := expandArgs(t.args, func(target string) (string, error) { 193 if res, ok := hostsFromTargets[target]; ok { 194 if t.proxy { 195 res = strings.Replace(res, ".dns", ".internal", 1) 196 } 197 return res, nil 198 } 199 return target, nil 200 }) 201 c.Check(err, gc.IsNil) 202 c.Check(expanded, gc.DeepEquals, args) 203 } 204 } 205 206 var expandTests = []struct { 207 about string 208 args []string 209 result []string 210 }{ 211 { 212 "don't expand params that start with '-'", 213 []string{"-0:stuff", "0:foo", "."}, 214 []string{"-0:stuff", "ubuntu@dummyenv-0.dns:foo", "."}, 215 }, 216 } 217 218 func (s *expandArgsSuite) TestExpandArgs(c *gc.C) { 219 for i, t := range expandTests { 220 c.Logf("test %d: %s -> %s\n", i, t.about, t.args) 221 expanded, err := expandArgs(t.args, dummyHostsFromTarget) 222 c.Check(err, gc.IsNil) 223 c.Check(expanded, gc.DeepEquals, t.result) 224 } 225 } 226 227 func (s *expandArgsSuite) TestExpandArgsPropagatesErrors(c *gc.C) { 228 erroringHostFromTargets := func(string) (string, error) { 229 return "", fmt.Errorf("this is my error") 230 } 231 expanded, err := expandArgs([]string{"foo:1", "bar"}, erroringHostFromTargets) 232 c.Assert(err, gc.ErrorMatches, "this is my error") 233 c.Check(expanded, gc.IsNil) 234 }