github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/cmd/juju/scp.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  	"fmt"
     8  	"strings"
     9  
    10  	"launchpad.net/juju-core/cmd"
    11  	"launchpad.net/juju-core/utils/ssh"
    12  )
    13  
    14  // SCPCommand is responsible for launching a scp command to copy files to/from remote machine(s)
    15  type SCPCommand struct {
    16  	SSHCommon
    17  }
    18  
    19  const scpDoc = `
    20  Launch an scp command to copy files. Each argument <file1> ... <file2>
    21  is either local file path or remote locations of the form <target>:<path>,
    22  where <target> can be either a machine id as listed by "juju status" in the
    23  "machines" section or a unit name as listed in the "services" section.
    24  Any extra arguments to scp can be passed after at the end. In case OpenSSH
    25  scp command cannot be found in the system PATH environment variable, this
    26  command is also not available for use. Please refer to the man page of scp(1)
    27  for the supported extra arguments.
    28  
    29  Examples:
    30  
    31  Copy a single file from machine 2 to the local machine:
    32  
    33      juju scp 2:/var/log/syslog .
    34  
    35  Copy 2 files from two units to the local backup/ directory, passing -v
    36  to scp as an extra argument:
    37  
    38      juju scp ubuntu/0:/path/file1 ubuntu/1:/path/file2 backup/ -v
    39  
    40  Recursively copy the directory /var/log/mongodb/ on the first mongodb
    41  server to the local directory remote-logs:
    42  
    43      juju scp mongodb/0:/var/log/mongodb/ remote-logs/ -r
    44  
    45  Copy a local file to the second apache unit of the environment "testing":
    46  
    47      juju scp -e testing foo.txt apache2/1:
    48  `
    49  
    50  func (c *SCPCommand) Info() *cmd.Info {
    51  	return &cmd.Info{
    52  		Name:    "scp",
    53  		Args:    "<file1> ... <file2> [scp-option...]",
    54  		Purpose: "launch a scp command to copy files to/from remote machine(s)",
    55  		Doc:     scpDoc,
    56  	}
    57  }
    58  
    59  func (c *SCPCommand) Init(args []string) error {
    60  	if len(args) < 2 {
    61  		return fmt.Errorf("at least two arguments required")
    62  	}
    63  	c.Args = args
    64  	return nil
    65  }
    66  
    67  // Run resolves c.Target to a machine, or host of a unit and
    68  // forks ssh with c.Args, if provided.
    69  func (c *SCPCommand) Run(ctx *cmd.Context) error {
    70  	var err error
    71  	c.apiClient, err = c.initAPIClient()
    72  	if err != nil {
    73  		return err
    74  	}
    75  	defer c.apiClient.Close()
    76  
    77  	// Parse all arguments, translating those in the form 0:/somepath
    78  	// or service/0:/somepath into ubuntu@machine:/somepath so they
    79  	// can be given to scp as targets (source(s) and destination(s)),
    80  	// and passing any others that look like extra arguments (starting
    81  	// with "-") verbatim to scp.
    82  	var targets, extraArgs []string
    83  	for i, arg := range c.Args {
    84  		if v := strings.SplitN(arg, ":", 2); len(v) > 1 {
    85  			host, err := c.hostFromTarget(v[0])
    86  			if err != nil {
    87  				return err
    88  			}
    89  			// To ensure this works with IPv6 addresses, we need to
    90  			// wrap the host with \[..\], so the colons inside will be
    91  			// interpreted as part of the address and the last one as
    92  			// separator between host and remote path.
    93  			if strings.Contains(host, ":") {
    94  				host = fmt.Sprintf(`\[%s\]`, host)
    95  			}
    96  			targets = append(targets, "ubuntu@"+host+":"+v[1])
    97  			continue
    98  		}
    99  		if strings.HasPrefix(arg, "-") {
   100  			if i != len(c.Args)-1 {
   101  				return fmt.Errorf("unexpected argument %q; extra arguments must be last", arg)
   102  			}
   103  			extraArgs = append(extraArgs, arg)
   104  		} else {
   105  			// Local path
   106  			targets = append(targets, arg)
   107  		}
   108  	}
   109  	return ssh.Copy(targets, extraArgs, nil)
   110  }