1414package v2
1515
1616import (
17+ "context"
1718 "errors"
1819 "fmt"
1920 "log/slog"
@@ -40,6 +41,7 @@ import (
4041 "github.com/prometheus/alertmanager/api/v2/restapi/operations"
4142 alert_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alert"
4243 alertgroup_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup"
44+ alertinfo_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertinfo"
4345 general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general"
4446 receiver_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/receiver"
4547 silence_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/silence"
@@ -82,7 +84,7 @@ type API struct {
8284}
8385
8486type (
85- groupsFn func (func (* dispatch.Route ) bool , func (* types.Alert , time.Time ) bool ) (dispatch.AlertGroups , map [prometheus_model.Fingerprint ][]string )
87+ groupsFn func (func (* dispatch.Route ) bool , func (* types.Alert , time.Time ) bool , func ( string ) bool ) (dispatch.AlertGroups , map [prometheus_model.Fingerprint ][]string )
8688 groupMutedFunc func (routeID , groupKey string ) ([]string , bool )
8789 groupInfosFn func (func (* dispatch.Route ) bool ) dispatch.AlertGroupInfos
8890 getAlertStatusFn func (prometheus_model.Fingerprint ) types.AlertStatus
@@ -138,6 +140,7 @@ func NewAPI(
138140 }
139141
140142 openAPI .AlertGetAlertsHandler = alert_ops .GetAlertsHandlerFunc (api .getAlertsHandler )
143+ openAPI .AlertinfoGetAlertInfosHandler = alertinfo_ops .GetAlertInfosHandlerFunc (api .getAlertInfosHandler )
141144 openAPI .AlertPostAlertsHandler = alert_ops .PostAlertsHandlerFunc (api .postAlertsHandler )
142145 openAPI .AlertgroupGetAlertGroupsHandler = alertgroup_ops .GetAlertGroupsHandlerFunc (api .getAlertGroupsHandler )
143146 openAPI .AlertgroupinfolistGetAlertGroupInfoListHandler = alertgroupinfolist_ops .GetAlertGroupInfoListHandlerFunc (api .getAlertGroupInfoListHandler )
@@ -252,10 +255,7 @@ func (api *API) getReceiversHandler(params receiver_ops.GetReceiversParams) midd
252255func (api * API ) getAlertsHandler (params alert_ops.GetAlertsParams ) middleware.Responder {
253256 var (
254257 receiverFilter * regexp.Regexp
255- // Initialize result slice to prevent api returning `null` when there
256- // are no alerts present
257- res = open_api_models.GettableAlerts {}
258- ctx = params .HTTPRequest .Context ()
258+ ctx = params .HTTPRequest .Context ()
259259
260260 logger = api .requestLogger (params .HTTPRequest )
261261 )
@@ -278,56 +278,87 @@ func (api *API) getAlertsHandler(params alert_ops.GetAlertsParams) middleware.Re
278278 }
279279 }
280280
281- alerts := api .alerts .GetPending ()
282- defer alerts .Close ()
283-
284281 alertFilter := api .alertFilter (matchers , * params .Silenced , * params .Inhibited , * params .Active )
285- now := time .Now ()
282+ alerts , err := api .getAlerts (ctx , receiverFilter , alertFilter )
283+ if err != nil {
284+ logger .Error ("Failed to get alerts" , "err" , err )
285+ return alert_ops .NewGetAlertsInternalServerError ().WithPayload (err .Error ())
286+ }
286287
287- api .mtx .RLock ()
288- for a := range alerts .Next () {
289- if err = alerts .Err (); err != nil {
290- break
291- }
292- if err = ctx .Err (); err != nil {
293- break
294- }
288+ callbackRes , err := api .apiCallback .V2GetAlertsCallback (alerts )
289+ if err != nil {
290+ logger .Error ("Failed to call api callback" , "err" , err )
291+ return alert_ops .NewGetAlertsInternalServerError ().WithPayload (err .Error ())
292+ }
295293
296- routes := api .route .Match (a .Labels )
297- receivers := make ([]string , 0 , len (routes ))
298- for _ , r := range routes {
299- receivers = append (receivers , r .RouteOpts .Receiver )
300- }
294+ return alert_ops .NewGetAlertsOK ().WithPayload (callbackRes )
295+ }
301296
302- if receiverFilter != nil && ! receiversMatchFilter (receivers , receiverFilter ) {
303- continue
304- }
297+ func (api * API ) getAlertInfosHandler (params alertinfo_ops.GetAlertInfosParams ) middleware.Responder {
298+ var (
299+ alerts open_api_models.GettableAlerts
300+ receiverFilter * regexp.Regexp
301+ ctx = params .HTTPRequest .Context ()
305302
306- if ! alertFilter (a , now ) {
307- continue
303+ logger = api .requestLogger (params .HTTPRequest )
304+ )
305+
306+ matchers , err := parseFilter (params .Filter )
307+ if err != nil {
308+ logger .Debug ("Failed to parse matchers" , "err" , err )
309+ return alertinfo_ops .NewGetAlertInfosBadRequest ().WithPayload (err .Error ())
310+ }
311+
312+ if params .Receiver != nil {
313+ receiverFilter , err = regexp .Compile ("^(?:" + * params .Receiver + ")$" )
314+ if err != nil {
315+ logger .Debug ("Failed to compile receiver regex" , "err" , err )
316+ return alertinfo_ops .
317+ NewGetAlertInfosBadRequest ().
318+ WithPayload (
319+ fmt .Sprintf ("failed to parse receiver param: %v" , err .Error ()),
320+ )
308321 }
322+ }
309323
310- alert := AlertToOpenAPIAlert (a , api .getAlertStatus (a .Fingerprint ()), receivers , nil )
324+ if err = validateMaxResult (params .MaxResults ); err != nil {
325+ logger .Error ("Failed to parse MaxResults parameter" , "err" , err )
326+ return alertinfo_ops .
327+ NewGetAlertInfosBadRequest ().
328+ WithPayload (
329+ fmt .Sprintf ("failed to parse MaxResults param: %v" , * params .MaxResults ),
330+ )
331+ }
311332
312- res = append (res , alert )
333+ if err = validateAlertInfoNextToken (params .NextToken ); err != nil {
334+ logger .Error ("Failed to parse NextToken parameter" , "err" , err )
335+ return alertinfo_ops .
336+ NewGetAlertInfosBadRequest ().
337+ WithPayload (
338+ fmt .Sprintf ("failed to parse NextToken param: %v" , * params .NextToken ),
339+ )
313340 }
314- api .mtx .RUnlock ()
315341
342+ alertFilter := api .alertFilter (matchers , * params .Silenced , * params .Inhibited , * params .Active )
343+ groupIdsFilter := api .groupIDFilter (params .GroupID )
344+ if len (params .GroupID ) > 0 {
345+ alerts , err = api .getAlertsFromAlertGroup (ctx , receiverFilter , alertFilter , groupIdsFilter )
346+ } else {
347+ alerts , err = api .getAlerts (ctx , receiverFilter , alertFilter )
348+ }
316349 if err != nil {
317350 logger .Error ("Failed to get alerts" , "err" , err )
318- return alert_ops . NewGetAlertsInternalServerError ().WithPayload (err .Error ())
351+ return alertinfo_ops . NewGetAlertInfosInternalServerError ().WithPayload (err .Error ())
319352 }
320- sort .Slice (res , func (i , j int ) bool {
321- return * res [i ].Fingerprint < * res [j ].Fingerprint
322- })
323353
324- callbackRes , err := api .apiCallback .V2GetAlertsCallback (res )
325- if err != nil {
326- logger .Error ("Failed to call api callback" , "err" , err )
327- return alert_ops .NewGetAlertsInternalServerError ().WithPayload (err .Error ())
354+ returnAlertInfos , nextItem := AlertInfosTruncate (alerts , params .MaxResults , params .NextToken )
355+
356+ response := & open_api_models.GettableAlertInfos {
357+ Alerts : returnAlertInfos ,
358+ NextToken : nextItem ,
328359 }
329360
330- return alert_ops . NewGetAlertsOK ().WithPayload (callbackRes )
361+ return alertinfo_ops . NewGetAlertInfosOK ().WithPayload (response )
331362}
332363
333364func (api * API ) postAlertsHandler (params alert_ops.PostAlertsParams ) middleware.Responder {
@@ -414,18 +445,10 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams
414445 }
415446 }
416447
417- rf := func (receiverFilter * regexp.Regexp ) func (r * dispatch.Route ) bool {
418- return func (r * dispatch.Route ) bool {
419- receiver := r .RouteOpts .Receiver
420- if receiverFilter != nil && ! receiverFilter .MatchString (receiver ) {
421- return false
422- }
423- return true
424- }
425- }(receiverFilter )
426-
448+ rf := api .routeFilter (receiverFilter )
427449 af := api .alertFilter (matchers , * params .Silenced , * params .Inhibited , * params .Active )
428- alertGroups , allReceivers := api .alertGroups (rf , af )
450+ gf := api .groupIDFilter ([]string {})
451+ alertGroups , allReceivers := api .alertGroups (rf , af , gf )
429452
430453 res := make (open_api_models.AlertGroups , 0 , len (alertGroups ))
431454
@@ -532,6 +555,32 @@ func (api *API) getAlertGroupInfoListHandler(params alertgroupinfolist_ops.GetAl
532555 return alertgroupinfolist_ops .NewGetAlertGroupInfoListOK ().WithPayload (response )
533556}
534557
558+ func (api * API ) groupIDFilter (groupIDsFilter []string ) func (groupId string ) bool {
559+ return func (groupId string ) bool {
560+ if len (groupIDsFilter ) <= 0 {
561+ return true
562+ }
563+ for _ , groupIDFilter := range groupIDsFilter {
564+ if groupIDFilter == groupId {
565+ return true
566+ }
567+ }
568+ return false
569+ }
570+ }
571+
572+ func (api * API ) routeFilter (receiverFilter * regexp.Regexp ) func (r * dispatch.Route ) bool {
573+ return func (receiverFilter * regexp.Regexp ) func (r * dispatch.Route ) bool {
574+ return func (r * dispatch.Route ) bool {
575+ receiver := r .RouteOpts .Receiver
576+ if receiverFilter != nil && ! receiverFilter .MatchString (receiver ) {
577+ return false
578+ }
579+ return true
580+ }
581+ }(receiverFilter )
582+ }
583+
535584func (api * API ) alertFilter (matchers []* labels.Matcher , silenced , inhibited , active bool ) func (a * types.Alert , now time.Time ) bool {
536585 return func (a * types.Alert , now time.Time ) bool {
537586 if ! a .EndsAt .IsZero () && a .EndsAt .Before (now ) {
@@ -822,6 +871,78 @@ func getSwaggerSpec() (*loads.Document, *analysis.Spec, error) {
822871 return swaggerSpec , swaggerSpecAnalysisCache , nil
823872}
824873
874+ 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 ) {
875+ res := open_api_models.GettableAlerts {}
876+ routeFilter := api .routeFilter (receiverFilter )
877+ alertGroups , allReceivers := api .alertGroups (routeFilter , alertFilter , groupIdsFilter )
878+ for _ , alertGroup := range alertGroups {
879+ for _ , alert := range alertGroup .Alerts {
880+ if err := ctx .Err (); err != nil {
881+ break
882+ }
883+ fp := alert .Fingerprint ()
884+ receivers := allReceivers [fp ]
885+ status := api .getAlertStatus (fp )
886+ apiAlert := AlertToOpenAPIAlert (alert , status , receivers )
887+ res = append (res , apiAlert )
888+ }
889+ }
890+
891+ sort .Slice (res , func (i , j int ) bool {
892+ return * res [i ].Fingerprint < * res [j ].Fingerprint
893+ })
894+
895+ return res , nil
896+ }
897+
898+ func (api * API ) getAlerts (ctx context.Context , receiverFilter * regexp.Regexp , alertFilter func (a * types.Alert , now time.Time ) bool ) (open_api_models.GettableAlerts , error ) {
899+ var err error
900+ res := open_api_models.GettableAlerts {}
901+
902+ alerts := api .alerts .GetPending ()
903+ defer alerts .Close ()
904+
905+ now := time .Now ()
906+
907+ api .mtx .RLock ()
908+ for a := range alerts .Next () {
909+ if err = alerts .Err (); err != nil {
910+ break
911+ }
912+ if err = ctx .Err (); err != nil {
913+ break
914+ }
915+
916+ routes := api .route .Match (a .Labels )
917+ receivers := make ([]string , 0 , len (routes ))
918+ for _ , r := range routes {
919+ receivers = append (receivers , r .RouteOpts .Receiver )
920+ }
921+
922+ if receiverFilter != nil && ! receiversMatchFilter (receivers , receiverFilter ) {
923+ continue
924+ }
925+
926+ if ! alertFilter (a , now ) {
927+ continue
928+ }
929+
930+ alert := AlertToOpenAPIAlert (a , api .getAlertStatus (a .Fingerprint ()), receivers , nil )
931+
932+ res = append (res , alert )
933+ }
934+ api .mtx .RUnlock ()
935+
936+ if err != nil {
937+ return res , err
938+ }
939+ sort .Slice (res , func (i , j int ) bool {
940+ return * res [i ].Fingerprint < * res [j ].Fingerprint
941+ })
942+
943+ return res , nil
944+ }
945+
825946func validateMaxResult (maxItem * int64 ) error {
826947 if maxItem != nil {
827948 if * maxItem < 0 {
@@ -831,6 +952,16 @@ func validateMaxResult(maxItem *int64) error {
831952 return nil
832953}
833954
955+ func validateAlertInfoNextToken (nextToken * string ) error {
956+ if nextToken != nil {
957+ _ , err := prometheus_model .ParseFingerprint (* nextToken )
958+ if err != nil {
959+ return err
960+ }
961+ }
962+ return nil
963+ }
964+
834965func validateNextToken (nextToken * string ) error {
835966 if nextToken != nil {
836967 match , _ := regexp .MatchString ("^[a-fA-F0-9]{40}$" , * nextToken )
0 commit comments