Files
geoip-rest/internal/schedule/scheduler.go
2025-12-10 09:55:35 +09:00

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)
}