1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
14
utilexec "launchpad.net/juju-core/utils/exec"
17
// ExecParams are used for the parameters for ExecuteCommandOnMachine.
18
type ExecParams struct {
25
// ExecuteCommandOnMachine will execute the command passed through on
26
// the host specified. This is done using ssh, and passing the commands
27
// through /bin/bash. If the command is not finished within the timeout
28
// specified, an error is returned. Any output captured during that time
29
// is also returned in the remote response.
30
func ExecuteCommandOnMachine(params ExecParams) (result utilexec.ExecResponse, err error) {
31
// execute bash accepting commands on stdin
32
if params.Host == "" {
33
return result, fmt.Errorf("missing host address")
35
logger.Debugf("execute on %s", params.Host)
37
if params.IdentityFile != "" {
38
options.SetIdentities(params.IdentityFile)
40
command := Command(params.Host, []string{"/bin/bash", "-s"}, &options)
41
// start a go routine to do the actual execution
42
var stdout, stderr bytes.Buffer
43
command.Stdout = &stdout
44
command.Stderr = &stderr
45
command.Stdin = strings.NewReader(params.Command + "\n")
47
if err = command.Start(); err != nil {
50
commandDone := make(chan error)
52
defer close(commandDone)
54
logger.Debugf("command.Wait finished: %v", err)
59
case err = <-commandDone:
60
logger.Debugf("select from commandDone channel: %v", err)
61
// command finished and returned us the results
62
if ee, ok := err.(*exec.ExitError); ok && err != nil {
63
status := ee.ProcessState.Sys().(syscall.WaitStatus)
65
// A non-zero return code isn't considered an error here.
66
result.Code = status.ExitStatus()
71
case <-time.After(params.Timeout):
72
logger.Infof("killing the command due to timeout")
73
err = fmt.Errorf("command timed out")
76
// In either case, gather as much as we have from stdout and stderr
77
result.Stderr = stderr.Bytes()
78
result.Stdout = stdout.Bytes()