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"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"decl/internal/codec"
|
"decl/internal/codec"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrUnknownCommand error = errors.New("Unable to find command in path")
|
var ErrUnknownCommand error = errors.New("Unable to find command in path")
|
||||||
@ -26,17 +27,23 @@ type CommandExists func() error
|
|||||||
|
|
||||||
type CommandArg string
|
type CommandArg string
|
||||||
|
|
||||||
|
type CommandInput string
|
||||||
|
|
||||||
type Command struct {
|
type Command struct {
|
||||||
Path string `json:"path" yaml:"path"`
|
Path string `json:"path" yaml:"path"`
|
||||||
Args []CommandArg `json:"args" yaml:"args"`
|
Args []CommandArg `json:"args" yaml:"args"`
|
||||||
Env []string `json:"env" yaml:"env"`
|
Env []string `json:"env" yaml:"env"`
|
||||||
Split bool `json:"split" yaml:"split"`
|
Split bool `json:"split" yaml:"split"`
|
||||||
FailOnError bool `json:"failonerror" yaml:"failonerror"`
|
FailOnError bool `json:"failonerror" yaml:"failonerror"`
|
||||||
StdinAvailable bool `json:"stdinavailable,omitempty" yaml:"stdinavailable,omitempty"`
|
StdinAvailable bool `json:"stdinavailable,omitempty" yaml:"stdinavailable,omitempty"`
|
||||||
Executor CommandExecutor `json:"-" yaml:"-"`
|
ExitCode int `json:"exitcode,omitempty" yaml:"exitcode,omitempty"`
|
||||||
Extractor CommandExtractAttributes `json:"-" yaml:"-"`
|
Stdout string `json:"stdout,omitempty" yaml:"stdout,omitempty"`
|
||||||
CommandExists CommandExists `json:"-" yaml:"-"`
|
Stderr string `json:"stderr,omitempty" yaml:"stderr,omitempty"`
|
||||||
stdin io.Reader `json:"-" yaml:"-"`
|
Executor CommandExecutor `json:"-" yaml:"-"`
|
||||||
|
Extractor CommandExtractAttributes `json:"-" yaml:"-"`
|
||||||
|
CommandExists CommandExists `json:"-" yaml:"-"`
|
||||||
|
Input CommandInput `json:"-" yaml:"-"`
|
||||||
|
stdin io.Reader `json:"-" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommand() *Command {
|
func NewCommand() *Command {
|
||||||
@ -45,7 +52,14 @@ func NewCommand() *Command {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Command) ClearOutput() {
|
||||||
|
c.Stdout = ""
|
||||||
|
c.Stderr = ""
|
||||||
|
c.ExitCode = 0
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Command) Defaults() {
|
func (c *Command) Defaults() {
|
||||||
|
c.ClearOutput()
|
||||||
c.Split = true
|
c.Split = true
|
||||||
c.FailOnError = true
|
c.FailOnError = true
|
||||||
c.CommandExists = func() error {
|
c.CommandExists = func() error {
|
||||||
@ -55,10 +69,14 @@ func (c *Command) Defaults() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
c.Executor = func(value any) ([]byte, error) {
|
c.Executor = func(value any) ([]byte, error) {
|
||||||
|
c.ClearOutput()
|
||||||
args, err := c.Template(value)
|
args, err := c.Template(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if inputErr := c.SetInput(value); inputErr != nil {
|
||||||
|
return nil, inputErr
|
||||||
|
}
|
||||||
cmd := exec.Command(c.Path, args...)
|
cmd := exec.Command(c.Path, args...)
|
||||||
c.SetCmdEnv(cmd)
|
c.SetCmdEnv(cmd)
|
||||||
|
|
||||||
@ -91,6 +109,10 @@ func (c *Command) Defaults() {
|
|||||||
}
|
}
|
||||||
waitErr := cmd.Wait()
|
waitErr := cmd.Wait()
|
||||||
|
|
||||||
|
c.Stdout = string(stdOutOutput)
|
||||||
|
c.Stderr = string(stdErrOutput)
|
||||||
|
c.ExitCode = c.GetExitCodeFromError(waitErr)
|
||||||
|
|
||||||
if len(stdOutOutput) > 100 {
|
if len(stdOutOutput) > 100 {
|
||||||
slog.Info("execute()", "path", c.Path, "args", args, "output", string(stdOutOutput[:100]), "error", string(stdErrOutput))
|
slog.Info("execute()", "path", c.Path, "args", args, "output", string(stdOutOutput[:100]), "error", string(stdErrOutput))
|
||||||
} else {
|
} else {
|
||||||
@ -126,6 +148,15 @@ func (c *Command) Exists() bool {
|
|||||||
return c.CommandExists() == nil
|
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) {
|
func (c *Command) Template(value any) ([]string, error) {
|
||||||
var args []string = make([]string, 0, len(c.Args) * 2)
|
var args []string = make([]string, 0, len(c.Args) * 2)
|
||||||
for i, arg := range c.Args {
|
for i, arg := range c.Args {
|
||||||
@ -154,6 +185,22 @@ func (c *Command) Execute(value any) ([]byte, error) {
|
|||||||
return c.Executor(value)
|
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 {
|
func (c *CommandArg) UnmarshalValue(value string) error {
|
||||||
*c = CommandArg(value)
|
*c = CommandArg(value)
|
||||||
return nil
|
return nil
|
||||||
|
@ -81,3 +81,22 @@ stdinavailable: true
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, expected, string(out))
|
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