github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cloudconfig/sshinit/configure.go (about) 1 // Copyright 2013, 2015 Canonical Ltd. 2 // Copyright 2015 Cloudbase Solutions SRL 3 // Licensed under the AGPLv3, see LICENCE file for details. 4 5 package sshinit 6 7 import ( 8 "encoding/base64" 9 "fmt" 10 "io" 11 "strings" 12 13 "github.com/juju/loggo" 14 "github.com/juju/utils" 15 "github.com/juju/utils/ssh" 16 17 "github.com/juju/juju/cloudconfig/cloudinit" 18 ) 19 20 var logger = loggo.GetLogger("juju.cloudinit.sshinit") 21 22 type ConfigureParams struct { 23 // Host is the host to configure, in the format [user@]hostname. 24 Host string 25 26 // Client is the SSH client to connect with. 27 // If Client is nil, ssh.DefaultClient will be used. 28 Client ssh.Client 29 30 // Config is the cloudinit config to carry out. 31 Config cloudinit.CloudConfig 32 33 // ProgressWriter is an io.Writer to which progress will be written, 34 // for realtime feedback. 35 ProgressWriter io.Writer 36 37 // Series is the series of the machine on which the script will be carried out 38 Series string 39 } 40 41 // RunConfigureScript connects to the specified host over 42 // SSH, and executes the provided script which is expected 43 // to have been returned by cloudinit ConfigureScript. 44 func RunConfigureScript(script string, params ConfigureParams) error { 45 logger.Tracef("Running script on %s: %s", params.Host, script) 46 47 encoded := base64.StdEncoding.EncodeToString([]byte(` 48 set -e 49 tmpfile=$(mktemp) 50 trap "rm -f $tmpfile" EXIT 51 cat > $tmpfile 52 /bin/bash $tmpfile 53 `)) 54 55 // bash will read a byte at a time when consuming commands 56 // from stdin. We avoid sending the entire script -- which 57 // will be very large when uploading tools -- directly to 58 // bash for this reason. Instead, run cat which will write 59 // the script to disk, and then execute it from there. 60 cmd := ssh.Command(params.Host, []string{ 61 "sudo", "/bin/bash", "-c", 62 // The outer bash interprets the $(...), and executes 63 // the decoded script in the nested bash. This avoids 64 // linebreaks in the commandline, which the go.crypto- 65 // based client has trouble with. 66 fmt.Sprintf( 67 `/bin/bash -c "$(echo %s | base64 -d)"`, 68 utils.ShQuote(encoded), 69 ), 70 }, nil) 71 72 cmd.Stdin = strings.NewReader(script) 73 cmd.Stderr = params.ProgressWriter 74 return cmd.Run() 75 }