Skip to content

Commit 5979dff

Browse files
Show muted alerts in Alert Groups API (prometheus#3797)
This commit updates /api/v2/alerts/groups to show if an alert is suppressed from one or more active or mute time intervals. While the muted by field can be found in /api/v2/alerts, it is not used here because /api/v2/alerts does not take aggregation or routing into consideration. It also updates the UI to support filtering muted alerts via the Muted checkbox. Signed-off-by: George Robinson <[email protected]>
1 parent 8572fe8 commit 5979dff

File tree

23 files changed

+273
-51
lines changed

23 files changed

+273
-51
lines changed

api/api.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,20 @@ type API struct {
4545
inFlightSem chan struct{}
4646
}
4747

48-
// Options for the creation of an API object. Alerts, Silences, and StatusFunc
49-
// are mandatory to set. The zero value for everything else is a safe default.
48+
// Options for the creation of an API object. Alerts, Silences, AlertStatusFunc
49+
// and GroupMutedFunc are mandatory. The zero value for everything else is a safe
50+
// default.
5051
type Options struct {
5152
// Alerts to be used by the API. Mandatory.
5253
Alerts provider.Alerts
5354
// Silences to be used by the API. Mandatory.
5455
Silences *silence.Silences
55-
// StatusFunc is used be the API to retrieve the AlertStatus of an
56+
// AlertStatusFunc is used be the API to retrieve the AlertStatus of an
5657
// alert. Mandatory.
57-
StatusFunc func(model.Fingerprint) types.AlertStatus
58+
AlertStatusFunc func(model.Fingerprint) types.AlertStatus
59+
// GroupMutedFunc is used be the API to know if an alert is muted.
60+
// Mandatory.
61+
GroupMutedFunc func(routeID, groupKey string) ([]string, bool)
5862
// Peer from the gossip cluster. If nil, no clustering will be used.
5963
Peer cluster.ClusterPeer
6064
// Timeout for all HTTP connections. The zero value (and negative
@@ -83,8 +87,11 @@ func (o Options) validate() error {
8387
if o.Silences == nil {
8488
return errors.New("mandatory field Silences not set")
8589
}
86-
if o.StatusFunc == nil {
87-
return errors.New("mandatory field StatusFunc not set")
90+
if o.AlertStatusFunc == nil {
91+
return errors.New("mandatory field AlertStatusFunc not set")
92+
}
93+
if o.GroupMutedFunc == nil {
94+
return errors.New("mandatory field GroupMutedFunc not set")
8895
}
8996
if o.GroupFunc == nil {
9097
return errors.New("mandatory field GroupFunc not set")
@@ -113,7 +120,8 @@ func New(opts Options) (*API, error) {
113120
v2, err := apiv2.NewAPI(
114121
opts.Alerts,
115122
opts.GroupFunc,
116-
opts.StatusFunc,
123+
opts.AlertStatusFunc,
124+
opts.GroupMutedFunc,
117125
opts.Silences,
118126
opts.Peer,
119127
log.With(l, "version", "v2"),

api/v2/api.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ type API struct {
6060
alerts provider.Alerts
6161
alertGroups groupsFn
6262
getAlertStatus getAlertStatusFn
63+
groupMutedFunc groupMutedFunc
6364
uptime time.Time
6465

6566
// mtx protects alertmanagerConfig, setAlertStatus and route.
@@ -78,6 +79,7 @@ type API struct {
7879

7980
type (
8081
groupsFn func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool) (dispatch.AlertGroups, map[prometheus_model.Fingerprint][]string)
82+
groupMutedFunc func(routeID, groupKey string) ([]string, bool)
8183
getAlertStatusFn func(prometheus_model.Fingerprint) types.AlertStatus
8284
setAlertStatusFn func(prometheus_model.LabelSet)
8385
)
@@ -86,16 +88,18 @@ type (
8688
func NewAPI(
8789
alerts provider.Alerts,
8890
gf groupsFn,
89-
sf getAlertStatusFn,
91+
asf getAlertStatusFn,
92+
gmf groupMutedFunc,
9093
silences *silence.Silences,
9194
peer cluster.ClusterPeer,
9295
l log.Logger,
9396
r prometheus.Registerer,
9497
) (*API, error) {
9598
api := API{
9699
alerts: alerts,
97-
getAlertStatus: sf,
100+
getAlertStatus: asf,
98101
alertGroups: gf,
102+
groupMutedFunc: gmf,
99103
peer: peer,
100104
silences: silences,
101105
logger: l,
@@ -290,7 +294,7 @@ func (api *API) getAlertsHandler(params alert_ops.GetAlertsParams) middleware.Re
290294
continue
291295
}
292296

293-
alert := AlertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers)
297+
alert := AlertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers, nil)
294298

295299
res = append(res, alert)
296300
}
@@ -407,6 +411,11 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams
407411
res := make(open_api_models.AlertGroups, 0, len(alertGroups))
408412

409413
for _, alertGroup := range alertGroups {
414+
mutedBy, isMuted := api.groupMutedFunc(alertGroup.RouteID, alertGroup.GroupKey)
415+
if !*params.Muted && isMuted {
416+
continue
417+
}
418+
410419
ag := &open_api_models.AlertGroup{
411420
Receiver: &open_api_models.Receiver{Name: &alertGroup.Receiver},
412421
Labels: ModelLabelSetToAPILabelSet(alertGroup.Labels),
@@ -417,7 +426,7 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams
417426
fp := alert.Fingerprint()
418427
receivers := allReceivers[fp]
419428
status := api.getAlertStatus(fp)
420-
apiAlert := AlertToOpenAPIAlert(alert, status, receivers)
429+
apiAlert := AlertToOpenAPIAlert(alert, status, receivers, mutedBy)
421430
ag.Alerts = append(ag.Alerts, apiAlert)
422431
}
423432
res = append(res, ag)

api/v2/api_test.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -484,17 +484,12 @@ func TestAlertToOpenAPIAlert(t *testing.T) {
484484
UpdatedAt: updated,
485485
}
486486
)
487-
openAPIAlert := AlertToOpenAPIAlert(alert, types.AlertStatus{State: types.AlertStateActive}, receivers)
487+
openAPIAlert := AlertToOpenAPIAlert(alert, types.AlertStatus{State: types.AlertStateActive}, receivers, nil)
488488
require.Equal(t, &open_api_models.GettableAlert{
489489
Annotations: open_api_models.LabelSet{},
490490
Alert: open_api_models.Alert{
491491
Labels: open_api_models.LabelSet{"severity": "critical", "alertname": "alert1"},
492492
},
493-
Status: &open_api_models.AlertStatus{
494-
State: &active,
495-
InhibitedBy: []string{},
496-
SilencedBy: []string{},
497-
},
498493
StartsAt: convertDateTime(start),
499494
EndsAt: convertDateTime(time.Time{}),
500495
UpdatedAt: convertDateTime(updated),
@@ -503,6 +498,12 @@ func TestAlertToOpenAPIAlert(t *testing.T) {
503498
{Name: &receivers[0]},
504499
{Name: &receivers[1]},
505500
},
501+
Status: &open_api_models.AlertStatus{
502+
State: &active,
503+
InhibitedBy: []string{},
504+
SilencedBy: []string{},
505+
MutedBy: []string{},
506+
},
506507
}, openAPIAlert)
507508
}
508509

api/v2/client/alertgroup/get_alert_groups_parameters.go

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/v2/compat.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func PostableSilenceToProto(s *open_api_models.PostableSilence) (*silencepb.Sile
117117
}
118118

119119
// AlertToOpenAPIAlert converts internal alerts, alert types, and receivers to *open_api_models.GettableAlert.
120-
func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers []string) *open_api_models.GettableAlert {
120+
func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers, mutedBy []string) *open_api_models.GettableAlert {
121121
startsAt := strfmt.DateTime(alert.StartsAt)
122122
updatedAt := strfmt.DateTime(alert.UpdatedAt)
123123
endsAt := strfmt.DateTime(alert.EndsAt)
@@ -128,7 +128,13 @@ func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers
128128
}
129129

130130
fp := alert.Fingerprint().String()
131+
131132
state := string(status.State)
133+
if len(mutedBy) > 0 {
134+
// If the alert is muted, change the state to suppressed.
135+
state = open_api_models.AlertStatusStateSuppressed
136+
}
137+
132138
aa := &open_api_models.GettableAlert{
133139
Alert: open_api_models.Alert{
134140
GeneratorURL: strfmt.URI(alert.GeneratorURL),
@@ -144,6 +150,7 @@ func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers
144150
State: &state,
145151
SilencedBy: status.SilencedBy,
146152
InhibitedBy: status.InhibitedBy,
153+
MutedBy: mutedBy,
147154
},
148155
}
149156

@@ -155,6 +162,10 @@ func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers
155162
aa.Status.InhibitedBy = []string{}
156163
}
157164

165+
if aa.Status.MutedBy == nil {
166+
aa.Status.MutedBy = []string{}
167+
}
168+
158169
return aa
159170
}
160171

api/v2/models/alert_status.go

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/v2/openapi.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ paths:
223223
type: boolean
224224
description: Show inhibited alerts
225225
default: true
226+
- in: query
227+
name: muted
228+
type: boolean
229+
description: Show muted alerts
230+
default: true
226231
- name: filter
227232
in: query
228233
description: A list of matchers to filter alerts by
@@ -501,10 +506,15 @@ definitions:
501506
type: array
502507
items:
503508
type: string
509+
mutedBy:
510+
type: array
511+
items:
512+
type: string
504513
required:
505514
- state
506515
- silencedBy
507516
- inhibitedBy
517+
- mutedBy
508518
receiver:
509519
type: object
510520
properties:

0 commit comments

Comments
 (0)