Documentation
¶
Overview ¶
Package maintenance holds one-off scheduled maintenance windows: their validation, state, and the lead-time announcement through alert channels.
Index ¶
- Constants
- Variables
- func AnnounceDue(w store.MaintenanceWindow, now time.Time) bool
- func DesiredMatcher(app string) alerting.SilenceMatcher
- func DomainWindow(m store.DomainMaintenance, app string, now, until time.Time) store.MaintenanceWindow
- func Elapsed(startedAt, now time.Time) time.Duration
- func EndReasonFor(m store.DomainMaintenance, now time.Time) string
- func KeepStart(prev store.DomainMaintenance, wasOn bool, now time.Time) time.Time
- func NewAuditEntry(action string, d AuditDetail, a Actor, now time.Time) (store.AuditEntry, error)
- func NormalizeBypass(entries []string) ([]string, error)
- func NoticeText(w store.MaintenanceWindow) string
- func Overdue(active []store.DomainMaintenance, now time.Time, threshold time.Duration) []store.DomainMaintenance
- func ResolveAutoOff(now time.Time, until *time.Time, requested *time.Duration, def time.Duration) (time.Time, error)
- func SilenceDomain(createdBy string) (string, bool)
- func SilenceEnd(m store.DomainMaintenance, now time.Time) time.Time
- func StateAt(w store.MaintenanceWindow, now time.Time) string
- type Actor
- type AnnounceStore
- type Announcer
- type AuditDetail
- type Channels
- type DomainExpirer
- type DomainExpiryStore
- type DomainInput
- type DomainOwnerStore
- type Input
- type Notifier
- type OngoingWindowStore
- type SilenceStore
- type Silencer
- func (s *Silencer) Active(ctx context.Context, domain string, now time.Time) (*alerting.Silence, error)
- func (s *Silencer) Discard(ctx context.Context, id string, now time.Time) error
- func (s *Silencer) Open(ctx context.Context, m store.DomainMaintenance, app string, now time.Time) (string, error)
- func (s *Silencer) Settle(ctx context.Context, domain, keep string, now time.Time) error
- func (s *Silencer) Sync(ctx context.Context, active []store.DomainMaintenance, apps map[string]string, ...) error
Constants ¶
const ( EndManual = "manual" EndUntil = "until" EndMaxDuration = "max_duration" EndDomainRemoved = "domain_removed" )
Why domain maintenance ended, recorded on the end audit row.
const ( AuditActionStarted = "domain_maintenance.started" AuditActionEnded = store.AuditActionDomainMaintenanceEnded )
Audit actions for the maintenance lifecycle rows.
const ( MaxDomainTitle = 120 MaxDomainMessage = 2000 MaxBypassEntries = 32 MaxRetryAfterSecond = 86400 )
Bounds on a domain's maintenance settings.
const ( StateScheduled = "scheduled" StateActive = "active" StateEnded = "ended" )
Window states relative to one instant.
const ( MaxDuration = 30 * 24 * time.Hour MaxLeadMinutes = 7 * 24 * 60 )
Bounds on user input.
const DefaultInterval = time.Minute
DefaultInterval is how often the announcer looks for windows entering their lead time.
const SilenceCreatorPrefix = "maintenance:"
SilenceCreatorPrefix marks a silence owned by domain maintenance; the rest of CreatedBy is the domain. Users cannot forge it: manual silences carry a user name.
Variables ¶
var SystemActor = Actor{Type: "system", ID: "maintenance-expiry", Name: "Scheduled maintenance", ClientKind: "system"}
SystemActor is the actor for automatic ends.
Functions ¶
func AnnounceDue ¶
func AnnounceDue(w store.MaintenanceWindow, now time.Time) bool
AnnounceDue reports whether the lead-time notice should go out now.
func DesiredMatcher ¶
func DesiredMatcher(app string) alerting.SilenceMatcher
DesiredMatcher is the matcher every maintenance silence for app must carry.
func DomainWindow ¶
func DomainWindow(m store.DomainMaintenance, app string, now, until time.Time) store.MaintenanceWindow
DomainWindow is the scheduled window that carries a domain's end time, so the banner, status page and announcer see it without a second state.
func Elapsed ¶
Elapsed is how long m has been on at now, never negative so a clock that stepped back cannot show a future start.
func EndReasonFor ¶
func EndReasonFor(m store.DomainMaintenance, now time.Time) string
EndReasonFor says which limit ended m on its own at now.
func KeepStart ¶
KeepStart carries the original start across a re-save, so toggling the settings twice does not reset how long the site has been down.
func NewAuditEntry ¶
func NewAuditEntry(action string, d AuditDetail, a Actor, now time.Time) (store.AuditEntry, error)
NewAuditEntry builds the lifecycle audit row for action with detail.
func NormalizeBypass ¶
NormalizeBypass turns IP addresses and CIDRs into deduplicated, masked CIDRs. A catch-all range is refused: that is ending maintenance.
func NoticeText ¶
func NoticeText(w store.MaintenanceWindow) string
NoticeText is the plain-text announcement for w.
func Overdue ¶
func Overdue(active []store.DomainMaintenance, now time.Time, threshold time.Duration) []store.DomainMaintenance
Overdue returns the maintenance rows that have been on longer than threshold. A zero threshold turns the check off.
func ResolveAutoOff ¶
func ResolveAutoOff(now time.Time, until *time.Time, requested *time.Duration, def time.Duration) (time.Time, error)
ResolveAutoOff returns when open ended maintenance switches itself off. A requested duration of nil takes def; zero means no limit. A dated end (until set) needs no cut-off, its window already ends it.
func SilenceDomain ¶
SilenceDomain returns the domain a maintenance silence belongs to.
func SilenceEnd ¶
SilenceEnd is when the silence for m stops: its end time, its auto-off, or a renewable default.
Types ¶
type Actor ¶
type Actor struct {
Type, ID, Name, ClientKind, RemoteAddr string
}
Actor is who caused a maintenance change.
type AnnounceStore ¶
type AnnounceStore interface {
ListMaintenanceWindows(ctx context.Context, since time.Time) ([]store.MaintenanceWindow, error)
MarkMaintenanceAnnounced(ctx context.Context, id string, at time.Time) error
}
AnnounceStore is the store surface the announcer needs.
type Announcer ¶
type Announcer struct {
Store AnnounceStore
Channels Channels
Notifier Notifier
Logger *slog.Logger
Now func() time.Time
}
Announcer sends each window's lead-time notice once.
type AuditDetail ¶
type AuditDetail struct {
Domain string `json:"domain"`
App string `json:"app,omitempty"`
StartedAt string `json:"started_at,omitempty"`
DurationSeconds int64 `json:"duration_seconds"`
EndReason string `json:"end_reason,omitempty"`
Until string `json:"until,omitempty"`
AutoOffAt string `json:"auto_off_at,omitempty"`
Actor string `json:"actor"`
}
AuditDetail is the JSON detail on a maintenance lifecycle audit row.
func EndedDetail ¶
func EndedDetail(m store.DomainMaintenance, app, reason string, a Actor, now time.Time) AuditDetail
EndedDetail describes maintenance that has just ended for reason.
func StartedDetail ¶
func StartedDetail(m store.DomainMaintenance, app string, a Actor) AuditDetail
StartedDetail describes maintenance that has just begun.
type Channels ¶
type Channels interface {
ListNotificationChannels(ctx context.Context) ([]alerting.NotificationChannel, error)
}
Channels lists notification channels. *alerting.DB satisfies it.
type DomainExpirer ¶
type DomainExpirer struct {
Store DomainExpiryStore
Silencer *Silencer
Nudge func()
Logger *slog.Logger
Now func() time.Time
}
DomainExpirer ends a domain's maintenance once its linked window ends, its auto-off passes or its domain is removed. The ingress already stops serving the page at that instant; this removes the row, audits it, lifts the alert silence and asks for a prompt reconcile.
type DomainExpiryStore ¶
type DomainExpiryStore interface {
ListExpiredDomainMaintenance(ctx context.Context, now time.Time) ([]store.DomainMaintenance, error)
DeleteDomainMaintenance(ctx context.Context, domain string) error
SaveAuditEntry(ctx context.Context, e store.AuditEntry) error
}
DomainExpiryStore is what ending timed domain maintenance needs.
type DomainInput ¶
type DomainInput struct {
Title string
Message string
RetryAfterSeconds int
// Until, when set, ends maintenance automatically through a linked window.
Until *time.Time
Bypass []string
BypassHealthCheck bool
}
DomainInput is a request to put one domain into maintenance.
func (DomainInput) Validate ¶
func (in DomainInput) Validate(domain string, now time.Time) (store.DomainMaintenance, error)
Validate checks in and returns the settings to store, without a window id.
type DomainOwnerStore ¶
type DomainOwnerStore interface {
ListActiveDomainMaintenance(ctx context.Context, now time.Time) ([]store.DomainMaintenance, error)
ListServiceDomains(ctx context.Context) ([]store.ServiceDomain, error)
}
DomainOwnerStore is optional: with it the expirer ends maintenance on a domain that left its app and keeps alert silences in step.
type Input ¶
type Input struct {
Title string
Message string
Scope string
Target string
StartsAt time.Time
EndsAt time.Time
Freeze bool
AnnounceLeadMins int
}
Input is a request to create a window.
type Notifier ¶
type Notifier interface {
SendNotice(ctx context.Context, c alerting.NotificationChannel, text string) error
}
Notifier sends one plain-text notice. *alerting.DeployDispatcher satisfies it.
type OngoingWindowStore ¶
type OngoingWindowStore interface {
EndOngoingMaintenanceWindow(ctx context.Context, id string, at time.Time) error
}
OngoingWindowStore is optional: it resolves the status page entry of open ended maintenance.
type SilenceStore ¶
type SilenceStore interface {
CreateSilence(ctx context.Context, s alerting.Silence) error
ListSilences(ctx context.Context, now time.Time, includeExpired bool, limit int) ([]alerting.Silence, error)
ExpireSilence(ctx context.Context, id string, now time.Time) (*alerting.Silence, error)
}
SilenceStore is the part of *alerting.DB the coupling needs.
type Silencer ¶
type Silencer struct {
Store SilenceStore
}
Silencer keeps one alert silence per domain in maintenance.
func (*Silencer) Active ¶
func (s *Silencer) Active(ctx context.Context, domain string, now time.Time) (*alerting.Silence, error)
Active returns the active silence for domain, if any.
func (*Silencer) Discard ¶
Discard expires one silence by id, for undoing an Open that was not committed.
func (*Silencer) Open ¶
func (s *Silencer) Open(ctx context.Context, m store.DomainMaintenance, app string, now time.Time) (string, error)
Open creates the silence for m on app and returns its id. It never touches older silences: callers Settle once the maintenance row is saved.
func (*Silencer) Settle ¶
Settle expires every active maintenance silence of domain except keep. An empty keep expires them all.
func (*Silencer) Sync ¶
func (s *Silencer) Sync(ctx context.Context, active []store.DomainMaintenance, apps map[string]string, now time.Time) error
Sync makes silences match active: one fresh silence per row, none for a domain that is no longer in maintenance. It heals a crash between the two writes and an expiry that happened while the control plane was down. apps maps domain to its owning app; a row with no owner is left to the caller.