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  }