mirror of
https://github.com/prometheus/alertmanager
synced 2025-01-02 19:42:03 +00:00
Return no error when deleting expired silence (#2817)
* Changed Silences.expire(id) to not return error for already expired silence Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Added comment explaining idempotency change for Silences.expire() Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Trigger build Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Trigger build Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Fixed typo in comment Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Trigger build Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Trigger build Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Fixed another typo in comment Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Promoted comment to function-level Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Added API v2 test for DeleteSilence, PostSilence Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Fixed lint errors Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Trigger build Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Trigger build Signed-off-by: Soon-Ping Phang <soonping@amazon.com> * Trigger build Signed-off-by: Soon-Ping Phang <soonping@amazon.com>
This commit is contained in:
parent
4030e3670b
commit
a2d18c93de
@ -14,20 +14,31 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"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"
|
||||
silence_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/silence"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/alertmanager/pkg/labels"
|
||||
"github.com/prometheus/alertmanager/silence"
|
||||
"github.com/prometheus/alertmanager/silence/silencepb"
|
||||
"github.com/prometheus/alertmanager/types"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// If api.peers == nil, Alertmanager cluster feature is disabled. Make sure to
|
||||
@ -72,6 +83,13 @@ var (
|
||||
createdBy = "test"
|
||||
)
|
||||
|
||||
func newSilences(t *testing.T) *silence.Silences {
|
||||
silences, err := silence.New(silence.Options{})
|
||||
require.NoError(t, err)
|
||||
|
||||
return silences
|
||||
}
|
||||
|
||||
func gettableSilence(id string, state string,
|
||||
updatedAt string, start string, end string,
|
||||
) *open_api_models.GettableSilence {
|
||||
@ -131,6 +149,170 @@ func TestGetSilencesHandler(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteSilenceHandler(t *testing.T) {
|
||||
now := time.Now()
|
||||
silences := newSilences(t)
|
||||
|
||||
m := &silencepb.Matcher{Type: silencepb.Matcher_EQUAL, Name: "a", Pattern: "b"}
|
||||
|
||||
unexpiredSil := &silencepb.Silence{
|
||||
Matchers: []*silencepb.Matcher{m},
|
||||
StartsAt: now,
|
||||
EndsAt: now.Add(time.Hour),
|
||||
UpdatedAt: now,
|
||||
}
|
||||
unexpiredSid, err := silences.Set(unexpiredSil)
|
||||
require.NoError(t, err)
|
||||
|
||||
expiredSil := &silencepb.Silence{
|
||||
Matchers: []*silencepb.Matcher{m},
|
||||
StartsAt: now.Add(-time.Hour),
|
||||
EndsAt: now.Add(time.Hour),
|
||||
UpdatedAt: now,
|
||||
}
|
||||
expiredSid, err := silences.Set(expiredSil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, silences.Expire(expiredSid))
|
||||
|
||||
for i, tc := range []struct {
|
||||
sid string
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
"unknownSid",
|
||||
500,
|
||||
},
|
||||
{
|
||||
unexpiredSid,
|
||||
200,
|
||||
},
|
||||
{
|
||||
expiredSid,
|
||||
200,
|
||||
},
|
||||
} {
|
||||
api := API{
|
||||
uptime: time.Now(),
|
||||
silences: silences,
|
||||
logger: log.NewNopLogger(),
|
||||
}
|
||||
|
||||
r, err := http.NewRequest("DELETE", "/api/v2/silence/${tc.sid}", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
p := runtime.TextProducer()
|
||||
responder := api.deleteSilenceHandler(silence_ops.DeleteSilenceParams{
|
||||
SilenceID: strfmt.UUID(tc.sid),
|
||||
HTTPRequest: r,
|
||||
})
|
||||
responder.WriteResponse(w, p)
|
||||
body, _ := ioutil.ReadAll(w.Result().Body)
|
||||
|
||||
require.Equal(t, tc.expectedCode, w.Code, fmt.Sprintf("test case: %d, response: %s", i, string(body)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostSilencesHandler(t *testing.T) {
|
||||
now := time.Now()
|
||||
silences := newSilences(t)
|
||||
|
||||
m := &silencepb.Matcher{Type: silencepb.Matcher_EQUAL, Name: "a", Pattern: "b"}
|
||||
|
||||
unexpiredSil := &silencepb.Silence{
|
||||
Matchers: []*silencepb.Matcher{m},
|
||||
StartsAt: now,
|
||||
EndsAt: now.Add(time.Hour),
|
||||
UpdatedAt: now,
|
||||
}
|
||||
unexpiredSid, err := silences.Set(unexpiredSil)
|
||||
require.NoError(t, err)
|
||||
|
||||
expiredSil := &silencepb.Silence{
|
||||
Matchers: []*silencepb.Matcher{m},
|
||||
StartsAt: now.Add(-time.Hour),
|
||||
EndsAt: now.Add(time.Hour),
|
||||
UpdatedAt: now,
|
||||
}
|
||||
expiredSid, err := silences.Set(expiredSil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, silences.Expire(expiredSid))
|
||||
|
||||
for i, tc := range []struct {
|
||||
sid string
|
||||
start, end time.Time
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
"unknownSid",
|
||||
now.Add(time.Hour),
|
||||
now.Add(time.Hour * 2),
|
||||
404,
|
||||
},
|
||||
{
|
||||
"",
|
||||
now.Add(time.Hour),
|
||||
now.Add(time.Hour * 2),
|
||||
200,
|
||||
},
|
||||
{
|
||||
unexpiredSid,
|
||||
now.Add(time.Hour),
|
||||
now.Add(time.Hour * 2),
|
||||
200,
|
||||
},
|
||||
{
|
||||
expiredSid,
|
||||
now.Add(time.Hour),
|
||||
now.Add(time.Hour * 2),
|
||||
200,
|
||||
},
|
||||
} {
|
||||
createdBy := "silenceCreator"
|
||||
comment := "test"
|
||||
matcherName := "a"
|
||||
matcherValue := "b"
|
||||
isRegex := false
|
||||
startsAt := strfmt.DateTime(tc.start)
|
||||
endsAt := strfmt.DateTime(tc.end)
|
||||
|
||||
sil := open_api_models.PostableSilence{
|
||||
ID: tc.sid,
|
||||
Silence: open_api_models.Silence{
|
||||
Matchers: open_api_models.Matchers{&open_api_models.Matcher{Name: &matcherName, Value: &matcherValue, IsRegex: &isRegex}},
|
||||
StartsAt: &startsAt,
|
||||
EndsAt: &endsAt,
|
||||
CreatedBy: &createdBy,
|
||||
Comment: &comment,
|
||||
},
|
||||
}
|
||||
b, err := json.Marshal(&sil)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
}
|
||||
|
||||
api := API{
|
||||
uptime: time.Now(),
|
||||
silences: silences,
|
||||
logger: log.NewNopLogger(),
|
||||
}
|
||||
|
||||
r, err := http.NewRequest("POST", "/api/v2/silence/${tc.sid}", bytes.NewReader(b))
|
||||
require.NoError(t, err)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
p := runtime.TextProducer()
|
||||
responder := api.postSilencesHandler(silence_ops.PostSilencesParams{
|
||||
HTTPRequest: r,
|
||||
Silence: &sil,
|
||||
})
|
||||
responder.WriteResponse(w, p)
|
||||
body, _ := ioutil.ReadAll(w.Result().Body)
|
||||
|
||||
require.Equal(t, tc.expectedCode, w.Code, fmt.Sprintf("test case: %d, response: %s", i, string(body)))
|
||||
}
|
||||
}
|
||||
|
||||
func createSilenceMatcher(name string, pattern string, matcherType silencepb.Matcher_Type) *silencepb.Matcher {
|
||||
return &silencepb.Matcher{
|
||||
Name: name,
|
||||
|
@ -612,6 +612,8 @@ func (s *Silences) Expire(id string) error {
|
||||
}
|
||||
|
||||
// Expire the silence with the given ID immediately.
|
||||
// It is idempotent, nil is returned if the silence already expired before it is GC'd.
|
||||
// If the silence is not found an error is returned.
|
||||
func (s *Silences) expire(id string) error {
|
||||
sil, ok := s.getSilence(id)
|
||||
if !ok {
|
||||
@ -622,7 +624,7 @@ func (s *Silences) expire(id string) error {
|
||||
|
||||
switch getState(sil, now) {
|
||||
case types.SilenceStateExpired:
|
||||
return errors.Errorf("silence %s already expired", id)
|
||||
return nil
|
||||
case types.SilenceStateActive:
|
||||
sil.EndsAt = now
|
||||
case types.SilenceStatePending:
|
||||
|
@ -835,9 +835,7 @@ func TestSilenceExpire(t *testing.T) {
|
||||
require.NoError(t, s.Expire("pending"))
|
||||
require.NoError(t, s.Expire("active"))
|
||||
|
||||
err = s.Expire("expired")
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "already expired")
|
||||
require.NoError(t, s.Expire("expired"))
|
||||
|
||||
sil, err := s.QueryOne(QIDs("pending"))
|
||||
require.NoError(t, err)
|
||||
@ -935,9 +933,7 @@ func TestSilenceExpireWithZeroRetention(t *testing.T) {
|
||||
require.NoError(t, s.Expire("pending"))
|
||||
require.NoError(t, s.Expire("active"))
|
||||
|
||||
err = s.Expire("expired")
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "already expired")
|
||||
require.NoError(t, s.Expire("expired"))
|
||||
|
||||
_, err = s.QueryOne(QIDs("pending"))
|
||||
require.NoError(t, err)
|
||||
|
Loading…
Reference in New Issue
Block a user