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