93 lines
2.0 KiB
Go
93 lines
2.0 KiB
Go
package schedule
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
|
|
"github.com/robfig/cron/v3"
|
|
)
|
|
|
|
type Config struct {
|
|
CronExpr string
|
|
Command string
|
|
Args []string
|
|
Logger *log.Logger
|
|
}
|
|
|
|
type Scheduler struct {
|
|
cron *cron.Cron
|
|
logger *log.Logger
|
|
}
|
|
|
|
// Start configures and starts the cron scheduler. It runs the given script at the
|
|
// specified cron expression (KST). The caller owns the returned scheduler and must
|
|
// call Stop on shutdown.
|
|
func Start(cfg Config) (*Scheduler, error) {
|
|
if cfg.CronExpr == "" {
|
|
return nil, errors.New("CronExpr is required")
|
|
}
|
|
if cfg.Command == "" {
|
|
return nil, errors.New("Command is required")
|
|
}
|
|
|
|
if cfg.Logger == nil {
|
|
cfg.Logger = log.Default()
|
|
}
|
|
|
|
kst, err := time.LoadLocation("Asia/Seoul")
|
|
if err != nil {
|
|
kst = time.FixedZone("KST", 9*60*60)
|
|
}
|
|
|
|
parser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
|
|
spec, err := parser.Parse(cfg.CronExpr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c := cron.New(cron.WithLocation(kst), cron.WithParser(parser))
|
|
c.Schedule(spec, cron.FuncJob(func() {
|
|
runCommand(cfg.Logger, cfg.Command, cfg.Args...)
|
|
}))
|
|
|
|
c.Start()
|
|
|
|
cfg.Logger.Printf("scheduler started with cron=%s command=%s args=%v tz=%s", cfg.CronExpr, cfg.Command, cfg.Args, kst)
|
|
|
|
return &Scheduler{
|
|
cron: c,
|
|
logger: cfg.Logger,
|
|
}, nil
|
|
}
|
|
|
|
// Stop halts the scheduler. It does not cancel a currently running job.
|
|
func (s *Scheduler) Stop() context.Context {
|
|
if s == nil || s.cron == nil {
|
|
return context.Background()
|
|
}
|
|
return s.cron.Stop()
|
|
}
|
|
|
|
func runCommand(logger *log.Logger, command string, args ...string) {
|
|
start := time.Now()
|
|
logger.Printf("scheduler: running %s %v", command, args)
|
|
|
|
cmd := exec.Command(command, args...)
|
|
cmd.Env = os.Environ()
|
|
out, err := cmd.CombinedOutput()
|
|
duration := time.Since(start)
|
|
|
|
if len(out) > 0 {
|
|
logger.Printf("scheduler: output:\n%s", string(out))
|
|
}
|
|
if err != nil {
|
|
logger.Printf("scheduler: %s failed after %s: %v", command, duration, err)
|
|
return
|
|
}
|
|
logger.Printf("scheduler: %s completed in %s", command, duration)
|
|
}
|