mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
ad8642e2c2
Since all use cases of ExitStatus are just putting it into fmt.Errorf, usually with the command string, have ExitStatus do that for the caller.
97 lines
2.2 KiB
Go
97 lines
2.2 KiB
Go
package remote
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"sync"
|
|
)
|
|
|
|
// Cmd represents a remote command being prepared or run.
|
|
type Cmd struct {
|
|
// Command is the command to run remotely. This is executed as if
|
|
// it were a shell command, so you are expected to do any shell escaping
|
|
// necessary.
|
|
Command string
|
|
|
|
// Stdin specifies the process's standard input. If Stdin is
|
|
// nil, the process reads from an empty bytes.Buffer.
|
|
Stdin io.Reader
|
|
|
|
// Stdout and Stderr represent the process's standard output and
|
|
// error.
|
|
//
|
|
// If either is nil, it will be set to ioutil.Discard.
|
|
Stdout io.Writer
|
|
Stderr io.Writer
|
|
|
|
// Once Wait returns, his will contain the exit code of the process.
|
|
exitStatus int
|
|
|
|
// Internal fields
|
|
exitCh chan struct{}
|
|
|
|
// err is used to store any error reported by the Communicator during
|
|
// execution.
|
|
err error
|
|
|
|
// This thing is a mutex, lock when making modifications concurrently
|
|
sync.Mutex
|
|
}
|
|
|
|
// Init must be called by the Communicator before executing the command.
|
|
func (c *Cmd) Init() {
|
|
c.Lock()
|
|
defer c.Unlock()
|
|
|
|
c.exitCh = make(chan struct{})
|
|
}
|
|
|
|
// SetExitStatus stores the exit status of the remote command as well as any
|
|
// communicator related error. SetExitStatus then unblocks any pending calls
|
|
// to Wait.
|
|
// This should only be called by communicators executing the remote.Cmd.
|
|
func (c *Cmd) SetExitStatus(status int, err error) {
|
|
c.Lock()
|
|
defer c.Unlock()
|
|
|
|
c.exitStatus = status
|
|
c.err = err
|
|
|
|
close(c.exitCh)
|
|
}
|
|
|
|
// Wait waits for the remote command to complete.
|
|
// Wait may return an error from the communicator, or an ExitError if the
|
|
// process exits with a non-zero exit status.
|
|
func (c *Cmd) Wait() error {
|
|
<-c.exitCh
|
|
|
|
c.Lock()
|
|
defer c.Unlock()
|
|
|
|
if c.err != nil || c.exitStatus != 0 {
|
|
return &ExitError{
|
|
Command: c.Command,
|
|
ExitStatus: c.exitStatus,
|
|
Err: c.err,
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ExitError is returned by Wait to indicate and error executing the remote
|
|
// command, or a non-zero exit status.
|
|
type ExitError struct {
|
|
Command string
|
|
ExitStatus int
|
|
Err error
|
|
}
|
|
|
|
func (e *ExitError) Error() string {
|
|
if e.Err != nil {
|
|
return fmt.Sprintf("error executing %q: %v", e.Command, e.Err)
|
|
}
|
|
return fmt.Sprintf("%q exit status: %d", e.Command, e.ExitStatus)
|
|
}
|