jx/internal/resource/command.go

131 lines
3.3 KiB
Go
Raw Normal View History

2024-04-05 17:22:17 +00:00
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
package resource
import (
2024-04-09 19:30:05 +00:00
_ "context"
"encoding/json"
2024-04-05 17:22:17 +00:00
"fmt"
"gopkg.in/yaml.v3"
2024-04-09 19:30:05 +00:00
"io"
"log/slog"
_ "net/url"
_ "os"
2024-04-05 17:22:17 +00:00
"os/exec"
"strings"
"text/template"
)
2024-04-09 19:30:05 +00:00
type CommandExecutor func(value any) ([]byte, error)
type CommandExtractAttributes func(output []byte, target any) error
2024-04-05 17:22:17 +00:00
type CommandArg string
type Command struct {
2024-04-09 19:30:05 +00:00
Path string `json:"path" yaml:"path"`
Args []CommandArg `json:"args" yaml:"args"`
2024-05-09 07:39:45 +00:00
Split bool `json:"split" yaml:"split`
2024-04-09 19:30:05 +00:00
Executor CommandExecutor `json:"-" yaml:"-"`
Extractor CommandExtractAttributes `json:"-" yaml:"-"`
2024-04-05 17:22:17 +00:00
}
func NewCommand() *Command {
2024-05-09 07:39:45 +00:00
c := &Command{ Split: true }
2024-04-09 19:30:05 +00:00
c.Executor = func(value any) ([]byte, error) {
args, err := c.Template(value)
if err != nil {
return nil, err
}
cmd := exec.Command(c.Path, args...)
2024-05-06 00:48:54 +00:00
slog.Info("execute() - cmd", "path", c.Path, "args", args)
output, stdoutPipeErr := cmd.StdoutPipe()
if stdoutPipeErr != nil {
return nil, stdoutPipeErr
}
2024-04-09 19:30:05 +00:00
stderr, pipeErr := cmd.StderrPipe()
if pipeErr != nil {
return nil, pipeErr
}
2024-05-06 00:48:54 +00:00
if startErr := cmd.Start(); startErr != nil {
return nil, startErr
}
slog.Info("execute() - start", "cmd", cmd)
stdOutOutput, _ := io.ReadAll(output)
2024-04-09 19:30:05 +00:00
stdErrOutput, _ := io.ReadAll(stderr)
2024-05-06 00:48:54 +00:00
slog.Info("execute() - io", "stdout", string(stdOutOutput), "stderr", string(stdErrOutput))
waitErr := cmd.Wait()
slog.Info("execute()", "path", c.Path, "args", args, "output", string(stdOutOutput), "error", string(stdErrOutput))
if len(stdErrOutput) > 0 {
return stdOutOutput, fmt.Errorf(string(stdErrOutput))
}
return stdOutOutput, waitErr
2024-04-09 19:30:05 +00:00
}
return c
2024-04-05 17:22:17 +00:00
}
func (c *Command) Load(r io.Reader) error {
decoder := NewYAMLDecoder(r)
return decoder.Decode(c)
}
func (c *Command) LoadDecl(yamlResourceDeclaration string) error {
decoder := NewYAMLStringDecoder(yamlResourceDeclaration)
return decoder.Decode(c)
}
func (c *Command) Template(value any) ([]string, error) {
2024-05-06 00:48:54 +00:00
var args []string = make([]string, 0, len(c.Args) * 2)
2024-04-05 17:22:17 +00:00
for i, arg := range c.Args {
var commandLineArg strings.Builder
err := template.Must(template.New(fmt.Sprintf("arg%d", i)).Parse(string(arg))).Execute(&commandLineArg, value)
if err != nil {
return nil, err
}
2024-05-06 00:48:54 +00:00
if commandLineArg.Len() > 0 {
2024-05-09 07:39:45 +00:00
var splitArg []string
if c.Split {
splitArg = strings.Split(commandLineArg.String(), " ")
} else {
splitArg = []string{commandLineArg.String()}
}
2024-05-06 00:48:54 +00:00
slog.Info("Template()", "split", splitArg, "len", len(splitArg))
args = append(args, splitArg...)
}
2024-04-05 17:22:17 +00:00
}
2024-05-06 00:48:54 +00:00
slog.Info("Template()", "Args", c.Args, "lencargs", len(c.Args), "args", args, "lenargs", len(args), "value", value)
2024-04-05 17:22:17 +00:00
return args, nil
}
func (c *Command) Execute(value any) ([]byte, error) {
2024-04-09 19:30:05 +00:00
return c.Executor(value)
2024-04-05 17:22:17 +00:00
}
func (c *CommandArg) UnmarshalValue(value string) error {
*c = CommandArg(value)
return nil
}
func (c *CommandArg) UnmarshalJSON(data []byte) error {
var s string
if unmarshalRouteTypeErr := json.Unmarshal(data, &s); unmarshalRouteTypeErr != nil {
return unmarshalRouteTypeErr
}
return c.UnmarshalValue(s)
}
func (c *CommandArg) UnmarshalYAML(value *yaml.Node) error {
var s string
if err := value.Decode(&s); err != nil {
return err
}
return c.UnmarshalValue(s)
}