mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
[Alerting]: Alertmanager API implementation (#32174)
* Add validation for grafana recipient * Alertmanager API implementation (WIP) * Fix encoding/decoding receiver settings from/to YAML * Save templates together with the configuration * update POST to apply latest config * Alertmanager service enabled by the ngalert toggle * Silence API integration with Alertmanager * Apply suggestions from code review Co-authored-by: gotjosh <josue@grafana.com> Co-authored-by: Ganesh Vernekar <15064823+codesome@users.noreply.github.com>
This commit is contained in:
parent
b0ffcfd558
commit
a5e95823b2
2
go.mod
2
go.mod
@ -40,7 +40,7 @@ require (
|
|||||||
github.com/google/go-cmp v0.5.5
|
github.com/google/go-cmp v0.5.5
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
github.com/gosimple/slug v1.9.0
|
github.com/gosimple/slug v1.9.0
|
||||||
github.com/grafana/alerting-api v0.0.0-20210330162237-0b5408c529a8
|
github.com/grafana/alerting-api v0.0.0-20210331130828-17c19ddf88ee
|
||||||
github.com/grafana/grafana-aws-sdk v0.4.0
|
github.com/grafana/grafana-aws-sdk v0.4.0
|
||||||
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4
|
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4
|
||||||
github.com/grafana/grafana-plugin-sdk-go v0.90.0
|
github.com/grafana/grafana-plugin-sdk-go v0.90.0
|
||||||
|
4
go.sum
4
go.sum
@ -798,6 +798,8 @@ github.com/gosimple/slug v1.9.0 h1:r5vDcYrFz9BmfIAMC829un9hq7hKM4cHUrsv36LbEqs=
|
|||||||
github.com/gosimple/slug v1.9.0/go.mod h1:AMZ+sOVe65uByN3kgEyf9WEBKBCSS+dJjMX9x4vDJbg=
|
github.com/gosimple/slug v1.9.0/go.mod h1:AMZ+sOVe65uByN3kgEyf9WEBKBCSS+dJjMX9x4vDJbg=
|
||||||
github.com/grafana/alerting-api v0.0.0-20210330162237-0b5408c529a8 h1:okhEX26LU7AGN/3C8NDWfdjBmKclvoFvJz9o/LsNcK8=
|
github.com/grafana/alerting-api v0.0.0-20210330162237-0b5408c529a8 h1:okhEX26LU7AGN/3C8NDWfdjBmKclvoFvJz9o/LsNcK8=
|
||||||
github.com/grafana/alerting-api v0.0.0-20210330162237-0b5408c529a8/go.mod h1:5IppnPguSHcCbVLGCVzVjBvuQZNbYgVJ4KyXXjhCyWY=
|
github.com/grafana/alerting-api v0.0.0-20210330162237-0b5408c529a8/go.mod h1:5IppnPguSHcCbVLGCVzVjBvuQZNbYgVJ4KyXXjhCyWY=
|
||||||
|
github.com/grafana/alerting-api v0.0.0-20210331130828-17c19ddf88ee h1:jpZdUOta4PK3CH3+2UCuzqn1SGZ+dQj+dWH45B0c1aI=
|
||||||
|
github.com/grafana/alerting-api v0.0.0-20210331130828-17c19ddf88ee/go.mod h1:5IppnPguSHcCbVLGCVzVjBvuQZNbYgVJ4KyXXjhCyWY=
|
||||||
github.com/grafana/go-mssqldb v0.0.0-20210326084033-d0ce3c521036 h1:GplhUk6Xes5JIhUUrggPcPBhOn+eT8+WsHiebvq7GgA=
|
github.com/grafana/go-mssqldb v0.0.0-20210326084033-d0ce3c521036 h1:GplhUk6Xes5JIhUUrggPcPBhOn+eT8+WsHiebvq7GgA=
|
||||||
github.com/grafana/go-mssqldb v0.0.0-20210326084033-d0ce3c521036/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
github.com/grafana/go-mssqldb v0.0.0-20210326084033-d0ce3c521036/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
github.com/grafana/grafana v1.9.2-0.20210308201921-4ce0a49eac03/go.mod h1:AHRRvd4utJGY25J5nW8aL7wZzn/LcJ0z2za9oOp14j4=
|
github.com/grafana/grafana v1.9.2-0.20210308201921-4ce0a49eac03/go.mod h1:AHRRvd4utJGY25J5nW8aL7wZzn/LcJ0z2za9oOp14j4=
|
||||||
@ -1335,6 +1337,7 @@ github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f/go.mod h1:hoL
|
|||||||
github.com/prometheus/alertmanager v0.18.0/go.mod h1:WcxHBl40VSPuOaqWae6l6HpnEOVRIycEJ7i9iYkadEE=
|
github.com/prometheus/alertmanager v0.18.0/go.mod h1:WcxHBl40VSPuOaqWae6l6HpnEOVRIycEJ7i9iYkadEE=
|
||||||
github.com/prometheus/alertmanager v0.19.0/go.mod h1:Eyp94Yi/T+kdeb2qvq66E3RGuph5T/jm/RBVh4yz1xo=
|
github.com/prometheus/alertmanager v0.19.0/go.mod h1:Eyp94Yi/T+kdeb2qvq66E3RGuph5T/jm/RBVh4yz1xo=
|
||||||
github.com/prometheus/alertmanager v0.20.0/go.mod h1:9g2i48FAyZW6BtbsnvHtMHQXl2aVtrORKwKVCQ+nbrg=
|
github.com/prometheus/alertmanager v0.20.0/go.mod h1:9g2i48FAyZW6BtbsnvHtMHQXl2aVtrORKwKVCQ+nbrg=
|
||||||
|
github.com/prometheus/alertmanager v0.21.0 h1:qK51JcUR9l/unhawGA9F9B64OCYfcGewhPNprem/Acc=
|
||||||
github.com/prometheus/alertmanager v0.21.0/go.mod h1:h7tJ81NA0VLWvWEayi1QltevFkLF3KxmC/malTcT8Go=
|
github.com/prometheus/alertmanager v0.21.0/go.mod h1:h7tJ81NA0VLWvWEayi1QltevFkLF3KxmC/malTcT8Go=
|
||||||
github.com/prometheus/alertmanager v0.21.1-0.20200911160112-1fdff6b3f939/go.mod h1:imXRHOP6QTsE0fFsIsAV/cXimS32m7gVZOiUj11m6Ig=
|
github.com/prometheus/alertmanager v0.21.1-0.20200911160112-1fdff6b3f939/go.mod h1:imXRHOP6QTsE0fFsIsAV/cXimS32m7gVZOiUj11m6Ig=
|
||||||
github.com/prometheus/alertmanager v0.21.1-0.20210211203738-a7ca7b1d2951/go.mod h1:6Yc2n2ap5/oP99x1yN6Ho+yL0w8a0oClIR5xxW/JLGs=
|
github.com/prometheus/alertmanager v0.21.1-0.20210211203738-a7ca7b1d2951/go.mod h1:6Yc2n2ap5/oP99x1yN6Ho+yL0w8a0oClIR5xxW/JLGs=
|
||||||
@ -1421,6 +1424,7 @@ github.com/prometheus/prometheus v1.8.2-0.20210217141258-a6be548dbc17 h1:VN3p3Nb
|
|||||||
github.com/prometheus/prometheus v1.8.2-0.20210217141258-a6be548dbc17/go.mod h1:dv3B1syqmkrkmo665MPCU6L8PbTXIiUeg/OEQULLNxA=
|
github.com/prometheus/prometheus v1.8.2-0.20210217141258-a6be548dbc17/go.mod h1:dv3B1syqmkrkmo665MPCU6L8PbTXIiUeg/OEQULLNxA=
|
||||||
github.com/prometheus/statsd_exporter v0.15.0/go.mod h1:Dv8HnkoLQkeEjkIE4/2ndAA7WL1zHKK7WMqFQqu72rw=
|
github.com/prometheus/statsd_exporter v0.15.0/go.mod h1:Dv8HnkoLQkeEjkIE4/2ndAA7WL1zHKK7WMqFQqu72rw=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/quasilyte/go-ruleguard/dsl/fluent v0.0.0-20201222093424-5d7e62a465d3 h1:eL7x4/zMnlquMxYe7V078BD7MGskZ0daGln+SJCVzuY=
|
||||||
github.com/quasilyte/go-ruleguard/dsl/fluent v0.0.0-20201222093424-5d7e62a465d3/go.mod h1:P7JlQWFT7jDcFZMtUPQbtGzzzxva3rBn6oIF+LPwFcM=
|
github.com/quasilyte/go-ruleguard/dsl/fluent v0.0.0-20201222093424-5d7e62a465d3/go.mod h1:P7JlQWFT7jDcFZMtUPQbtGzzzxva3rBn6oIF+LPwFcM=
|
||||||
github.com/rafaeljusto/redigomock v0.0.0-20190202135759-257e089e14a1/go.mod h1:JaY6n2sDr+z2WTsXkOmNRUfDy6FN0L6Nk7x06ndm4tY=
|
github.com/rafaeljusto/redigomock v0.0.0-20190202135759-257e089e14a1/go.mod h1:JaY6n2sDr+z2WTsXkOmNRUfDy6FN0L6Nk7x06ndm4tY=
|
||||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
|
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
|
||||||
|
@ -482,3 +482,18 @@ func (j *Json) MustUint64(args ...uint64) uint64 {
|
|||||||
|
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalYAML implements yaml.Marshaller.
|
||||||
|
func (j *Json) MarshalYAML() (interface{}, error) {
|
||||||
|
return j.data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalYAML implements yaml.Unmarshaller.
|
||||||
|
func (j *Json) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
var data interface{}
|
||||||
|
if err := unmarshal(&data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
j.data = data
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -30,6 +30,10 @@ var timeNow = time.Now
|
|||||||
|
|
||||||
type Alertmanager interface {
|
type Alertmanager interface {
|
||||||
ApplyConfig(config *apimodels.PostableUserConfig) error
|
ApplyConfig(config *apimodels.PostableUserConfig) error
|
||||||
|
CreateSilence(ps *apimodels.PostableSilence) (string, error)
|
||||||
|
DeleteSilence(silenceID string) error
|
||||||
|
GetSilence(silenceID string) (apimodels.GettableSilence, error)
|
||||||
|
ListSilences(filters []string) (apimodels.GettableSilences, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// API handlers.
|
// API handlers.
|
||||||
@ -40,6 +44,7 @@ type API struct {
|
|||||||
DataService *tsdb.Service
|
DataService *tsdb.Service
|
||||||
Schedule schedule.ScheduleService
|
Schedule schedule.ScheduleService
|
||||||
Store store.Store
|
Store store.Store
|
||||||
|
AlertingStore store.AlertingStore
|
||||||
DataProxy *datasourceproxy.DatasourceProxyService
|
DataProxy *datasourceproxy.DatasourceProxyService
|
||||||
Alertmanager Alertmanager
|
Alertmanager Alertmanager
|
||||||
}
|
}
|
||||||
@ -53,7 +58,7 @@ func (api *API) RegisterAPIEndpoints() {
|
|||||||
api.RegisterAlertmanagerApiEndpoints(NewForkedAM(
|
api.RegisterAlertmanagerApiEndpoints(NewForkedAM(
|
||||||
api.DatasourceCache,
|
api.DatasourceCache,
|
||||||
NewLotexAM(proxy, logger),
|
NewLotexAM(proxy, logger),
|
||||||
AlertmanagerApiMock{log: logger},
|
AlertmanagerSrv{store: api.AlertingStore, am: api.Alertmanager, log: logger},
|
||||||
))
|
))
|
||||||
api.RegisterPrometheusApiEndpoints(NewForkedProm(
|
api.RegisterPrometheusApiEndpoints(NewForkedProm(
|
||||||
api.DatasourceCache,
|
api.DatasourceCache,
|
||||||
|
359
pkg/services/ngalert/api/api_alertmanager.go
Normal file
359
pkg/services/ngalert/api/api_alertmanager.go
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
apimodels "github.com/grafana/alerting-api/pkg/api"
|
||||||
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
amv2 "github.com/prometheus/alertmanager/api/v2/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AlertmanagerSrv struct {
|
||||||
|
am Alertmanager
|
||||||
|
store store.AlertingStore
|
||||||
|
log log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv AlertmanagerSrv) RouteCreateSilence(c *models.ReqContext, postableSilence apimodels.PostableSilence) response.Response {
|
||||||
|
silenceID, err := srv.am.CreateSilence(&postableSilence)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, notifier.ErrSilenceNotFound) {
|
||||||
|
return response.Error(http.StatusNotFound, err.Error(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.Is(err, notifier.ErrCreateSilenceBadPayload) {
|
||||||
|
return response.Error(http.StatusBadRequest, err.Error(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Error(http.StatusInternalServerError, "failed to create silence", err)
|
||||||
|
}
|
||||||
|
return response.JSON(http.StatusAccepted, util.DynMap{"message": "silence created", "id": silenceID})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv AlertmanagerSrv) RouteDeleteAlertingConfig(c *models.ReqContext) response.Response {
|
||||||
|
// not implemented
|
||||||
|
return response.Error(http.StatusNotImplemented, "", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv AlertmanagerSrv) RouteDeleteSilence(c *models.ReqContext) response.Response {
|
||||||
|
silenceID := c.Params(":SilenceId")
|
||||||
|
if err := srv.am.DeleteSilence(silenceID); err != nil {
|
||||||
|
if errors.Is(err, notifier.ErrSilenceNotFound) {
|
||||||
|
return response.Error(http.StatusNotFound, err.Error(), nil)
|
||||||
|
}
|
||||||
|
return response.Error(http.StatusInternalServerError, err.Error(), nil)
|
||||||
|
}
|
||||||
|
return response.JSON(http.StatusOK, util.DynMap{"message": "silence deleted"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv AlertmanagerSrv) RouteGetAlertingConfig(c *models.ReqContext) response.Response {
|
||||||
|
query := ngmodels.GetLatestAlertmanagerConfigurationQuery{}
|
||||||
|
err := srv.store.GetLatestAlertmanagerConfiguration(&query)
|
||||||
|
if err != nil {
|
||||||
|
return response.Error(http.StatusInternalServerError, "failed to get latest configuration", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := apimodels.PostableUserConfig{}
|
||||||
|
err = yaml.Unmarshal([]byte(query.Result.AlertmanagerConfiguration), &cfg)
|
||||||
|
if err != nil {
|
||||||
|
return response.Error(http.StatusInternalServerError, "failed to unmarshal alertmanager configuration", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiReceiverName string
|
||||||
|
var receivers []*apimodels.GettableGrafanaReceiver
|
||||||
|
alertmanagerCfg := cfg.AlertmanagerConfig
|
||||||
|
if len(alertmanagerCfg.Receivers) > 0 {
|
||||||
|
apiReceiverName = alertmanagerCfg.Receivers[0].Name
|
||||||
|
receivers = make([]*apimodels.GettableGrafanaReceiver, 0, len(alertmanagerCfg.Receivers[0].PostableGrafanaReceivers.GrafanaManagedReceivers))
|
||||||
|
for _, pr := range alertmanagerCfg.Receivers[0].PostableGrafanaReceivers.GrafanaManagedReceivers {
|
||||||
|
secureFields := make(map[string]bool, len(pr.SecureSettings))
|
||||||
|
for k := range pr.SecureSettings {
|
||||||
|
secureFields[k] = true
|
||||||
|
}
|
||||||
|
gr := apimodels.GettableGrafanaReceiver{
|
||||||
|
Uid: pr.Uid,
|
||||||
|
Name: pr.Name,
|
||||||
|
Type: pr.Type,
|
||||||
|
IsDefault: pr.IsDefault,
|
||||||
|
SendReminder: pr.SendReminder,
|
||||||
|
DisableResolveMessage: pr.DisableResolveMessage,
|
||||||
|
Frequency: pr.Frequency,
|
||||||
|
Settings: pr.Settings,
|
||||||
|
SecureFields: secureFields,
|
||||||
|
}
|
||||||
|
receivers = append(receivers, &gr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gettableApiReceiver := apimodels.GettableApiReceiver{
|
||||||
|
GettableGrafanaReceivers: apimodels.GettableGrafanaReceivers{
|
||||||
|
GrafanaManagedReceivers: receivers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
gettableApiReceiver.Name = apiReceiverName
|
||||||
|
result := apimodels.GettableUserConfig{
|
||||||
|
TemplateFiles: cfg.TemplateFiles,
|
||||||
|
AlertmanagerConfig: apimodels.GettableApiAlertingConfig{
|
||||||
|
Config: alertmanagerCfg.Config,
|
||||||
|
Receivers: []*apimodels.GettableApiReceiver{
|
||||||
|
&gettableApiReceiver,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.JSON(http.StatusOK, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv AlertmanagerSrv) RouteGetAMAlertGroups(c *models.ReqContext) response.Response {
|
||||||
|
recipient := c.Params(":Recipient")
|
||||||
|
srv.log.Info("RouteGetAMAlertGroups: ", "Recipient", recipient)
|
||||||
|
now := time.Now()
|
||||||
|
result := apimodels.AlertGroups{
|
||||||
|
&amv2.AlertGroup{
|
||||||
|
Alerts: []*amv2.GettableAlert{
|
||||||
|
{
|
||||||
|
Annotations: amv2.LabelSet{
|
||||||
|
"annotation1-1": "value1",
|
||||||
|
"annotation1-2": "value2",
|
||||||
|
},
|
||||||
|
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
||||||
|
Fingerprint: stringPtr("fingerprint 1"),
|
||||||
|
Receivers: []*amv2.Receiver{
|
||||||
|
{
|
||||||
|
Name: stringPtr("receiver identifier 1-1"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: stringPtr("receiver identifier 1-2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StartsAt: timePtr(strfmt.DateTime(now)),
|
||||||
|
Status: &amv2.AlertStatus{
|
||||||
|
InhibitedBy: []string{"inhibitedBy 1"},
|
||||||
|
SilencedBy: []string{"silencedBy 1"},
|
||||||
|
State: stringPtr(amv2.AlertStatusStateActive),
|
||||||
|
},
|
||||||
|
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
||||||
|
Alert: amv2.Alert{
|
||||||
|
GeneratorURL: strfmt.URI("a URL"),
|
||||||
|
Labels: amv2.LabelSet{
|
||||||
|
"label1-1": "value1",
|
||||||
|
"label1-2": "value2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Annotations: amv2.LabelSet{
|
||||||
|
"annotation2-1": "value1",
|
||||||
|
"annotation2-2": "value2",
|
||||||
|
},
|
||||||
|
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
||||||
|
Fingerprint: stringPtr("fingerprint 2"),
|
||||||
|
Receivers: []*amv2.Receiver{
|
||||||
|
{
|
||||||
|
Name: stringPtr("receiver identifier 2-1"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: stringPtr("receiver identifier 2-2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StartsAt: timePtr(strfmt.DateTime(now)),
|
||||||
|
Status: &amv2.AlertStatus{
|
||||||
|
InhibitedBy: []string{"inhibitedBy 2"},
|
||||||
|
SilencedBy: []string{"silencedBy 2"},
|
||||||
|
State: stringPtr(amv2.AlertStatusStateActive),
|
||||||
|
},
|
||||||
|
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
||||||
|
Alert: amv2.Alert{
|
||||||
|
GeneratorURL: strfmt.URI("a URL"),
|
||||||
|
Labels: amv2.LabelSet{
|
||||||
|
"label2-1": "value1",
|
||||||
|
"label2-2": "value2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Labels: amv2.LabelSet{
|
||||||
|
"label1-1": "value1",
|
||||||
|
"label1-2": "value2",
|
||||||
|
},
|
||||||
|
Receiver: &amv2.Receiver{
|
||||||
|
Name: stringPtr("receiver identifier 2-1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&amv2.AlertGroup{
|
||||||
|
Alerts: []*amv2.GettableAlert{
|
||||||
|
{
|
||||||
|
Annotations: amv2.LabelSet{
|
||||||
|
"annotation2-1": "value1",
|
||||||
|
"annotation2-2": "value2",
|
||||||
|
},
|
||||||
|
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
||||||
|
Fingerprint: stringPtr("fingerprint 2"),
|
||||||
|
Receivers: []*amv2.Receiver{
|
||||||
|
{
|
||||||
|
Name: stringPtr("receiver identifier 2-1"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: stringPtr("receiver identifier 2-2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StartsAt: timePtr(strfmt.DateTime(now)),
|
||||||
|
Status: &amv2.AlertStatus{
|
||||||
|
InhibitedBy: []string{"inhibitedBy 2"},
|
||||||
|
SilencedBy: []string{"silencedBy 2"},
|
||||||
|
State: stringPtr(amv2.AlertStatusStateActive),
|
||||||
|
},
|
||||||
|
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
||||||
|
Alert: amv2.Alert{
|
||||||
|
GeneratorURL: strfmt.URI("a URL"),
|
||||||
|
Labels: amv2.LabelSet{
|
||||||
|
"label2-1": "value1",
|
||||||
|
"label2-2": "value2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Labels: amv2.LabelSet{
|
||||||
|
"label2-1": "value1",
|
||||||
|
"label2-2": "value2",
|
||||||
|
},
|
||||||
|
Receiver: &amv2.Receiver{
|
||||||
|
Name: stringPtr("receiver identifier 2-1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return response.JSON(http.StatusOK, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv AlertmanagerSrv) RouteGetAMAlerts(c *models.ReqContext) response.Response {
|
||||||
|
recipient := c.Params(":Recipient")
|
||||||
|
srv.log.Info("RouteGetAMAlerts: ", "Recipient", recipient)
|
||||||
|
now := time.Now()
|
||||||
|
result := apimodels.GettableAlerts{
|
||||||
|
&amv2.GettableAlert{
|
||||||
|
Annotations: amv2.LabelSet{
|
||||||
|
"annotation1-1": "value1",
|
||||||
|
"annotation1-2": "value2",
|
||||||
|
},
|
||||||
|
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
||||||
|
Fingerprint: stringPtr("fingerprint 1"),
|
||||||
|
Receivers: []*amv2.Receiver{
|
||||||
|
{
|
||||||
|
Name: stringPtr("receiver identifier 1-1"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: stringPtr("receiver identifier 1-2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StartsAt: timePtr(strfmt.DateTime(now)),
|
||||||
|
Status: &amv2.AlertStatus{
|
||||||
|
InhibitedBy: []string{"inhibitedBy 1"},
|
||||||
|
SilencedBy: []string{"silencedBy 1"},
|
||||||
|
State: stringPtr(amv2.AlertStatusStateActive),
|
||||||
|
},
|
||||||
|
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
||||||
|
Alert: amv2.Alert{
|
||||||
|
GeneratorURL: strfmt.URI("a URL"),
|
||||||
|
Labels: amv2.LabelSet{
|
||||||
|
"label1-1": "value1",
|
||||||
|
"label1-2": "value2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&amv2.GettableAlert{
|
||||||
|
Annotations: amv2.LabelSet{
|
||||||
|
"annotation2-1": "value1",
|
||||||
|
"annotation2-2": "value2",
|
||||||
|
},
|
||||||
|
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
||||||
|
Fingerprint: stringPtr("fingerprint 2"),
|
||||||
|
Receivers: []*amv2.Receiver{
|
||||||
|
{
|
||||||
|
Name: stringPtr("receiver identifier 2-1"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: stringPtr("receiver identifier 2-2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StartsAt: timePtr(strfmt.DateTime(now)),
|
||||||
|
Status: &amv2.AlertStatus{
|
||||||
|
InhibitedBy: []string{"inhibitedBy 2"},
|
||||||
|
SilencedBy: []string{"silencedBy 2"},
|
||||||
|
State: stringPtr(amv2.AlertStatusStateActive),
|
||||||
|
},
|
||||||
|
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
||||||
|
Alert: amv2.Alert{
|
||||||
|
GeneratorURL: strfmt.URI("a URL"),
|
||||||
|
Labels: amv2.LabelSet{
|
||||||
|
"label2-1": "value1",
|
||||||
|
"label2-2": "value2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return response.JSON(http.StatusOK, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv AlertmanagerSrv) RouteGetSilence(c *models.ReqContext) response.Response {
|
||||||
|
silenceID := c.Params(":SilenceId")
|
||||||
|
gettableSilence, err := srv.am.GetSilence(silenceID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, notifier.ErrSilenceNotFound) {
|
||||||
|
return response.Error(http.StatusNotFound, err.Error(), nil)
|
||||||
|
}
|
||||||
|
// any other error here should be an unexpected failure and thus an internal error
|
||||||
|
return response.Error(http.StatusInternalServerError, err.Error(), nil)
|
||||||
|
}
|
||||||
|
return response.JSON(http.StatusOK, gettableSilence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv AlertmanagerSrv) RouteGetSilences(c *models.ReqContext) response.Response {
|
||||||
|
filters := c.QueryStrings("Filter")
|
||||||
|
gettableSilences, err := srv.am.ListSilences(filters)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, notifier.ErrListSilencesBadPayload) {
|
||||||
|
return response.Error(http.StatusBadRequest, err.Error(), nil)
|
||||||
|
}
|
||||||
|
// any other error here should be an unexpected failure and thus an internal error
|
||||||
|
return response.Error(http.StatusInternalServerError, err.Error(), nil)
|
||||||
|
}
|
||||||
|
return response.JSON(http.StatusOK, gettableSilences)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv AlertmanagerSrv) RoutePostAlertingConfig(c *models.ReqContext, body apimodels.PostableUserConfig) response.Response {
|
||||||
|
config, err := yaml.Marshal(&body)
|
||||||
|
if err != nil {
|
||||||
|
return response.Error(http.StatusInternalServerError, "failed to serialize to the Alertmanager configuration", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := ngmodels.SaveAlertmanagerConfigurationCmd{
|
||||||
|
AlertmanagerConfiguration: string(config),
|
||||||
|
ConfigurationVersion: fmt.Sprintf("v%d", ngmodels.AlertConfigurationVersion),
|
||||||
|
}
|
||||||
|
if err := srv.store.SaveAlertmanagerConfiguration(&cmd); err != nil {
|
||||||
|
return response.Error(http.StatusInternalServerError, "failed to save Alertmanager configuration", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := srv.am.ApplyConfig(&body); err != nil {
|
||||||
|
return response.Error(http.StatusInternalServerError, "failed to apply Alertmanager configuration", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.JSON(http.StatusAccepted, util.DynMap{"message": "configuration created"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv AlertmanagerSrv) RoutePostAMAlerts(c *models.ReqContext, body apimodels.PostableAlerts) response.Response {
|
||||||
|
// not implemented
|
||||||
|
return response.Error(http.StatusNotImplemented, "", nil)
|
||||||
|
}
|
@ -18,15 +18,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AlertmanagerApiService interface {
|
type AlertmanagerApiService interface {
|
||||||
RouteCreateSilence(*models.ReqContext, apimodels.CreateSilenceParams) response.Response
|
RouteCreateSilence(*models.ReqContext, apimodels.PostableSilence) response.Response
|
||||||
RouteGetSilences(*models.ReqContext) response.Response
|
|
||||||
RouteGetSilence(*models.ReqContext) response.Response
|
|
||||||
RouteDeleteSilence(*models.ReqContext) response.Response
|
|
||||||
|
|
||||||
RouteDeleteAlertingConfig(*models.ReqContext) response.Response
|
RouteDeleteAlertingConfig(*models.ReqContext) response.Response
|
||||||
|
RouteDeleteSilence(*models.ReqContext) response.Response
|
||||||
RouteGetAMAlertGroups(*models.ReqContext) response.Response
|
RouteGetAMAlertGroups(*models.ReqContext) response.Response
|
||||||
RouteGetAMAlerts(*models.ReqContext) response.Response
|
RouteGetAMAlerts(*models.ReqContext) response.Response
|
||||||
RouteGetAlertingConfig(*models.ReqContext) response.Response
|
RouteGetAlertingConfig(*models.ReqContext) response.Response
|
||||||
|
RouteGetSilence(*models.ReqContext) response.Response
|
||||||
|
RouteGetSilences(*models.ReqContext) response.Response
|
||||||
RoutePostAMAlerts(*models.ReqContext, apimodels.PostableAlerts) response.Response
|
RoutePostAMAlerts(*models.ReqContext, apimodels.PostableAlerts) response.Response
|
||||||
RoutePostAlertingConfig(*models.ReqContext, apimodels.PostableUserConfig) response.Response
|
RoutePostAlertingConfig(*models.ReqContext, apimodels.PostableUserConfig) response.Response
|
||||||
}
|
}
|
||||||
@ -37,44 +36,29 @@ type AlertmanagerApiBase struct {
|
|||||||
|
|
||||||
func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApiService) {
|
func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApiService) {
|
||||||
api.RouteRegister.Group("", func(group routing.RouteRegister) {
|
api.RouteRegister.Group("", func(group routing.RouteRegister) {
|
||||||
// Silences
|
group.Post(toMacaronPath("/alertmanager/{Recipient}/api/v2/silences"), binding.Bind(apimodels.PostableSilence{}), routing.Wrap(srv.RouteCreateSilence))
|
||||||
group.Post(toMacaronPath("/alertmanager/{Recipient}/api/v2/silences"), binding.Bind(apimodels.CreateSilenceParams{}), routing.Wrap(srv.RouteCreateSilence))
|
group.Delete(toMacaronPath("/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteDeleteAlertingConfig))
|
||||||
group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/silences"), routing.Wrap(srv.RouteGetSilences))
|
|
||||||
group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteGetSilence))
|
|
||||||
group.Delete(toMacaronPath("/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteDeleteSilence))
|
group.Delete(toMacaronPath("/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteDeleteSilence))
|
||||||
|
|
||||||
// Alerts
|
|
||||||
group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/alerts/groups"), routing.Wrap(srv.RouteGetAMAlertGroups))
|
group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/alerts/groups"), routing.Wrap(srv.RouteGetAMAlertGroups))
|
||||||
group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/alerts"), routing.Wrap(srv.RouteGetAMAlerts))
|
group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/alerts"), routing.Wrap(srv.RouteGetAMAlerts))
|
||||||
group.Post(toMacaronPath("/alertmanager/{Recipient}/api/v2/alerts"), binding.Bind(apimodels.PostableAlerts{}), routing.Wrap(srv.RoutePostAMAlerts))
|
|
||||||
|
|
||||||
// Configuration
|
|
||||||
group.Delete(toMacaronPath("/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteDeleteAlertingConfig))
|
|
||||||
group.Get(toMacaronPath("/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertingConfig))
|
group.Get(toMacaronPath("/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertingConfig))
|
||||||
|
group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteGetSilence))
|
||||||
|
group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/silences"), routing.Wrap(srv.RouteGetSilences))
|
||||||
|
group.Post(toMacaronPath("/alertmanager/{Recipient}/api/v2/alerts"), binding.Bind(apimodels.PostableAlerts{}), routing.Wrap(srv.RoutePostAMAlerts))
|
||||||
group.Post(toMacaronPath("/alertmanager/{Recipient}/config/api/v1/alerts"), binding.Bind(apimodels.PostableUserConfig{}), routing.Wrap(srv.RoutePostAlertingConfig))
|
group.Post(toMacaronPath("/alertmanager/{Recipient}/config/api/v1/alerts"), binding.Bind(apimodels.PostableUserConfig{}), routing.Wrap(srv.RoutePostAlertingConfig))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (base AlertmanagerApiBase) RouteCreateSilence(c *models.ReqContext, params apimodels.CreateSilenceParams) response.Response {
|
func (base AlertmanagerApiBase) RouteCreateSilence(c *models.ReqContext, body apimodels.PostableSilence) response.Response {
|
||||||
recipient := c.Params(":Recipient")
|
recipient := c.Params(":Recipient")
|
||||||
base.log.Info("RouteCreateSilence: ", "Recipient", recipient)
|
base.log.Info("RouteCreateSilence: ", "Recipient", recipient)
|
||||||
base.log.Info("RouteCreateSilence: ", "params", params)
|
base.log.Info("RouteCreateSilence: ", "body", body)
|
||||||
return response.Error(http.StatusNotImplemented, "", nil)
|
return response.Error(http.StatusNotImplemented, "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (base AlertmanagerApiBase) RouteGetSilences(c *models.ReqContext) response.Response {
|
func (base AlertmanagerApiBase) RouteDeleteAlertingConfig(c *models.ReqContext) response.Response {
|
||||||
recipient := c.Params(":Recipient")
|
recipient := c.Params(":Recipient")
|
||||||
base.log.Info("RouteGetSilences: ", "Recipient", recipient)
|
base.log.Info("RouteDeleteAlertingConfig: ", "Recipient", recipient)
|
||||||
filter := c.Params(":Filter")
|
|
||||||
base.log.Info("RouteGetSilences: ", "params", filter)
|
|
||||||
return response.Error(http.StatusNotImplemented, "", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (base AlertmanagerApiBase) RouteGetSilence(c *models.ReqContext) response.Response {
|
|
||||||
silenceId := c.Params(":SilenceId")
|
|
||||||
base.log.Info("RouteGetSilence: ", "SilenceId", silenceId)
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
base.log.Info("RouteGetSilence: ", "Recipient", recipient)
|
|
||||||
return response.Error(http.StatusNotImplemented, "", nil)
|
return response.Error(http.StatusNotImplemented, "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,12 +70,6 @@ func (base AlertmanagerApiBase) RouteDeleteSilence(c *models.ReqContext) respons
|
|||||||
return response.Error(http.StatusNotImplemented, "", nil)
|
return response.Error(http.StatusNotImplemented, "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (base AlertmanagerApiBase) RouteDeleteAlertingConfig(c *models.ReqContext) response.Response {
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
base.log.Info("RouteDeleteAlertingConfig: ", "Recipient", recipient)
|
|
||||||
return response.Error(http.StatusNotImplemented, "", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (base AlertmanagerApiBase) RouteGetAMAlertGroups(c *models.ReqContext) response.Response {
|
func (base AlertmanagerApiBase) RouteGetAMAlertGroups(c *models.ReqContext) response.Response {
|
||||||
recipient := c.Params(":Recipient")
|
recipient := c.Params(":Recipient")
|
||||||
base.log.Info("RouteGetAMAlertGroups: ", "Recipient", recipient)
|
base.log.Info("RouteGetAMAlertGroups: ", "Recipient", recipient)
|
||||||
@ -110,6 +88,20 @@ func (base AlertmanagerApiBase) RouteGetAlertingConfig(c *models.ReqContext) res
|
|||||||
return response.Error(http.StatusNotImplemented, "", nil)
|
return response.Error(http.StatusNotImplemented, "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (base AlertmanagerApiBase) RouteGetSilence(c *models.ReqContext) response.Response {
|
||||||
|
silenceId := c.Params(":SilenceId")
|
||||||
|
base.log.Info("RouteGetSilence: ", "SilenceId", silenceId)
|
||||||
|
recipient := c.Params(":Recipient")
|
||||||
|
base.log.Info("RouteGetSilence: ", "Recipient", recipient)
|
||||||
|
return response.Error(http.StatusNotImplemented, "", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (base AlertmanagerApiBase) RouteGetSilences(c *models.ReqContext) response.Response {
|
||||||
|
recipient := c.Params(":Recipient")
|
||||||
|
base.log.Info("RouteGetSilences: ", "Recipient", recipient)
|
||||||
|
return response.Error(http.StatusNotImplemented, "", nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (base AlertmanagerApiBase) RoutePostAMAlerts(c *models.ReqContext, body apimodels.PostableAlerts) response.Response {
|
func (base AlertmanagerApiBase) RoutePostAMAlerts(c *models.ReqContext, body apimodels.PostableAlerts) response.Response {
|
||||||
recipient := c.Params(":Recipient")
|
recipient := c.Params(":Recipient")
|
||||||
base.log.Info("RoutePostAMAlerts: ", "Recipient", recipient)
|
base.log.Info("RoutePostAMAlerts: ", "Recipient", recipient)
|
||||||
|
@ -1,891 +0,0 @@
|
|||||||
/*Package api contains mock API implementation of unified alerting
|
|
||||||
*
|
|
||||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
|
||||||
*
|
|
||||||
* Need to remove unused imports.
|
|
||||||
*/
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/securejsondata"
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
||||||
|
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
apimodels "github.com/grafana/alerting-api/pkg/api"
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
|
||||||
amv2 "github.com/prometheus/alertmanager/api/v2/models"
|
|
||||||
"github.com/prometheus/alertmanager/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toSimpleJSON(blob string) *simplejson.Json {
|
|
||||||
json, _ := simplejson.NewJson([]byte(blob))
|
|
||||||
return json
|
|
||||||
}
|
|
||||||
|
|
||||||
var alertmanagerReceiver = models.AlertNotification{
|
|
||||||
Id: 1,
|
|
||||||
Uid: "alertmanager UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "an alert manager receiver",
|
|
||||||
Type: "prometheus-alertmanager",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"basicAuthUser": "user",
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false,
|
|
||||||
"url": "http://localhost:9093"
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{
|
|
||||||
"basicAuthPassword": "<basicAuthPassword>",
|
|
||||||
}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var alertmanagerReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&alertmanagerReceiver))
|
|
||||||
|
|
||||||
var dingdingReceiver = models.AlertNotification{
|
|
||||||
Id: 2,
|
|
||||||
Uid: "dingding UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a dingding receiver",
|
|
||||||
Type: "dingding",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"msgType": "link",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false,
|
|
||||||
"url": "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx"
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var dingdingReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&dingdingReceiver))
|
|
||||||
|
|
||||||
var discordReceiver = models.AlertNotification{
|
|
||||||
Id: 3,
|
|
||||||
Uid: "discord UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a discord receiver",
|
|
||||||
Type: "discord",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"content": "@user",
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false,
|
|
||||||
"url": "http://"
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var discordReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&discordReceiver))
|
|
||||||
|
|
||||||
var emailReceiver = models.AlertNotification{
|
|
||||||
Id: 4,
|
|
||||||
Uid: "email UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "an email receiver",
|
|
||||||
Type: "email",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"addresses": "<email>",
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"severity": "critical",
|
|
||||||
"singleEmail": true,
|
|
||||||
"uploadImage": false
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var emailReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&emailReceiver))
|
|
||||||
|
|
||||||
var googlechatReceiver = models.AlertNotification{
|
|
||||||
Id: 5,
|
|
||||||
Uid: "googlechatReceiver UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a googlechat receiver",
|
|
||||||
Type: "googlechat",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false,
|
|
||||||
"url": "http://"
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var googlechatReceiverDTOs = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&googlechatReceiver))
|
|
||||||
|
|
||||||
var hipchatReceiver = models.AlertNotification{
|
|
||||||
Id: 6,
|
|
||||||
Uid: "hipchat UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a hipchat receiver",
|
|
||||||
Type: "hipchat",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"apiKey": "<apikey>",
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"roomid": "12345",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false,
|
|
||||||
"url": "http://"
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var hipchatReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&hipchatReceiver))
|
|
||||||
|
|
||||||
var kafkaReceiver = models.AlertNotification{
|
|
||||||
Id: 7,
|
|
||||||
Uid: "kafka UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a kafka receiver",
|
|
||||||
Type: "kafka",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"kafkaRestProxy": "http://localhost:8082",
|
|
||||||
"kafkaTopic": "topic1",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var kafkaReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&kafkaReceiver))
|
|
||||||
|
|
||||||
var lineReceiver = models.AlertNotification{
|
|
||||||
Id: 8,
|
|
||||||
Uid: "line UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a line receiver",
|
|
||||||
Type: "line",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(` "settings": {
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false
|
|
||||||
},`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{
|
|
||||||
"token": "<token>",
|
|
||||||
}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var lineReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&lineReceiver))
|
|
||||||
|
|
||||||
var opsgenieReceiver = models.AlertNotification{
|
|
||||||
Id: 9,
|
|
||||||
Uid: "opsgenie UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a opsgenie receiver",
|
|
||||||
Type: "opsgenie",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(` "settings": {
|
|
||||||
"apiUrl": "https://api.opsgenie.com/v2/alerts",
|
|
||||||
"autoClose": true,
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"overridePriority": true,
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false
|
|
||||||
},`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{
|
|
||||||
"apiKey": "<apiKey>",
|
|
||||||
}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var opsgenieReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&opsgenieReceiver))
|
|
||||||
|
|
||||||
var pagerdutyReceiver = models.AlertNotification{
|
|
||||||
Id: 10,
|
|
||||||
Uid: "pagerduty UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a pagerduty receiver",
|
|
||||||
Type: "pagerduty",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": true
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{
|
|
||||||
"integrationKey": "<integrationKey>",
|
|
||||||
}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var pagerdutyReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&pagerdutyReceiver))
|
|
||||||
|
|
||||||
var pushoverReceiver = models.AlertNotification{
|
|
||||||
Id: 11,
|
|
||||||
Uid: "pushover UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a pushover receiver",
|
|
||||||
Type: "pushover",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"apiToken": "",
|
|
||||||
"autoResolve": true,
|
|
||||||
"device": "",
|
|
||||||
"expire": "",
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"okPriority": "0",
|
|
||||||
"okSound": "cosmic",
|
|
||||||
"priority": "1",
|
|
||||||
"retry": "30",
|
|
||||||
"severity": "critical",
|
|
||||||
"sound": "pushover",
|
|
||||||
"uploadImage": true,
|
|
||||||
"userKey": ""
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{
|
|
||||||
"apiToken": "<apiToken>",
|
|
||||||
"userKey": "<userKey>",
|
|
||||||
}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var pushoverReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&pushoverReceiver))
|
|
||||||
|
|
||||||
var sensuReceiver = models.AlertNotification{
|
|
||||||
Id: 12,
|
|
||||||
Uid: "sensu UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a sensu receiver",
|
|
||||||
Type: "sensu",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"handler": "",
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"severity": "critical",
|
|
||||||
"source": "",
|
|
||||||
"uploadImage": false,
|
|
||||||
"url": "http://sensu-api.local:4567/results",
|
|
||||||
"username": ""
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var sensuReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&sensuReceiver))
|
|
||||||
|
|
||||||
var sensugoReceiver = models.AlertNotification{
|
|
||||||
Id: 13,
|
|
||||||
Uid: "sensugo UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a sensugo receiver",
|
|
||||||
Type: "sensugo",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"check": "",
|
|
||||||
"entity": "",
|
|
||||||
"handler": "",
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"namespace": "",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false,
|
|
||||||
"url": "http://sensu-api.local:8080"
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{
|
|
||||||
"apikey": "<apikey>",
|
|
||||||
}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var sensugoReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&sensugoReceiver))
|
|
||||||
|
|
||||||
var slackReceiver = models.AlertNotification{
|
|
||||||
Id: 14,
|
|
||||||
Uid: "slack UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a slack receiver",
|
|
||||||
Type: "slack",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"iconEmoji": "",
|
|
||||||
"iconUrl": "",
|
|
||||||
"mentionGroups": "",
|
|
||||||
"mentionUsers": "",
|
|
||||||
"recipient": "",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false,
|
|
||||||
"username": ""
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{
|
|
||||||
"token": "<token>",
|
|
||||||
"url": "<url>",
|
|
||||||
}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var slackReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&slackReceiver))
|
|
||||||
|
|
||||||
var teamsReceiver = models.AlertNotification{
|
|
||||||
Id: 15,
|
|
||||||
Uid: "teams UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a teams receiver",
|
|
||||||
Type: "teams",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false,
|
|
||||||
"url": "http://"
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var teamsReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&teamsReceiver))
|
|
||||||
|
|
||||||
var telegramReceiver = models.AlertNotification{
|
|
||||||
Id: 16,
|
|
||||||
Uid: "telegram UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a telegram receiver",
|
|
||||||
Type: "telegram",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"chatid": "12345",
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{
|
|
||||||
"bottoken": "<bottoken>",
|
|
||||||
}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var telegramReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&telegramReceiver))
|
|
||||||
|
|
||||||
var threemaReceiver = models.AlertNotification{
|
|
||||||
Id: 17,
|
|
||||||
Uid: "threema UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a threema receiver",
|
|
||||||
Type: "threema",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"gateway_id": "*3MAGWID",
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"recipient_id": "YOUR3MID",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false
|
|
||||||
},`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{
|
|
||||||
"api_secret": "<api_secret>",
|
|
||||||
}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var threemaDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&threemaReceiver))
|
|
||||||
|
|
||||||
var victoropsReceiver = models.AlertNotification{
|
|
||||||
Id: 18,
|
|
||||||
Uid: "victorops UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a victorops receiver",
|
|
||||||
Type: "victorops",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`{
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": false,
|
|
||||||
"url": "http://"
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var victoropsReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&victoropsReceiver))
|
|
||||||
|
|
||||||
var webhookReceiver = models.AlertNotification{
|
|
||||||
Id: 19,
|
|
||||||
Uid: "webhook UID",
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "a webhook receiver",
|
|
||||||
Type: "webhook",
|
|
||||||
SendReminder: false,
|
|
||||||
DisableResolveMessage: false,
|
|
||||||
Frequency: 5 * time.Minute,
|
|
||||||
IsDefault: false,
|
|
||||||
Settings: toSimpleJSON(`x{
|
|
||||||
"autoResolve": true,
|
|
||||||
"httpMethod": "POST",
|
|
||||||
"password": "",
|
|
||||||
"severity": "critical",
|
|
||||||
"uploadImage": true,
|
|
||||||
"url": "http://localhost:3010",
|
|
||||||
"username": ""
|
|
||||||
}`),
|
|
||||||
SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{
|
|
||||||
"password": "<password>",
|
|
||||||
}),
|
|
||||||
Created: time.Now().Add(-time.Hour),
|
|
||||||
Updated: time.Now().Add(-5 * time.Minute),
|
|
||||||
}
|
|
||||||
var webhookReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&webhookReceiver))
|
|
||||||
|
|
||||||
type AlertmanagerApiMock struct {
|
|
||||||
log log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mock AlertmanagerApiMock) RouteCreateSilence(c *models.ReqContext, body apimodels.CreateSilenceParams) response.Response {
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
mock.log.Info("RouteCreateSilence: ", "Recipient", recipient)
|
|
||||||
mock.log.Info("RouteCreateSilence: ", "body", body)
|
|
||||||
return response.JSON(http.StatusAccepted, util.DynMap{"message": "silence created"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mock AlertmanagerApiMock) RouteDeleteAlertingConfig(c *models.ReqContext) response.Response {
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
mock.log.Info("RouteDeleteAlertingConfig: ", "Recipient", recipient)
|
|
||||||
return response.JSON(http.StatusOK, util.DynMap{"message": "config deleted"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mock AlertmanagerApiMock) RouteDeleteSilence(c *models.ReqContext) response.Response {
|
|
||||||
silenceID := c.Params(":SilenceId")
|
|
||||||
mock.log.Info("RouteDeleteSilence: ", "SilenceId", silenceID)
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
mock.log.Info("RouteDeleteSilence: ", "Recipient", recipient)
|
|
||||||
return response.JSON(http.StatusOK, util.DynMap{"message": "silence deleted"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mock AlertmanagerApiMock) RouteGetAlertingConfig(c *models.ReqContext) response.Response {
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
mock.log.Info("RouteGetAlertingConfig: ", "Recipient", recipient)
|
|
||||||
// now := time.Now()
|
|
||||||
result := apimodels.GettableUserConfig{
|
|
||||||
TemplateFiles: map[string]string{
|
|
||||||
"tmpl1": "val1",
|
|
||||||
"tmpl2": "val2",
|
|
||||||
},
|
|
||||||
AlertmanagerConfig: apimodels.GettableApiAlertingConfig{
|
|
||||||
Config: apimodels.Config{
|
|
||||||
Global: &config.GlobalConfig{},
|
|
||||||
Route: &config.Route{},
|
|
||||||
InhibitRules: []*config.InhibitRule{},
|
|
||||||
Receivers: []*config.Receiver{},
|
|
||||||
Templates: []string{},
|
|
||||||
},
|
|
||||||
Receivers: []*apimodels.GettableApiReceiver{
|
|
||||||
{
|
|
||||||
GettableGrafanaReceivers: apimodels.GettableGrafanaReceivers{
|
|
||||||
GrafanaManagedReceivers: []*apimodels.GettableGrafanaReceiver{
|
|
||||||
&alertmanagerReceiverDTO,
|
|
||||||
&dingdingReceiverDTO,
|
|
||||||
&discordReceiverDTO,
|
|
||||||
&emailReceiverDTO,
|
|
||||||
&googlechatReceiverDTOs,
|
|
||||||
&hipchatReceiverDTO,
|
|
||||||
&kafkaReceiverDTO,
|
|
||||||
&lineReceiverDTO,
|
|
||||||
&opsgenieReceiverDTO,
|
|
||||||
&pagerdutyReceiverDTO,
|
|
||||||
&pushoverReceiverDTO,
|
|
||||||
&sensuReceiverDTO,
|
|
||||||
&sensugoReceiverDTO,
|
|
||||||
&slackReceiverDTO,
|
|
||||||
&teamsReceiverDTO,
|
|
||||||
&telegramReceiverDTO,
|
|
||||||
&threemaDTO,
|
|
||||||
&victoropsReceiverDTO,
|
|
||||||
&webhookReceiverDTO,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return response.JSON(http.StatusOK, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mock AlertmanagerApiMock) RouteGetAMAlertGroups(c *models.ReqContext) response.Response {
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
mock.log.Info("RouteGetAMAlertGroups: ", "Recipient", recipient)
|
|
||||||
now := time.Now()
|
|
||||||
result := apimodels.AlertGroups{
|
|
||||||
&amv2.AlertGroup{
|
|
||||||
Alerts: []*amv2.GettableAlert{
|
|
||||||
{
|
|
||||||
Annotations: amv2.LabelSet{
|
|
||||||
"annotation1-1": "value1",
|
|
||||||
"annotation1-2": "value2",
|
|
||||||
},
|
|
||||||
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
|
||||||
Fingerprint: stringPtr("fingerprint 1"),
|
|
||||||
Receivers: []*amv2.Receiver{
|
|
||||||
{
|
|
||||||
Name: stringPtr("receiver identifier 1-1"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: stringPtr("receiver identifier 1-2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
StartsAt: timePtr(strfmt.DateTime(now)),
|
|
||||||
Status: &amv2.AlertStatus{
|
|
||||||
InhibitedBy: []string{"inhibitedBy 1"},
|
|
||||||
SilencedBy: []string{"silencedBy 1"},
|
|
||||||
State: stringPtr(amv2.AlertStatusStateActive),
|
|
||||||
},
|
|
||||||
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
|
||||||
Alert: amv2.Alert{
|
|
||||||
GeneratorURL: strfmt.URI("a URL"),
|
|
||||||
Labels: amv2.LabelSet{
|
|
||||||
"label1-1": "value1",
|
|
||||||
"label1-2": "value2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Annotations: amv2.LabelSet{
|
|
||||||
"annotation2-1": "value1",
|
|
||||||
"annotation2-2": "value2",
|
|
||||||
},
|
|
||||||
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
|
||||||
Fingerprint: stringPtr("fingerprint 2"),
|
|
||||||
Receivers: []*amv2.Receiver{
|
|
||||||
{
|
|
||||||
Name: stringPtr("receiver identifier 2-1"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: stringPtr("receiver identifier 2-2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
StartsAt: timePtr(strfmt.DateTime(now)),
|
|
||||||
Status: &amv2.AlertStatus{
|
|
||||||
InhibitedBy: []string{"inhibitedBy 2"},
|
|
||||||
SilencedBy: []string{"silencedBy 2"},
|
|
||||||
State: stringPtr(amv2.AlertStatusStateActive),
|
|
||||||
},
|
|
||||||
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
|
||||||
Alert: amv2.Alert{
|
|
||||||
GeneratorURL: strfmt.URI("a URL"),
|
|
||||||
Labels: amv2.LabelSet{
|
|
||||||
"label2-1": "value1",
|
|
||||||
"label2-2": "value2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Labels: amv2.LabelSet{
|
|
||||||
"label1-1": "value1",
|
|
||||||
"label1-2": "value2",
|
|
||||||
},
|
|
||||||
Receiver: &amv2.Receiver{
|
|
||||||
Name: stringPtr("receiver identifier 2-1"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&amv2.AlertGroup{
|
|
||||||
Alerts: []*amv2.GettableAlert{
|
|
||||||
{
|
|
||||||
Annotations: amv2.LabelSet{
|
|
||||||
"annotation2-1": "value1",
|
|
||||||
"annotation2-2": "value2",
|
|
||||||
},
|
|
||||||
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
|
||||||
Fingerprint: stringPtr("fingerprint 2"),
|
|
||||||
Receivers: []*amv2.Receiver{
|
|
||||||
{
|
|
||||||
Name: stringPtr("receiver identifier 2-1"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: stringPtr("receiver identifier 2-2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
StartsAt: timePtr(strfmt.DateTime(now)),
|
|
||||||
Status: &amv2.AlertStatus{
|
|
||||||
InhibitedBy: []string{"inhibitedBy 2"},
|
|
||||||
SilencedBy: []string{"silencedBy 2"},
|
|
||||||
State: stringPtr(amv2.AlertStatusStateActive),
|
|
||||||
},
|
|
||||||
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
|
||||||
Alert: amv2.Alert{
|
|
||||||
GeneratorURL: strfmt.URI("a URL"),
|
|
||||||
Labels: amv2.LabelSet{
|
|
||||||
"label2-1": "value1",
|
|
||||||
"label2-2": "value2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Labels: amv2.LabelSet{
|
|
||||||
"label2-1": "value1",
|
|
||||||
"label2-2": "value2",
|
|
||||||
},
|
|
||||||
Receiver: &amv2.Receiver{
|
|
||||||
Name: stringPtr("receiver identifier 2-1"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return response.JSON(http.StatusOK, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mock AlertmanagerApiMock) RouteGetAMAlerts(c *models.ReqContext) response.Response {
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
mock.log.Info("RouteGetAMAlerts: ", "Recipient", recipient)
|
|
||||||
now := time.Now()
|
|
||||||
result := apimodels.GettableAlerts{
|
|
||||||
&amv2.GettableAlert{
|
|
||||||
Annotations: amv2.LabelSet{
|
|
||||||
"annotation1-1": "value1",
|
|
||||||
"annotation1-2": "value2",
|
|
||||||
},
|
|
||||||
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
|
||||||
Fingerprint: stringPtr("fingerprint 1"),
|
|
||||||
Receivers: []*amv2.Receiver{
|
|
||||||
{
|
|
||||||
Name: stringPtr("receiver identifier 1-1"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: stringPtr("receiver identifier 1-2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
StartsAt: timePtr(strfmt.DateTime(now)),
|
|
||||||
Status: &amv2.AlertStatus{
|
|
||||||
InhibitedBy: []string{"inhibitedBy 1"},
|
|
||||||
SilencedBy: []string{"silencedBy 1"},
|
|
||||||
State: stringPtr(amv2.AlertStatusStateActive),
|
|
||||||
},
|
|
||||||
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
|
||||||
Alert: amv2.Alert{
|
|
||||||
GeneratorURL: strfmt.URI("a URL"),
|
|
||||||
Labels: amv2.LabelSet{
|
|
||||||
"label1-1": "value1",
|
|
||||||
"label1-2": "value2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&amv2.GettableAlert{
|
|
||||||
Annotations: amv2.LabelSet{
|
|
||||||
"annotation2-1": "value1",
|
|
||||||
"annotation2-2": "value2",
|
|
||||||
},
|
|
||||||
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
|
||||||
Fingerprint: stringPtr("fingerprint 2"),
|
|
||||||
Receivers: []*amv2.Receiver{
|
|
||||||
{
|
|
||||||
Name: stringPtr("receiver identifier 2-1"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: stringPtr("receiver identifier 2-2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
StartsAt: timePtr(strfmt.DateTime(now)),
|
|
||||||
Status: &amv2.AlertStatus{
|
|
||||||
InhibitedBy: []string{"inhibitedBy 2"},
|
|
||||||
SilencedBy: []string{"silencedBy 2"},
|
|
||||||
State: stringPtr(amv2.AlertStatusStateActive),
|
|
||||||
},
|
|
||||||
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
|
||||||
Alert: amv2.Alert{
|
|
||||||
GeneratorURL: strfmt.URI("a URL"),
|
|
||||||
Labels: amv2.LabelSet{
|
|
||||||
"label2-1": "value1",
|
|
||||||
"label2-2": "value2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return response.JSON(http.StatusOK, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mock AlertmanagerApiMock) RouteGetSilence(c *models.ReqContext) response.Response {
|
|
||||||
silenceID := c.Params(":SilenceId")
|
|
||||||
mock.log.Info("RouteGetSilence: ", "SilenceId", silenceID)
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
mock.log.Info("RouteGetSilence: ", "Recipient", recipient)
|
|
||||||
now := time.Now()
|
|
||||||
result := apimodels.GettableSilence{
|
|
||||||
ID: stringPtr("id"),
|
|
||||||
Status: &amv2.SilenceStatus{
|
|
||||||
State: stringPtr("active"),
|
|
||||||
},
|
|
||||||
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
|
||||||
Silence: amv2.Silence{
|
|
||||||
Comment: stringPtr("comment"),
|
|
||||||
CreatedBy: stringPtr("created by"),
|
|
||||||
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
|
||||||
StartsAt: timePtr(strfmt.DateTime(now)),
|
|
||||||
Matchers: []*amv2.Matcher{
|
|
||||||
{
|
|
||||||
IsRegex: boolPtr(false),
|
|
||||||
Name: stringPtr("name"),
|
|
||||||
Value: stringPtr("value"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
IsRegex: boolPtr(false),
|
|
||||||
Name: stringPtr("name2"),
|
|
||||||
Value: stringPtr("value2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return response.JSON(http.StatusOK, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mock AlertmanagerApiMock) RouteGetSilences(c *models.ReqContext) response.Response {
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
mock.log.Info("RouteGetSilences: ", "Recipient", recipient)
|
|
||||||
now := time.Now()
|
|
||||||
result := apimodels.GettableSilences{
|
|
||||||
&amv2.GettableSilence{
|
|
||||||
ID: stringPtr("silence1"),
|
|
||||||
Status: &amv2.SilenceStatus{
|
|
||||||
State: stringPtr("active"),
|
|
||||||
},
|
|
||||||
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
|
||||||
Silence: amv2.Silence{
|
|
||||||
Comment: stringPtr("silence1 comment"),
|
|
||||||
CreatedBy: stringPtr("silence1 created by"),
|
|
||||||
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
|
||||||
StartsAt: timePtr(strfmt.DateTime(now)),
|
|
||||||
Matchers: []*amv2.Matcher{
|
|
||||||
{
|
|
||||||
IsRegex: boolPtr(false),
|
|
||||||
Name: stringPtr("silence1 name"),
|
|
||||||
Value: stringPtr("silence1 value"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
IsRegex: boolPtr(true),
|
|
||||||
Name: stringPtr("silence1 name2"),
|
|
||||||
Value: stringPtr("silence1 value2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&amv2.GettableSilence{
|
|
||||||
ID: stringPtr("silence2"),
|
|
||||||
Status: &amv2.SilenceStatus{
|
|
||||||
State: stringPtr("pending"),
|
|
||||||
},
|
|
||||||
UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))),
|
|
||||||
Silence: amv2.Silence{
|
|
||||||
Comment: stringPtr("silence2 comment"),
|
|
||||||
CreatedBy: stringPtr("silence2 created by"),
|
|
||||||
EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))),
|
|
||||||
StartsAt: timePtr(strfmt.DateTime(now)),
|
|
||||||
Matchers: []*amv2.Matcher{
|
|
||||||
{
|
|
||||||
IsRegex: boolPtr(false),
|
|
||||||
Name: stringPtr("silence2 name"),
|
|
||||||
Value: stringPtr("silence2 value"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
IsRegex: boolPtr(true),
|
|
||||||
Name: stringPtr("silence2 name2"),
|
|
||||||
Value: stringPtr("silence2 value2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return response.JSON(http.StatusOK, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mock AlertmanagerApiMock) RoutePostAlertingConfig(c *models.ReqContext, body apimodels.PostableUserConfig) response.Response {
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
mock.log.Info("RoutePostAlertingConfig: ", "Recipient", recipient)
|
|
||||||
mock.log.Info("RoutePostAlertingConfig: ", "body", body)
|
|
||||||
return response.JSON(http.StatusAccepted, util.DynMap{"message": "configuration created"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mock AlertmanagerApiMock) RoutePostAMAlerts(c *models.ReqContext, body apimodels.PostableAlerts) response.Response {
|
|
||||||
recipient := c.Params(":Recipient")
|
|
||||||
mock.log.Info("RoutePostAMAlerts: ", "Recipient", recipient)
|
|
||||||
mock.log.Info("RoutePostAMAlerts: ", "body", body)
|
|
||||||
return response.JSON(http.StatusOK, util.DynMap{"message": "alerts created"})
|
|
||||||
}
|
|
@ -38,7 +38,7 @@ func (am *ForkedAMSvc) getService(ctx *models.ReqContext) (AlertmanagerApiServic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *ForkedAMSvc) RouteCreateSilence(ctx *models.ReqContext, body apimodels.CreateSilenceParams) response.Response {
|
func (am *ForkedAMSvc) RouteCreateSilence(ctx *models.ReqContext, body apimodels.PostableSilence) response.Response {
|
||||||
s, err := am.getService(ctx)
|
s, err := am.getService(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(400, err.Error(), nil)
|
return response.Error(400, err.Error(), nil)
|
||||||
|
@ -32,7 +32,7 @@ func NewLotexAM(proxy *AlertingProxy, log log.Logger) *LotexAM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *LotexAM) RouteCreateSilence(ctx *models.ReqContext, silenceBody apimodels.CreateSilenceParams) response.Response {
|
func (am *LotexAM) RouteCreateSilence(ctx *models.ReqContext, silenceBody apimodels.PostableSilence) response.Response {
|
||||||
blob, err := json.Marshal(silenceBody)
|
blob, err := json.Marshal(silenceBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Failed marshal silence", err)
|
return response.Error(500, "Failed marshal silence", err)
|
||||||
|
@ -10,23 +10,109 @@ GET http://admin:admin@localhost:3000/alertmanager/{{lokiDatasourceID}}/config/a
|
|||||||
GET http://admin:admin@localhost:3000/alertmanager/{{prometheusDatasourceID}}/config/api/v1/alerts
|
GET http://admin:admin@localhost:3000/alertmanager/{{prometheusDatasourceID}}/config/api/v1/alerts
|
||||||
|
|
||||||
###
|
###
|
||||||
# grafana requests
|
# create AM configuration
|
||||||
GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts
|
|
||||||
|
|
||||||
###
|
|
||||||
DELETE http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts
|
|
||||||
|
|
||||||
###
|
|
||||||
POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts
|
POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
|
|
||||||
< ./post-user-config.json
|
< ./post-user-config.json
|
||||||
|
|
||||||
###
|
###
|
||||||
|
# get latest AM configuration
|
||||||
|
GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts
|
||||||
|
content-type: application/json
|
||||||
|
|
||||||
|
###
|
||||||
|
# delete AM configuration
|
||||||
|
DELETE http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts
|
||||||
|
|
||||||
|
###
|
||||||
|
# create AM alerts
|
||||||
|
POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/alerts
|
||||||
|
content-type: application/json
|
||||||
|
|
||||||
|
###
|
||||||
|
# get silences - no silences
|
||||||
|
GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences?Filter=foo="bar"&Filter=bar="foo"
|
||||||
|
|
||||||
|
###
|
||||||
|
# create silence
|
||||||
POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences
|
POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
|
|
||||||
< ./post-silence-data.json
|
{
|
||||||
|
"comment": "string",
|
||||||
|
"createdBy": "string",
|
||||||
|
"endsAt": "2023-03-31T14:17:04.419Z",
|
||||||
|
"matchers": [
|
||||||
|
{
|
||||||
|
"isRegex": true,
|
||||||
|
"name": "string",
|
||||||
|
"value": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startsAt": "2021-03-31T13:17:04.419Z"
|
||||||
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences
|
# update silence - does not exist
|
||||||
|
POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences
|
||||||
|
content-type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "something",
|
||||||
|
"comment": "string",
|
||||||
|
"createdBy": "string",
|
||||||
|
"endsAt": "2023-03-31T14:17:04.419Z",
|
||||||
|
"matchers": [
|
||||||
|
{
|
||||||
|
"isRegex": true,
|
||||||
|
"name": "string",
|
||||||
|
"value": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startsAt": "2021-03-31T13:17:04.419Z"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
# create silence - bad paylaad - start time must be before end time
|
||||||
|
POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences
|
||||||
|
content-type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "string",
|
||||||
|
"createdBy": "string",
|
||||||
|
"endsAt": "2019-03-31T14:17:04.419Z",
|
||||||
|
"matchers": [
|
||||||
|
{
|
||||||
|
"isRegex": true,
|
||||||
|
"name": "string",
|
||||||
|
"value": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startsAt": "2021-03-31T13:17:04.419Z"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
# get silences
|
||||||
|
# @name getSilences
|
||||||
|
GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
@silenceID = {{getSilences.response.body.$.[0].id}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# get silence
|
||||||
|
GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silence/{{silenceID}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# get silence - unknown
|
||||||
|
GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silence/unknown
|
||||||
|
|
||||||
|
###
|
||||||
|
# delete silence
|
||||||
|
DELETE http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silence/{{silenceID}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# delete silence - unknown
|
||||||
|
DELETE http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silence/unknown
|
||||||
|
@ -8,11 +8,13 @@
|
|||||||
"resolve_timeout": "1s",
|
"resolve_timeout": "1s",
|
||||||
"smtp_smarthost": ""
|
"smtp_smarthost": ""
|
||||||
},
|
},
|
||||||
"route": {},
|
"route": {
|
||||||
|
"receiver": "grafana_managed"
|
||||||
|
},
|
||||||
"templates": [],
|
"templates": [],
|
||||||
"receivers": [
|
"receivers": [
|
||||||
{
|
{
|
||||||
"name": "",
|
"name": "grafana_managed",
|
||||||
"grafana_managed_receiver_configs": [
|
"grafana_managed_receiver_configs": [
|
||||||
{
|
{
|
||||||
"uid": "alertmanager UID",
|
"uid": "alertmanager UID",
|
||||||
|
@ -35,10 +35,6 @@ func stringPtr(s string) *string {
|
|||||||
return &s
|
return &s
|
||||||
}
|
}
|
||||||
|
|
||||||
func boolPtr(b bool) *bool {
|
|
||||||
return &b
|
|
||||||
}
|
|
||||||
|
|
||||||
func backendType(ctx *models.ReqContext, cache datasources.CacheService) (apimodels.Backend, error) {
|
func backendType(ctx *models.ReqContext, cache datasources.CacheService) (apimodels.Backend, error) {
|
||||||
recipient := ctx.Params("Recipient")
|
recipient := ctx.Params("Recipient")
|
||||||
if recipient == apimodels.GrafanaBackend.String() {
|
if recipient == apimodels.GrafanaBackend.String() {
|
||||||
|
@ -2,6 +2,8 @@ package models
|
|||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
|
const AlertConfigurationVersion = 1
|
||||||
|
|
||||||
// AlertConfiguration represents a single version of the Alerting Engine Configuration.
|
// AlertConfiguration represents a single version of the Alerting Engine Configuration.
|
||||||
type AlertConfiguration struct {
|
type AlertConfiguration struct {
|
||||||
ID int64 `xorm:"pk autoincr 'id'"`
|
ID int64 `xorm:"pk autoincr 'id'"`
|
||||||
@ -13,7 +15,22 @@ type AlertConfiguration struct {
|
|||||||
|
|
||||||
// GetLatestAlertmanagerConfigurationQuery is the query to get the latest alertmanager configuration.
|
// GetLatestAlertmanagerConfigurationQuery is the query to get the latest alertmanager configuration.
|
||||||
type GetLatestAlertmanagerConfigurationQuery struct {
|
type GetLatestAlertmanagerConfigurationQuery struct {
|
||||||
|
Result *AlertConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAlertmanagerConfigurationQuery is the query to get the latest alertmanager configuration.
|
||||||
|
type GetAlertmanagerConfigurationQuery struct {
|
||||||
ID int64
|
ID int64
|
||||||
|
|
||||||
Result *AlertConfiguration
|
Result *AlertConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveAlertmanagerConfigurationCmd is the command to save an alertmanager configuration.
|
||||||
|
type SaveAlertmanagerConfigurationCmd struct {
|
||||||
|
AlertmanagerConfiguration string
|
||||||
|
ConfigurationVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteAlertmanagerConfigurationCmd struct {
|
||||||
|
ID int64
|
||||||
|
}
|
||||||
|
119
pkg/services/ngalert/models/silence.go
Normal file
119
pkg/services/ngalert/models/silence.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
|
||||||
|
amv2 "github.com/prometheus/alertmanager/api/v2/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrSilenceNotFound is an error for an unknown silence.
|
||||||
|
ErrSilenceNotFound = fmt.Errorf("could not find silence")
|
||||||
|
// ErrSilenceFailedGenerateUniqueUID is an error for failure to generate silence UID
|
||||||
|
ErrSilenceFailedGenerateUniqueUID = errors.New("failed to generate silence UID")
|
||||||
|
)
|
||||||
|
|
||||||
|
type SilenceSettings amv2.GettableSilence
|
||||||
|
|
||||||
|
type SilenceStatus amv2.SilenceStatus
|
||||||
|
|
||||||
|
// FromDB loads silence status stored in the database.
|
||||||
|
// FromDB is part of the xorm Conversion interface.
|
||||||
|
func (st *SilenceStatus) FromDB(b []byte) error {
|
||||||
|
str := string(b)
|
||||||
|
*st = SilenceStatus{State: &str}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDB serializes silence status to be stored in the database.
|
||||||
|
// ToDB is part of the xorm Conversion interface.
|
||||||
|
func (st *SilenceStatus) ToDB() ([]byte, error) {
|
||||||
|
return []byte(*st.State), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Matchers amv2.Matchers
|
||||||
|
|
||||||
|
// FromDB loads matchers stored in the database.
|
||||||
|
// FromDB is part of the xorm Conversion interface.
|
||||||
|
func (m *Matchers) FromDB(b []byte) error {
|
||||||
|
err := json.Unmarshal(b, &m)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to convert matchers from database: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDB serializes matchers to be stored in the database.
|
||||||
|
// ToDB is part of the xorm Conversion interface.
|
||||||
|
func (m *Matchers) ToDB() ([]byte, error) {
|
||||||
|
blobMatchers, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to convert matchers to send to the database: %w", err)
|
||||||
|
}
|
||||||
|
return blobMatchers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Silence struct {
|
||||||
|
ID int64 `xorm:"pk autoincr 'id'"`
|
||||||
|
OrgID int64 `xorm:"org_id" json:"orgId"`
|
||||||
|
UID string `xorm:"uid" json:"uid"`
|
||||||
|
Status SilenceStatus `json:"status"`
|
||||||
|
UpdatedAt strfmt.DateTime `json:"updatedAt"`
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
CreatedBy string `json:"createdBy"`
|
||||||
|
EndsAt strfmt.DateTime `json:"endsAt"`
|
||||||
|
Matchers Matchers `json:"matchers"`
|
||||||
|
StartsAt strfmt.DateTime `json:"startsAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Silence) ToGettableSilence() amv2.GettableSilence {
|
||||||
|
gettableSilence := amv2.GettableSilence{
|
||||||
|
ID: &s.UID,
|
||||||
|
Status: &amv2.SilenceStatus{State: s.Status.State},
|
||||||
|
UpdatedAt: &s.UpdatedAt,
|
||||||
|
}
|
||||||
|
gettableSilence.Comment = &s.Comment
|
||||||
|
gettableSilence.CreatedBy = &s.CreatedBy
|
||||||
|
gettableSilence.EndsAt = &s.EndsAt
|
||||||
|
gettableSilence.Matchers = amv2.Matchers(s.Matchers)
|
||||||
|
gettableSilence.StartsAt = &s.StartsAt
|
||||||
|
return gettableSilence
|
||||||
|
}
|
||||||
|
|
||||||
|
type SaveSilenceCommand struct {
|
||||||
|
amv2.Silence
|
||||||
|
UID string
|
||||||
|
OrgID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteSilenceByUIDCommand struct {
|
||||||
|
UID string
|
||||||
|
OrgID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteSilenceByIDCommand struct {
|
||||||
|
ID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSilenceByUIDQuery struct {
|
||||||
|
UID string
|
||||||
|
OrgID int64
|
||||||
|
|
||||||
|
Result *Silence
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSilenceByIDQuery struct {
|
||||||
|
ID int64
|
||||||
|
|
||||||
|
Result *Silence
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSilencesQuery struct {
|
||||||
|
OrgID int64
|
||||||
|
|
||||||
|
Result []*Silence
|
||||||
|
}
|
@ -81,6 +81,7 @@ func (ng *AlertNG) Init() error {
|
|||||||
Schedule: ng.schedule,
|
Schedule: ng.schedule,
|
||||||
DataProxy: ng.DataProxy,
|
DataProxy: ng.DataProxy,
|
||||||
Store: store,
|
Store: store,
|
||||||
|
AlertingStore: store,
|
||||||
Alertmanager: ng.Alertmanager,
|
Alertmanager: ng.Alertmanager,
|
||||||
}
|
}
|
||||||
api.RegisterAPIEndpoints()
|
api.RegisterAPIEndpoints()
|
||||||
@ -99,7 +100,6 @@ func (ng *AlertNG) IsDisabled() bool {
|
|||||||
if ng.Cfg == nil {
|
if ng.Cfg == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Check also about expressions?
|
|
||||||
return !ng.Cfg.IsNgAlertEnabled()
|
return !ng.Cfg.IsNgAlertEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,8 +109,11 @@ func (ng *AlertNG) AddMigration(mg *migrator.Migrator) {
|
|||||||
if ng.IsDisabled() {
|
if ng.IsDisabled() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
addAlertDefinitionMigrations(mg)
|
store.AddAlertDefinitionMigrations(mg, defaultIntervalSeconds)
|
||||||
addAlertDefinitionVersionMigrations(mg)
|
store.AddAlertDefinitionVersionMigrations(mg)
|
||||||
// Create alert_instance table
|
// Create alert_instance table
|
||||||
alertInstanceMigration(mg)
|
store.AlertInstanceMigration(mg)
|
||||||
|
|
||||||
|
// Create silence table
|
||||||
|
store.SilenceMigration(mg)
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,15 @@ package notifier
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/components/securejsondata"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
|
||||||
gokit_log "github.com/go-kit/kit/log"
|
gokit_log "github.com/go-kit/kit/log"
|
||||||
"github.com/grafana/alerting-api/pkg/api"
|
"github.com/grafana/alerting-api/pkg/api"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -20,7 +25,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels"
|
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
@ -61,7 +66,10 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (am *Alertmanager) IsDisabled() bool {
|
func (am *Alertmanager) IsDisabled() bool {
|
||||||
return !setting.AlertingEnabled || !setting.ExecuteAlerts
|
if am.Settings == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !am.Settings.IsNgAlertEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *Alertmanager) Init() (err error) {
|
func (am *Alertmanager) Init() (err error) {
|
||||||
@ -146,7 +154,7 @@ func (am *Alertmanager) SyncAndApplyConfigFromDatabase() error {
|
|||||||
|
|
||||||
func (am *Alertmanager) getConfigFromDatabase() (*api.PostableUserConfig, error) {
|
func (am *Alertmanager) getConfigFromDatabase() (*api.PostableUserConfig, error) {
|
||||||
// First, let's get the configuration we need from the database.
|
// First, let's get the configuration we need from the database.
|
||||||
q := &models.GetLatestAlertmanagerConfigurationQuery{}
|
q := &ngmodels.GetLatestAlertmanagerConfigurationQuery{}
|
||||||
if err := am.Store.GetLatestAlertmanagerConfiguration(q); err != nil {
|
if err := am.Store.GetLatestAlertmanagerConfiguration(q); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -233,7 +241,22 @@ func (am *Alertmanager) buildReceiverIntegrations(receiver *api.PostableApiRecei
|
|||||||
for i, r := range receiver.GrafanaManagedReceivers {
|
for i, r := range receiver.GrafanaManagedReceivers {
|
||||||
switch r.Type {
|
switch r.Type {
|
||||||
case "email":
|
case "email":
|
||||||
n, err := channels.NewEmailNotifier(r.Result)
|
frequency, err := time.ParseDuration(r.Frequency)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse receiver frequency %s, %w", r.Frequency, err)
|
||||||
|
}
|
||||||
|
notification := models.AlertNotification{
|
||||||
|
Uid: r.Uid,
|
||||||
|
Name: r.Name,
|
||||||
|
Type: r.Type,
|
||||||
|
IsDefault: r.IsDefault,
|
||||||
|
SendReminder: r.SendReminder,
|
||||||
|
DisableResolveMessage: r.DisableResolveMessage,
|
||||||
|
Frequency: frequency,
|
||||||
|
Settings: r.Settings,
|
||||||
|
SecureSettings: securejsondata.GetEncryptedJsonData(r.SecureSettings),
|
||||||
|
}
|
||||||
|
n, err := channels.NewEmailNotifier(¬ification)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PersistTemplates(cfg *api.PostableUserConfig, path string) ([]string, bool, error) {
|
func PersistTemplates(cfg *api.PostableUserConfig, path string) ([]string, bool, error) {
|
||||||
@ -78,7 +78,7 @@ func PersistTemplates(cfg *api.PostableUserConfig, path string) ([]string, bool,
|
|||||||
func Load(rawConfig string) (*api.PostableUserConfig, error) {
|
func Load(rawConfig string) (*api.PostableUserConfig, error) {
|
||||||
cfg := &api.PostableUserConfig{}
|
cfg := &api.PostableUserConfig{}
|
||||||
|
|
||||||
if err := yaml.UnmarshalStrict([]byte(rawConfig), cfg); err != nil {
|
if err := yaml.Unmarshal([]byte(rawConfig), cfg); err != nil {
|
||||||
return nil, errors.Wrap(err, "unable to parse Alertmanager configuration")
|
return nil, errors.Wrap(err, "unable to parse Alertmanager configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,22 +13,71 @@ var (
|
|||||||
ErrNoAlertmanagerConfiguration = fmt.Errorf("could not find an Alertmanager configuration")
|
ErrNoAlertmanagerConfiguration = fmt.Errorf("could not find an Alertmanager configuration")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func getAlertmanagerConfigurationByID(sess *sqlstore.DBSession, id int64) (*models.AlertConfiguration, error) {
|
||||||
|
c := &models.AlertConfiguration{}
|
||||||
|
|
||||||
|
has, err := sess.ID(id).Get(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return nil, ErrNoAlertmanagerConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLatestAlertmanagerConfiguration(sess *sqlstore.DBSession) (*models.AlertConfiguration, error) {
|
||||||
|
c := &models.AlertConfiguration{}
|
||||||
|
// The ID is already an auto incremental column, using the ID as an order should guarantee the latest.
|
||||||
|
ok, err := sess.Desc("id").Limit(1).Get(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrNoAlertmanagerConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetLatestAlertmanagerConfiguration returns the lastest version of the alertmanager configuration.
|
// GetLatestAlertmanagerConfiguration returns the lastest version of the alertmanager configuration.
|
||||||
// It returns ErrNoAlertmanagerConfiguration if no configuration is found.
|
// It returns ErrNoAlertmanagerConfiguration if no configuration is found.
|
||||||
func (st DBstore) GetLatestAlertmanagerConfiguration(query *models.GetLatestAlertmanagerConfigurationQuery) error {
|
func (st DBstore) GetLatestAlertmanagerConfiguration(query *models.GetLatestAlertmanagerConfigurationQuery) error {
|
||||||
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
c := &models.AlertConfiguration{}
|
c, err := getLatestAlertmanagerConfiguration(sess)
|
||||||
// The ID is already an auto incremental column, using the ID as an order should guarantee the latest.
|
|
||||||
ok, err := sess.Desc("id").Limit(1).Get(c)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return ErrNoAlertmanagerConfiguration
|
|
||||||
}
|
|
||||||
|
|
||||||
query.Result = c
|
query.Result = c
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAlertmanagerConfiguration returns the alertmanager configuration identified by the query.
|
||||||
|
// It returns ErrNoAlertmanagerConfiguration if no such configuration is found.
|
||||||
|
func (st DBstore) GetAlertmanagerConfiguration(query *models.GetAlertmanagerConfigurationQuery) error {
|
||||||
|
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
|
c, err := getAlertmanagerConfigurationByID(sess, query.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
query.Result = c
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveAlertmanagerConfiguration creates an alertmanager configuration.
|
||||||
|
func (st DBstore) SaveAlertmanagerConfiguration(cmd *models.SaveAlertmanagerConfigurationCmd) error {
|
||||||
|
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
|
config := models.AlertConfiguration{
|
||||||
|
AlertmanagerConfiguration: cmd.AlertmanagerConfiguration,
|
||||||
|
ConfigurationVersion: cmd.ConfigurationVersion,
|
||||||
|
}
|
||||||
|
if _, err := sess.Insert(config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -26,20 +26,28 @@ var ErrEmptyTitleError = errors.New("title is empty")
|
|||||||
type Store interface {
|
type Store interface {
|
||||||
DeleteAlertDefinitionByUID(*models.DeleteAlertDefinitionByUIDCommand) error
|
DeleteAlertDefinitionByUID(*models.DeleteAlertDefinitionByUIDCommand) error
|
||||||
GetAlertDefinitionByUID(*models.GetAlertDefinitionByUIDQuery) error
|
GetAlertDefinitionByUID(*models.GetAlertDefinitionByUIDQuery) error
|
||||||
GetAlertDefinitions(query *models.ListAlertDefinitionsQuery) error
|
GetAlertDefinitions(*models.ListAlertDefinitionsQuery) error
|
||||||
GetOrgAlertDefinitions(query *models.ListAlertDefinitionsQuery) error
|
GetOrgAlertDefinitions(*models.ListAlertDefinitionsQuery) error
|
||||||
SaveAlertDefinition(*models.SaveAlertDefinitionCommand) error
|
SaveAlertDefinition(*models.SaveAlertDefinitionCommand) error
|
||||||
UpdateAlertDefinition(*models.UpdateAlertDefinitionCommand) error
|
UpdateAlertDefinition(*models.UpdateAlertDefinitionCommand) error
|
||||||
GetAlertInstance(*models.GetAlertInstanceQuery) error
|
GetAlertInstance(*models.GetAlertInstanceQuery) error
|
||||||
ListAlertInstances(cmd *models.ListAlertInstancesQuery) error
|
ListAlertInstances(*models.ListAlertInstancesQuery) error
|
||||||
SaveAlertInstance(cmd *models.SaveAlertInstanceCommand) error
|
SaveAlertInstance(*models.SaveAlertInstanceCommand) error
|
||||||
ValidateAlertDefinition(*models.AlertDefinition, bool) error
|
ValidateAlertDefinition(*models.AlertDefinition, bool) error
|
||||||
UpdateAlertDefinitionPaused(*models.UpdateAlertDefinitionPausedCommand) error
|
UpdateAlertDefinitionPaused(*models.UpdateAlertDefinitionPausedCommand) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// AlertingStore is the database interface used by the Alertmanager service.
|
// AlertingStore is the database interface used by the Alertmanager service.
|
||||||
type AlertingStore interface {
|
type AlertingStore interface {
|
||||||
GetLatestAlertmanagerConfiguration(cmd *models.GetLatestAlertmanagerConfigurationQuery) error
|
GetLatestAlertmanagerConfiguration(*models.GetLatestAlertmanagerConfigurationQuery) error
|
||||||
|
GetAlertmanagerConfiguration(*models.GetAlertmanagerConfigurationQuery) error
|
||||||
|
SaveAlertmanagerConfiguration(*models.SaveAlertmanagerConfigurationCmd) error
|
||||||
|
GetOrgSilences(*models.GetSilencesQuery) error
|
||||||
|
GetSilenceByUID(*models.GetSilenceByUIDQuery) error
|
||||||
|
GetSilenceByID(*models.GetSilenceByIDQuery) error
|
||||||
|
SaveSilence(*models.SaveSilenceCommand) error
|
||||||
|
DeleteSilenceByUID(*models.DeleteSilenceByUIDCommand) error
|
||||||
|
DeleteSilenceByID(*models.DeleteSilenceByIDCommand) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// DBstore stores the alert definitions and instances in the database.
|
// DBstore stores the alert definitions and instances in the database.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package ngalert
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
)
|
)
|
||||||
|
|
||||||
func addAlertDefinitionMigrations(mg *migrator.Migrator) {
|
func AddAlertDefinitionMigrations(mg *migrator.Migrator, defaultIntervalSeconds int64) {
|
||||||
mg.AddMigration("delete alert_definition table", migrator.NewDropTableMigration("alert_definition"))
|
mg.AddMigration("delete alert_definition table", migrator.NewDropTableMigration("alert_definition"))
|
||||||
|
|
||||||
alertDefinition := migrator.Table{
|
alertDefinition := migrator.Table{
|
||||||
@ -52,7 +52,7 @@ func addAlertDefinitionMigrations(mg *migrator.Migrator) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func addAlertDefinitionVersionMigrations(mg *migrator.Migrator) {
|
func AddAlertDefinitionVersionMigrations(mg *migrator.Migrator) {
|
||||||
mg.AddMigration("delete alert_definition_version table", migrator.NewDropTableMigration("alert_definition_version"))
|
mg.AddMigration("delete alert_definition_version table", migrator.NewDropTableMigration("alert_definition_version"))
|
||||||
|
|
||||||
alertDefinitionVersion := migrator.Table{
|
alertDefinitionVersion := migrator.Table{
|
||||||
@ -83,7 +83,7 @@ func addAlertDefinitionVersionMigrations(mg *migrator.Migrator) {
|
|||||||
Mysql("ALTER TABLE alert_definition_version MODIFY data MEDIUMTEXT;"))
|
Mysql("ALTER TABLE alert_definition_version MODIFY data MEDIUMTEXT;"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func alertInstanceMigration(mg *migrator.Migrator) {
|
func AlertInstanceMigration(mg *migrator.Migrator) {
|
||||||
alertInstance := migrator.Table{
|
alertInstance := migrator.Table{
|
||||||
Name: "alert_instance",
|
Name: "alert_instance",
|
||||||
Columns: []*migrator.Column{
|
Columns: []*migrator.Column{
|
||||||
@ -107,3 +107,27 @@ func alertInstanceMigration(mg *migrator.Migrator) {
|
|||||||
mg.AddMigration("add index in alert_instance table on def_org_id, def_uid and current_state columns", migrator.NewAddIndexMigration(alertInstance, alertInstance.Indices[0]))
|
mg.AddMigration("add index in alert_instance table on def_org_id, def_uid and current_state columns", migrator.NewAddIndexMigration(alertInstance, alertInstance.Indices[0]))
|
||||||
mg.AddMigration("add index in alert_instance table on def_org_id, current_state columns", migrator.NewAddIndexMigration(alertInstance, alertInstance.Indices[1]))
|
mg.AddMigration("add index in alert_instance table on def_org_id, current_state columns", migrator.NewAddIndexMigration(alertInstance, alertInstance.Indices[1]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SilenceMigration(mg *migrator.Migrator) {
|
||||||
|
silence := migrator.Table{
|
||||||
|
Name: "silence",
|
||||||
|
Columns: []*migrator.Column{
|
||||||
|
{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||||
|
{Name: "org_id", Type: migrator.DB_BigInt, Nullable: false},
|
||||||
|
{Name: "uid", Type: migrator.DB_NVarchar, Length: 190, Nullable: false, Default: "0"},
|
||||||
|
{Name: "comment", Type: migrator.DB_NVarchar, Length: 190, Nullable: true},
|
||||||
|
{Name: "created_by", Type: migrator.DB_NVarchar, Length: 190, Nullable: true},
|
||||||
|
{Name: "matchers", Type: migrator.DB_Text, Nullable: false},
|
||||||
|
{Name: "ends_at", Type: migrator.DB_DateTime, Nullable: false},
|
||||||
|
{Name: "starts_at", Type: migrator.DB_DateTime, Nullable: false},
|
||||||
|
{Name: "updated_at", Type: migrator.DB_DateTime, Nullable: true},
|
||||||
|
{Name: "status", Type: migrator.DB_NVarchar, Length: 8, Nullable: false},
|
||||||
|
},
|
||||||
|
Indices: []*migrator.Index{
|
||||||
|
{Cols: []string{"org_id", "uid"}, Type: migrator.IndexType},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mg.AddMigration("create_silence_table", migrator.NewAddTableMigration(silence))
|
||||||
|
mg.AddMigration("add unique index in silence on org_id and uid columns", migrator.NewAddIndexMigration(silence, silence.Indices[0]))
|
||||||
|
}
|
165
pkg/services/ngalert/store/silence.go
Normal file
165
pkg/services/ngalert/store/silence.go
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
amv2 "github.com/prometheus/alertmanager/api/v2/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getSilenceByUID(sess *sqlstore.DBSession, silenceUID string, orgID int64) (*models.Silence, error) {
|
||||||
|
silence := &models.Silence{
|
||||||
|
OrgID: orgID,
|
||||||
|
UID: silenceUID,
|
||||||
|
}
|
||||||
|
has, err := sess.Get(silence)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return nil, models.ErrSilenceNotFound
|
||||||
|
}
|
||||||
|
return silence, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSilenceByID(sess *sqlstore.DBSession, id int64) (*models.Silence, error) {
|
||||||
|
silence := &models.Silence{}
|
||||||
|
|
||||||
|
has, err := sess.ID(id).Get(silence)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return nil, ErrNoAlertmanagerConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
return silence, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st DBstore) GetOrgSilences(query *models.GetSilencesQuery) error {
|
||||||
|
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
|
silences := make([]*models.Silence, 0)
|
||||||
|
q := "SELECT * FROM silence WHERE org_id = ?"
|
||||||
|
if err := sess.SQL(q, query.OrgID).Find(&silences); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
query.Result = silences
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st DBstore) GetSilenceByUID(query *models.GetSilenceByUIDQuery) error {
|
||||||
|
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
|
silence, err := getSilenceByUID(sess, query.UID, query.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
query.Result = silence
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st DBstore) GetSilenceByID(query *models.GetSilenceByIDQuery) error {
|
||||||
|
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
|
silence, err := getSilenceByID(sess, query.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
query.Result = silence
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st DBstore) DeleteSilenceByUID(cmd *models.DeleteSilenceByUIDCommand) error {
|
||||||
|
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
|
_, err := getSilenceByUID(sess, cmd.UID, cmd.OrgID)
|
||||||
|
if err != nil && errors.Is(err, models.ErrSilenceNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = sess.Exec("DELETE FROM silence WHERE uid = ? AND org_id = ?", cmd.UID, cmd.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st DBstore) DeleteSilenceByID(cmd *models.DeleteSilenceByIDCommand) error {
|
||||||
|
return st.SQLStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
|
_, err := getSilenceByID(sess, cmd.ID)
|
||||||
|
if err != nil && errors.Is(err, models.ErrSilenceNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = sess.Exec("DELETE FROM silence WHERE id ?", cmd.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st DBstore) SaveSilence(cmd *models.SaveSilenceCommand) error {
|
||||||
|
return st.SQLStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
|
var firstSeen bool
|
||||||
|
existingSilence, err := getSilenceByUID(sess, cmd.UID, cmd.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, models.ErrSilenceNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
firstSeen = true
|
||||||
|
}
|
||||||
|
|
||||||
|
statusPending := amv2.SilenceStatusStatePending
|
||||||
|
updatedAt := strfmt.DateTime(TimeNow())
|
||||||
|
|
||||||
|
silenceModel := models.Silence{
|
||||||
|
OrgID: cmd.OrgID,
|
||||||
|
UID: cmd.UID,
|
||||||
|
Status: models.SilenceStatus{State: &statusPending},
|
||||||
|
UpdatedAt: updatedAt,
|
||||||
|
Comment: *cmd.Comment,
|
||||||
|
CreatedBy: *cmd.CreatedBy,
|
||||||
|
EndsAt: *cmd.EndsAt,
|
||||||
|
Matchers: models.Matchers(cmd.Matchers),
|
||||||
|
StartsAt: *cmd.StartsAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch firstSeen {
|
||||||
|
case true:
|
||||||
|
if _, err := sess.Insert(&silenceModel); err != nil {
|
||||||
|
return fmt.Errorf("failed to insert silence: %w", err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if _, err := sess.ID(existingSilence.ID).Update(&silenceModel); err != nil {
|
||||||
|
return fmt.Errorf("failed to update silence: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateNewSilenceUID(sess *sqlstore.DBSession, orgID int64) (string, error) {
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
uid := util.GenerateShortUID()
|
||||||
|
|
||||||
|
exists, err := sess.Where("org_id=? AND uid=?", orgID, uid).Get(&models.Silence{})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return uid, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", models.ErrSilenceFailedGenerateUniqueUID
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user