api: support zero StartsAt for alerts ()

When the API receives alerts where StartsAt is zero, it updates the
value to EndsAt (if not zero itself) or "now". This ensures that the
alert validation will not fail since StartsAt has to be less than or
equal to EndsAt.
This commit is contained in:
pasquier-s 2018-02-13 16:26:34 +01:00 committed by stuart nelson
parent 32f90a02ca
commit 382a0d8089
2 changed files with 96 additions and 11 deletions

View File

@ -103,6 +103,10 @@ func New(
peer *cluster.Peer, peer *cluster.Peer,
l log.Logger, l log.Logger,
) *API { ) *API {
if l == nil {
l = log.NewNopLogger()
}
return &API{ return &API{
alerts: alerts, alerts: alerts,
silences: silences, silences: silences,
@ -466,7 +470,11 @@ func (api *API) insertAlerts(w http.ResponseWriter, r *http.Request, alerts ...*
// Ensure StartsAt is set. // Ensure StartsAt is set.
if alert.StartsAt.IsZero() { if alert.StartsAt.IsZero() {
if alert.EndsAt.IsZero() {
alert.StartsAt = now alert.StartsAt = now
} else {
alert.StartsAt = alert.EndsAt
}
} }
// If no end time is defined, set a timeout after which an alert // If no end time is defined, set a timeout after which an alert
// is marked resolved if it is not updated. // is marked resolved if it is not updated.

View File

@ -1,16 +1,93 @@
package api package api
import ( import (
"bytes"
"encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"regexp" "regexp"
"testing" "testing"
"time"
"github.com/prometheus/alertmanager/dispatch"
"github.com/prometheus/alertmanager/provider"
"github.com/prometheus/alertmanager/types" "github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
"github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/pkg/labels"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
type fakeAlerts struct {
putWithErr bool
}
func newEmptyIterator() provider.AlertIterator {
return provider.NewAlertIterator(make(chan *types.Alert), make(chan struct{}), nil)
}
func (f *fakeAlerts) Subscribe() provider.AlertIterator { return newEmptyIterator() }
func (f *fakeAlerts) GetPending() provider.AlertIterator { return newEmptyIterator() }
func (f *fakeAlerts) Get(model.Fingerprint) (*types.Alert, error) { return nil, nil }
func (f *fakeAlerts) Put(alerts ...*types.Alert) error {
if f.putWithErr {
return errors.New("Error occured")
}
return nil
}
func groupAlerts([]*labels.Matcher) dispatch.AlertOverview { return dispatch.AlertOverview{} }
func getAlertStatus(model.Fingerprint) types.AlertStatus { return types.AlertStatus{} }
func TestAddAlerts(t *testing.T) {
now := func(offset int) time.Time {
return time.Now().Add(time.Duration(offset) * time.Second)
}
for i, tc := range []struct {
start, end time.Time
err bool
code int
}{
{time.Time{}, time.Time{}, false, 200},
{now(0), time.Time{}, false, 200},
{time.Time{}, now(-1), false, 200},
{time.Time{}, now(0), false, 200},
{time.Time{}, now(1), false, 200},
{now(-2), now(-1), false, 200},
{now(1), now(2), false, 200},
{now(1), now(0), false, 400},
{now(0), time.Time{}, true, 500},
} {
alerts := []model.Alert{{
StartsAt: tc.start,
EndsAt: tc.end,
Labels: model.LabelSet{"label1": "test1"},
Annotations: model.LabelSet{"annotation1": "some text"},
}}
b, err := json.Marshal(&alerts)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
api := New(&fakeAlerts{putWithErr: tc.err}, nil, groupAlerts, getAlertStatus, nil, nil)
r, err := http.NewRequest("POST", "/api/v1/alerts", bytes.NewReader(b))
w := httptest.NewRecorder()
if err != nil {
t.Errorf("Unexpected error %v", err)
}
api.addAlerts(w, r)
res := w.Result()
body, _ := ioutil.ReadAll(res.Body)
require.Equal(t, tc.code, w.Code, fmt.Sprintf("test case: %d, StartsAt %v, EndsAt %v, Response: %s", i, tc.start, tc.end, string(body)))
}
}
func TestAlertFiltering(t *testing.T) { func TestAlertFiltering(t *testing.T) {
type test struct { type test struct {
alert *model.Alert alert *model.Alert
@ -21,7 +98,7 @@ func TestAlertFiltering(t *testing.T) {
// Equal // Equal
equal, err := labels.NewMatcher(labels.MatchEqual, "label1", "test1") equal, err := labels.NewMatcher(labels.MatchEqual, "label1", "test1")
if err != nil { if err != nil {
t.Error("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
tests := []test{ tests := []test{
@ -39,7 +116,7 @@ func TestAlertFiltering(t *testing.T) {
// Not Equal // Not Equal
notEqual, err := labels.NewMatcher(labels.MatchNotEqual, "label1", "test1") notEqual, err := labels.NewMatcher(labels.MatchNotEqual, "label1", "test1")
if err != nil { if err != nil {
t.Error("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
tests = []test{ tests = []test{
@ -57,7 +134,7 @@ func TestAlertFiltering(t *testing.T) {
// Regexp Equal // Regexp Equal
regexpEqual, err := labels.NewMatcher(labels.MatchRegexp, "label1", "tes.*") regexpEqual, err := labels.NewMatcher(labels.MatchRegexp, "label1", "tes.*")
if err != nil { if err != nil {
t.Error("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
tests = []test{ tests = []test{
@ -75,7 +152,7 @@ func TestAlertFiltering(t *testing.T) {
// Regexp Not Equal // Regexp Not Equal
regexpNotEqual, err := labels.NewMatcher(labels.MatchNotRegexp, "label1", "tes.*") regexpNotEqual, err := labels.NewMatcher(labels.MatchNotRegexp, "label1", "tes.*")
if err != nil { if err != nil {
t.Error("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
tests = []test{ tests = []test{
@ -101,7 +178,7 @@ func TestSilenceFiltering(t *testing.T) {
// Equal // Equal
equal, err := labels.NewMatcher(labels.MatchEqual, "label1", "test1") equal, err := labels.NewMatcher(labels.MatchEqual, "label1", "test1")
if err != nil { if err != nil {
t.Error("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
tests := []test{ tests := []test{
@ -131,7 +208,7 @@ func TestSilenceFiltering(t *testing.T) {
// Not Equal // Not Equal
notEqual, err := labels.NewMatcher(labels.MatchNotEqual, "label1", "test1") notEqual, err := labels.NewMatcher(labels.MatchNotEqual, "label1", "test1")
if err != nil { if err != nil {
t.Error("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
tests = []test{ tests = []test{
@ -161,7 +238,7 @@ func TestSilenceFiltering(t *testing.T) {
// Regexp Equal // Regexp Equal
regexpEqual, err := labels.NewMatcher(labels.MatchRegexp, "label1", "tes.*") regexpEqual, err := labels.NewMatcher(labels.MatchRegexp, "label1", "tes.*")
if err != nil { if err != nil {
t.Error("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
tests = []test{ tests = []test{
@ -191,7 +268,7 @@ func TestSilenceFiltering(t *testing.T) {
// Regexp Not Equal // Regexp Not Equal
regexpNotEqual, err := labels.NewMatcher(labels.MatchNotRegexp, "label1", "tes.*") regexpNotEqual, err := labels.NewMatcher(labels.MatchNotRegexp, "label1", "tes.*")
if err != nil { if err != nil {
t.Error("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
tests = []test{ tests = []test{
@ -224,13 +301,13 @@ func TestReceiversMatchFilter(t *testing.T) {
filter, err := regexp.Compile(fmt.Sprintf("^(?:%s)$", "hip.*")) filter, err := regexp.Compile(fmt.Sprintf("^(?:%s)$", "hip.*"))
if err != nil { if err != nil {
t.Error("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
require.True(t, receiversMatchFilter(receivers, filter)) require.True(t, receiversMatchFilter(receivers, filter))
filter, err = regexp.Compile(fmt.Sprintf("^(?:%s)$", "hip")) filter, err = regexp.Compile(fmt.Sprintf("^(?:%s)$", "hip"))
if err != nil { if err != nil {
t.Error("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
require.False(t, receiversMatchFilter(receivers, filter)) require.False(t, receiversMatchFilter(receivers, filter))
} }