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  }