diff --git a/api/api.go b/api/api.go index 8afb686bc2..cb444b7882 100644 --- a/api/api.go +++ b/api/api.go @@ -76,7 +76,9 @@ type Options struct { // according to the current active configuration. Alerts returned are // filtered by the arguments provided to the function. GroupFunc func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool) (dispatch.AlertGroups, map[model.Fingerprint][]string) - + // GroupInfoFunc returns a list of alert groups information. The alerts are grouped + // according to the current active configuration. This function will not return the alerts inside each group. + GroupInfoFunc func(func(*dispatch.Route) bool) dispatch.AlertGroupInfos // APICallback define the callback function that each api call will perform before returned. APICallback callback.Callback } @@ -118,6 +120,7 @@ func New(opts Options) (*API, error) { v2, err := apiv2.NewAPI( opts.Alerts, opts.GroupFunc, + opts.GroupInfoFunc, opts.StatusFunc, opts.Silences, opts.APICallback, diff --git a/api/v2/api.go b/api/v2/api.go index a0bc803726..441502b5d1 100644 --- a/api/v2/api.go +++ b/api/v2/api.go @@ -22,7 +22,6 @@ import ( "sync" "time" - "github.com/prometheus/alertmanager/util/callback" "github.com/go-kit/log" "github.com/go-kit/log/level" "github.com/go-openapi/analysis" @@ -34,6 +33,9 @@ import ( "github.com/prometheus/common/version" "github.com/rs/cors" + alertgroupinfolist_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroupinfolist" + "github.com/prometheus/alertmanager/util/callback" + "github.com/prometheus/alertmanager/api/metrics" open_api_models "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/api/v2/restapi" @@ -56,13 +58,14 @@ import ( // API represents an Alertmanager API v2 type API struct { - peer cluster.ClusterPeer - silences *silence.Silences - alerts provider.Alerts - alertGroups groupsFn - getAlertStatus getAlertStatusFn - apiCallback callback.Callback - uptime time.Time + peer cluster.ClusterPeer + silences *silence.Silences + alerts provider.Alerts + alertGroups groupsFn + alertGroupInfos groupInfosFn + getAlertStatus getAlertStatusFn + apiCallback callback.Callback + uptime time.Time // mtx protects alertmanagerConfig, setAlertStatus and route. mtx sync.RWMutex @@ -80,6 +83,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) ) @@ -88,6 +92,7 @@ type ( func NewAPI( alerts provider.Alerts, gf groupsFn, + gif groupInfosFn, sf getAlertStatusFn, silences *silence.Silences, apiCallback callback.Callback, @@ -99,15 +104,16 @@ func NewAPI( apiCallback = callback.NoopAPICallback{} } api := API{ - alerts: alerts, - getAlertStatus: sf, - alertGroups: gf, - peer: peer, - silences: silences, - apiCallback: apiCallback, - logger: l, - m: metrics.NewAlerts(r), - uptime: time.Now(), + alerts: alerts, + getAlertStatus: sf, + alertGroups: gf, + alertGroupInfos: gif, + peer: peer, + silences: silences, + apiCallback: apiCallback, + logger: l, + m: metrics.NewAlerts(r), + uptime: time.Now(), } // Load embedded swagger file. @@ -131,6 +137,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) @@ -445,6 +452,78 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams return alertgroup_ops.NewGetAlertGroupsOK().WithPayload(callbackRes) } +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) { @@ -735,3 +814,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 { + if nextToken != nil { + match, _ := regexp.MatchString("^[a-fA-F0-9]{40}$", *nextToken) + if !match { + return fmt.Errorf("invalid nextToken: %s", *nextToken) + } + } + return nil +} diff --git a/api/v2/api_test.go b/api/v2/api_test.go index b5f62465c8..ba91cbcf8d 100644 --- a/api/v2/api_test.go +++ b/api/v2/api_test.go @@ -23,15 +23,18 @@ import ( "strconv" "testing" "time" - "github.com/prometheus/alertmanager/dispatch" - alert_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alert" - alertgroup_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup" - "github.com/prometheus/alertmanager/util/callback" + "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" + alert_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alert" + alertgroup_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup" + alertgroupinfolist_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroupinfolist" + "github.com/prometheus/alertmanager/dispatch" + "github.com/prometheus/alertmanager/util/callback" + open_api_models "github.com/prometheus/alertmanager/api/v2/models" general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general" receiver_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/receiver" @@ -124,6 +127,138 @@ func gettableSilence(id, state string, } } +func convertIntToPointerInt64(x int64) *int64 { + return &x +} + +func convertStringToPointer(x string) *string { + return &x +} + +func TestGetAlertGroupInfosHandler(t *testing.T) { + aginfos := dispatch.AlertGroupInfos{ + &dispatch.AlertGroupInfo{ + Labels: model.LabelSet{ + "alertname": "TestingAlert", + "service": "api", + }, + Receiver: "testing", + ID: "478b4114226224a35910d449fdba8186ebfb441f", + }, + &dispatch.AlertGroupInfo{ + Labels: model.LabelSet{ + "alertname": "HighErrorRate", + "service": "api", + "cluster": "bb", + }, + Receiver: "prod", + ID: "7f4084a078a3fe29d6de82fad15af8f1411e803f", + }, + &dispatch.AlertGroupInfo{ + Labels: model.LabelSet{ + "alertname": "OtherAlert", + }, + Receiver: "prod", + ID: "d525244929240cbdb75a497913c1890ab8de1962", + }, + &dispatch.AlertGroupInfo{ + Labels: model.LabelSet{ + "alertname": "HighErrorRate", + "service": "api", + "cluster": "aa", + }, + Receiver: "prod", + ID: "d73984d43949112ae1ea59dcc5af4af7b630a5b1", + }, + } + for _, tc := range []struct { + maxResult *int64 + nextToken *string + body string + expectedCode int + }{ + // Invalid next token. + { + convertIntToPointerInt64(int64(1)), + convertStringToPointer("$$$"), + `failed to parse NextToken param: $$$`, + 400, + }, + // Invalid next token. + { + convertIntToPointerInt64(int64(1)), + convertStringToPointer("1234s"), + `failed to parse NextToken param: 1234s`, + 400, + }, + // Invalid MaxResults. + { + convertIntToPointerInt64(int64(-1)), + convertStringToPointer("478b4114226224a35910d449fdba8186ebfb441f"), + `failed to parse MaxResults param: -1`, + 400, + }, + // One item to return, no next token. + { + convertIntToPointerInt64(int64(1)), + nil, + `{"alertGroupInfoList":[{"id":"478b4114226224a35910d449fdba8186ebfb441f","labels":{"alertname":"TestingAlert","service":"api"},"receiver":{"name":"testing"}}],"nextToken":"478b4114226224a35910d449fdba8186ebfb441f"}`, + 200, + }, + // One item to return, has next token. + { + convertIntToPointerInt64(int64(1)), + convertStringToPointer("478b4114226224a35910d449fdba8186ebfb441f"), + `{"alertGroupInfoList":[{"id":"7f4084a078a3fe29d6de82fad15af8f1411e803f","labels":{"alertname":"HighErrorRate","cluster":"bb","service":"api"},"receiver":{"name":"prod"}}],"nextToken":"7f4084a078a3fe29d6de82fad15af8f1411e803f"}`, + 200, + }, + // Five item to return, has next token. + { + convertIntToPointerInt64(int64(5)), + convertStringToPointer("7f4084a078a3fe29d6de82fad15af8f1411e803f"), + `{"alertGroupInfoList":[{"id":"d525244929240cbdb75a497913c1890ab8de1962","labels":{"alertname":"OtherAlert"},"receiver":{"name":"prod"}},{"id":"d73984d43949112ae1ea59dcc5af4af7b630a5b1","labels":{"alertname":"HighErrorRate","cluster":"aa","service":"api"},"receiver":{"name":"prod"}}]}`, + 200, + }, + // Return all results. + { + nil, + nil, + `{"alertGroupInfoList":[{"id":"478b4114226224a35910d449fdba8186ebfb441f","labels":{"alertname":"TestingAlert","service":"api"},"receiver":{"name":"testing"}},{"id":"7f4084a078a3fe29d6de82fad15af8f1411e803f","labels":{"alertname":"HighErrorRate","cluster":"bb","service":"api"},"receiver":{"name":"prod"}},{"id":"d525244929240cbdb75a497913c1890ab8de1962","labels":{"alertname":"OtherAlert"},"receiver":{"name":"prod"}},{"id":"d73984d43949112ae1ea59dcc5af4af7b630a5b1","labels":{"alertname":"HighErrorRate","cluster":"aa","service":"api"},"receiver":{"name":"prod"}}]}`, + 200, + }, + // return 0 result + { + convertIntToPointerInt64(int64(0)), + nil, + `{"alertGroupInfoList":[]}`, + 200, + }, + } { + api := API{ + uptime: time.Now(), + alertGroupInfos: func(f func(*dispatch.Route) bool) dispatch.AlertGroupInfos { + return aginfos + }, + logger: log.NewNopLogger(), + } + r, err := http.NewRequest("GET", "/api/v2/alertgroups", nil) + require.NoError(t, err) + + w := httptest.NewRecorder() + p := runtime.TextProducer() + responder := api.getAlertGroupInfoListHandler(alertgroupinfolist_ops.GetAlertGroupInfoListParams{ + MaxResults: tc.maxResult, + NextToken: tc.nextToken, + HTTPRequest: r, + }) + responder.WriteResponse(w, p) + body, _ := io.ReadAll(w.Result().Body) + + require.Equal(t, tc.expectedCode, w.Code) + require.Equal(t, tc.body, string(body)) + } +} + func TestGetSilencesHandler(t *testing.T) { updateTime := "2019-01-01T12:00:00+00:00" silences := []*open_api_models.GettableSilence{ diff --git a/api/v2/client/alertgroupinfolist/alertgroupinfolist_client.go b/api/v2/client/alertgroupinfolist/alertgroupinfolist_client.go new file mode 100644 index 0000000000..62171e8605 --- /dev/null +++ b/api/v2/client/alertgroupinfolist/alertgroupinfolist_client.go @@ -0,0 +1,93 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package alertgroupinfolist + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" +) + +// New creates a new alertgroupinfolist API client. +func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { + return &Client{transport: transport, formats: formats} +} + +/* +Client for alertgroupinfolist API +*/ +type Client struct { + transport runtime.ClientTransport + formats strfmt.Registry +} + +// ClientOption is the option for Client methods +type ClientOption func(*runtime.ClientOperation) + +// ClientService is the interface for Client methods +type ClientService interface { + GetAlertGroupInfoList(params *GetAlertGroupInfoListParams, opts ...ClientOption) (*GetAlertGroupInfoListOK, error) + + SetTransport(transport runtime.ClientTransport) +} + +/* +GetAlertGroupInfoList Get a list of alert groups information +*/ +func (a *Client) GetAlertGroupInfoList(params *GetAlertGroupInfoListParams, opts ...ClientOption) (*GetAlertGroupInfoListOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetAlertGroupInfoListParams() + } + op := &runtime.ClientOperation{ + ID: "getAlertGroupInfoList", + Method: "GET", + PathPattern: "/alertgroups", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetAlertGroupInfoListReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetAlertGroupInfoListOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for getAlertGroupInfoList: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + +// SetTransport changes the transport on the client +func (a *Client) SetTransport(transport runtime.ClientTransport) { + a.transport = transport +} diff --git a/api/v2/client/alertgroupinfolist/get_alert_group_info_list_parameters.go b/api/v2/client/alertgroupinfolist/get_alert_group_info_list_parameters.go new file mode 100644 index 0000000000..4761245904 --- /dev/null +++ b/api/v2/client/alertgroupinfolist/get_alert_group_info_list_parameters.go @@ -0,0 +1,246 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package alertgroupinfolist + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// NewGetAlertGroupInfoListParams creates a new GetAlertGroupInfoListParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetAlertGroupInfoListParams() *GetAlertGroupInfoListParams { + return &GetAlertGroupInfoListParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetAlertGroupInfoListParamsWithTimeout creates a new GetAlertGroupInfoListParams object +// with the ability to set a timeout on a request. +func NewGetAlertGroupInfoListParamsWithTimeout(timeout time.Duration) *GetAlertGroupInfoListParams { + return &GetAlertGroupInfoListParams{ + timeout: timeout, + } +} + +// NewGetAlertGroupInfoListParamsWithContext creates a new GetAlertGroupInfoListParams object +// with the ability to set a context for a request. +func NewGetAlertGroupInfoListParamsWithContext(ctx context.Context) *GetAlertGroupInfoListParams { + return &GetAlertGroupInfoListParams{ + Context: ctx, + } +} + +// NewGetAlertGroupInfoListParamsWithHTTPClient creates a new GetAlertGroupInfoListParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetAlertGroupInfoListParamsWithHTTPClient(client *http.Client) *GetAlertGroupInfoListParams { + return &GetAlertGroupInfoListParams{ + HTTPClient: client, + } +} + +/* +GetAlertGroupInfoListParams contains all the parameters to send to the API endpoint + + for the get alert group info list operation. + + Typically these are written to a http.Request. +*/ +type GetAlertGroupInfoListParams struct { + + /* MaxResults. + + The maximum number of alert groups to return in one getAlertGroupInfoList operation. + */ + MaxResults *int64 + + /* NextToken. + + The token for the next set of items to return + */ + NextToken *string + + /* Receiver. + + A regex matching receivers to filter alerts by + */ + Receiver *string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get alert group info list params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetAlertGroupInfoListParams) WithDefaults() *GetAlertGroupInfoListParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get alert group info list params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetAlertGroupInfoListParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get alert group info list params +func (o *GetAlertGroupInfoListParams) WithTimeout(timeout time.Duration) *GetAlertGroupInfoListParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get alert group info list params +func (o *GetAlertGroupInfoListParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get alert group info list params +func (o *GetAlertGroupInfoListParams) WithContext(ctx context.Context) *GetAlertGroupInfoListParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get alert group info list params +func (o *GetAlertGroupInfoListParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get alert group info list params +func (o *GetAlertGroupInfoListParams) WithHTTPClient(client *http.Client) *GetAlertGroupInfoListParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get alert group info list params +func (o *GetAlertGroupInfoListParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithMaxResults adds the maxResults to the get alert group info list params +func (o *GetAlertGroupInfoListParams) WithMaxResults(maxResults *int64) *GetAlertGroupInfoListParams { + o.SetMaxResults(maxResults) + return o +} + +// SetMaxResults adds the maxResults to the get alert group info list params +func (o *GetAlertGroupInfoListParams) SetMaxResults(maxResults *int64) { + o.MaxResults = maxResults +} + +// WithNextToken adds the nextToken to the get alert group info list params +func (o *GetAlertGroupInfoListParams) WithNextToken(nextToken *string) *GetAlertGroupInfoListParams { + o.SetNextToken(nextToken) + return o +} + +// SetNextToken adds the nextToken to the get alert group info list params +func (o *GetAlertGroupInfoListParams) SetNextToken(nextToken *string) { + o.NextToken = nextToken +} + +// WithReceiver adds the receiver to the get alert group info list params +func (o *GetAlertGroupInfoListParams) WithReceiver(receiver *string) *GetAlertGroupInfoListParams { + o.SetReceiver(receiver) + return o +} + +// SetReceiver adds the receiver to the get alert group info list params +func (o *GetAlertGroupInfoListParams) SetReceiver(receiver *string) { + o.Receiver = receiver +} + +// WriteToRequest writes these params to a swagger request +func (o *GetAlertGroupInfoListParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if o.MaxResults != nil { + + // query param maxResults + var qrMaxResults int64 + + if o.MaxResults != nil { + qrMaxResults = *o.MaxResults + } + qMaxResults := swag.FormatInt64(qrMaxResults) + if qMaxResults != "" { + + if err := r.SetQueryParam("maxResults", qMaxResults); err != nil { + return err + } + } + } + + if o.NextToken != nil { + + // query param nextToken + var qrNextToken string + + if o.NextToken != nil { + qrNextToken = *o.NextToken + } + qNextToken := qrNextToken + if qNextToken != "" { + + if err := r.SetQueryParam("nextToken", qNextToken); err != nil { + return err + } + } + } + + if o.Receiver != nil { + + // query param receiver + var qrReceiver string + + if o.Receiver != nil { + qrReceiver = *o.Receiver + } + qReceiver := qrReceiver + if qReceiver != "" { + + if err := r.SetQueryParam("receiver", qReceiver); err != nil { + return err + } + } + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/api/v2/client/alertgroupinfolist/get_alert_group_info_list_responses.go b/api/v2/client/alertgroupinfolist/get_alert_group_info_list_responses.go new file mode 100644 index 0000000000..97d458ea07 --- /dev/null +++ b/api/v2/client/alertgroupinfolist/get_alert_group_info_list_responses.go @@ -0,0 +1,246 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package alertgroupinfolist + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/prometheus/alertmanager/api/v2/models" +) + +// GetAlertGroupInfoListReader is a Reader for the GetAlertGroupInfoList structure. +type GetAlertGroupInfoListReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetAlertGroupInfoListReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetAlertGroupInfoListOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewGetAlertGroupInfoListBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 500: + result := NewGetAlertGroupInfoListInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewGetAlertGroupInfoListOK creates a GetAlertGroupInfoListOK with default headers values +func NewGetAlertGroupInfoListOK() *GetAlertGroupInfoListOK { + return &GetAlertGroupInfoListOK{} +} + +/* +GetAlertGroupInfoListOK describes a response with status code 200, with default header values. + +Get alert groups info response +*/ +type GetAlertGroupInfoListOK struct { + Payload *models.AlertGroupInfoList +} + +// IsSuccess returns true when this get alert group info list o k response has a 2xx status code +func (o *GetAlertGroupInfoListOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get alert group info list o k response has a 3xx status code +func (o *GetAlertGroupInfoListOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get alert group info list o k response has a 4xx status code +func (o *GetAlertGroupInfoListOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get alert group info list o k response has a 5xx status code +func (o *GetAlertGroupInfoListOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get alert group info list o k response a status code equal to that given +func (o *GetAlertGroupInfoListOK) IsCode(code int) bool { + return code == 200 +} + +func (o *GetAlertGroupInfoListOK) Error() string { + return fmt.Sprintf("[GET /alertgroups][%d] getAlertGroupInfoListOK %+v", 200, o.Payload) +} + +func (o *GetAlertGroupInfoListOK) String() string { + return fmt.Sprintf("[GET /alertgroups][%d] getAlertGroupInfoListOK %+v", 200, o.Payload) +} + +func (o *GetAlertGroupInfoListOK) GetPayload() *models.AlertGroupInfoList { + return o.Payload +} + +func (o *GetAlertGroupInfoListOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.AlertGroupInfoList) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetAlertGroupInfoListBadRequest creates a GetAlertGroupInfoListBadRequest with default headers values +func NewGetAlertGroupInfoListBadRequest() *GetAlertGroupInfoListBadRequest { + return &GetAlertGroupInfoListBadRequest{} +} + +/* +GetAlertGroupInfoListBadRequest describes a response with status code 400, with default header values. + +Bad request +*/ +type GetAlertGroupInfoListBadRequest struct { + Payload string +} + +// IsSuccess returns true when this get alert group info list bad request response has a 2xx status code +func (o *GetAlertGroupInfoListBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get alert group info list bad request response has a 3xx status code +func (o *GetAlertGroupInfoListBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get alert group info list bad request response has a 4xx status code +func (o *GetAlertGroupInfoListBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this get alert group info list bad request response has a 5xx status code +func (o *GetAlertGroupInfoListBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this get alert group info list bad request response a status code equal to that given +func (o *GetAlertGroupInfoListBadRequest) IsCode(code int) bool { + return code == 400 +} + +func (o *GetAlertGroupInfoListBadRequest) Error() string { + return fmt.Sprintf("[GET /alertgroups][%d] getAlertGroupInfoListBadRequest %+v", 400, o.Payload) +} + +func (o *GetAlertGroupInfoListBadRequest) String() string { + return fmt.Sprintf("[GET /alertgroups][%d] getAlertGroupInfoListBadRequest %+v", 400, o.Payload) +} + +func (o *GetAlertGroupInfoListBadRequest) GetPayload() string { + return o.Payload +} + +func (o *GetAlertGroupInfoListBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetAlertGroupInfoListInternalServerError creates a GetAlertGroupInfoListInternalServerError with default headers values +func NewGetAlertGroupInfoListInternalServerError() *GetAlertGroupInfoListInternalServerError { + return &GetAlertGroupInfoListInternalServerError{} +} + +/* +GetAlertGroupInfoListInternalServerError describes a response with status code 500, with default header values. + +Internal server error +*/ +type GetAlertGroupInfoListInternalServerError struct { + Payload string +} + +// IsSuccess returns true when this get alert group info list internal server error response has a 2xx status code +func (o *GetAlertGroupInfoListInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get alert group info list internal server error response has a 3xx status code +func (o *GetAlertGroupInfoListInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get alert group info list internal server error response has a 4xx status code +func (o *GetAlertGroupInfoListInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this get alert group info list internal server error response has a 5xx status code +func (o *GetAlertGroupInfoListInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this get alert group info list internal server error response a status code equal to that given +func (o *GetAlertGroupInfoListInternalServerError) IsCode(code int) bool { + return code == 500 +} + +func (o *GetAlertGroupInfoListInternalServerError) Error() string { + return fmt.Sprintf("[GET /alertgroups][%d] getAlertGroupInfoListInternalServerError %+v", 500, o.Payload) +} + +func (o *GetAlertGroupInfoListInternalServerError) String() string { + return fmt.Sprintf("[GET /alertgroups][%d] getAlertGroupInfoListInternalServerError %+v", 500, o.Payload) +} + +func (o *GetAlertGroupInfoListInternalServerError) GetPayload() string { + return o.Payload +} + +func (o *GetAlertGroupInfoListInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/api/v2/client/alertmanager_api_client.go b/api/v2/client/alertmanager_api_client.go index 5b7c56e5e5..e786b339ff 100644 --- a/api/v2/client/alertmanager_api_client.go +++ b/api/v2/client/alertmanager_api_client.go @@ -26,6 +26,7 @@ import ( "github.com/prometheus/alertmanager/api/v2/client/alert" "github.com/prometheus/alertmanager/api/v2/client/alertgroup" + "github.com/prometheus/alertmanager/api/v2/client/alertgroupinfolist" "github.com/prometheus/alertmanager/api/v2/client/general" "github.com/prometheus/alertmanager/api/v2/client/receiver" "github.com/prometheus/alertmanager/api/v2/client/silence" @@ -75,6 +76,7 @@ func New(transport runtime.ClientTransport, formats strfmt.Registry) *Alertmanag cli.Transport = transport cli.Alert = alert.New(transport, formats) cli.Alertgroup = alertgroup.New(transport, formats) + cli.Alertgroupinfolist = alertgroupinfolist.New(transport, formats) cli.General = general.New(transport, formats) cli.Receiver = receiver.New(transport, formats) cli.Silence = silence.New(transport, formats) @@ -126,6 +128,8 @@ type AlertmanagerAPI struct { Alertgroup alertgroup.ClientService + Alertgroupinfolist alertgroupinfolist.ClientService + General general.ClientService Receiver receiver.ClientService @@ -140,6 +144,7 @@ func (c *AlertmanagerAPI) SetTransport(transport runtime.ClientTransport) { c.Transport = transport c.Alert.SetTransport(transport) c.Alertgroup.SetTransport(transport) + c.Alertgroupinfolist.SetTransport(transport) c.General.SetTransport(transport) c.Receiver.SetTransport(transport) c.Silence.SetTransport(transport) diff --git a/api/v2/compat.go b/api/v2/compat.go index 112bfc4f2f..27091649e1 100644 --- a/api/v2/compat.go +++ b/api/v2/compat.go @@ -196,3 +196,28 @@ func APILabelSetToModelLabelSet(apiLabelSet open_api_models.LabelSet) prometheus return modelLabelSet } + +func AlertGroupInfoListTruncate(alertGroupInfos []*open_api_models.AlertGroupInfo, maxResult *int64) ([]*open_api_models.AlertGroupInfo, string) { + resultNumber := 0 + var previousAgID *string + var returnPaginationToken string + returnAlertGroupInfos := make([]*open_api_models.AlertGroupInfo, 0, len(alertGroupInfos)) + for _, alertGroup := range alertGroupInfos { + + // Add the aggregation group to the return slice if the maxItem is not hit + if maxResult == nil || resultNumber < int(*maxResult) { + previousAgID = alertGroup.ID + returnAlertGroupInfos = append(returnAlertGroupInfos, alertGroup) + resultNumber++ + continue + } + + // Return the next token if there is more aggregation group + if resultNumber == int(*maxResult) && previousAgID != nil { + returnPaginationToken = *previousAgID + break + } + } + + return returnAlertGroupInfos, returnPaginationToken +} diff --git a/api/v2/models/alert_group_info.go b/api/v2/models/alert_group_info.go new file mode 100644 index 0000000000..ba4c91ba07 --- /dev/null +++ b/api/v2/models/alert_group_info.go @@ -0,0 +1,184 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// AlertGroupInfo alert group info +// +// swagger:model alertGroupInfo +type AlertGroupInfo struct { + + // id + // Required: true + ID *string `json:"id"` + + // labels + // Required: true + Labels LabelSet `json:"labels"` + + // receiver + // Required: true + Receiver *Receiver `json:"receiver"` +} + +// Validate validates this alert group info +func (m *AlertGroupInfo) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateLabels(formats); err != nil { + res = append(res, err) + } + + if err := m.validateReceiver(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *AlertGroupInfo) validateID(formats strfmt.Registry) error { + + if err := validate.Required("id", "body", m.ID); err != nil { + return err + } + + return nil +} + +func (m *AlertGroupInfo) validateLabels(formats strfmt.Registry) error { + + if err := validate.Required("labels", "body", m.Labels); err != nil { + return err + } + + if m.Labels != nil { + if err := m.Labels.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("labels") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("labels") + } + return err + } + } + + return nil +} + +func (m *AlertGroupInfo) validateReceiver(formats strfmt.Registry) error { + + if err := validate.Required("receiver", "body", m.Receiver); err != nil { + return err + } + + if m.Receiver != nil { + if err := m.Receiver.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("receiver") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("receiver") + } + return err + } + } + + return nil +} + +// ContextValidate validate this alert group info based on the context it is used +func (m *AlertGroupInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateLabels(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateReceiver(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *AlertGroupInfo) contextValidateLabels(ctx context.Context, formats strfmt.Registry) error { + + if err := m.Labels.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("labels") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("labels") + } + return err + } + + return nil +} + +func (m *AlertGroupInfo) contextValidateReceiver(ctx context.Context, formats strfmt.Registry) error { + + if m.Receiver != nil { + if err := m.Receiver.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("receiver") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("receiver") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *AlertGroupInfo) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *AlertGroupInfo) UnmarshalBinary(b []byte) error { + var res AlertGroupInfo + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/api/v2/models/alert_group_info_list.go b/api/v2/models/alert_group_info_list.go new file mode 100644 index 0000000000..b907ba1083 --- /dev/null +++ b/api/v2/models/alert_group_info_list.go @@ -0,0 +1,133 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// AlertGroupInfoList alert group info list +// +// swagger:model alertGroupInfoList +type AlertGroupInfoList struct { + + // alert group info list + AlertGroupInfoList []*AlertGroupInfo `json:"alertGroupInfoList"` + + // next token + NextToken string `json:"nextToken,omitempty"` +} + +// Validate validates this alert group info list +func (m *AlertGroupInfoList) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateAlertGroupInfoList(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *AlertGroupInfoList) validateAlertGroupInfoList(formats strfmt.Registry) error { + if swag.IsZero(m.AlertGroupInfoList) { // not required + return nil + } + + for i := 0; i < len(m.AlertGroupInfoList); i++ { + if swag.IsZero(m.AlertGroupInfoList[i]) { // not required + continue + } + + if m.AlertGroupInfoList[i] != nil { + if err := m.AlertGroupInfoList[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("alertGroupInfoList" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("alertGroupInfoList" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this alert group info list based on the context it is used +func (m *AlertGroupInfoList) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateAlertGroupInfoList(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *AlertGroupInfoList) contextValidateAlertGroupInfoList(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.AlertGroupInfoList); i++ { + + if m.AlertGroupInfoList[i] != nil { + if err := m.AlertGroupInfoList[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("alertGroupInfoList" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("alertGroupInfoList" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *AlertGroupInfoList) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *AlertGroupInfoList) UnmarshalBinary(b []byte) error { + var res AlertGroupInfoList + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/api/v2/openapi.yaml b/api/v2/openapi.yaml index 549cc33e7f..5c4059555b 100644 --- a/api/v2/openapi.yaml +++ b/api/v2/openapi.yaml @@ -245,6 +245,37 @@ paths: $ref: '#/responses/BadRequest' '500': $ref: '#/responses/InternalServerError' + /alertgroups: + get: + tags: + - alertgroupinfolist + operationId: getAlertGroupInfoList + description: Get a list of alert groups information + parameters: + - name: receiver + in: query + description: A regex matching receivers to filter alerts by + required: false + type: string + - name: nextToken + in: query + description: The token for the next set of items to return + required: false + type: string + - name: maxResults + in: query + description: The maximum number of alert groups to return in one getAlertGroupInfoList operation. + required: false + type: integer + responses: + '200': + description: Get alert groups info response + schema: + '$ref': '#/definitions/alertGroupInfoList' + '400': + $ref: '#/responses/BadRequest' + '500': + $ref: '#/responses/InternalServerError' responses: BadRequest: @@ -487,6 +518,28 @@ definitions: - labels - receiver - alerts + alertGroupInfoList: + type: object + properties: + alertGroupInfoList: + type: array + items: + $ref: '#/definitions/alertGroupInfo' + nextToken: + type: string + alertGroupInfo: + type: object + properties: + labels: + $ref: '#/definitions/labelSet' + receiver: + $ref: '#/definitions/receiver' + id: + type: string + required: + - labels + - receiver + - id alertStatus: type: object properties: diff --git a/api/v2/restapi/configure_alertmanager.go b/api/v2/restapi/configure_alertmanager.go index 69dfe66bf9..797d2c80ca 100644 --- a/api/v2/restapi/configure_alertmanager.go +++ b/api/v2/restapi/configure_alertmanager.go @@ -27,6 +27,7 @@ import ( "github.com/prometheus/alertmanager/api/v2/restapi/operations" "github.com/prometheus/alertmanager/api/v2/restapi/operations/alert" "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup" + "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroupinfolist" "github.com/prometheus/alertmanager/api/v2/restapi/operations/general" "github.com/prometheus/alertmanager/api/v2/restapi/operations/receiver" "github.com/prometheus/alertmanager/api/v2/restapi/operations/silence" @@ -61,6 +62,11 @@ func configureAPI(api *operations.AlertmanagerAPI) http.Handler { return middleware.NotImplemented("operation silence.DeleteSilence has not yet been implemented") }) } + if api.AlertgroupinfolistGetAlertGroupInfoListHandler == nil { + api.AlertgroupinfolistGetAlertGroupInfoListHandler = alertgroupinfolist.GetAlertGroupInfoListHandlerFunc(func(params alertgroupinfolist.GetAlertGroupInfoListParams) middleware.Responder { + return middleware.NotImplemented("operation alertgroupinfolist.GetAlertGroupInfoList has not yet been implemented") + }) + } if api.AlertgroupGetAlertGroupsHandler == nil { api.AlertgroupGetAlertGroupsHandler = alertgroup.GetAlertGroupsHandlerFunc(func(params alertgroup.GetAlertGroupsParams) middleware.Responder { return middleware.NotImplemented("operation alertgroup.GetAlertGroups has not yet been implemented") diff --git a/api/v2/restapi/embedded_spec.go b/api/v2/restapi/embedded_spec.go index 0e23efd060..6a7f7db376 100644 --- a/api/v2/restapi/embedded_spec.go +++ b/api/v2/restapi/embedded_spec.go @@ -50,6 +50,49 @@ func init() { }, "basePath": "/api/v2/", "paths": { + "/alertgroups": { + "get": { + "description": "Get a list of alert groups information", + "tags": [ + "alertgroupinfolist" + ], + "operationId": "getAlertGroupInfoList", + "parameters": [ + { + "type": "string", + "description": "A regex matching receivers to filter alerts by", + "name": "receiver", + "in": "query" + }, + { + "type": "string", + "description": "The token for the next set of items to return", + "name": "nextToken", + "in": "query" + }, + { + "type": "integer", + "description": "The maximum number of alert groups to return in one getAlertGroupInfoList operation.", + "name": "maxResults", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Get alert groups info response", + "schema": { + "$ref": "#/definitions/alertGroupInfoList" + } + }, + "400": { + "$ref": "#/responses/BadRequest" + }, + "500": { + "$ref": "#/responses/InternalServerError" + } + } + } + }, "/alerts": { "get": { "description": "Get a list of alerts", @@ -422,6 +465,39 @@ func init() { } } }, + "alertGroupInfo": { + "type": "object", + "required": [ + "labels", + "receiver", + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "labels": { + "$ref": "#/definitions/labelSet" + }, + "receiver": { + "$ref": "#/definitions/receiver" + } + } + }, + "alertGroupInfoList": { + "type": "object", + "properties": { + "alertGroupInfoList": { + "type": "array", + "items": { + "$ref": "#/definitions/alertGroupInfo" + } + }, + "nextToken": { + "type": "string" + } + } + }, "alertGroups": { "type": "array", "items": { @@ -840,6 +916,55 @@ func init() { }, "basePath": "/api/v2/", "paths": { + "/alertgroups": { + "get": { + "description": "Get a list of alert groups information", + "tags": [ + "alertgroupinfolist" + ], + "operationId": "getAlertGroupInfoList", + "parameters": [ + { + "type": "string", + "description": "A regex matching receivers to filter alerts by", + "name": "receiver", + "in": "query" + }, + { + "type": "string", + "description": "The token for the next set of items to return", + "name": "nextToken", + "in": "query" + }, + { + "type": "integer", + "description": "The maximum number of alert groups to return in one getAlertGroupInfoList operation.", + "name": "maxResults", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Get alert groups info response", + "schema": { + "$ref": "#/definitions/alertGroupInfoList" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, "/alerts": { "get": { "description": "Get a list of alerts", @@ -1245,6 +1370,39 @@ func init() { } } }, + "alertGroupInfo": { + "type": "object", + "required": [ + "labels", + "receiver", + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "labels": { + "$ref": "#/definitions/labelSet" + }, + "receiver": { + "$ref": "#/definitions/receiver" + } + } + }, + "alertGroupInfoList": { + "type": "object", + "properties": { + "alertGroupInfoList": { + "type": "array", + "items": { + "$ref": "#/definitions/alertGroupInfo" + } + }, + "nextToken": { + "type": "string" + } + } + }, "alertGroups": { "type": "array", "items": { diff --git a/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list.go b/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list.go new file mode 100644 index 0000000000..3820f73720 --- /dev/null +++ b/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list.go @@ -0,0 +1,70 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package alertgroupinfolist + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// GetAlertGroupInfoListHandlerFunc turns a function with the right signature into a get alert group info list handler +type GetAlertGroupInfoListHandlerFunc func(GetAlertGroupInfoListParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetAlertGroupInfoListHandlerFunc) Handle(params GetAlertGroupInfoListParams) middleware.Responder { + return fn(params) +} + +// GetAlertGroupInfoListHandler interface for that can handle valid get alert group info list params +type GetAlertGroupInfoListHandler interface { + Handle(GetAlertGroupInfoListParams) middleware.Responder +} + +// NewGetAlertGroupInfoList creates a new http.Handler for the get alert group info list operation +func NewGetAlertGroupInfoList(ctx *middleware.Context, handler GetAlertGroupInfoListHandler) *GetAlertGroupInfoList { + return &GetAlertGroupInfoList{Context: ctx, Handler: handler} +} + +/* + GetAlertGroupInfoList swagger:route GET /alertgroups alertgroupinfolist getAlertGroupInfoList + +Get a list of alert groups information +*/ +type GetAlertGroupInfoList struct { + Context *middleware.Context + Handler GetAlertGroupInfoListHandler +} + +func (o *GetAlertGroupInfoList) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetAlertGroupInfoListParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list_parameters.go b/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list_parameters.go new file mode 100644 index 0000000000..7c01e4aac2 --- /dev/null +++ b/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list_parameters.go @@ -0,0 +1,151 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package alertgroupinfolist + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// NewGetAlertGroupInfoListParams creates a new GetAlertGroupInfoListParams object +// +// There are no default values defined in the spec. +func NewGetAlertGroupInfoListParams() GetAlertGroupInfoListParams { + + return GetAlertGroupInfoListParams{} +} + +// GetAlertGroupInfoListParams contains all the bound params for the get alert group info list operation +// typically these are obtained from a http.Request +// +// swagger:parameters getAlertGroupInfoList +type GetAlertGroupInfoListParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /*The maximum number of alert groups to return in one getAlertGroupInfoList operation. + In: query + */ + MaxResults *int64 + /*The token for the next set of items to return + In: query + */ + NextToken *string + /*A regex matching receivers to filter alerts by + In: query + */ + Receiver *string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetAlertGroupInfoListParams() beforehand. +func (o *GetAlertGroupInfoListParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + qs := runtime.Values(r.URL.Query()) + + qMaxResults, qhkMaxResults, _ := qs.GetOK("maxResults") + if err := o.bindMaxResults(qMaxResults, qhkMaxResults, route.Formats); err != nil { + res = append(res, err) + } + + qNextToken, qhkNextToken, _ := qs.GetOK("nextToken") + if err := o.bindNextToken(qNextToken, qhkNextToken, 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) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindMaxResults binds and validates parameter MaxResults from query. +func (o *GetAlertGroupInfoListParams) bindMaxResults(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 + return nil + } + + value, err := swag.ConvertInt64(raw) + if err != nil { + return errors.InvalidType("maxResults", "query", "int64", raw) + } + o.MaxResults = &value + + return nil +} + +// bindNextToken binds and validates parameter NextToken from query. +func (o *GetAlertGroupInfoListParams) bindNextToken(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 + return nil + } + o.NextToken = &raw + + return nil +} + +// bindReceiver binds and validates parameter Receiver from query. +func (o *GetAlertGroupInfoListParams) bindReceiver(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 + return nil + } + o.Receiver = &raw + + return nil +} diff --git a/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list_responses.go b/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list_responses.go new file mode 100644 index 0000000000..12b3a4a613 --- /dev/null +++ b/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list_responses.go @@ -0,0 +1,159 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package alertgroupinfolist + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/prometheus/alertmanager/api/v2/models" +) + +// GetAlertGroupInfoListOKCode is the HTTP code returned for type GetAlertGroupInfoListOK +const GetAlertGroupInfoListOKCode int = 200 + +/* +GetAlertGroupInfoListOK Get alert groups info response + +swagger:response getAlertGroupInfoListOK +*/ +type GetAlertGroupInfoListOK struct { + + /* + In: Body + */ + Payload *models.AlertGroupInfoList `json:"body,omitempty"` +} + +// NewGetAlertGroupInfoListOK creates GetAlertGroupInfoListOK with default headers values +func NewGetAlertGroupInfoListOK() *GetAlertGroupInfoListOK { + + return &GetAlertGroupInfoListOK{} +} + +// WithPayload adds the payload to the get alert group info list o k response +func (o *GetAlertGroupInfoListOK) WithPayload(payload *models.AlertGroupInfoList) *GetAlertGroupInfoListOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get alert group info list o k response +func (o *GetAlertGroupInfoListOK) SetPayload(payload *models.AlertGroupInfoList) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetAlertGroupInfoListOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetAlertGroupInfoListBadRequestCode is the HTTP code returned for type GetAlertGroupInfoListBadRequest +const GetAlertGroupInfoListBadRequestCode int = 400 + +/* +GetAlertGroupInfoListBadRequest Bad request + +swagger:response getAlertGroupInfoListBadRequest +*/ +type GetAlertGroupInfoListBadRequest struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewGetAlertGroupInfoListBadRequest creates GetAlertGroupInfoListBadRequest with default headers values +func NewGetAlertGroupInfoListBadRequest() *GetAlertGroupInfoListBadRequest { + + return &GetAlertGroupInfoListBadRequest{} +} + +// WithPayload adds the payload to the get alert group info list bad request response +func (o *GetAlertGroupInfoListBadRequest) WithPayload(payload string) *GetAlertGroupInfoListBadRequest { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get alert group info list bad request response +func (o *GetAlertGroupInfoListBadRequest) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetAlertGroupInfoListBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(400) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// GetAlertGroupInfoListInternalServerErrorCode is the HTTP code returned for type GetAlertGroupInfoListInternalServerError +const GetAlertGroupInfoListInternalServerErrorCode int = 500 + +/* +GetAlertGroupInfoListInternalServerError Internal server error + +swagger:response getAlertGroupInfoListInternalServerError +*/ +type GetAlertGroupInfoListInternalServerError struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewGetAlertGroupInfoListInternalServerError creates GetAlertGroupInfoListInternalServerError with default headers values +func NewGetAlertGroupInfoListInternalServerError() *GetAlertGroupInfoListInternalServerError { + + return &GetAlertGroupInfoListInternalServerError{} +} + +// WithPayload adds the payload to the get alert group info list internal server error response +func (o *GetAlertGroupInfoListInternalServerError) WithPayload(payload string) *GetAlertGroupInfoListInternalServerError { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get alert group info list internal server error response +func (o *GetAlertGroupInfoListInternalServerError) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetAlertGroupInfoListInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} diff --git a/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list_urlbuilder.go b/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list_urlbuilder.go new file mode 100644 index 0000000000..6224bb9da5 --- /dev/null +++ b/api/v2/restapi/operations/alertgroupinfolist/get_alert_group_info_list_urlbuilder.go @@ -0,0 +1,137 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package alertgroupinfolist + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + + "github.com/go-openapi/swag" +) + +// GetAlertGroupInfoListURL generates an URL for the get alert group info list operation +type GetAlertGroupInfoListURL struct { + MaxResults *int64 + NextToken *string + Receiver *string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetAlertGroupInfoListURL) WithBasePath(bp string) *GetAlertGroupInfoListURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetAlertGroupInfoListURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetAlertGroupInfoListURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/alertgroups" + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v2/" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + qs := make(url.Values) + + var maxResultsQ string + if o.MaxResults != nil { + maxResultsQ = swag.FormatInt64(*o.MaxResults) + } + if maxResultsQ != "" { + qs.Set("maxResults", maxResultsQ) + } + + var nextTokenQ string + if o.NextToken != nil { + nextTokenQ = *o.NextToken + } + if nextTokenQ != "" { + qs.Set("nextToken", nextTokenQ) + } + + var receiverQ string + if o.Receiver != nil { + receiverQ = *o.Receiver + } + if receiverQ != "" { + qs.Set("receiver", receiverQ) + } + + _result.RawQuery = qs.Encode() + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetAlertGroupInfoListURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetAlertGroupInfoListURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetAlertGroupInfoListURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetAlertGroupInfoListURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetAlertGroupInfoListURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetAlertGroupInfoListURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/api/v2/restapi/operations/alertmanager_api.go b/api/v2/restapi/operations/alertmanager_api.go index e28c76b32e..5c41351eb3 100644 --- a/api/v2/restapi/operations/alertmanager_api.go +++ b/api/v2/restapi/operations/alertmanager_api.go @@ -35,6 +35,7 @@ import ( "github.com/prometheus/alertmanager/api/v2/restapi/operations/alert" "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup" + "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroupinfolist" "github.com/prometheus/alertmanager/api/v2/restapi/operations/general" "github.com/prometheus/alertmanager/api/v2/restapi/operations/receiver" "github.com/prometheus/alertmanager/api/v2/restapi/operations/silence" @@ -65,6 +66,9 @@ func NewAlertmanagerAPI(spec *loads.Document) *AlertmanagerAPI { SilenceDeleteSilenceHandler: silence.DeleteSilenceHandlerFunc(func(params silence.DeleteSilenceParams) middleware.Responder { return middleware.NotImplemented("operation silence.DeleteSilence has not yet been implemented") }), + AlertgroupinfolistGetAlertGroupInfoListHandler: alertgroupinfolist.GetAlertGroupInfoListHandlerFunc(func(params alertgroupinfolist.GetAlertGroupInfoListParams) middleware.Responder { + return middleware.NotImplemented("operation alertgroupinfolist.GetAlertGroupInfoList has not yet been implemented") + }), AlertgroupGetAlertGroupsHandler: alertgroup.GetAlertGroupsHandlerFunc(func(params alertgroup.GetAlertGroupsParams) middleware.Responder { return middleware.NotImplemented("operation alertgroup.GetAlertGroups has not yet been implemented") }), @@ -127,6 +131,8 @@ type AlertmanagerAPI struct { // SilenceDeleteSilenceHandler sets the operation handler for the delete silence operation SilenceDeleteSilenceHandler silence.DeleteSilenceHandler + // AlertgroupinfolistGetAlertGroupInfoListHandler sets the operation handler for the get alert group info list operation + AlertgroupinfolistGetAlertGroupInfoListHandler alertgroupinfolist.GetAlertGroupInfoListHandler // AlertgroupGetAlertGroupsHandler sets the operation handler for the get alert groups operation AlertgroupGetAlertGroupsHandler alertgroup.GetAlertGroupsHandler // AlertGetAlertsHandler sets the operation handler for the get alerts operation @@ -223,6 +229,9 @@ func (o *AlertmanagerAPI) Validate() error { if o.SilenceDeleteSilenceHandler == nil { unregistered = append(unregistered, "silence.DeleteSilenceHandler") } + if o.AlertgroupinfolistGetAlertGroupInfoListHandler == nil { + unregistered = append(unregistered, "alertgroupinfolist.GetAlertGroupInfoListHandler") + } if o.AlertgroupGetAlertGroupsHandler == nil { unregistered = append(unregistered, "alertgroup.GetAlertGroupsHandler") } @@ -342,6 +351,10 @@ func (o *AlertmanagerAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } + o.handlers["GET"]["/alertgroups"] = alertgroupinfolist.NewGetAlertGroupInfoList(o.context, o.AlertgroupinfolistGetAlertGroupInfoListHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } o.handlers["GET"]["/alerts/groups"] = alertgroup.NewGetAlertGroups(o.context, o.AlertgroupGetAlertGroupsHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) diff --git a/dispatch/dispatch.go b/dispatch/dispatch.go index 640b22abe2..f8b770885f 100644 --- a/dispatch/dispatch.go +++ b/dispatch/dispatch.go @@ -15,7 +15,10 @@ package dispatch import ( "context" + "errors" + + "crypto/sha1" "fmt" "sort" "sync" @@ -279,6 +282,48 @@ func (d *Dispatcher) Groups(routeFilter func(*Route) bool, alertFilter func(*typ return groups, receivers } +// AlertGroupInfo represents the aggrGroup information. +type AlertGroupInfo struct { + Labels model.LabelSet + Receiver string + ID string +} + +type AlertGroupInfos []*AlertGroupInfo + +func (ag AlertGroupInfos) Swap(i, j int) { ag[i], ag[j] = ag[j], ag[i] } +func (ag AlertGroupInfos) Less(i, j int) bool { + return ag[i].ID < ag[j].ID +} +func (ag AlertGroupInfos) Len() int { return len(ag) } + +func (d *Dispatcher) GroupInfos(routeFilter func(*Route) bool) AlertGroupInfos { + groups := AlertGroupInfos{} + + d.mtx.RLock() + defer d.mtx.RUnlock() + + for route, ags := range d.aggrGroupsPerRoute { + if !routeFilter(route) { + continue + } + + for _, ag := range ags { + receiver := route.RouteOpts.Receiver + alertGroup := &AlertGroupInfo{ + Labels: ag.labels, + Receiver: receiver, + ID: ag.GroupID(), + } + + groups = append(groups, alertGroup) + } + } + sort.Sort(groups) + + return groups +} + // Stop the dispatcher. func (d *Dispatcher) Stop() { if d == nil { @@ -375,6 +420,7 @@ type aggrGroup struct { opts *RouteOpts logger log.Logger routeKey string + routeID string alerts *store.Alerts ctx context.Context @@ -395,6 +441,7 @@ func newAggrGroup(ctx context.Context, labels model.LabelSet, r *Route, to func( ag := &aggrGroup{ labels: labels, routeKey: r.Key(), + routeID: r.ID(), opts: &r.RouteOpts, timeout: to, alerts: store.NewAlerts(), @@ -415,6 +462,12 @@ func (ag *aggrGroup) fingerprint() model.Fingerprint { return ag.labels.Fingerprint() } +func (ag *aggrGroup) GroupID() string { + h := sha1.New() + h.Write([]byte(fmt.Sprintf("%s:%s", ag.routeID, ag.labels))) + return fmt.Sprintf("%x", h.Sum(nil)) +} + func (ag *aggrGroup) GroupKey() string { return fmt.Sprintf("%s:%s", ag.routeKey, ag.labels) } diff --git a/dispatch/dispatch_test.go b/dispatch/dispatch_test.go index 85bd62dc4d..6933d4fcda 100644 --- a/dispatch/dispatch_test.go +++ b/dispatch/dispatch_test.go @@ -568,6 +568,161 @@ route: require.Len(t, alertGroups, 6) } +func TestGroupInfos(t *testing.T) { + confData := `receivers: +- name: 'kafka' +- name: 'prod' +- name: 'testing' + +route: + group_by: ['alertname'] + group_wait: 10ms + group_interval: 10ms + receiver: 'prod' + routes: + - match: + env: 'testing' + receiver: 'testing' + group_by: ['alertname', 'service'] + - match: + env: 'prod' + receiver: 'prod' + group_by: ['alertname', 'service', 'cluster'] + continue: true + - match: + kafka: 'yes' + receiver: 'kafka' + group_by: ['alertname', 'service', 'cluster']` + conf, err := config.Load(confData) + if err != nil { + t.Fatal(err) + } + + logger := log.NewNopLogger() + route := NewRoute(conf.Route, nil) + marker := types.NewMarker(prometheus.NewRegistry()) + alerts, err := mem.NewAlerts(context.Background(), marker, time.Hour, nil, logger, nil) + if err != nil { + t.Fatal(err) + } + defer alerts.Close() + + timeout := func(d time.Duration) time.Duration { return time.Duration(0) } + recorder := &recordStage{alerts: make(map[string]map[model.Fingerprint]*types.Alert)} + dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, nil, logger, NewDispatcherMetrics(false, prometheus.NewRegistry())) + go dispatcher.Run() + defer dispatcher.Stop() + + // Create alerts. the dispatcher will automatically create the groups. + inputAlerts := []*types.Alert{ + // Matches the parent route. + newAlert(model.LabelSet{"alertname": "OtherAlert", "cluster": "cc", "service": "dd"}), + // Matches the first sub-route. + newAlert(model.LabelSet{"env": "testing", "alertname": "TestingAlert", "service": "api", "instance": "inst1"}), + // Matches the second sub-route. + newAlert(model.LabelSet{"env": "prod", "alertname": "HighErrorRate", "cluster": "aa", "service": "api", "instance": "inst1"}), + newAlert(model.LabelSet{"env": "prod", "alertname": "HighErrorRate", "cluster": "aa", "service": "api", "instance": "inst2"}), + // Matches the second sub-route. + newAlert(model.LabelSet{"env": "prod", "alertname": "HighErrorRate", "cluster": "bb", "service": "api", "instance": "inst1"}), + // Matches the second and third sub-route. + newAlert(model.LabelSet{"env": "prod", "alertname": "HighLatency", "cluster": "bb", "service": "db", "kafka": "yes", "instance": "inst3"}), + } + alerts.Put(inputAlerts...) + + // Let alerts get processed. + for i := 0; len(recorder.Alerts()) != 7 && i < 10; i++ { + time.Sleep(200 * time.Millisecond) + } + require.Equal(t, 7, len(recorder.Alerts())) + + alertGroupInfos := dispatcher.GroupInfos( + func(*Route) bool { + return true + }, + ) + + require.Equal(t, AlertGroupInfos{ + &AlertGroupInfo{ + Labels: model.LabelSet{ + "alertname": "TestingAlert", + "service": "api", + }, + Receiver: "testing", + // Matches the first sub-route. + ID: dispatcher.aggrGroupsPerRoute[dispatcher.route.Routes[0]][model.LabelSet{ + "alertname": "TestingAlert", + "service": "api", + }.Fingerprint()].GroupID(), + }, + &AlertGroupInfo{ + Labels: model.LabelSet{ + "alertname": "HighLatency", + "service": "db", + "cluster": "bb", + }, + Receiver: "kafka", + // Matches the third sub-route. + ID: dispatcher.aggrGroupsPerRoute[dispatcher.route.Routes[2]][model.LabelSet{ + "alertname": "HighLatency", + "service": "db", + "cluster": "bb", + }.Fingerprint()].GroupID(), + }, + &AlertGroupInfo{ + Labels: model.LabelSet{ + "alertname": "HighErrorRate", + "service": "api", + "cluster": "bb", + }, + Receiver: "prod", + // Matches the second sub-route. + ID: dispatcher.aggrGroupsPerRoute[dispatcher.route.Routes[1]][model.LabelSet{ + "alertname": "HighErrorRate", + "service": "api", + "cluster": "bb", + }.Fingerprint()].GroupID(), + }, + &AlertGroupInfo{ + Labels: model.LabelSet{ + "alertname": "HighLatency", + "service": "db", + "cluster": "bb", + }, + Receiver: "prod", + // Matches the second sub-route. + ID: dispatcher.aggrGroupsPerRoute[dispatcher.route.Routes[1]][model.LabelSet{ + "alertname": "HighLatency", + "service": "db", + "cluster": "bb", + }.Fingerprint()].GroupID(), + }, + &AlertGroupInfo{ + Labels: model.LabelSet{ + "alertname": "OtherAlert", + }, + Receiver: "prod", + // Matches the parent route. + ID: dispatcher.aggrGroupsPerRoute[dispatcher.route][model.LabelSet{ + "alertname": "OtherAlert", + }.Fingerprint()].GroupID(), + }, + &AlertGroupInfo{ + Labels: model.LabelSet{ + "alertname": "HighErrorRate", + "service": "api", + "cluster": "aa", + }, + Receiver: "prod", + // Matches the second sub-route. + ID: dispatcher.aggrGroupsPerRoute[dispatcher.route.Routes[1]][model.LabelSet{ + "alertname": "HighErrorRate", + "service": "api", + "cluster": "aa", + }.Fingerprint()].GroupID(), + }, + }, alertGroupInfos) +} + type recordStage struct { mtx sync.RWMutex alerts map[string]map[model.Fingerprint]*types.Alert