alertmanager/api/v2/api_test.go

317 lines
9.9 KiB
Go
Raw Normal View History

// Copyright 2019 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v2
import (
"strconv"
"testing"
"time"
"github.com/go-openapi/strfmt"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
open_api_models "github.com/prometheus/alertmanager/api/v2/models"
general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general"
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/pkg/labels"
"github.com/prometheus/alertmanager/silence/silencepb"
"github.com/prometheus/alertmanager/types"
)
// If api.peers == nil, Alertmanager cluster feature is disabled. Make sure to
// not try to access properties of peer, which would trigger a nil pointer
// dereference.
func TestGetStatusHandlerWithNilPeer(t *testing.T) {
api := API{
uptime: time.Now(),
peer: nil,
alertmanagerConfig: &config.Config{},
}
// Test ensures this method call does not panic.
status := api.getStatusHandler(general_ops.GetStatusParams{}).(*general_ops.GetStatusOK)
c := status.Payload.Cluster
if c == nil || c.Status == nil {
t.Fatal("expected cluster status not to be nil, violating the openapi specification")
}
if c.Peers == nil {
t.Fatal("expected cluster peers to be not nil when api.peer is nil, violating the openapi specification")
}
if len(c.Peers) != 0 {
t.Fatal("expected cluster peers to be empty when api.peer is nil, violating the openapi specification")
}
if c.Name != "" {
t.Fatal("expected cluster name to be empty, violating the openapi specification")
}
}
func assertEqualStrings(t *testing.T, expected string, actual string) {
if expected != actual {
t.Fatal("expected: ", expected, ", actual: ", actual)
}
}
var (
testComment = "comment"
createdBy = "test"
)
func gettableSilence(id string, state string,
updatedAt string, start string, end string,
) *open_api_models.GettableSilence {
updAt, err := strfmt.ParseDateTime(updatedAt)
if err != nil {
panic(err)
}
strAt, err := strfmt.ParseDateTime(start)
if err != nil {
panic(err)
}
endAt, err := strfmt.ParseDateTime(end)
if err != nil {
panic(err)
}
return &open_api_models.GettableSilence{
Silence: open_api_models.Silence{
StartsAt: &strAt,
EndsAt: &endAt,
Comment: &testComment,
CreatedBy: &createdBy,
},
ID: &id,
UpdatedAt: &updAt,
Status: &open_api_models.SilenceStatus{
State: &state,
},
}
}
func TestGetSilencesHandler(t *testing.T) {
updateTime := "2019-01-01T12:00:00+00:00"
silences := []*open_api_models.GettableSilence{
gettableSilence("silence-6-expired", "expired", updateTime,
"2019-01-01T12:00:00+00:00", "2019-01-01T11:00:00+00:00"),
gettableSilence("silence-1-active", "active", updateTime,
"2019-01-01T12:00:00+00:00", "2019-01-01T13:00:00+00:00"),
gettableSilence("silence-7-expired", "expired", updateTime,
"2019-01-01T12:00:00+00:00", "2019-01-01T10:00:00+00:00"),
gettableSilence("silence-5-expired", "expired", updateTime,
"2019-01-01T12:00:00+00:00", "2019-01-01T12:00:00+00:00"),
gettableSilence("silence-0-active", "active", updateTime,
"2019-01-01T12:00:00+00:00", "2019-01-01T12:00:00+00:00"),
gettableSilence("silence-4-pending", "pending", updateTime,
"2019-01-01T13:00:00+00:00", "2019-01-01T12:00:00+00:00"),
gettableSilence("silence-3-pending", "pending", updateTime,
"2019-01-01T12:00:00+00:00", "2019-01-01T12:00:00+00:00"),
gettableSilence("silence-2-active", "active", updateTime,
"2019-01-01T12:00:00+00:00", "2019-01-01T14:00:00+00:00"),
}
SortSilences(open_api_models.GettableSilences(silences))
for i, sil := range silences {
assertEqualStrings(t, "silence-"+strconv.Itoa(i)+"-"+*sil.Status.State, *sil.ID)
}
}
func createSilenceMatcher(name string, pattern string, matcherType silencepb.Matcher_Type) *silencepb.Matcher {
return &silencepb.Matcher{
Name: name,
Pattern: pattern,
Type: matcherType,
}
}
func createLabelMatcher(name string, value string, matchType labels.MatchType) *labels.Matcher {
matcher, _ := labels.NewMatcher(matchType, name, value)
return matcher
}
func TestCheckSilenceMatchesFilterLabels(t *testing.T) {
type test struct {
silenceMatchers []*silencepb.Matcher
filterMatchers []*labels.Matcher
expected bool
}
tests := []test{
{
[]*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_EQUAL)},
[]*labels.Matcher{createLabelMatcher("label", "value", labels.MatchEqual)},
true,
},
{
[]*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_EQUAL)},
[]*labels.Matcher{createLabelMatcher("label", "novalue", labels.MatchEqual)},
false,
},
{
[]*silencepb.Matcher{createSilenceMatcher("label", "(foo|bar)", silencepb.Matcher_REGEXP)},
[]*labels.Matcher{createLabelMatcher("label", "(foo|bar)", labels.MatchRegexp)},
true,
},
{
[]*silencepb.Matcher{createSilenceMatcher("label", "foo", silencepb.Matcher_REGEXP)},
[]*labels.Matcher{createLabelMatcher("label", "(foo|bar)", labels.MatchRegexp)},
false,
},
{
[]*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_EQUAL)},
[]*labels.Matcher{createLabelMatcher("label", "value", labels.MatchRegexp)},
false,
},
{
[]*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_REGEXP)},
[]*labels.Matcher{createLabelMatcher("label", "value", labels.MatchEqual)},
false,
},
{
[]*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_NOT_EQUAL)},
[]*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotEqual)},
true,
},
{
[]*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_NOT_REGEXP)},
[]*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotRegexp)},
true,
},
{
[]*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_EQUAL)},
[]*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotEqual)},
false,
},
{
[]*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_REGEXP)},
[]*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotRegexp)},
false,
},
{
[]*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_NOT_EQUAL)},
[]*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotRegexp)},
false,
},
{
[]*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_NOT_REGEXP)},
[]*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotEqual)},
false,
},
{
[]*silencepb.Matcher{
createSilenceMatcher("label", "(foo|bar)", silencepb.Matcher_REGEXP),
createSilenceMatcher("label", "value", silencepb.Matcher_EQUAL),
},
[]*labels.Matcher{createLabelMatcher("label", "(foo|bar)", labels.MatchRegexp)},
true,
},
}
for _, test := range tests {
silence := silencepb.Silence{
Matchers: test.silenceMatchers,
}
actual := CheckSilenceMatchesFilterLabels(&silence, test.filterMatchers)
if test.expected != actual {
t.Fatal("unexpected match result between silence and filter. expected:", test.expected, ", actual:", actual)
}
}
}
func convertDateTime(ts time.Time) *strfmt.DateTime {
dt := strfmt.DateTime(ts)
return &dt
}
func TestAlertToOpenAPIAlert(t *testing.T) {
var (
start = time.Now().Add(-time.Minute)
updated = time.Now()
active = "active"
fp = "0223b772b51c29e1"
receivers = []string{"receiver1", "receiver2"}
alert = &types.Alert{
Alert: model.Alert{
Labels: model.LabelSet{"severity": "critical", "alertname": "alert1"},
StartsAt: start,
},
UpdatedAt: updated,
}
)
openAPIAlert := AlertToOpenAPIAlert(alert, types.AlertStatus{State: types.AlertStateActive}, receivers)
require.Equal(t, &open_api_models.GettableAlert{
Annotations: open_api_models.LabelSet{},
Alert: open_api_models.Alert{
Labels: open_api_models.LabelSet{"severity": "critical", "alertname": "alert1"},
},
Status: &open_api_models.AlertStatus{
State: &active,
InhibitedBy: []string{},
SilencedBy: []string{},
},
StartsAt: convertDateTime(start),
EndsAt: convertDateTime(time.Time{}),
UpdatedAt: convertDateTime(updated),
Fingerprint: &fp,
Receivers: []*open_api_models.Receiver{
&open_api_models.Receiver{Name: &receivers[0]},
&open_api_models.Receiver{Name: &receivers[1]},
},
}, openAPIAlert)
}
func TestMatchFilterLabels(t *testing.T) {
sms := map[string]string{
"foo": "bar",
}
testCases := []struct {
matcher labels.MatchType
name string
val string
expected bool
}{
{labels.MatchEqual, "foo", "bar", true},
{labels.MatchEqual, "baz", "", true},
{labels.MatchEqual, "baz", "qux", false},
{labels.MatchEqual, "baz", "qux|", false},
{labels.MatchRegexp, "foo", "bar", true},
{labels.MatchRegexp, "baz", "", true},
{labels.MatchRegexp, "baz", "qux", false},
{labels.MatchRegexp, "baz", "qux|", true},
{labels.MatchNotEqual, "foo", "bar", false},
{labels.MatchNotEqual, "baz", "", false},
{labels.MatchNotEqual, "baz", "qux", true},
{labels.MatchNotEqual, "baz", "qux|", true},
{labels.MatchNotRegexp, "foo", "bar", false},
{labels.MatchNotRegexp, "baz", "", false},
{labels.MatchNotRegexp, "baz", "qux", true},
{labels.MatchNotRegexp, "baz", "qux|", false},
}
for _, tc := range testCases {
m, err := labels.NewMatcher(tc.matcher, tc.name, tc.val)
require.NoError(t, err)
ms := []*labels.Matcher{m}
require.Equal(t, tc.expected, matchFilterLabels(ms, sms))
}
}