add the exit code and stdout/stderr output to the cmd struct
This commit is contained in:
parent
0d748fb0bf
commit
37ed8bfa83
@ -16,6 +16,7 @@ import (
|
||||
"strings"
|
||||
"text/template"
|
||||
"decl/internal/codec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var ErrUnknownCommand error = errors.New("Unable to find command in path")
|
||||
@ -26,17 +27,23 @@ type CommandExists func() error
|
||||
|
||||
type CommandArg string
|
||||
|
||||
type CommandInput string
|
||||
|
||||
type Command struct {
|
||||
Path string `json:"path" yaml:"path"`
|
||||
Args []CommandArg `json:"args" yaml:"args"`
|
||||
Env []string `json:"env" yaml:"env"`
|
||||
Split bool `json:"split" yaml:"split"`
|
||||
FailOnError bool `json:"failonerror" yaml:"failonerror"`
|
||||
StdinAvailable bool `json:"stdinavailable,omitempty" yaml:"stdinavailable,omitempty"`
|
||||
Executor CommandExecutor `json:"-" yaml:"-"`
|
||||
Extractor CommandExtractAttributes `json:"-" yaml:"-"`
|
||||
CommandExists CommandExists `json:"-" yaml:"-"`
|
||||
stdin io.Reader `json:"-" yaml:"-"`
|
||||
Path string `json:"path" yaml:"path"`
|
||||
Args []CommandArg `json:"args" yaml:"args"`
|
||||
Env []string `json:"env" yaml:"env"`
|
||||
Split bool `json:"split" yaml:"split"`
|
||||
FailOnError bool `json:"failonerror" yaml:"failonerror"`
|
||||
StdinAvailable bool `json:"stdinavailable,omitempty" yaml:"stdinavailable,omitempty"`
|
||||
ExitCode int `json:"exitcode,omitempty" yaml:"exitcode,omitempty"`
|
||||
Stdout string `json:"stdout,omitempty" yaml:"stdout,omitempty"`
|
||||
Stderr string `json:"stderr,omitempty" yaml:"stderr,omitempty"`
|
||||
Executor CommandExecutor `json:"-" yaml:"-"`
|
||||
Extractor CommandExtractAttributes `json:"-" yaml:"-"`
|
||||
CommandExists CommandExists `json:"-" yaml:"-"`
|
||||
Input CommandInput `json:"-" yaml:"-"`
|
||||
stdin io.Reader `json:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
func NewCommand() *Command {
|
||||
@ -45,7 +52,14 @@ func NewCommand() *Command {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Command) ClearOutput() {
|
||||
c.Stdout = ""
|
||||
c.Stderr = ""
|
||||
c.ExitCode = 0
|
||||
}
|
||||
|
||||
func (c *Command) Defaults() {
|
||||
c.ClearOutput()
|
||||
c.Split = true
|
||||
c.FailOnError = true
|
||||
c.CommandExists = func() error {
|
||||
@ -55,10 +69,14 @@ func (c *Command) Defaults() {
|
||||
return nil
|
||||
}
|
||||
c.Executor = func(value any) ([]byte, error) {
|
||||
c.ClearOutput()
|
||||
args, err := c.Template(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if inputErr := c.SetInput(value); inputErr != nil {
|
||||
return nil, inputErr
|
||||
}
|
||||
cmd := exec.Command(c.Path, args...)
|
||||
c.SetCmdEnv(cmd)
|
||||
|
||||
@ -91,6 +109,10 @@ func (c *Command) Defaults() {
|
||||
}
|
||||
waitErr := cmd.Wait()
|
||||
|
||||
c.Stdout = string(stdOutOutput)
|
||||
c.Stderr = string(stdErrOutput)
|
||||
c.ExitCode = c.GetExitCodeFromError(waitErr)
|
||||
|
||||
if len(stdOutOutput) > 100 {
|
||||
slog.Info("execute()", "path", c.Path, "args", args, "output", string(stdOutOutput[:100]), "error", string(stdErrOutput))
|
||||
} else {
|
||||
@ -126,6 +148,15 @@ func (c *Command) Exists() bool {
|
||||
return c.CommandExists() == nil
|
||||
}
|
||||
|
||||
func (c *Command) GetExitCodeFromError(err error) (ec int) {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||
return status.ExitStatus()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Command) Template(value any) ([]string, error) {
|
||||
var args []string = make([]string, 0, len(c.Args) * 2)
|
||||
for i, arg := range c.Args {
|
||||
@ -154,6 +185,22 @@ func (c *Command) Execute(value any) ([]byte, error) {
|
||||
return c.Executor(value)
|
||||
}
|
||||
|
||||
func (c *Command) SetInput(value any) error {
|
||||
if len(c.Input) > 0 {
|
||||
if r, err := c.Input.Template(value); err != nil {
|
||||
return err
|
||||
} else {
|
||||
c.SetStdinReader(strings.NewReader(r.String()))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CommandInput) Template(value any) (result strings.Builder, err error) {
|
||||
err = template.Must(template.New("commandInput").Parse(string(*c))).Execute(&result, value)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CommandArg) UnmarshalValue(value string) error {
|
||||
*c = CommandArg(value)
|
||||
return nil
|
||||
|
@ -81,3 +81,22 @@ stdinavailable: true
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expected, string(out))
|
||||
}
|
||||
|
||||
func TestCommandExitCode(t *testing.T) {
|
||||
c := NewCommand()
|
||||
assert.NotNil(t, c)
|
||||
decl := `
|
||||
path: ls
|
||||
args:
|
||||
- "amissingfile"
|
||||
`
|
||||
|
||||
assert.Nil(t, c.LoadDecl(decl))
|
||||
assert.Equal(t, "ls", c.Path)
|
||||
|
||||
out, err := c.Execute(nil)
|
||||
assert.NotNil(t, err)
|
||||
assert.Greater(t, c.ExitCode, 0)
|
||||
assert.Equal(t, string(out), c.Stdout)
|
||||
assert.Equal(t, string("ls: amissingfile: No such file or directory\n"), c.Stderr)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user