github.com/gavinw2006/hashicorp-terraform@v0.11.12-beta1/communicator/remote/command.go (about) 1 package remote 2 3 import ( 4 "fmt" 5 "io" 6 "sync" 7 ) 8 9 // Cmd represents a remote command being prepared or run. 10 type Cmd struct { 11 // Command is the command to run remotely. This is executed as if 12 // it were a shell command, so you are expected to do any shell escaping 13 // necessary. 14 Command string 15 16 // Stdin specifies the process's standard input. If Stdin is 17 // nil, the process reads from an empty bytes.Buffer. 18 Stdin io.Reader 19 20 // Stdout and Stderr represent the process's standard output and 21 // error. 22 // 23 // If either is nil, it will be set to ioutil.Discard. 24 Stdout io.Writer 25 Stderr io.Writer 26 27 // Once Wait returns, his will contain the exit code of the process. 28 exitStatus int 29 30 // Internal fields 31 exitCh chan struct{} 32 33 // err is used to store any error reported by the Communicator during 34 // execution. 35 err error 36 37 // This thing is a mutex, lock when making modifications concurrently 38 sync.Mutex 39 } 40 41 // Init must be called by the Communicator before executing the command. 42 func (c *Cmd) Init() { 43 c.Lock() 44 defer c.Unlock() 45 46 c.exitCh = make(chan struct{}) 47 } 48 49 // SetExitStatus stores the exit status of the remote command as well as any 50 // communicator related error. SetExitStatus then unblocks any pending calls 51 // to Wait. 52 // This should only be called by communicators executing the remote.Cmd. 53 func (c *Cmd) SetExitStatus(status int, err error) { 54 c.Lock() 55 defer c.Unlock() 56 57 c.exitStatus = status 58 c.err = err 59 60 close(c.exitCh) 61 } 62 63 // Wait waits for the remote command to complete. 64 // Wait may return an error from the communicator, or an ExitError if the 65 // process exits with a non-zero exit status. 66 func (c *Cmd) Wait() error { 67 <-c.exitCh 68 69 c.Lock() 70 defer c.Unlock() 71 72 if c.err != nil || c.exitStatus != 0 { 73 return &ExitError{ 74 Command: c.Command, 75 ExitStatus: c.exitStatus, 76 Err: c.err, 77 } 78 } 79 80 return nil 81 } 82 83 // ExitError is returned by Wait to indicate and error executing the remote 84 // command, or a non-zero exit status. 85 type ExitError struct { 86 Command string 87 ExitStatus int 88 Err error 89 } 90 91 func (e *ExitError) Error() string { 92 if e.Err != nil { 93 return fmt.Sprintf("error executing %q: %v", e.Command, e.Err) 94 } 95 return fmt.Sprintf("%q exit status: %d", e.Command, e.ExitStatus) 96 }