firestormy/scheduler/job.go

157 lines
3.4 KiB
Go
Executable File

package scheduler
import (
"bytes"
"encoding/gob"
"fmt"
"gogs.inhome.blapointe.com/local/firestormy/config"
"gogs.inhome.blapointe.com/local/firestormy/config/ns"
"gogs.inhome.blapointe.com/local/firestormy/logger"
"gogs.inhome.blapointe.com/local/logb"
"gogs.inhome.blapointe.com/local/storage"
"os/exec"
"strings"
"time"
"github.com/google/uuid"
)
type Job struct {
Title string
Name string
Schedule string
Runner Runner
Disabled bool
foo func()
LastStatus int
LastOutput string
LastRuntime time.Duration
LastRun time.Time
}
func NewJob(runner Runner, schedule, raw string) (*Job, error) {
switch runner {
case Bash:
return newBashJob(schedule, raw)
default:
return nil, ErrBadRunner
}
}
func newBashJob(schedule, sh string, title ...string) (*Job, error) {
if !validCron(schedule) {
return nil, ErrBadCron
}
key := uuid.New().String()
if err := config.Store.Set(key, []byte(sh), ns.JobsRaw...); err != nil {
return nil, err
}
j := &Job{
Name: key,
Schedule: schedule,
Runner: Bash,
}
if len(title) == 0 || len(title[0]) == 0 {
j.Title = j.Name
} else {
j.Title = title[0]
}
j.foo = func() {
do := func() ([]byte, error) {
logb.Debugf("[sched] run %s/%s? %v", j.Title, j.Name, j.Disabled)
if j.Disabled {
return nil, nil
}
sh, err := config.Store.Get(j.Name, ns.JobsRaw...)
if err != nil {
return nil, err
}
cmd := exec.Command("bash", "-c", string(sh))
j.LastRun = time.Now()
start := time.Now()
out, err := cmd.CombinedOutput()
j.LastRuntime = time.Since(start)
if cmd != nil && cmd.ProcessState != nil {
j.LastStatus = cmd.ProcessState.ExitCode()
} else {
j.LastStatus = 1
}
return out, err
}
b, err := do()
logb.Debugf("[sched] run %s: (%v) %s", j.Name, err, b)
if err != nil {
b = []byte(fmt.Sprintf("err running command: %s: %s", err.Error(), b))
}
j.LastOutput = strings.TrimSpace(string(b))
b2, err := j.Encode()
if err == nil {
err = config.Store.Set(j.Name, b2, ns.Jobs...)
}
logger.New().Info("result", fmt.Sprintf("(%v) %s", err, b))
}
return j, nil
}
func (j *Job) Rename(name string) error {
sh, err := config.Store.Get(j.Name, ns.JobsRaw...)
if err != nil {
return err
}
b, err := j.Encode()
if err != nil {
return err
}
if err := config.Store.Set(name, sh, ns.JobsRaw...); err != nil {
return err
}
if err := config.Store.Set(name, b, ns.Jobs...); err != nil {
return err
}
if err := config.Store.Set(j.Name, nil, ns.Jobs...); err != nil && err != storage.ErrNotFound {
return err
}
if err := config.Store.Set(j.Name, nil, ns.JobsRaw...); err != nil && err != storage.ErrNotFound {
return err
}
j.Name = name
return nil
}
func (j *Job) Run() {
j.foo()
}
func (j *Job) Encode() ([]byte, error) {
buff := bytes.NewBuffer(nil)
encoder := gob.NewEncoder(buff)
err := encoder.Encode(*j)
return buff.Bytes(), err
}
func (j *Job) Decode(b []byte) error {
buff := bytes.NewReader(b)
decoder := gob.NewDecoder(buff)
err := decoder.Decode(j)
if err != nil {
return err
}
k, err := NewJob(j.Runner, j.Schedule, "")
if err == nil {
config.Store.Set(k.Name, nil, ns.JobsRaw...)
config.Store.Set(k.Name, nil, ns.Jobs...)
k.Name = j.Name
k.Title = j.Title
k.LastStatus = j.LastStatus
k.LastOutput = j.LastOutput
k.LastRuntime = j.LastRuntime
k.LastRun = j.LastRun
k.Disabled = j.Disabled
*j = *k
}
return err
}