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