Documentation
¶
Overview ¶
Package upgradesched runs scheduled and automatic control plane upgrades. It decides when to start an upgrade and whether it is safe to; the upgrade itself is the existing host-run self-upgrade engine (internal/selfupgrade), started through Host. Every decision is level-triggered from persisted state, so a restart at any point resumes instead of repeating.
Index ¶
- Constants
- Variables
- func BlockerSummary(checks []upgrade.Check) string
- func CrossesMajor(current, target string) bool
- func NewScheduleID() string
- func SamePatchLine(current, target string) bool
- type AckError
- type AttemptInfo
- type Breaking
- type Candidate
- type Config
- type Host
- type Notifier
- type Plan
- type Policy
- type PreflightResult
- type Request
- type Scheduler
- func (s *Scheduler) Cancel(ctx context.Context, actor string) (store.UpgradeSchedule, error)
- func (s *Scheduler) NextWindow(ctx context.Context, now time.Time) (Span, bool, error)
- func (s *Scheduler) Run(ctx context.Context, interval time.Duration) error
- func (s *Scheduler) Schedule(ctx context.Context, req Request) (store.UpgradeSchedule, error)
- func (s *Scheduler) Tick(ctx context.Context) error
- type Span
- type Store
- type Verdict
- type Window
Constants ¶
const ( ModeOff = "off" ModePatch = "patch" ModeAny = "any" )
Policy modes.
const ( MinWindowDuration = 10 * time.Minute MaxWindowDuration = 24 * time.Hour MaxMinReleaseAge = 90 )
Window bounds, so a typo cannot make a window that never closes or never fits an upgrade.
const ( VerdictEligible = "eligible" VerdictNotNewer = "not_newer" VerdictOff = "off" VerdictPaused = "paused" VerdictNotPatch = "not_patch" VerdictMajor = "major_jump" VerdictBreaking = "breaking_changes" VerdictNoNotes = "notes_unavailable" VerdictTooYoung = "too_young" VerdictNoWindow = "no_window" VerdictPriorFailed = "prior_failed" )
Verdict codes.
const DefaultInterval = 30 * time.Second
DefaultInterval is how often Run evaluates schedules.
Variables ¶
var ( ErrReleaseNotFound = errors.New("upgradesched: release not found on the configured channel") ErrBackupNotAvail = errors.New("upgradesched: control plane backups are not configured") ErrAlreadyScheduled = errors.New("upgradesched: an upgrade is already scheduled") ErrRunning = errors.New("upgradesched: the upgrade is already running") ErrNothingScheduled = errors.New("upgradesched: no upgrade is scheduled") ErrNoWindow = errors.New("upgradesched: no maintenance window is configured") ErrNeedsAck = errors.New("upgradesched: breaking changes need acknowledgement") ErrNeedsMajorAck = errors.New("upgradesched: a major version jump needs acknowledgement") ErrInvalid = errors.New("upgradesched: invalid request") // ErrEdgeTarget marks a target that is an edge container image: it has // no release binary, so no upgrade can be scheduled for it. ErrEdgeTarget = errors.New("upgradesched: edge builds are container images and cannot be applied by an upgrade") )
Sentinel errors the API maps to status codes.
Functions ¶
func BlockerSummary ¶
BlockerSummary renders the blocking checks as one line.
func CrossesMajor ¶
CrossesMajor reports whether target has a different major version than current.
func SamePatchLine ¶
SamePatchLine reports whether target differs from current only in the patch number.
Types ¶
type AckError ¶
type AckError struct{ IDs []string }
AckError lists the breaking changes a request left unacknowledged.
type AttemptInfo ¶
AttemptInfo is the outcome of a self-upgrade run.
type Config ¶
type Config struct {
// Grace is how long after a manual run_at the scheduler keeps retrying.
Grace time.Duration
// RetryInterval is the minimum gap between fire-time re-checks.
RetryInterval time.Duration
// StartTimeout is how long a fired upgrade may show no attempt before it
// is reported as not started.
StartTimeout time.Duration
// MaxAhead bounds how far in the future a manual schedule may be.
MaxAhead time.Duration
// MinLead is the shortest time between now and a scheduled run.
MinLead time.Duration
}
Config holds the scheduler's tunables, all overridable from the environment.
func ConfigFromEnv ¶
ConfigFromEnv applies APP_UPGRADE_SCHEDULE_* overrides to the defaults.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns the defaults used when no environment override is set.
type Host ¶
type Host interface {
// Latest is the newest release on the configured channel. It returns the
// candidate together with ErrEdgeTarget when that is an edge image.
Latest(ctx context.Context) (Candidate, bool, error)
// Plan describes target, or returns ErrReleaseNotFound.
Plan(ctx context.Context, target string) (Plan, error)
// Preflight evaluates the checks for target, bypassing release caches.
Preflight(ctx context.Context, target string) (PreflightResult, error)
// BackupNow takes a control plane backup and returns its name.
BackupNow(ctx context.Context) (string, error)
// Apply starts the host upgrade.
Apply(ctx context.Context, target string, ack []string, initiator string) error
// Running reports whether a self-upgrade is in progress on the host.
Running(ctx context.Context) bool
// Attempt finds the newest recorded attempt started by initiator.
Attempt(ctx context.Context, initiator string) (AttemptInfo, bool, error)
}
Host is everything the scheduler needs from the running control plane. The API router implements it, so the scheduler uses the same release lookup, preflight and launcher a person clicking "Upgrade now" does.
type Notifier ¶
Notifier sends a plain-text notice to alert channels. An empty id list means every enabled channel, so an unconfigured policy still alerts.
type Plan ¶
type Plan struct {
Current string
Target string
Breaking []Breaking
NotesAvailable bool
CanApply bool
CannotApplyWhy string
PublishedAt time.Time
}
Plan is what a target release would change.
func (Plan) MissingAcks ¶
MissingAcks returns the ids of breaking changes that need an acknowledgement and were not given one.
func (Plan) NeedAckCount ¶
NeedAckCount counts breaking changes that need an acknowledgement.
type Policy ¶
type Policy struct {
Mode string
Window Window
MinReleaseAgeDays int
Paused bool
NotifyChannelIDs []string
}
Policy is the automatic upgrade policy.
func PolicyFromStore ¶
func PolicyFromStore(row store.UpgradePolicy, channelIDs []string) Policy
PolicyFromStore converts the persisted row.
type PreflightResult ¶
type PreflightResult struct {
Checks []upgrade.Check
Blocked bool
// Edge is set when the target is an edge container image, which is never
// installable by an upgrade.
Edge bool
}
PreflightResult is a fresh evaluation of the upgrade preflight.
type Request ¶
type Request struct {
// Target is the release tag, empty for the newest on the channel.
Target string
// At is the earliest run time. Ignored when Window is set.
At time.Time
// Window runs in the next maintenance window instead of at At.
Window bool
// Timezone is the zone the operator thinks in, recorded for display.
Timezone string
Ack []string
MajorAck bool
// Replace cancels a pending or blocked schedule first.
Replace bool
Actor string
}
Request asks for one upgrade at a time.
type Scheduler ¶
type Scheduler struct {
Store Store
Host Host
Windows func(ctx context.Context) ([]alerting.MaintenanceWindow, error)
Notifier Notifier
Config Config
Logger *slog.Logger
Now func() time.Time
// Current returns the running version.
Current func() string
// Audit records a scheduler-initiated change in the audit log.
Audit func(ctx context.Context, detail string)
// NewID returns a schedule id.
NewID func() string
// contains filtered or unexported fields
}
Scheduler fires scheduled upgrades and, when a policy allows, creates them.
func (*Scheduler) NextWindow ¶
NextWindow picks the earliest opening among the upgrade policy's window and the enabled alert maintenance windows that cover everything.
type Store ¶
type Store interface {
GetUpgradePolicy(ctx context.Context) (store.UpgradePolicy, error)
SetUpgradeDecision(ctx context.Context, tag, decision, at string) error
InsertUpgradeSchedule(ctx context.Context, s store.UpgradeSchedule) error
ActiveUpgradeSchedule(ctx context.Context) (*store.UpgradeSchedule, error)
ListUpgradeSchedules(ctx context.Context, limit int) ([]store.UpgradeSchedule, error)
UpdateUpgradeSchedule(ctx context.Context, s store.UpgradeSchedule, from ...string) (bool, error)
}
Store is the persistence the scheduler uses. *store.DB satisfies it.
type Verdict ¶
type Verdict struct {
OK bool
Code string
Reason string
// WaitUntil is set when the only thing missing is time.
WaitUntil time.Time
}
Verdict is the result of weighing a candidate against a policy.
func Eligible ¶
func Eligible(p Policy, current string, cand Candidate, breakingNeedAck int, notesAvailable bool, now time.Time) Verdict
Eligible decides whether the policy may apply cand on its own. Anything that needs a human (breaking changes, a major jump, unreadable notes) is never eligible: automatic upgrades only take releases nobody needs to read.
type Window ¶
Window is a recurring maintenance window: it opens at each cron match in Timezone and stays open for Duration. It reuses the alerting window model so cron and daylight saving semantics are the ones operators already know.