api/v2: sort silences similarly to v1 api (#1786)

* api/v2: sort silences similarly to v1 api

Sort the queried silences to match behaviour in the v1 api.

Sort silences in-place instead of creating multiple slices.
Use separate function for sorting silences for easier testing.
Add unit test for sort order.

Signed-off-by: Paul Gier <pgier@redhat.com>
This commit is contained in:
Paul Gier 2019-03-11 08:19:52 -05:00 committed by Max Inden
parent c5e7dca3dc
commit 3ffe6cfdc8
2 changed files with 114 additions and 0 deletions

View File

@ -508,9 +508,49 @@ func (api *API) getSilencesHandler(params silence_ops.GetSilencesParams) middlew
sils = append(sils, &silence) sils = append(sils, &silence)
} }
sortSilences(sils)
return silence_ops.NewGetSilencesOK().WithPayload(sils) return silence_ops.NewGetSilencesOK().WithPayload(sils)
} }
var (
silenceStateOrder = map[types.SilenceState]int{
types.SilenceStateActive: 1,
types.SilenceStatePending: 2,
types.SilenceStateExpired: 3,
}
)
// sortSilences sorts first according to the state "active, pending, expired"
// then by end time or start time depending on the state.
// active silences should show the next to expire first
// pending silences are ordered based on which one starts next
// expired are ordered base on which one expired most recently
func sortSilences(sils open_api_models.GettableSilences) {
sort.Slice(sils, func(i, j int) bool {
state1 := types.SilenceState(*sils[i].Status.State)
state2 := types.SilenceState(*sils[j].Status.State)
if state1 != state2 {
return silenceStateOrder[state1] < silenceStateOrder[state2]
}
switch state1 {
case types.SilenceStateActive:
endsAt1 := time.Time(*sils[i].Silence.EndsAt)
endsAt2 := time.Time(*sils[j].Silence.EndsAt)
return endsAt1.Before(endsAt2)
case types.SilenceStatePending:
startsAt1 := time.Time(*sils[i].Silence.StartsAt)
startsAt2 := time.Time(*sils[j].Silence.StartsAt)
return startsAt1.Before(startsAt2)
case types.SilenceStateExpired:
endsAt1 := time.Time(*sils[i].Silence.EndsAt)
endsAt2 := time.Time(*sils[j].Silence.EndsAt)
return endsAt1.After(endsAt2)
}
return false
})
}
func gettableSilenceMatchesFilterLabels(s open_api_models.GettableSilence, matchers []*labels.Matcher) bool { func gettableSilenceMatchesFilterLabels(s open_api_models.GettableSilence, matchers []*labels.Matcher) bool {
sms := make(map[string]string) sms := make(map[string]string)
for _, m := range s.Matchers { for _, m := range s.Matchers {

View File

@ -14,9 +14,13 @@
package v2 package v2
import ( import (
"strconv"
"testing" "testing"
"time" "time"
"github.com/go-openapi/strfmt"
open_api_models "github.com/prometheus/alertmanager/api/v2/models"
general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general" general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general"
"github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/config"
) )
@ -48,3 +52,73 @@ func TestGetStatusHandlerWithNilPeer(t *testing.T) {
t.Fatal("expected cluster name to be empty, violating the openapi specification") 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)
}
}