github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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/juju/cmd" 11 "github.com/juju/juju/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 -v ubuntu/0:/path/file1 ubuntu/1:/path/file2 backup/ 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 -r mongodb/0:/var/log/mongodb/ remote-logs/ 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 // expandArgs takes a list of arguments and looks for ones in the form of 68 // 0:some/path or service/0:some/path, and translates them into 69 // ubuntu@machine:some/path so they can be passed as arguments to scp, and pass 70 // the rest verbatim on to scp 71 func expandArgs(args []string, hostFromTarget func(string) (string, error)) ([]string, error) { 72 outArgs := make([]string, len(args)) 73 for i, arg := range args { 74 v := strings.SplitN(arg, ":", 2) 75 if strings.HasPrefix(arg, "-") || len(v) <= 1 { 76 // Can't be an interesting target, so just pass it along 77 outArgs[i] = arg 78 continue 79 } 80 host, err := hostFromTarget(v[0]) 81 if err != nil { 82 return nil, err 83 } 84 // To ensure this works with IPv6 addresses, we need to 85 // wrap the host with \[..\], so the colons inside will be 86 // interpreted as part of the address and the last one as 87 // separator between host and remote path. 88 if strings.Contains(host, ":") { 89 host = fmt.Sprintf(`\[%s\]`, host) 90 } 91 outArgs[i] = "ubuntu@" + host + ":" + v[1] 92 } 93 return outArgs, nil 94 } 95 96 // Run resolves c.Target to a machine, or host of a unit and 97 // forks ssh with c.Args, if provided. 98 func (c *SCPCommand) Run(ctx *cmd.Context) error { 99 var err error 100 c.apiClient, err = c.initAPIClient() 101 if err != nil { 102 return err 103 } 104 defer c.apiClient.Close() 105 106 options, err := c.getSSHOptions(false) 107 if err != nil { 108 return err 109 } 110 args, err := expandArgs(c.Args, c.hostFromTarget) 111 if err != nil { 112 return err 113 } 114 return ssh.Copy(args, options) 115 }