-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding the alert group info API #3364
base: main
Are you sure you want to change the base?
Changes from all commits
539a611
bd3c88b
0476e0e
a3da05c
8a60bc3
242e3e4
e382ded
054a525
9f06e14
3578acb
4370e49
fe45b95
6d88d43
f4fd6ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,13 +14,16 @@ | |
package v2 | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"regexp" | ||
"sort" | ||
"sync" | ||
"time" | ||
|
||
alertgroupinfolist_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroupinfolist" | ||
|
||
"github.com/go-kit/log" | ||
"github.com/go-kit/log/level" | ||
"github.com/go-openapi/analysis" | ||
|
@@ -53,12 +56,13 @@ import ( | |
|
||
// API represents an Alertmanager API v2 | ||
type API struct { | ||
peer cluster.ClusterPeer | ||
silences *silence.Silences | ||
alerts provider.Alerts | ||
alertGroups groupsFn | ||
getAlertStatus getAlertStatusFn | ||
uptime time.Time | ||
peer cluster.ClusterPeer | ||
silences *silence.Silences | ||
alerts provider.Alerts | ||
alertGroups groupsFn | ||
alertGroupInfos groupInfosFn | ||
getAlertStatus getAlertStatusFn | ||
uptime time.Time | ||
|
||
// mtx protects alertmanagerConfig, setAlertStatus and route. | ||
mtx sync.RWMutex | ||
|
@@ -76,6 +80,7 @@ type API struct { | |
|
||
type ( | ||
groupsFn func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool) (dispatch.AlertGroups, map[prometheus_model.Fingerprint][]string) | ||
groupInfosFn func(func(*dispatch.Route) bool) dispatch.AlertGroupInfos | ||
getAlertStatusFn func(prometheus_model.Fingerprint) types.AlertStatus | ||
setAlertStatusFn func(prometheus_model.LabelSet) | ||
) | ||
|
@@ -84,21 +89,23 @@ type ( | |
func NewAPI( | ||
alerts provider.Alerts, | ||
gf groupsFn, | ||
gif groupInfosFn, | ||
sf getAlertStatusFn, | ||
silences *silence.Silences, | ||
peer cluster.ClusterPeer, | ||
l log.Logger, | ||
r prometheus.Registerer, | ||
) (*API, error) { | ||
api := API{ | ||
alerts: alerts, | ||
getAlertStatus: sf, | ||
alertGroups: gf, | ||
peer: peer, | ||
silences: silences, | ||
logger: l, | ||
m: metrics.NewAlerts("v2", r), | ||
uptime: time.Now(), | ||
alerts: alerts, | ||
getAlertStatus: sf, | ||
alertGroups: gf, | ||
alertGroupInfos: gif, | ||
peer: peer, | ||
silences: silences, | ||
logger: l, | ||
m: metrics.NewAlerts("v2", r), | ||
uptime: time.Now(), | ||
} | ||
|
||
// Load embedded swagger file. | ||
|
@@ -122,6 +129,7 @@ func NewAPI( | |
openAPI.AlertGetAlertsHandler = alert_ops.GetAlertsHandlerFunc(api.getAlertsHandler) | ||
openAPI.AlertPostAlertsHandler = alert_ops.PostAlertsHandlerFunc(api.postAlertsHandler) | ||
openAPI.AlertgroupGetAlertGroupsHandler = alertgroup_ops.GetAlertGroupsHandlerFunc(api.getAlertGroupsHandler) | ||
openAPI.AlertgroupinfolistGetAlertGroupInfoListHandler = alertgroupinfolist_ops.GetAlertGroupInfoListHandlerFunc(api.getAlertGroupInfoListHandler) | ||
openAPI.GeneralGetStatusHandler = general_ops.GetStatusHandlerFunc(api.getStatusHandler) | ||
openAPI.ReceiverGetReceiversHandler = receiver_ops.GetReceiversHandlerFunc(api.getReceiversHandler) | ||
openAPI.SilenceDeleteSilenceHandler = silence_ops.DeleteSilenceHandlerFunc(api.deleteSilenceHandler) | ||
|
@@ -424,6 +432,78 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams | |
return alertgroup_ops.NewGetAlertGroupsOK().WithPayload(res) | ||
} | ||
|
||
func (api *API) getAlertGroupInfoListHandler(params alertgroupinfolist_ops.GetAlertGroupInfoListParams) middleware.Responder { | ||
logger := api.requestLogger(params.HTTPRequest) | ||
|
||
var receiverFilter *regexp.Regexp | ||
var err error | ||
if params.Receiver != nil { | ||
receiverFilter, err = regexp.Compile("^(?:" + *params.Receiver + ")$") | ||
if err != nil { | ||
level.Error(logger).Log("msg", "Failed to compile receiver regex", "err", err) | ||
return alertgroupinfolist_ops. | ||
NewGetAlertGroupInfoListBadRequest(). | ||
WithPayload( | ||
fmt.Sprintf("failed to parse receiver param: %v", err.Error()), | ||
) | ||
} | ||
} | ||
|
||
rf := func(receiverFilter *regexp.Regexp) func(r *dispatch.Route) bool { | ||
return func(r *dispatch.Route) bool { | ||
receiver := r.RouteOpts.Receiver | ||
if receiverFilter != nil && !receiverFilter.MatchString(receiver) { | ||
return false | ||
} | ||
return true | ||
} | ||
}(receiverFilter) | ||
|
||
if err = validateNextToken(params.NextToken); err != nil { | ||
level.Error(logger).Log("msg", "Failed to parse NextToken parameter", "err", err) | ||
return alertgroupinfolist_ops. | ||
NewGetAlertGroupInfoListBadRequest(). | ||
WithPayload( | ||
fmt.Sprintf("failed to parse NextToken param: %v", *params.NextToken), | ||
) | ||
} | ||
|
||
if err = validateMaxResult(params.MaxResults); err != nil { | ||
level.Error(logger).Log("msg", "Failed to parse MaxResults parameter", "err", err) | ||
return alertgroupinfolist_ops. | ||
NewGetAlertGroupInfoListBadRequest(). | ||
WithPayload( | ||
fmt.Sprintf("failed to parse MaxResults param: %v", *params.MaxResults), | ||
) | ||
} | ||
|
||
ags := api.alertGroupInfos(rf) | ||
alertGroupInfos := make([]*open_api_models.AlertGroupInfo, 0, len(ags)) | ||
for _, alertGroup := range ags { | ||
|
||
// Skip the aggregation group if the next token is set and hasn't arrived the nextToken item yet. | ||
if params.NextToken != nil && *params.NextToken >= alertGroup.ID { | ||
continue | ||
} | ||
|
||
ag := &open_api_models.AlertGroupInfo{ | ||
Receiver: &open_api_models.Receiver{Name: &alertGroup.Receiver}, | ||
Labels: ModelLabelSetToAPILabelSet(alertGroup.Labels), | ||
ID: &alertGroup.ID, | ||
} | ||
alertGroupInfos = append(alertGroupInfos, ag) | ||
} | ||
|
||
returnAlertGroupInfos, nextItem := AlertGroupInfoListTruncate(alertGroupInfos, params.MaxResults) | ||
|
||
response := &open_api_models.AlertGroupInfoList{ | ||
AlertGroupInfoList: returnAlertGroupInfos, | ||
NextToken: nextItem, | ||
} | ||
|
||
return alertgroupinfolist_ops.NewGetAlertGroupInfoListOK().WithPayload(response) | ||
} | ||
|
||
func (api *API) alertFilter(matchers []*labels.Matcher, silenced, inhibited, active bool) func(a *types.Alert, now time.Time) bool { | ||
return func(a *types.Alert, now time.Time) bool { | ||
if !a.EndsAt.IsZero() && a.EndsAt.Before(now) { | ||
|
@@ -721,3 +801,22 @@ func getSwaggerSpec() (*loads.Document, *analysis.Spec, error) { | |
swaggerSpecAnalysisCache = analysis.New(swaggerSpec.Spec()) | ||
return swaggerSpec, swaggerSpecAnalysisCache, nil | ||
} | ||
|
||
func validateMaxResult(maxItem *int64) error { | ||
if maxItem != nil { | ||
if *maxItem < 0 { | ||
return errors.New("the maxItem need to be larger than or equal to 0") | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func validateNextToken(nextToken *string) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to escape the validation when nextToken is nil, kind of want to keep that if into this function rather in main function |
||
if nextToken != nil { | ||
match, _ := regexp.MatchString("^[a-fA-F0-9]{40}$", *nextToken) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's compile the regex once and match here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed the nextToken != "" |
||
if !match { | ||
return fmt.Errorf("invalid nextToken: %s", *nextToken) | ||
} | ||
} | ||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question, do we need to use regex here? That means the input
params.Receiver
contains multiple receivers?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is more about support regex search for receivers, i am following convention of receiver filter parameter in the other apis
See https://github.com/prometheus/alertmanager/blob/main/api/v2/api.go#L250-L260