Select Git revision
-
Will McCutchen authoredWill McCutchen authored
runnable-shell.go 3.05 KiB
// Copyright 2023 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0
package jobqueue
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"sync"
)
func NewShellRunnableFromMap(data map[string]interface{}) (*ShellRunnable, error) {
scriptPath, _ := data["scriptpath"].(string)
script, _ := data["script"].(string)
if scriptPath != "" && script != "" {
return nil, fmt.Errorf("%w: ScriptPath and Script are mutually exclusive", ErrInvalidData)
}
if scriptPath == "" && script == "" {
return nil, fmt.Errorf("%w: ScriptPath or Script is required", ErrInvalidData)
}
return &ShellRunnable{
ScriptPath: scriptPath,
Script: script,
}, nil
}
// ShellResult is a result of a shell script
type ShellResult struct {
Output string
Error string
ExitCode int
}
func (s *ShellResult) GetResult() string {
return s.Output
}
func (s *ShellResult) GetError() (string, int) {
if s.ExitCode != 0 {
return s.Error, s.ExitCode
}
return "", 0
}
type ShellRunnable struct {
ScriptPath string
Script string
mu sync.Mutex
}
func (s *ShellRunnable) Run(ctx context.Context) (RunResult[ShellResult], error) {
scriptPath := s.ScriptPath
if s.Script != "" {
// write to temp
tmp, err := os.CreateTemp("", "script-*.sh")
if err != nil {
Error("Failed to create temp file: %v", err)
return RunResult[ShellResult]{
Status: ResultStatusFailed,
Data: ShellResult{
Output: "",
ExitCode: DefaultErrorExitCode,
Error: fmt.Errorf("%w: %v", ErrFailedToCreateTempFile, err).Error(),
},
}, err
}
scriptPath = tmp.Name()
defer func() {
_ = os.Remove(scriptPath)
}()
_, err = tmp.WriteString(s.Script)
defer func() {
_ = tmp.Close()
}()
if err != nil {
Error("Failed to write temp file: %v", err)
return RunResult[ShellResult]{
Status: ResultStatusFailed,
Data: ShellResult{
Output: "",
ExitCode: DefaultErrorExitCode,
Error: fmt.Errorf("%w: %v", ErrFailedToWriteTempFile, err).Error(),
},
}, err
}
}
// #nosec
cmd := exec.CommandContext(ctx, "sh", scriptPath)
output, err := cmd.Output()
var stderr []byte
if err != nil {
Error("Failed to run script: %v", err)
stderr = err.(*exec.ExitError).Stderr
}
exitCode := SuccessExitCode
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
exitCode = exitError.ExitCode()
}
Error("Failed to run script: %v", err)
return RunResult[ShellResult]{
Status: ResultStatusFailed,
Data: ShellResult{
Output: string(output),
ExitCode: exitCode,
Error: string(stderr),
},
}, err
}
return RunResult[ShellResult]{
Status: ResultStatusSuccess,
Data: ShellResult{
Output: strings.TrimSpace(string(output)),
ExitCode: exitCode,
Error: string(stderr),
},
}, nil
}
func (s *ShellRunnable) GetType() string {
return "shell"
}
func (s *ShellRunnable) GetPersistence() RunnableImport {
s.mu.Lock()
defer s.mu.Unlock()
data := JSONMap{
"scriptPath": s.ScriptPath,
"script": s.Script,
}
return RunnableImport{
Type: s.GetType(),
Data: data,
}
}