1414package v2
1515
1616import (
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
8385type (
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
248251func (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
329360func (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+
526575func (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+
824945func 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+
833964func validateNextToken (nextToken * string ) error {
834965 if nextToken != nil {
835966 match , _ := regexp .MatchString ("^[a-fA-F0-9]{40}$" , * nextToken )
0 commit comments