Skip to content

Commit e65af86

Browse files
qinxx108alexqyle
authored andcommitted
add the paginated version of list alerts api
1 parent 57e5a0a commit e65af86

22 files changed

+2658
-89
lines changed

api/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ type Options struct {
7575
// GroupFunc returns a list of alert groups. The alerts are grouped
7676
// according to the current active configuration. Alerts returned are
7777
// filtered by the arguments provided to the function.
78-
GroupFunc func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool) (dispatch.AlertGroups, map[model.Fingerprint][]string)
78+
GroupFunc func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool, func(string) bool) (dispatch.AlertGroups, map[model.Fingerprint][]string)
7979
// GroupInfoFunc returns a list of alert groups information. The alerts are grouped
8080
// according to the current active configuration. This function will not return the alerts inside each group.
8181
GroupInfoFunc func(func(*dispatch.Route) bool) dispatch.AlertGroupInfos

api/v2/api.go

Lines changed: 181 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package v2
1515

1616
import (
17+
"context"
1718
"errors"
1819
"fmt"
1920
"net/http"
@@ -42,6 +43,7 @@ import (
4243
"github.com/prometheus/alertmanager/api/v2/restapi/operations"
4344
alert_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alert"
4445
alertgroup_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup"
46+
alertinfo_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertinfo"
4547
general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general"
4648
receiver_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/receiver"
4749
silence_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/silence"
@@ -81,7 +83,7 @@ type API struct {
8183
}
8284

8385
type (
84-
groupsFn func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool) (dispatch.AlertGroups, map[prometheus_model.Fingerprint][]string)
86+
groupsFn func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool, func(string) bool) (dispatch.AlertGroups, map[prometheus_model.Fingerprint][]string)
8587
groupInfosFn func(func(*dispatch.Route) bool) dispatch.AlertGroupInfos
8688
getAlertStatusFn func(prometheus_model.Fingerprint) types.AlertStatus
8789
setAlertStatusFn func(prometheus_model.LabelSet)
@@ -134,6 +136,7 @@ func NewAPI(
134136
}
135137

136138
openAPI.AlertGetAlertsHandler = alert_ops.GetAlertsHandlerFunc(api.getAlertsHandler)
139+
openAPI.AlertinfoGetAlertInfosHandler = alertinfo_ops.GetAlertInfosHandlerFunc(api.getAlertInfosHandler)
137140
openAPI.AlertPostAlertsHandler = alert_ops.PostAlertsHandlerFunc(api.postAlertsHandler)
138141
openAPI.AlertgroupGetAlertGroupsHandler = alertgroup_ops.GetAlertGroupsHandlerFunc(api.getAlertGroupsHandler)
139142
openAPI.AlertgroupinfolistGetAlertGroupInfoListHandler = alertgroupinfolist_ops.GetAlertGroupInfoListHandlerFunc(api.getAlertGroupInfoListHandler)
@@ -248,10 +251,7 @@ func (api *API) getReceiversHandler(params receiver_ops.GetReceiversParams) midd
248251
func (api *API) getAlertsHandler(params alert_ops.GetAlertsParams) middleware.Responder {
249252
var (
250253
receiverFilter *regexp.Regexp
251-
// Initialize result slice to prevent api returning `null` when there
252-
// are no alerts present
253-
res = open_api_models.GettableAlerts{}
254-
ctx = params.HTTPRequest.Context()
254+
ctx = params.HTTPRequest.Context()
255255

256256
logger = api.requestLogger(params.HTTPRequest)
257257
)
@@ -274,56 +274,87 @@ func (api *API) getAlertsHandler(params alert_ops.GetAlertsParams) middleware.Re
274274
}
275275
}
276276

277-
alerts := api.alerts.GetPending()
278-
defer alerts.Close()
279-
280277
alertFilter := api.alertFilter(matchers, *params.Silenced, *params.Inhibited, *params.Active)
281-
now := time.Now()
278+
alerts, err := api.getAlerts(ctx, receiverFilter, alertFilter)
279+
if err != nil {
280+
level.Error(logger).Log("msg", "Failed to get alerts", "err", err)
281+
return alert_ops.NewGetAlertsInternalServerError().WithPayload(err.Error())
282+
}
282283

283-
api.mtx.RLock()
284-
for a := range alerts.Next() {
285-
if err = alerts.Err(); err != nil {
286-
break
287-
}
288-
if err = ctx.Err(); err != nil {
289-
break
290-
}
284+
callbackRes, err := api.apiCallback.V2GetAlertsCallback(alerts)
285+
if err != nil {
286+
level.Error(logger).Log("msg", "Failed to call api callback", "err", err)
287+
return alert_ops.NewGetAlertsInternalServerError().WithPayload(err.Error())
288+
}
291289

292-
routes := api.route.Match(a.Labels)
293-
receivers := make([]string, 0, len(routes))
294-
for _, r := range routes {
295-
receivers = append(receivers, r.RouteOpts.Receiver)
296-
}
290+
return alert_ops.NewGetAlertsOK().WithPayload(callbackRes)
291+
}
297292

298-
if receiverFilter != nil && !receiversMatchFilter(receivers, receiverFilter) {
299-
continue
300-
}
293+
func (api *API) getAlertInfosHandler(params alertinfo_ops.GetAlertInfosParams) middleware.Responder {
294+
var (
295+
alerts open_api_models.GettableAlerts
296+
receiverFilter *regexp.Regexp
297+
ctx = params.HTTPRequest.Context()
301298

302-
if !alertFilter(a, now) {
303-
continue
299+
logger = api.requestLogger(params.HTTPRequest)
300+
)
301+
302+
matchers, err := parseFilter(params.Filter)
303+
if err != nil {
304+
level.Debug(logger).Log("msg", "Failed to parse matchers", "err", err)
305+
return alertinfo_ops.NewGetAlertInfosBadRequest().WithPayload(err.Error())
306+
}
307+
308+
if params.Receiver != nil {
309+
receiverFilter, err = regexp.Compile("^(?:" + *params.Receiver + ")$")
310+
if err != nil {
311+
level.Debug(logger).Log("msg", "Failed to compile receiver regex", "err", err)
312+
return alertinfo_ops.
313+
NewGetAlertInfosBadRequest().
314+
WithPayload(
315+
fmt.Sprintf("failed to parse receiver param: %v", err.Error()),
316+
)
304317
}
318+
}
305319

306-
alert := AlertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers)
320+
if err = validateMaxResult(params.MaxResults); err != nil {
321+
level.Error(logger).Log("msg", "Failed to parse MaxResults parameter", "err", err)
322+
return alertinfo_ops.
323+
NewGetAlertInfosBadRequest().
324+
WithPayload(
325+
fmt.Sprintf("failed to parse MaxResults param: %v", *params.MaxResults),
326+
)
327+
}
307328

308-
res = append(res, alert)
329+
if err = validateAlertInfoNextToken(params.NextToken); err != nil {
330+
level.Error(logger).Log("msg", "Failed to parse NextToken parameter", "err", err)
331+
return alertinfo_ops.
332+
NewGetAlertInfosBadRequest().
333+
WithPayload(
334+
fmt.Sprintf("failed to parse NextToken param: %v", *params.NextToken),
335+
)
309336
}
310-
api.mtx.RUnlock()
311337

338+
alertFilter := api.alertFilter(matchers, *params.Silenced, *params.Inhibited, *params.Active)
339+
groupIdsFilter := api.groupIDFilter(params.GroupID)
340+
if len(params.GroupID) > 0 {
341+
alerts, err = api.getAlertsFromAlertGroup(ctx, receiverFilter, alertFilter, groupIdsFilter)
342+
} else {
343+
alerts, err = api.getAlerts(ctx, receiverFilter, alertFilter)
344+
}
312345
if err != nil {
313346
level.Error(logger).Log("msg", "Failed to get alerts", "err", err)
314-
return alert_ops.NewGetAlertsInternalServerError().WithPayload(err.Error())
347+
return alertinfo_ops.NewGetAlertInfosInternalServerError().WithPayload(err.Error())
315348
}
316-
sort.Slice(res, func(i, j int) bool {
317-
return *res[i].Fingerprint < *res[j].Fingerprint
318-
})
319349

320-
callbackRes, err := api.apiCallback.V2GetAlertsCallback(res)
321-
if err != nil {
322-
level.Error(logger).Log("msg", "Failed to call api callback", "err", err)
323-
return alert_ops.NewGetAlertsInternalServerError().WithPayload(err.Error())
350+
returnAlertInfos, nextItem := AlertInfosTruncate(alerts, params.MaxResults, params.NextToken)
351+
352+
response := &open_api_models.GettableAlertInfos{
353+
Alerts: returnAlertInfos,
354+
NextToken: nextItem,
324355
}
325356

326-
return alert_ops.NewGetAlertsOK().WithPayload(callbackRes)
357+
return alertinfo_ops.NewGetAlertInfosOK().WithPayload(response)
327358
}
328359

329360
func (api *API) postAlertsHandler(params alert_ops.PostAlertsParams) middleware.Responder {
@@ -410,18 +441,10 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams
410441
}
411442
}
412443

413-
rf := func(receiverFilter *regexp.Regexp) func(r *dispatch.Route) bool {
414-
return func(r *dispatch.Route) bool {
415-
receiver := r.RouteOpts.Receiver
416-
if receiverFilter != nil && !receiverFilter.MatchString(receiver) {
417-
return false
418-
}
419-
return true
420-
}
421-
}(receiverFilter)
422-
444+
rf := api.routeFilter(receiverFilter)
423445
af := api.alertFilter(matchers, *params.Silenced, *params.Inhibited, *params.Active)
424-
alertGroups, allReceivers := api.alertGroups(rf, af)
446+
gf := api.groupIDFilter([]string{})
447+
alertGroups, allReceivers := api.alertGroups(rf, af, gf)
425448

426449
res := make(open_api_models.AlertGroups, 0, len(alertGroups))
427450

@@ -523,6 +546,32 @@ func (api *API) getAlertGroupInfoListHandler(params alertgroupinfolist_ops.GetAl
523546
return alertgroupinfolist_ops.NewGetAlertGroupInfoListOK().WithPayload(response)
524547
}
525548

549+
func (api *API) groupIDFilter(groupIDsFilter []string) func(groupId string) bool {
550+
return func(groupId string) bool {
551+
if len(groupIDsFilter) <= 0 {
552+
return true
553+
}
554+
for _, groupIDFilter := range groupIDsFilter {
555+
if groupIDFilter == groupId {
556+
return true
557+
}
558+
}
559+
return false
560+
}
561+
}
562+
563+
func (api *API) routeFilter(receiverFilter *regexp.Regexp) func(r *dispatch.Route) bool {
564+
return func(receiverFilter *regexp.Regexp) func(r *dispatch.Route) bool {
565+
return func(r *dispatch.Route) bool {
566+
receiver := r.RouteOpts.Receiver
567+
if receiverFilter != nil && !receiverFilter.MatchString(receiver) {
568+
return false
569+
}
570+
return true
571+
}
572+
}(receiverFilter)
573+
}
574+
526575
func (api *API) alertFilter(matchers []*labels.Matcher, silenced, inhibited, active bool) func(a *types.Alert, now time.Time) bool {
527576
return func(a *types.Alert, now time.Time) bool {
528577
if !a.EndsAt.IsZero() && a.EndsAt.Before(now) {
@@ -821,6 +870,78 @@ func getSwaggerSpec() (*loads.Document, *analysis.Spec, error) {
821870
return swaggerSpec, swaggerSpecAnalysisCache, nil
822871
}
823872

873+
func (api *API) getAlertsFromAlertGroup(ctx context.Context, receiverFilter *regexp.Regexp, alertFilter func(a *types.Alert, now time.Time) bool, groupIdsFilter func(groupId string) bool) (open_api_models.GettableAlerts, error) {
874+
res := open_api_models.GettableAlerts{}
875+
routeFilter := api.routeFilter(receiverFilter)
876+
alertGroups, allReceivers := api.alertGroups(routeFilter, alertFilter, groupIdsFilter)
877+
for _, alertGroup := range alertGroups {
878+
for _, alert := range alertGroup.Alerts {
879+
if err := ctx.Err(); err != nil {
880+
break
881+
}
882+
fp := alert.Fingerprint()
883+
receivers := allReceivers[fp]
884+
status := api.getAlertStatus(fp)
885+
apiAlert := AlertToOpenAPIAlert(alert, status, receivers)
886+
res = append(res, apiAlert)
887+
}
888+
}
889+
890+
sort.Slice(res, func(i, j int) bool {
891+
return *res[i].Fingerprint < *res[j].Fingerprint
892+
})
893+
894+
return res, nil
895+
}
896+
897+
func (api *API) getAlerts(ctx context.Context, receiverFilter *regexp.Regexp, alertFilter func(a *types.Alert, now time.Time) bool) (open_api_models.GettableAlerts, error) {
898+
var err error
899+
res := open_api_models.GettableAlerts{}
900+
901+
alerts := api.alerts.GetPending()
902+
defer alerts.Close()
903+
904+
now := time.Now()
905+
906+
api.mtx.RLock()
907+
for a := range alerts.Next() {
908+
if err = alerts.Err(); err != nil {
909+
break
910+
}
911+
if err = ctx.Err(); err != nil {
912+
break
913+
}
914+
915+
routes := api.route.Match(a.Labels)
916+
receivers := make([]string, 0, len(routes))
917+
for _, r := range routes {
918+
receivers = append(receivers, r.RouteOpts.Receiver)
919+
}
920+
921+
if receiverFilter != nil && !receiversMatchFilter(receivers, receiverFilter) {
922+
continue
923+
}
924+
925+
if !alertFilter(a, now) {
926+
continue
927+
}
928+
929+
alert := AlertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers)
930+
931+
res = append(res, alert)
932+
}
933+
api.mtx.RUnlock()
934+
935+
if err != nil {
936+
return res, err
937+
}
938+
sort.Slice(res, func(i, j int) bool {
939+
return *res[i].Fingerprint < *res[j].Fingerprint
940+
})
941+
942+
return res, nil
943+
}
944+
824945
func validateMaxResult(maxItem *int64) error {
825946
if maxItem != nil {
826947
if *maxItem < 0 {
@@ -830,6 +951,16 @@ func validateMaxResult(maxItem *int64) error {
830951
return nil
831952
}
832953

954+
func validateAlertInfoNextToken(nextToken *string) error {
955+
if nextToken != nil {
956+
_, err := prometheus_model.ParseFingerprint(*nextToken)
957+
if err != nil {
958+
return err
959+
}
960+
}
961+
return nil
962+
}
963+
833964
func validateNextToken(nextToken *string) error {
834965
if nextToken != nil {
835966
match, _ := regexp.MatchString("^[a-fA-F0-9]{40}$", *nextToken)

0 commit comments

Comments
 (0)