github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/juju/commands/scp.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package commands
     5  
     6  import (
     7  	"net"
     8  	"strings"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/utils/ssh"
    13  
    14  	"github.com/juju/juju/cmd/modelcmd"
    15  )
    16  
    17  var usageSCPSummary = `
    18  Transfers files to/from a Juju machine.`[1:]
    19  
    20  var usageSCPDetails = `
    21  The source or destination arguments may either be a local path or a remote
    22  location. The syntax for a remote location is:
    23  
    24      [<user>@]<target>:[<path>]
    25  
    26  If the user is not specified, "ubuntu" is used. If <path> is not specified, it
    27  defaults to the home directory of the remote user account.
    28  
    29  The <target> may be either a 'unit name' or a 'machine id'. These can be
    30  obtained from the output of "juju status".
    31  
    32  Options specific to scp can be provided after a "--". Refer to the scp(1) man
    33  page for an explanation of those options. The "-r" option to recursively copy a
    34  directory is particularly useful.
    35  
    36  The SSH host keys of the target are verified. The --no-host-key-checks option
    37  can be used to disable these checks. Use of this option is not recommended as
    38  it opens up the possibility of a man-in-the-middle attack.
    39  
    40  Examples:
    41  
    42  Copy file /var/log/syslog from machine 2 to the client's current working
    43  directory:
    44  
    45      juju scp 2:/var/log/syslog .
    46  
    47  Recursively copy the /var/log/mongodb directory from a mongodb unit to the
    48  client's local remote-logs directory:
    49  
    50      juju scp -- -r mongodb/0:/var/log/mongodb/ remote-logs
    51  
    52  Copy foo.txt from the client's current working directory to an apache2 unit of
    53  model "prod". Proxy the SSH connection through the controller and turn on scp
    54  compression:
    55  
    56      juju scp -m prod --proxy -- -C foo.txt apache2/1:
    57  
    58  Copy multiple files from the client's current working directory to machine 2:
    59  
    60      juju scp file1 file2 2:
    61  
    62  Copy multiple files from the bob user account on machine 3 to the client's
    63  current working directory:
    64  
    65      juju scp bob@3:'file1 file2' .
    66  
    67  Copy file.dat from machine 0 to the machine hosting unit foo/0 (-3
    68  causes the transfer to be made via the client):
    69  
    70      juju scp -- -3 0:file.dat foo/0:
    71  
    72  See also: 
    73      ssh`
    74  
    75  func newSCPCommand() cmd.Command {
    76  	return modelcmd.Wrap(&scpCommand{})
    77  }
    78  
    79  // scpCommand is responsible for launching a scp command to copy files to/from remote machine(s)
    80  type scpCommand struct {
    81  	SSHCommon
    82  }
    83  
    84  func (c *scpCommand) Info() *cmd.Info {
    85  	return &cmd.Info{
    86  		Name:    "scp",
    87  		Args:    "<source> <destination>",
    88  		Purpose: usageSCPSummary,
    89  		Doc:     usageSCPDetails,
    90  	}
    91  }
    92  
    93  func (c *scpCommand) Init(args []string) error {
    94  	if len(args) < 2 {
    95  		return errors.Errorf("at least two arguments required")
    96  	}
    97  	c.Args = args
    98  	return nil
    99  }
   100  
   101  // Run resolves c.Target to a machine, or host of a unit and
   102  // forks ssh with c.Args, if provided.
   103  func (c *scpCommand) Run(ctx *cmd.Context) error {
   104  	err := c.initRun()
   105  	if err != nil {
   106  		return errors.Trace(err)
   107  	}
   108  	defer c.cleanupRun()
   109  
   110  	args, targets, err := expandArgs(c.Args, c.resolveTarget)
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	options, err := c.getSSHOptions(false, targets...)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	return ssh.Copy(args, options)
   121  }
   122  
   123  // expandArgs takes a list of arguments and looks for ones in the form of
   124  // 0:some/path or application/0:some/path, and translates them into
   125  // ubuntu@machine:some/path so they can be passed as arguments to scp, and pass
   126  // the rest verbatim on to scp
   127  func expandArgs(args []string, resolveTarget func(string) (*resolvedTarget, error)) (
   128  	[]string, []*resolvedTarget, error,
   129  ) {
   130  	outArgs := make([]string, len(args))
   131  	var targets []*resolvedTarget
   132  	for i, arg := range args {
   133  		v := strings.SplitN(arg, ":", 2)
   134  		if strings.HasPrefix(arg, "-") || len(v) <= 1 {
   135  			// Can't be an interesting target, so just pass it along
   136  			outArgs[i] = arg
   137  			continue
   138  		}
   139  
   140  		target, err := resolveTarget(v[0])
   141  		if err != nil {
   142  			return nil, nil, err
   143  		}
   144  		arg := net.JoinHostPort(target.host, v[1])
   145  		if target.user != "" {
   146  			arg = target.user + "@" + arg
   147  		}
   148  		outArgs[i] = arg
   149  
   150  		targets = append(targets, target)
   151  	}
   152  	return outArgs, targets, nil
   153  }