feat: Updated affected components on status page incidents
Chart Release / chart (push) Successful in 13s
Server Deploy / deploy (push) Canceled after 6m59s

This commit is contained in:
2026-08-25 14:56:50 +00:00
parent 3e4865884c
commit c440b59b93
4 changed files with 139 additions and 14 deletions
@@ -67,10 +67,66 @@ func validateIncident(inc *models.StatusIncident) error {
return nil
}
// checkAffectedOnPages refuses an incident naming a component none of its pages
// carries.
//
// An incident's affected components are the page's own components, not the
// fleet's monitors: publishing "api-gateway is degraded" on a page that never
// listed api-gateway names a machine to the public that the page deliberately
// does not, which is the same leak assembleSnapshot's redaction boundary exists
// to prevent — reached from the authoring side instead of the read side.
//
// It is a separate pass rather than part of validateIncident because it reads
// the database, and validateIncident is a pure function of the document. The
// UI only offers the page's components, but as elsewhere the API is the
// boundary and the UI is the courtesy.
//
// A monitor dropped from the page AFTER an incident named it makes the next
// edit of that incident fail, and that is intended: the fix is one unchecked
// box, and the alternative is a page quietly publishing a component it no
// longer has.
func checkAffectedOnPages(instanceID string, inc *models.StatusIncident) error {
if len(inc.AffectedMonitors) == 0 {
return nil
}
ctx, cancel := spCtx()
defer cancel()
cur, err := db.Col("status_pages").Find(ctx, bson.M{
"instance_id": instanceID,
"page_id": bson.M{"$in": inc.PageIDs},
})
if err != nil {
return err
}
var pages []models.StatusPage
if err := cur.All(ctx, &pages); err != nil {
return err
}
onPage := map[string]bool{}
for _, p := range pages {
for _, sec := range p.Sections {
for _, e := range sec.Entries {
onPage[e.MonitorID] = true
}
}
}
for _, id := range inc.AffectedMonitors {
if !onPage[id] {
return fmt.Errorf("%w: %s is not a component of this status page; add it to the page first, or leave it out of the incident",
ErrPageInvalid, id)
}
}
return nil
}
func CreateStatusIncident(instanceID string, inc *models.StatusIncident) (*models.StatusIncident, error) {
if err := validateIncident(inc); err != nil {
return nil, err
}
if err := checkAffectedOnPages(instanceID, inc); err != nil {
return nil, err
}
inc.ID = bson.ObjectID{}
inc.InstanceID = instanceID
inc.IncidentID = uuid.NewString()
@@ -172,6 +228,9 @@ func UpdateStatusIncident(instanceID, incidentID string, inc *models.StatusIncid
if err := validateIncident(inc); err != nil {
return nil, err
}
if err := checkAffectedOnPages(instanceID, inc); err != nil {
return nil, err
}
set := bson.M{
"page_ids": inc.PageIDs,