2019-10-25 14:58:59 +02:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Command defines the information required for executing a call to any executable
|
|
|
|
type Command struct {
|
|
|
|
dir string
|
2019-11-19 15:29:53 +02:00
|
|
|
stdout io.Writer
|
|
|
|
stderr io.Writer
|
2020-01-24 15:30:27 +02:00
|
|
|
env []string
|
2019-10-25 14:58:59 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 13:11:22 +02:00
|
|
|
// SetDir sets the working directory for the execution
|
|
|
|
func (c *Command) SetDir(d string) {
|
2019-10-25 14:58:59 +02:00
|
|
|
c.dir = d
|
|
|
|
}
|
|
|
|
|
2020-02-27 13:11:22 +02:00
|
|
|
// SetEnv sets explicit environment variables to be used for execution
|
|
|
|
func (c *Command) SetEnv(e []string) {
|
2020-01-24 15:30:27 +02:00
|
|
|
c.env = e
|
|
|
|
}
|
|
|
|
|
2019-11-19 15:29:53 +02:00
|
|
|
// Stdout ..
|
|
|
|
func (c *Command) Stdout(stdout io.Writer) {
|
|
|
|
c.stdout = stdout
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stderr ..
|
|
|
|
func (c *Command) Stderr(stderr io.Writer) {
|
|
|
|
c.stderr = stderr
|
|
|
|
}
|
|
|
|
|
2019-10-25 14:58:59 +02:00
|
|
|
// ExecCommand defines how to execute os commands
|
|
|
|
var ExecCommand = exec.Command
|
|
|
|
|
|
|
|
// RunShell runs the specified command on the shell
|
|
|
|
func (c *Command) RunShell(shell, script string) error {
|
|
|
|
|
2019-11-19 15:29:53 +02:00
|
|
|
_out, _err := prepareOut(c.stdout, c.stderr)
|
2019-10-25 14:58:59 +02:00
|
|
|
|
|
|
|
cmd := ExecCommand(shell)
|
|
|
|
|
2020-01-24 15:30:27 +02:00
|
|
|
if len(c.dir) > 0 {
|
|
|
|
cmd.Dir = c.dir
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(c.env) > 0 {
|
|
|
|
cmd.Env = c.env
|
|
|
|
}
|
|
|
|
|
2019-10-25 14:58:59 +02:00
|
|
|
in := bytes.Buffer{}
|
|
|
|
in.Write([]byte(script))
|
|
|
|
cmd.Stdin = &in
|
|
|
|
|
|
|
|
if err := runCmd(cmd, _out, _err); err != nil {
|
|
|
|
return errors.Wrapf(err, "running shell script failed with %v", shell)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RunExecutable runs the specified executable with parameters
|
|
|
|
func (c *Command) RunExecutable(executable string, params ...string) error {
|
|
|
|
|
2019-11-19 15:29:53 +02:00
|
|
|
_out, _err := prepareOut(c.stdout, c.stderr)
|
2019-10-25 14:58:59 +02:00
|
|
|
|
|
|
|
cmd := ExecCommand(executable, params...)
|
|
|
|
|
|
|
|
if len(c.dir) > 0 {
|
|
|
|
cmd.Dir = c.dir
|
|
|
|
}
|
|
|
|
|
2020-01-24 15:30:27 +02:00
|
|
|
if len(c.env) > 0 {
|
|
|
|
cmd.Env = c.env
|
|
|
|
}
|
|
|
|
|
2019-10-25 14:58:59 +02:00
|
|
|
if err := runCmd(cmd, _out, _err); err != nil {
|
|
|
|
return errors.Wrapf(err, "running command '%v' failed", executable)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func runCmd(cmd *exec.Cmd, _out, _err io.Writer) error {
|
|
|
|
|
|
|
|
stdout, stderr, err := cmdPipes(cmd)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "getting commmand pipes failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
err = cmd.Start()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "starting command failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(2)
|
|
|
|
|
|
|
|
var errStdout, errStderr error
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
_, errStdout = io.Copy(_out, stdout)
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
_, errStderr = io.Copy(_err, stderr)
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
err = cmd.Wait()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "cmd.Run() failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
if errStdout != nil || errStderr != nil {
|
|
|
|
return fmt.Errorf("failed to capture stdout/stderr: '%v'/'%v'", errStdout, errStderr)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func prepareOut(stdout, stderr io.Writer) (io.Writer, io.Writer) {
|
|
|
|
|
|
|
|
//ToDo: check use of multiwriter instead to always write into os.Stdout and os.Stdin?
|
|
|
|
//stdout := io.MultiWriter(os.Stdout, &stdoutBuf)
|
|
|
|
//stderr := io.MultiWriter(os.Stderr, &stderrBuf)
|
|
|
|
|
|
|
|
if stdout == nil {
|
|
|
|
stdout = os.Stdout
|
|
|
|
}
|
|
|
|
if stderr == nil {
|
|
|
|
stderr = os.Stderr
|
|
|
|
}
|
|
|
|
|
|
|
|
return stdout, stderr
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdPipes(cmd *exec.Cmd) (io.ReadCloser, io.ReadCloser, error) {
|
|
|
|
stdout, err := cmd.StdoutPipe()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, errors.Wrap(err, "getting Stdout pipe failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
stderr, err := cmd.StderrPipe()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, errors.Wrap(err, "getting Stderr pipe failed")
|
|
|
|
}
|
|
|
|
return stdout, stderr, nil
|
|
|
|
}
|