-
Notifications
You must be signed in to change notification settings - Fork 86
/
shell_job.go
122 lines (103 loc) · 2.63 KB
/
shell_job.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package job
import (
"bytes"
"context"
"fmt"
"io"
"os/exec"
"sync"
"github.com/reugn/go-quartz/quartz"
)
// ShellJob represents a shell command Job, implements the [quartz.Job] interface.
// The command will be executed using bash if available; otherwise, sh will be used.
// Consider the interpreter type and target environment when formulating commands
// for execution.
type ShellJob struct {
mtx sync.Mutex
cmd string
exitCode int
stdout string
stderr string
jobStatus Status
callback func(context.Context, *ShellJob)
}
var _ quartz.Job = (*ShellJob)(nil)
// NewShellJob returns a new [ShellJob] for the given command.
func NewShellJob(cmd string) *ShellJob {
return &ShellJob{
cmd: cmd,
jobStatus: StatusNA,
}
}
// NewShellJobWithCallback returns a new [ShellJob] with the given callback function.
func NewShellJobWithCallback(cmd string, f func(context.Context, *ShellJob)) *ShellJob {
return &ShellJob{
cmd: cmd,
jobStatus: StatusNA,
callback: f,
}
}
// Description returns the description of the ShellJob.
func (sh *ShellJob) Description() string {
return fmt.Sprintf("ShellJob%s%s", quartz.Sep, sh.cmd)
}
var (
shellOnce = sync.Once{}
shellPath = "bash"
)
func getShell() string {
shellOnce.Do(func() {
_, err := exec.LookPath("/bin/bash")
// if bash binary is not found, use `sh`.
if err != nil {
shellPath = "sh"
}
})
return shellPath
}
// Execute is called by a Scheduler when the Trigger associated with this job fires.
func (sh *ShellJob) Execute(ctx context.Context) error {
shell := getShell()
var stdout, stderr bytes.Buffer
cmd := exec.CommandContext(ctx, shell, "-c", sh.cmd)
cmd.Stdout = io.Writer(&stdout)
cmd.Stderr = io.Writer(&stderr)
err := cmd.Run() // run the command
sh.mtx.Lock()
sh.stdout, sh.stderr = stdout.String(), stderr.String()
sh.exitCode = cmd.ProcessState.ExitCode()
if err != nil {
sh.jobStatus = StatusFailure
} else {
sh.jobStatus = StatusOK
}
sh.mtx.Unlock()
if sh.callback != nil {
sh.callback(ctx, sh)
}
return err
}
// ExitCode returns the exit code of the ShellJob.
func (sh *ShellJob) ExitCode() int {
sh.mtx.Lock()
defer sh.mtx.Unlock()
return sh.exitCode
}
// Stdout returns the captured stdout output of the ShellJob.
func (sh *ShellJob) Stdout() string {
sh.mtx.Lock()
defer sh.mtx.Unlock()
return sh.stdout
}
// Stderr returns the captured stderr output of the ShellJob.
func (sh *ShellJob) Stderr() string {
sh.mtx.Lock()
defer sh.mtx.Unlock()
return sh.stderr
}
// JobStatus returns the status of the ShellJob.
func (sh *ShellJob) JobStatus() Status {
sh.mtx.Lock()
defer sh.mtx.Unlock()
return sh.jobStatus
}