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 }