2021-03-24 09:20:44 -05:00
|
|
|
package notifier
|
|
|
|
|
|
|
|
import (
|
2021-04-23 09:19:03 -05:00
|
|
|
"context"
|
2021-04-22 10:12:18 -05:00
|
|
|
"errors"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2021-04-27 06:33:22 -05:00
|
|
|
"sort"
|
2021-03-24 09:20:44 -05:00
|
|
|
"testing"
|
2021-04-22 10:12:18 -05:00
|
|
|
"time"
|
2021-03-24 09:20:44 -05:00
|
|
|
|
2021-04-23 09:19:03 -05:00
|
|
|
gokit_log "github.com/go-kit/kit/log"
|
2021-04-22 10:12:18 -05:00
|
|
|
"github.com/go-openapi/strfmt"
|
2021-10-07 09:33:50 -05:00
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
|
|
"github.com/grafana/grafana/pkg/services/encryption/ossencryption"
|
2021-04-22 10:12:18 -05:00
|
|
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
2021-05-17 12:06:47 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/logging"
|
2021-04-30 11:28:06 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
2021-05-13 13:01:38 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
2021-04-13 07:02:44 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2021-10-07 09:33:50 -05:00
|
|
|
"github.com/prometheus/alertmanager/api/v2/models"
|
|
|
|
"github.com/prometheus/alertmanager/provider/mem"
|
|
|
|
"github.com/prometheus/alertmanager/types"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
"github.com/prometheus/common/model"
|
|
|
|
"github.com/stretchr/testify/require"
|
2021-03-24 09:20:44 -05:00
|
|
|
)
|
|
|
|
|
2021-05-13 13:01:38 -05:00
|
|
|
func setupAMTest(t *testing.T) *Alertmanager {
|
2021-05-13 05:38:12 -05:00
|
|
|
dir, err := ioutil.TempDir("", "")
|
|
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
|
|
require.NoError(t, os.RemoveAll(dir))
|
|
|
|
})
|
2021-05-13 13:01:38 -05:00
|
|
|
cfg := &setting.Cfg{
|
2021-05-13 05:38:12 -05:00
|
|
|
DataPath: dir,
|
|
|
|
}
|
2021-05-13 13:01:38 -05:00
|
|
|
|
2021-09-14 06:55:01 -05:00
|
|
|
m := metrics.NewAlertmanagerMetrics(prometheus.NewRegistry())
|
2021-05-13 13:01:38 -05:00
|
|
|
sqlStore := sqlstore.InitTestDB(t)
|
2021-09-14 06:55:01 -05:00
|
|
|
s := &store.DBstore{
|
2021-09-28 05:00:16 -05:00
|
|
|
BaseInterval: 10 * time.Second,
|
|
|
|
DefaultInterval: 60 * time.Second,
|
|
|
|
SQLStore: sqlStore,
|
|
|
|
Logger: log.New("alertmanager-test"),
|
2021-05-13 13:01:38 -05:00
|
|
|
}
|
|
|
|
|
2021-09-09 11:25:22 -05:00
|
|
|
kvStore := newFakeKVStore(t)
|
2021-10-07 09:33:50 -05:00
|
|
|
decryptFn := ossencryption.ProvideService().GetDecryptedValue
|
|
|
|
am, err := newAlertmanager(1, cfg, s, kvStore, &NilPeer{}, decryptFn, m)
|
2021-05-13 13:01:38 -05:00
|
|
|
require.NoError(t, err)
|
|
|
|
return am
|
|
|
|
}
|
|
|
|
|
2021-04-22 10:12:18 -05:00
|
|
|
func TestPutAlert(t *testing.T) {
|
2021-05-13 13:01:38 -05:00
|
|
|
am := setupAMTest(t)
|
2021-04-22 10:12:18 -05:00
|
|
|
|
|
|
|
startTime := time.Now()
|
|
|
|
endTime := startTime.Add(2 * time.Hour)
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
title string
|
|
|
|
postableAlerts apimodels.PostableAlerts
|
|
|
|
expAlerts func(now time.Time) []*types.Alert
|
|
|
|
expError *AlertValidationError
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
title: "Valid alerts with different start/end set",
|
|
|
|
postableAlerts: apimodels.PostableAlerts{
|
|
|
|
PostableAlerts: []models.PostableAlert{
|
|
|
|
{ // Start and end set.
|
|
|
|
Annotations: models.LabelSet{"msg": "Alert1 annotation"},
|
|
|
|
Alert: models.Alert{
|
|
|
|
Labels: models.LabelSet{"alertname": "Alert1"},
|
|
|
|
GeneratorURL: "http://localhost/url1",
|
|
|
|
},
|
|
|
|
StartsAt: strfmt.DateTime(startTime),
|
|
|
|
EndsAt: strfmt.DateTime(endTime),
|
|
|
|
}, { // Only end is set.
|
|
|
|
Annotations: models.LabelSet{"msg": "Alert2 annotation"},
|
|
|
|
Alert: models.Alert{
|
|
|
|
Labels: models.LabelSet{"alertname": "Alert2"},
|
|
|
|
GeneratorURL: "http://localhost/url2",
|
|
|
|
},
|
|
|
|
StartsAt: strfmt.DateTime{},
|
|
|
|
EndsAt: strfmt.DateTime(endTime),
|
|
|
|
}, { // Only start is set.
|
|
|
|
Annotations: models.LabelSet{"msg": "Alert3 annotation"},
|
|
|
|
Alert: models.Alert{
|
|
|
|
Labels: models.LabelSet{"alertname": "Alert3"},
|
|
|
|
GeneratorURL: "http://localhost/url3",
|
|
|
|
},
|
|
|
|
StartsAt: strfmt.DateTime(startTime),
|
|
|
|
EndsAt: strfmt.DateTime{},
|
|
|
|
}, { // Both start and end are not set.
|
|
|
|
Annotations: models.LabelSet{"msg": "Alert4 annotation"},
|
|
|
|
Alert: models.Alert{
|
|
|
|
Labels: models.LabelSet{"alertname": "Alert4"},
|
|
|
|
GeneratorURL: "http://localhost/url4",
|
|
|
|
},
|
|
|
|
StartsAt: strfmt.DateTime{},
|
|
|
|
EndsAt: strfmt.DateTime{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expAlerts: func(now time.Time) []*types.Alert {
|
|
|
|
return []*types.Alert{
|
|
|
|
{
|
|
|
|
Alert: model.Alert{
|
|
|
|
Annotations: model.LabelSet{"msg": "Alert1 annotation"},
|
|
|
|
Labels: model.LabelSet{"alertname": "Alert1"},
|
|
|
|
StartsAt: startTime,
|
|
|
|
EndsAt: endTime,
|
|
|
|
GeneratorURL: "http://localhost/url1",
|
|
|
|
},
|
|
|
|
UpdatedAt: now,
|
|
|
|
}, {
|
|
|
|
Alert: model.Alert{
|
|
|
|
Annotations: model.LabelSet{"msg": "Alert2 annotation"},
|
|
|
|
Labels: model.LabelSet{"alertname": "Alert2"},
|
|
|
|
StartsAt: endTime,
|
|
|
|
EndsAt: endTime,
|
|
|
|
GeneratorURL: "http://localhost/url2",
|
|
|
|
},
|
|
|
|
UpdatedAt: now,
|
|
|
|
}, {
|
|
|
|
Alert: model.Alert{
|
|
|
|
Annotations: model.LabelSet{"msg": "Alert3 annotation"},
|
|
|
|
Labels: model.LabelSet{"alertname": "Alert3"},
|
|
|
|
StartsAt: startTime,
|
|
|
|
EndsAt: now.Add(defaultResolveTimeout),
|
|
|
|
GeneratorURL: "http://localhost/url3",
|
|
|
|
},
|
|
|
|
UpdatedAt: now,
|
|
|
|
Timeout: true,
|
|
|
|
}, {
|
|
|
|
Alert: model.Alert{
|
|
|
|
Annotations: model.LabelSet{"msg": "Alert4 annotation"},
|
|
|
|
Labels: model.LabelSet{"alertname": "Alert4"},
|
|
|
|
StartsAt: now,
|
|
|
|
EndsAt: now.Add(defaultResolveTimeout),
|
|
|
|
GeneratorURL: "http://localhost/url4",
|
|
|
|
},
|
|
|
|
UpdatedAt: now,
|
|
|
|
Timeout: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
title: "Removing empty labels and annotations",
|
|
|
|
postableAlerts: apimodels.PostableAlerts{
|
|
|
|
PostableAlerts: []models.PostableAlert{
|
|
|
|
{
|
|
|
|
Annotations: models.LabelSet{"msg": "Alert4 annotation", "empty": ""},
|
|
|
|
Alert: models.Alert{
|
|
|
|
Labels: models.LabelSet{"alertname": "Alert4", "emptylabel": ""},
|
|
|
|
GeneratorURL: "http://localhost/url1",
|
|
|
|
},
|
|
|
|
StartsAt: strfmt.DateTime{},
|
|
|
|
EndsAt: strfmt.DateTime{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expAlerts: func(now time.Time) []*types.Alert {
|
|
|
|
return []*types.Alert{
|
|
|
|
{
|
|
|
|
Alert: model.Alert{
|
|
|
|
Annotations: model.LabelSet{"msg": "Alert4 annotation"},
|
|
|
|
Labels: model.LabelSet{"alertname": "Alert4"},
|
|
|
|
StartsAt: now,
|
|
|
|
EndsAt: now.Add(defaultResolveTimeout),
|
|
|
|
GeneratorURL: "http://localhost/url1",
|
|
|
|
},
|
|
|
|
UpdatedAt: now,
|
|
|
|
Timeout: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
2021-07-08 07:56:09 -05:00
|
|
|
}, {
|
|
|
|
title: "Allow spaces in label and annotation name",
|
|
|
|
postableAlerts: apimodels.PostableAlerts{
|
|
|
|
PostableAlerts: []models.PostableAlert{
|
|
|
|
{
|
|
|
|
Annotations: models.LabelSet{"Dashboard URL": "http://localhost:3000"},
|
|
|
|
Alert: models.Alert{
|
|
|
|
Labels: models.LabelSet{"alertname": "Alert4", "Spaced Label": "works"},
|
|
|
|
GeneratorURL: "http://localhost/url1",
|
|
|
|
},
|
|
|
|
StartsAt: strfmt.DateTime{},
|
|
|
|
EndsAt: strfmt.DateTime{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expAlerts: func(now time.Time) []*types.Alert {
|
|
|
|
return []*types.Alert{
|
|
|
|
{
|
|
|
|
Alert: model.Alert{
|
|
|
|
Annotations: model.LabelSet{"Dashboard URL": "http://localhost:3000"},
|
|
|
|
Labels: model.LabelSet{"alertname": "Alert4", "Spaced Label": "works"},
|
|
|
|
StartsAt: now,
|
|
|
|
EndsAt: now.Add(defaultResolveTimeout),
|
|
|
|
GeneratorURL: "http://localhost/url1",
|
|
|
|
},
|
|
|
|
UpdatedAt: now,
|
|
|
|
Timeout: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
2021-04-22 10:12:18 -05:00
|
|
|
}, {
|
2021-10-04 08:06:40 -05:00
|
|
|
title: "Special characters in labels",
|
2021-04-22 10:12:18 -05:00
|
|
|
postableAlerts: apimodels.PostableAlerts{
|
|
|
|
PostableAlerts: []models.PostableAlert{
|
|
|
|
{
|
|
|
|
Alert: models.Alert{
|
2021-10-04 08:06:40 -05:00
|
|
|
Labels: models.LabelSet{"alertname$": "Alert1", "az3-- __...++!!!£@@312312": "1"},
|
2021-04-22 10:12:18 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2021-10-04 08:06:40 -05:00
|
|
|
expAlerts: func(now time.Time) []*types.Alert {
|
|
|
|
return []*types.Alert{
|
2021-04-22 10:12:18 -05:00
|
|
|
{
|
2021-10-04 08:06:40 -05:00
|
|
|
Alert: model.Alert{
|
|
|
|
Labels: model.LabelSet{"alertname$": "Alert1", "az3-- __...++!!!£@@312312": "1"},
|
|
|
|
Annotations: model.LabelSet{},
|
|
|
|
StartsAt: now,
|
|
|
|
EndsAt: now.Add(defaultResolveTimeout),
|
|
|
|
GeneratorURL: "",
|
2021-04-22 10:12:18 -05:00
|
|
|
},
|
2021-10-04 08:06:40 -05:00
|
|
|
UpdatedAt: now,
|
|
|
|
Timeout: true,
|
2021-04-22 10:12:18 -05:00
|
|
|
},
|
2021-10-04 08:06:40 -05:00
|
|
|
}
|
2021-04-22 10:12:18 -05:00
|
|
|
},
|
|
|
|
}, {
|
2021-10-04 08:06:40 -05:00
|
|
|
title: "Special characters in annotations",
|
2021-04-22 10:12:18 -05:00
|
|
|
postableAlerts: apimodels.PostableAlerts{
|
|
|
|
PostableAlerts: []models.PostableAlert{
|
|
|
|
{
|
2021-10-04 08:06:40 -05:00
|
|
|
Annotations: models.LabelSet{"az3-- __...++!!!£@@312312": "Alert4 annotation"},
|
2021-04-22 10:12:18 -05:00
|
|
|
Alert: models.Alert{
|
2021-10-04 08:06:40 -05:00
|
|
|
Labels: models.LabelSet{"alertname": "Alert4"},
|
2021-04-22 10:12:18 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2021-10-04 08:06:40 -05:00
|
|
|
expAlerts: func(now time.Time) []*types.Alert {
|
|
|
|
return []*types.Alert{
|
2021-04-22 10:12:18 -05:00
|
|
|
{
|
2021-10-04 08:06:40 -05:00
|
|
|
Alert: model.Alert{
|
|
|
|
Labels: model.LabelSet{"alertname": "Alert4"},
|
|
|
|
Annotations: model.LabelSet{"az3-- __...++!!!£@@312312": "Alert4 annotation"},
|
|
|
|
StartsAt: now,
|
|
|
|
EndsAt: now.Add(defaultResolveTimeout),
|
|
|
|
GeneratorURL: "",
|
2021-04-22 10:12:18 -05:00
|
|
|
},
|
2021-10-04 08:06:40 -05:00
|
|
|
UpdatedAt: now,
|
|
|
|
Timeout: true,
|
2021-04-22 10:12:18 -05:00
|
|
|
},
|
2021-10-04 08:06:40 -05:00
|
|
|
}
|
2021-04-22 10:12:18 -05:00
|
|
|
},
|
|
|
|
}, {
|
|
|
|
title: "No labels after removing empty",
|
|
|
|
postableAlerts: apimodels.PostableAlerts{
|
|
|
|
PostableAlerts: []models.PostableAlert{
|
|
|
|
{
|
|
|
|
Alert: models.Alert{
|
|
|
|
Labels: models.LabelSet{"alertname": ""},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expError: &AlertValidationError{
|
|
|
|
Alerts: []models.PostableAlert{
|
|
|
|
{
|
|
|
|
Alert: models.Alert{
|
|
|
|
Labels: models.LabelSet{"alertname": ""},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Errors: []error{errors.New("at least one label pair required")},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
title: "Start should be before end",
|
|
|
|
postableAlerts: apimodels.PostableAlerts{
|
|
|
|
PostableAlerts: []models.PostableAlert{
|
|
|
|
{
|
|
|
|
Alert: models.Alert{
|
|
|
|
Labels: models.LabelSet{"alertname": ""},
|
|
|
|
},
|
|
|
|
StartsAt: strfmt.DateTime(endTime),
|
|
|
|
EndsAt: strfmt.DateTime(startTime),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expError: &AlertValidationError{
|
|
|
|
Alerts: []models.PostableAlert{
|
|
|
|
{
|
|
|
|
Alert: models.Alert{
|
|
|
|
Labels: models.LabelSet{"alertname": ""},
|
|
|
|
},
|
|
|
|
StartsAt: strfmt.DateTime(endTime),
|
|
|
|
EndsAt: strfmt.DateTime(startTime),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Errors: []error{errors.New("start time must be before end time")},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
2021-05-13 13:01:38 -05:00
|
|
|
var err error
|
2021-04-22 10:12:18 -05:00
|
|
|
t.Run(c.title, func(t *testing.T) {
|
2021-04-23 09:19:03 -05:00
|
|
|
r := prometheus.NewRegistry()
|
|
|
|
am.marker = types.NewMarker(r)
|
2021-09-09 11:25:22 -05:00
|
|
|
am.alerts, err = mem.NewAlerts(context.Background(), am.marker, 15*time.Minute, nil, gokit_log.NewLogfmtLogger(logging.NewWrapper(am.logger)))
|
2021-04-23 09:19:03 -05:00
|
|
|
require.NoError(t, err)
|
2021-04-22 10:12:18 -05:00
|
|
|
|
2021-04-23 09:19:03 -05:00
|
|
|
alerts := []*types.Alert{}
|
2021-04-22 10:12:18 -05:00
|
|
|
err := am.PutAlerts(c.postableAlerts)
|
|
|
|
if c.expError != nil {
|
|
|
|
require.Error(t, err)
|
|
|
|
require.Equal(t, c.expError, err)
|
2021-04-23 09:19:03 -05:00
|
|
|
require.Equal(t, 0, len(alerts))
|
2021-04-22 10:12:18 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2021-04-23 09:19:03 -05:00
|
|
|
iter := am.alerts.GetPending()
|
|
|
|
defer iter.Close()
|
|
|
|
for a := range iter.Next() {
|
|
|
|
alerts = append(alerts, a)
|
|
|
|
}
|
|
|
|
|
2021-04-22 10:12:18 -05:00
|
|
|
// We take the "now" time from one of the UpdatedAt.
|
2021-04-23 09:19:03 -05:00
|
|
|
now := alerts[0].UpdatedAt
|
2021-04-27 06:33:22 -05:00
|
|
|
expAlerts := c.expAlerts(now)
|
|
|
|
|
|
|
|
sort.Sort(types.AlertSlice(expAlerts))
|
|
|
|
sort.Sort(types.AlertSlice(alerts))
|
|
|
|
|
|
|
|
require.Equal(t, expAlerts, alerts)
|
2021-04-22 10:12:18 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|