a602ff3efc
The API reference listed endpoints but never the alert object's fields, so
two of them were load-bearing for clients while being described nowhere.
received_at appeared only in passing, as a stats filter; resolution_source
only inside the stale-expiry prose.
Both carry meaning a client cannot derive on its own. starts_at comes from
Prometheus and never changes for an alert instance, so received_at is the
only signal that a firing alert is still being refreshed — it advances on
every accepted webhook, including the unchanged notifications Alertmanager
re-sends every repeat_interval. resolution_source then says how much to
trust ends_at: under 'alertmanager' it is an end time somebody reported,
but under 'expiry' nothing ever reported one, so it is either a stale
watermark or the sweep timestamp, and only an upper bound.
README gains an alert object field table plus a contract section for each,
including the nullability rules and the advice to tolerate unrecognised
resolution_source values. The field comments in models.Alert now say these
are public API rather than ingest details, and the upsert carries a note at
the received_at line, which is where a regression would be introduced.
Three tests lock the newly documented behaviour, none of which was covered
before — the whole suite passed with the received_at bump deleted from the
upsert, because the expiry tests only ever set that column via SQL:
- a re-send advances received_at and leaves starts_at alone
- a discarded out-of-order retry does not count as a heartbeat
- an expiry resolve preserves a reported ends_at watermark and stamps
sweep time only when none was known
50 lines
2.3 KiB
Go
50 lines
2.3 KiB
Go
package models
|
|
|
|
import "time"
|
|
|
|
type Alert struct {
|
|
ID int64 `json:"id"`
|
|
Fingerprint string `json:"fingerprint"`
|
|
Name string `json:"name"`
|
|
Status string `json:"status"` // "firing" or "resolved"
|
|
Labels map[string]string `json:"labels"`
|
|
Annotations map[string]string `json:"annotations"`
|
|
StartsAt time.Time `json:"starts_at"`
|
|
EndsAt *time.Time `json:"ends_at,omitempty"`
|
|
GeneratorURL string `json:"generator_url"`
|
|
|
|
// ReceivedAt is when the server last accepted a webhook for this
|
|
// fingerprint, including the unchanged firing notifications Alertmanager
|
|
// re-sends every repeat_interval.
|
|
//
|
|
// This is a documented part of the public API, not an internal ingest
|
|
// detail: StartsAt never changes for an alert instance, so ReceivedAt is
|
|
// the only signal a client has that a firing alert is still being
|
|
// refreshed. The sweeper stale-dates against it (see expireStale), API
|
|
// clients render it, and GET /api/alerts is ordered by it. Anything that
|
|
// stops the webhook handler from advancing it on a re-send is a breaking
|
|
// change — see "received_at is a liveness heartbeat" in the README and
|
|
// TestWebhook_ResendBumpsReceivedAt.
|
|
ReceivedAt time.Time `json:"received_at"`
|
|
|
|
// Populated when the alert has been acknowledged.
|
|
AcknowledgedByID *int64 `json:"acknowledged_by_id,omitempty"`
|
|
AcknowledgedByUser *string `json:"acknowledged_by,omitempty"`
|
|
AcknowledgedAt *time.Time `json:"acknowledged_at,omitempty"`
|
|
|
|
// ResolutionSource records why a resolved alert left the firing state:
|
|
// "alertmanager" for a real resolved webhook, "expiry" when the sweeper
|
|
// inferred it after the alert stopped being refreshed. Nil while firing, and
|
|
// cleared again by a re-fire under the same fingerprint.
|
|
//
|
|
// Also public API: it is how a client knows whether EndsAt was observed or
|
|
// inferred. Under "expiry" nothing ever reported an end, so EndsAt is only
|
|
// an upper bound (see expireStale) and ReceivedAt is the more truthful
|
|
// signal. Treat the value set as open — see "resolution_source says how much
|
|
// to trust ends_at" in the README, and TestWebhook_ResolvedSetsSource /
|
|
// TestExpiry_StaleFiringAlert.
|
|
ResolutionSource *string `json:"resolution_source,omitempty"`
|
|
|
|
ArchivedAt *time.Time `json:"archived_at,omitempty"`
|
|
}
|