scheduler

package
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 8, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package scheduler is a thin wrapper around robfig/cron/v3 that adds a JSONL ledger so each fired job leaves a durable record alongside the upgrade ledger (internal/agent/upgrade/ledger.go is its sibling shape — same Append+Tail pattern, different vocabulary).

Why a wrapper at all: outpost's first scheduled-work consumer (peer-coordinated backup) needs three things cron/v3 doesn't provide on its own — (1) a stable name → spec → fn registry that can be re-registered idempotently after a config reload, (2) panic recovery so a single misbehaving job can't take the daemon down, (3) a ledger so `outpost backup history` and the equivalent MCP resource can answer "when did the nightly backup last fire and did it succeed". A future cloudbox consumer can pull this package up to a sibling module under the umbrella; for now it lives here because outpost is the only caller and cloudbox's leader-only cron (peer-health sweep, retention) can be a bare time.Ticker until a second use case materializes.

Time zone: read once at Run() time from $TZ, falling back to time.Local. UTC normalisation was rejected because operators reason about "2 AM nightly" in local wall-clock time and we want "Europe/London" → "America/Los_Angeles" relocation to keep the schedule's human-meaning stable, not its instant.

Index

Constants

View Source
const (
	StepFired  = "fired"
	StepOK     = "ok"
	StepFailed = "failed"
)

Step names emitted by the wrap-fn in scheduler.go. Kept as constants so a CLI history-renderer doesn't string-match against loose literals.

Variables

This section is empty.

Functions

This section is empty.

Types

type JobFunc

type JobFunc func(ctx context.Context) error

JobFunc is the unit of scheduled work. The context is cancelled when Scheduler.Run's ctx is cancelled (so a long-running job can observe shutdown). A non-nil error is recorded in the ledger.

type Ledger

type Ledger struct {
	// contains filtered or unexported fields
}

Ledger is an append-only JSONL writer + bounded tail-reader. Path is fixed at construction; concurrent appends serialize through mu. An empty path disables writes silently (returns nil from Append).

func NewLedger

func NewLedger(path string) *Ledger

NewLedger returns a Ledger backed by `path`. Doesn't touch the filesystem until the first Append.

func (*Ledger) Append

func (l *Ledger) Append(entry LedgerEntry) error

Append writes one entry as a single JSON line. If `entry.At` is zero, it is filled with the current UTC time.

Errors writing the ledger are NOT fatal to the caller's flow — we would rather complete the scheduled job than abort it because we couldn't scribble a record. Callers can log Append's error but should continue.

func (*Ledger) LastByJob

func (l *Ledger) LastByJob(job string) (LedgerEntry, error)

LastByJob returns the most recent ledger entry for `job`, or a zero entry + nil error if the job has never fired. Used by status surfaces to render "last ran at, succeeded/failed".

func (*Ledger) Path

func (l *Ledger) Path() string

Path is exposed so callers can include it in diagnostics.

func (*Ledger) Tail

func (l *Ledger) Tail(n int) ([]LedgerEntry, error)

Tail returns up to the last `n` ledger entries, newest last. A missing ledger file returns an empty slice without error — a host that has never fired a scheduled job has no history.

type LedgerEntry

type LedgerEntry struct {
	At         time.Time `json:"at"`
	Job        string    `json:"job,omitempty"`
	Step       string    `json:"step"`
	DurationMs int64     `json:"duration_ms,omitempty"`
	Error      string    `json:"error,omitempty"`
	Detail     string    `json:"detail,omitempty"`
}

LedgerEntry is one JSONL line in the scheduler history file. Shape intentionally parallels internal/agent/upgrade.LedgerEntry so a single tail-renderer can handle both files later (the upgrade surface uses ReleaseID where the scheduler uses Job).

type Scheduler

type Scheduler struct {
	// contains filtered or unexported fields
}

Scheduler runs named jobs on cron expressions. Construct via New; register every job before calling Run; Run blocks until ctx ends.

func New

func New(ledgerPath string) *Scheduler

New returns a Scheduler that will write each fired job's outcome to ledgerPath. If ledgerPath is empty the ledger is silently disabled (useful for tests). Job functions receive ctx-cancellation when Run's ctx is cancelled.

func (*Scheduler) Ledger

func (s *Scheduler) Ledger() *Ledger

Ledger exposes the JSONL writer so callers can Tail it from CLI / MCP / HTTP surfaces (mirrors how upgrade.Worker exposes its upgrade.Ledger).

func (*Scheduler) Names

func (s *Scheduler) Names() []string

Names returns the registered job names in arbitrary order. Useful for the "what's scheduled" admin view.

func (*Scheduler) NextRun

func (s *Scheduler) NextRun(name string) time.Time

NextRun returns the next scheduled fire time for name, or zero time if the name is unknown.

func (*Scheduler) Register

func (s *Scheduler) Register(name, spec string, fn JobFunc) error

Register adds (or replaces) a named job. spec is a cron expression (5-field "M H D M W" or one of cron/v3's descriptors: "@daily", "@hourly", "@every 1h", etc.). fn must be safe to call repeatedly and tolerate context cancellation.

Re-registering under the same name removes the previous entry first — supports the "outpost polls cloudbox for its policy list every 5 min and re-installs the cron entries" pattern without leaking duplicate fires.

func (*Scheduler) Remove

func (s *Scheduler) Remove(name string)

Remove unregisters a named job. Unknown names are a no-op (caller dropped a policy concurrently with Remove — not an error).

func (*Scheduler) Run

func (s *Scheduler) Run(ctx context.Context) error

Run starts the cron loop and blocks until ctx is cancelled. On cancellation Run waits for any in-flight job to return before unblocking (cron.Stop returns a context that completes when the last in-flight entry exits).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL