mirror of
https://github.com/prometheus/alertmanager.git
synced 2026-02-05 15:45:34 +01:00
Show muted alerts in Alert Groups API (#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 <george.robinson@grafana.com>
This commit is contained in:
22
api/api.go
22
api/api.go
@@ -45,16 +45,20 @@ type API struct {
|
||||
inFlightSem chan struct{}
|
||||
}
|
||||
|
||||
// Options for the creation of an API object. Alerts, Silences, and StatusFunc
|
||||
// are mandatory to set. The zero value for everything else is a safe default.
|
||||
// Options for the creation of an API object. Alerts, Silences, AlertStatusFunc
|
||||
// and GroupMutedFunc are mandatory. The zero value for everything else is a safe
|
||||
// default.
|
||||
type Options struct {
|
||||
// Alerts to be used by the API. Mandatory.
|
||||
Alerts provider.Alerts
|
||||
// Silences to be used by the API. Mandatory.
|
||||
Silences *silence.Silences
|
||||
// StatusFunc is used be the API to retrieve the AlertStatus of an
|
||||
// AlertStatusFunc is used be the API to retrieve the AlertStatus of an
|
||||
// alert. Mandatory.
|
||||
StatusFunc func(model.Fingerprint) types.AlertStatus
|
||||
AlertStatusFunc func(model.Fingerprint) types.AlertStatus
|
||||
// GroupMutedFunc is used be the API to know if an alert is muted.
|
||||
// Mandatory.
|
||||
GroupMutedFunc func(routeID, groupKey string) ([]string, bool)
|
||||
// Peer from the gossip cluster. If nil, no clustering will be used.
|
||||
Peer cluster.ClusterPeer
|
||||
// Timeout for all HTTP connections. The zero value (and negative
|
||||
@@ -83,8 +87,11 @@ func (o Options) validate() error {
|
||||
if o.Silences == nil {
|
||||
return errors.New("mandatory field Silences not set")
|
||||
}
|
||||
if o.StatusFunc == nil {
|
||||
return errors.New("mandatory field StatusFunc not set")
|
||||
if o.AlertStatusFunc == nil {
|
||||
return errors.New("mandatory field AlertStatusFunc not set")
|
||||
}
|
||||
if o.GroupMutedFunc == nil {
|
||||
return errors.New("mandatory field GroupMutedFunc not set")
|
||||
}
|
||||
if o.GroupFunc == nil {
|
||||
return errors.New("mandatory field GroupFunc not set")
|
||||
@@ -113,7 +120,8 @@ func New(opts Options) (*API, error) {
|
||||
v2, err := apiv2.NewAPI(
|
||||
opts.Alerts,
|
||||
opts.GroupFunc,
|
||||
opts.StatusFunc,
|
||||
opts.AlertStatusFunc,
|
||||
opts.GroupMutedFunc,
|
||||
opts.Silences,
|
||||
opts.Peer,
|
||||
log.With(l, "version", "v2"),
|
||||
|
||||
@@ -60,6 +60,7 @@ type API struct {
|
||||
alerts provider.Alerts
|
||||
alertGroups groupsFn
|
||||
getAlertStatus getAlertStatusFn
|
||||
groupMutedFunc groupMutedFunc
|
||||
uptime time.Time
|
||||
|
||||
// mtx protects alertmanagerConfig, setAlertStatus and route.
|
||||
@@ -78,6 +79,7 @@ type API struct {
|
||||
|
||||
type (
|
||||
groupsFn func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool) (dispatch.AlertGroups, map[prometheus_model.Fingerprint][]string)
|
||||
groupMutedFunc func(routeID, groupKey string) ([]string, bool)
|
||||
getAlertStatusFn func(prometheus_model.Fingerprint) types.AlertStatus
|
||||
setAlertStatusFn func(prometheus_model.LabelSet)
|
||||
)
|
||||
@@ -86,7 +88,8 @@ type (
|
||||
func NewAPI(
|
||||
alerts provider.Alerts,
|
||||
gf groupsFn,
|
||||
sf getAlertStatusFn,
|
||||
asf getAlertStatusFn,
|
||||
gmf groupMutedFunc,
|
||||
silences *silence.Silences,
|
||||
peer cluster.ClusterPeer,
|
||||
l log.Logger,
|
||||
@@ -94,8 +97,9 @@ func NewAPI(
|
||||
) (*API, error) {
|
||||
api := API{
|
||||
alerts: alerts,
|
||||
getAlertStatus: sf,
|
||||
getAlertStatus: asf,
|
||||
alertGroups: gf,
|
||||
groupMutedFunc: gmf,
|
||||
peer: peer,
|
||||
silences: silences,
|
||||
logger: l,
|
||||
@@ -290,7 +294,7 @@ func (api *API) getAlertsHandler(params alert_ops.GetAlertsParams) middleware.Re
|
||||
continue
|
||||
}
|
||||
|
||||
alert := AlertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers)
|
||||
alert := AlertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers, nil)
|
||||
|
||||
res = append(res, alert)
|
||||
}
|
||||
@@ -407,6 +411,11 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams
|
||||
res := make(open_api_models.AlertGroups, 0, len(alertGroups))
|
||||
|
||||
for _, alertGroup := range alertGroups {
|
||||
mutedBy, isMuted := api.groupMutedFunc(alertGroup.RouteID, alertGroup.GroupKey)
|
||||
if !*params.Muted && isMuted {
|
||||
continue
|
||||
}
|
||||
|
||||
ag := &open_api_models.AlertGroup{
|
||||
Receiver: &open_api_models.Receiver{Name: &alertGroup.Receiver},
|
||||
Labels: ModelLabelSetToAPILabelSet(alertGroup.Labels),
|
||||
@@ -417,7 +426,7 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams
|
||||
fp := alert.Fingerprint()
|
||||
receivers := allReceivers[fp]
|
||||
status := api.getAlertStatus(fp)
|
||||
apiAlert := AlertToOpenAPIAlert(alert, status, receivers)
|
||||
apiAlert := AlertToOpenAPIAlert(alert, status, receivers, mutedBy)
|
||||
ag.Alerts = append(ag.Alerts, apiAlert)
|
||||
}
|
||||
res = append(res, ag)
|
||||
|
||||
@@ -484,17 +484,12 @@ func TestAlertToOpenAPIAlert(t *testing.T) {
|
||||
UpdatedAt: updated,
|
||||
}
|
||||
)
|
||||
openAPIAlert := AlertToOpenAPIAlert(alert, types.AlertStatus{State: types.AlertStateActive}, receivers)
|
||||
openAPIAlert := AlertToOpenAPIAlert(alert, types.AlertStatus{State: types.AlertStateActive}, receivers, nil)
|
||||
require.Equal(t, &open_api_models.GettableAlert{
|
||||
Annotations: open_api_models.LabelSet{},
|
||||
Alert: open_api_models.Alert{
|
||||
Labels: open_api_models.LabelSet{"severity": "critical", "alertname": "alert1"},
|
||||
},
|
||||
Status: &open_api_models.AlertStatus{
|
||||
State: &active,
|
||||
InhibitedBy: []string{},
|
||||
SilencedBy: []string{},
|
||||
},
|
||||
StartsAt: convertDateTime(start),
|
||||
EndsAt: convertDateTime(time.Time{}),
|
||||
UpdatedAt: convertDateTime(updated),
|
||||
@@ -503,6 +498,12 @@ func TestAlertToOpenAPIAlert(t *testing.T) {
|
||||
{Name: &receivers[0]},
|
||||
{Name: &receivers[1]},
|
||||
},
|
||||
Status: &open_api_models.AlertStatus{
|
||||
State: &active,
|
||||
InhibitedBy: []string{},
|
||||
SilencedBy: []string{},
|
||||
MutedBy: []string{},
|
||||
},
|
||||
}, openAPIAlert)
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,14 @@ type GetAlertGroupsParams struct {
|
||||
*/
|
||||
Inhibited *bool
|
||||
|
||||
/* Muted.
|
||||
|
||||
Show muted alerts
|
||||
|
||||
Default: true
|
||||
*/
|
||||
Muted *bool
|
||||
|
||||
/* Receiver.
|
||||
|
||||
A regex matching receivers to filter alerts by
|
||||
@@ -134,12 +142,15 @@ func (o *GetAlertGroupsParams) SetDefaults() {
|
||||
|
||||
inhibitedDefault = bool(true)
|
||||
|
||||
mutedDefault = bool(true)
|
||||
|
||||
silencedDefault = bool(true)
|
||||
)
|
||||
|
||||
val := GetAlertGroupsParams{
|
||||
Active: &activeDefault,
|
||||
Inhibited: &inhibitedDefault,
|
||||
Muted: &mutedDefault,
|
||||
Silenced: &silencedDefault,
|
||||
}
|
||||
|
||||
@@ -215,6 +226,17 @@ func (o *GetAlertGroupsParams) SetInhibited(inhibited *bool) {
|
||||
o.Inhibited = inhibited
|
||||
}
|
||||
|
||||
// WithMuted adds the muted to the get alert groups params
|
||||
func (o *GetAlertGroupsParams) WithMuted(muted *bool) *GetAlertGroupsParams {
|
||||
o.SetMuted(muted)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetMuted adds the muted to the get alert groups params
|
||||
func (o *GetAlertGroupsParams) SetMuted(muted *bool) {
|
||||
o.Muted = muted
|
||||
}
|
||||
|
||||
// WithReceiver adds the receiver to the get alert groups params
|
||||
func (o *GetAlertGroupsParams) WithReceiver(receiver *string) *GetAlertGroupsParams {
|
||||
o.SetReceiver(receiver)
|
||||
@@ -290,6 +312,23 @@ func (o *GetAlertGroupsParams) WriteToRequest(r runtime.ClientRequest, reg strfm
|
||||
}
|
||||
}
|
||||
|
||||
if o.Muted != nil {
|
||||
|
||||
// query param muted
|
||||
var qrMuted bool
|
||||
|
||||
if o.Muted != nil {
|
||||
qrMuted = *o.Muted
|
||||
}
|
||||
qMuted := swag.FormatBool(qrMuted)
|
||||
if qMuted != "" {
|
||||
|
||||
if err := r.SetQueryParam("muted", qMuted); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if o.Receiver != nil {
|
||||
|
||||
// query param receiver
|
||||
|
||||
@@ -117,7 +117,7 @@ func PostableSilenceToProto(s *open_api_models.PostableSilence) (*silencepb.Sile
|
||||
}
|
||||
|
||||
// AlertToOpenAPIAlert converts internal alerts, alert types, and receivers to *open_api_models.GettableAlert.
|
||||
func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers []string) *open_api_models.GettableAlert {
|
||||
func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers, mutedBy []string) *open_api_models.GettableAlert {
|
||||
startsAt := strfmt.DateTime(alert.StartsAt)
|
||||
updatedAt := strfmt.DateTime(alert.UpdatedAt)
|
||||
endsAt := strfmt.DateTime(alert.EndsAt)
|
||||
@@ -128,7 +128,13 @@ func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers
|
||||
}
|
||||
|
||||
fp := alert.Fingerprint().String()
|
||||
|
||||
state := string(status.State)
|
||||
if len(mutedBy) > 0 {
|
||||
// If the alert is muted, change the state to suppressed.
|
||||
state = open_api_models.AlertStatusStateSuppressed
|
||||
}
|
||||
|
||||
aa := &open_api_models.GettableAlert{
|
||||
Alert: open_api_models.Alert{
|
||||
GeneratorURL: strfmt.URI(alert.GeneratorURL),
|
||||
@@ -144,6 +150,7 @@ func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers
|
||||
State: &state,
|
||||
SilencedBy: status.SilencedBy,
|
||||
InhibitedBy: status.InhibitedBy,
|
||||
MutedBy: mutedBy,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -155,6 +162,10 @@ func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers
|
||||
aa.Status.InhibitedBy = []string{}
|
||||
}
|
||||
|
||||
if aa.Status.MutedBy == nil {
|
||||
aa.Status.MutedBy = []string{}
|
||||
}
|
||||
|
||||
return aa
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ type AlertStatus struct {
|
||||
// Required: true
|
||||
InhibitedBy []string `json:"inhibitedBy"`
|
||||
|
||||
// muted by
|
||||
// Required: true
|
||||
MutedBy []string `json:"mutedBy"`
|
||||
|
||||
// silenced by
|
||||
// Required: true
|
||||
SilencedBy []string `json:"silencedBy"`
|
||||
@@ -56,6 +60,10 @@ func (m *AlertStatus) Validate(formats strfmt.Registry) error {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateMutedBy(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateSilencedBy(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
@@ -79,6 +87,15 @@ func (m *AlertStatus) validateInhibitedBy(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AlertStatus) validateMutedBy(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("mutedBy", "body", m.MutedBy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AlertStatus) validateSilencedBy(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("silencedBy", "body", m.SilencedBy); err != nil {
|
||||
|
||||
@@ -223,6 +223,11 @@ paths:
|
||||
type: boolean
|
||||
description: Show inhibited alerts
|
||||
default: true
|
||||
- in: query
|
||||
name: muted
|
||||
type: boolean
|
||||
description: Show muted alerts
|
||||
default: true
|
||||
- name: filter
|
||||
in: query
|
||||
description: A list of matchers to filter alerts by
|
||||
@@ -501,10 +506,15 @@ definitions:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
mutedBy:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
required:
|
||||
- state
|
||||
- silencedBy
|
||||
- inhibitedBy
|
||||
- mutedBy
|
||||
receiver:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -177,6 +177,13 @@ func init() {
|
||||
"name": "inhibited",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Show muted alerts",
|
||||
"name": "muted",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -433,7 +440,8 @@ func init() {
|
||||
"required": [
|
||||
"state",
|
||||
"silencedBy",
|
||||
"inhibitedBy"
|
||||
"inhibitedBy",
|
||||
"mutedBy"
|
||||
],
|
||||
"properties": {
|
||||
"inhibitedBy": {
|
||||
@@ -442,6 +450,12 @@ func init() {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"mutedBy": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"silencedBy": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -979,6 +993,13 @@ func init() {
|
||||
"name": "inhibited",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Show muted alerts",
|
||||
"name": "muted",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -1256,7 +1277,8 @@ func init() {
|
||||
"required": [
|
||||
"state",
|
||||
"silencedBy",
|
||||
"inhibitedBy"
|
||||
"inhibitedBy",
|
||||
"mutedBy"
|
||||
],
|
||||
"properties": {
|
||||
"inhibitedBy": {
|
||||
@@ -1265,6 +1287,12 @@ func init() {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"mutedBy": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"silencedBy": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
@@ -39,6 +39,7 @@ func NewGetAlertGroupsParams() GetAlertGroupsParams {
|
||||
activeDefault = bool(true)
|
||||
|
||||
inhibitedDefault = bool(true)
|
||||
mutedDefault = bool(true)
|
||||
|
||||
silencedDefault = bool(true)
|
||||
)
|
||||
@@ -48,6 +49,8 @@ func NewGetAlertGroupsParams() GetAlertGroupsParams {
|
||||
|
||||
Inhibited: &inhibitedDefault,
|
||||
|
||||
Muted: &mutedDefault,
|
||||
|
||||
Silenced: &silencedDefault,
|
||||
}
|
||||
}
|
||||
@@ -76,6 +79,11 @@ type GetAlertGroupsParams struct {
|
||||
Default: true
|
||||
*/
|
||||
Inhibited *bool
|
||||
/*Show muted alerts
|
||||
In: query
|
||||
Default: true
|
||||
*/
|
||||
Muted *bool
|
||||
/*A regex matching receivers to filter alerts by
|
||||
In: query
|
||||
*/
|
||||
@@ -113,6 +121,11 @@ func (o *GetAlertGroupsParams) BindRequest(r *http.Request, route *middleware.Ma
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qMuted, qhkMuted, _ := qs.GetOK("muted")
|
||||
if err := o.bindMuted(qMuted, qhkMuted, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qReceiver, qhkReceiver, _ := qs.GetOK("receiver")
|
||||
if err := o.bindReceiver(qReceiver, qhkReceiver, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
@@ -198,6 +211,30 @@ func (o *GetAlertGroupsParams) bindInhibited(rawData []string, hasKey bool, form
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindMuted binds and validates parameter Muted from query.
|
||||
func (o *GetAlertGroupsParams) bindMuted(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
|
||||
if raw == "" { // empty values pass all other validations
|
||||
// Default values have been previously initialized by NewGetAlertGroupsParams()
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := swag.ConvertBool(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("muted", "query", "bool", raw)
|
||||
}
|
||||
o.Muted = &value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindReceiver binds and validates parameter Receiver from query.
|
||||
func (o *GetAlertGroupsParams) bindReceiver(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
|
||||
@@ -32,6 +32,7 @@ type GetAlertGroupsURL struct {
|
||||
Active *bool
|
||||
Filter []string
|
||||
Inhibited *bool
|
||||
Muted *bool
|
||||
Receiver *string
|
||||
Silenced *bool
|
||||
|
||||
@@ -99,6 +100,14 @@ func (o *GetAlertGroupsURL) Build() (*url.URL, error) {
|
||||
qs.Set("inhibited", inhibitedQ)
|
||||
}
|
||||
|
||||
var mutedQ string
|
||||
if o.Muted != nil {
|
||||
mutedQ = swag.FormatBool(*o.Muted)
|
||||
}
|
||||
if mutedQ != "" {
|
||||
qs.Set("muted", mutedQ)
|
||||
}
|
||||
|
||||
var receiverQ string
|
||||
if o.Receiver != nil {
|
||||
receiverQ = *o.Receiver
|
||||
|
||||
Reference in New Issue
Block a user