Skip to content
Snippets Groups Projects
Select Git revision
  • ef7fd18e57d115351cff654d7bbe02965ee7dc0a
  • main default protected
  • drip-server-timing
  • compress-middleware
  • v2.11.0
  • v2.10.0
  • v2.9.2
  • v2.9.1
  • v2.9.0
  • v2.8.0
  • v2.7.0
  • v2.6.0
  • v2.5.6
  • v2.5.5
  • v2.5.4
  • v2.5.3
  • v2.5.2
  • v2.5.1
  • v2.5.0
  • v2.4.2
  • v2.4.1
  • v2.4.0
  • v2.3.0
  • v2.2.2
24 results

README.md

Blame
  • 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,
    	}
    }