Skip to content

Bring in upstream fixes to silences #88

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
5 changes: 2 additions & 3 deletions api/v2/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -662,8 +662,7 @@ func (api *API) postSilencesHandler(params silence_ops.PostSilencesParams) middl
return silence_ops.NewPostSilencesBadRequest().WithPayload(msg)
}

sid, err := api.silences.Set(sil)
if err != nil {
if err = api.silences.Set(sil); err != nil {
level.Error(logger).Log("msg", "Failed to create silence", "err", err)
if errors.Is(err, silence.ErrNotFound) {
return silence_ops.NewPostSilencesNotFound().WithPayload(err.Error())
Expand All @@ -672,7 +671,7 @@ func (api *API) postSilencesHandler(params silence_ops.PostSilencesParams) middl
}

return silence_ops.NewPostSilencesOK().WithPayload(&silence_ops.PostSilencesOKBody{
SilenceID: sid,
SilenceID: sil.Id,
})
}

Expand Down
126 changes: 100 additions & 26 deletions api/v2/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package v2

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
Expand All @@ -24,6 +25,7 @@ import (
"time"

"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -159,18 +161,16 @@ func TestDeleteSilenceHandler(t *testing.T) {
EndsAt: now.Add(time.Hour),
UpdatedAt: now,
}
unexpiredSid, err := silences.Set(unexpiredSil)
require.NoError(t, err)
require.NoError(t, silences.Set(unexpiredSil))

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))
require.NoError(t, silences.Set(expiredSil))
require.NoError(t, silences.Expire(expiredSil.Id))

for i, tc := range []struct {
sid string
Expand All @@ -181,11 +181,11 @@ func TestDeleteSilenceHandler(t *testing.T) {
404,
},
{
unexpiredSid,
unexpiredSil.Id,
200,
},
{
expiredSid,
expiredSil.Id,
200,
},
} {
Expand Down Expand Up @@ -223,18 +223,16 @@ func TestPostSilencesHandler(t *testing.T) {
EndsAt: now.Add(time.Hour),
UpdatedAt: now,
}
unexpiredSid, err := silences.Set(unexpiredSil)
require.NoError(t, err)
require.NoError(t, silences.Set(unexpiredSil))

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))
require.NoError(t, silences.Set(expiredSil))
require.NoError(t, silences.Expire(expiredSil.Id))

t.Run("Silences CRUD", func(t *testing.T) {
for i, tc := range []struct {
Expand All @@ -259,46 +257,122 @@ func TestPostSilencesHandler(t *testing.T) {
},
{
"with an active silence ID - it extends the silence",
unexpiredSid,
unexpiredSil.Id,
now.Add(time.Hour),
now.Add(time.Hour * 2),
200,
},
{
"with an expired silence ID - it re-creates the silence",
expiredSid,
expiredSil.Id,
now.Add(time.Hour),
now.Add(time.Hour * 2),
200,
},
} {
t.Run(tc.name, func(t *testing.T) {
silence, silenceBytes := createSilence(t, tc.sid, "silenceCreator", tc.start, tc.end)

api := API{
uptime: time.Now(),
silences: silences,
logger: log.NewNopLogger(),
}

r, err := http.NewRequest("POST", "/api/v2/silence/${tc.sid}", bytes.NewReader(silenceBytes))
require.NoError(t, err)

sil := createSilence(t, tc.sid, "silenceCreator", tc.start, tc.end)
w := httptest.NewRecorder()
p := runtime.TextProducer()
responder := api.postSilencesHandler(silence_ops.PostSilencesParams{
HTTPRequest: r,
Silence: &silence,
})
responder.WriteResponse(w, p)
postSilences(t, w, api.postSilencesHandler, sil)
body, _ := io.ReadAll(w.Result().Body)

require.Equal(t, tc.expectedCode, w.Code, fmt.Sprintf("test case: %d, response: %s", i, string(body)))
})
}
})
}

func TestPostSilencesHandlerMissingIdCreatesSilence(t *testing.T) {
now := time.Now()
silences := newSilences(t)
api := API{
uptime: time.Now(),
silences: silences,
logger: log.NewNopLogger(),
}

// Create a new silence. It should be assigned a random UUID.
sil := createSilence(t, "", "silenceCreator", now.Add(time.Hour), now.Add(time.Hour*2))
w := httptest.NewRecorder()
postSilences(t, w, api.postSilencesHandler, sil)
require.Equal(t, http.StatusOK, w.Code)

// Get the silences from the API.
w = httptest.NewRecorder()
getSilences(t, w, api.getSilencesHandler)
require.Equal(t, http.StatusOK, w.Code)
var resp []open_api_models.GettableSilence
require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
require.Len(t, resp, 1)

// Change the ID. It should return 404 Not Found.
sil = open_api_models.PostableSilence{
ID: "unknownID",
Silence: resp[0].Silence,
}
w = httptest.NewRecorder()
postSilences(t, w, api.postSilencesHandler, sil)
require.Equal(t, http.StatusNotFound, w.Code)

// Remove the ID. It should duplicate the silence with a different UUID.
sil = open_api_models.PostableSilence{
ID: "",
Silence: resp[0].Silence,
}
w = httptest.NewRecorder()
postSilences(t, w, api.postSilencesHandler, sil)
require.Equal(t, http.StatusOK, w.Code)

// Get the silences from the API. There should now be 2 silences.
w = httptest.NewRecorder()
getSilences(t, w, api.getSilencesHandler)
require.Equal(t, http.StatusOK, w.Code)
require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
require.Len(t, resp, 2)
require.NotEqual(t, resp[0].ID, resp[1].ID)
}

func getSilences(
t *testing.T,
w *httptest.ResponseRecorder,
handlerFunc func(params silence_ops.GetSilencesParams) middleware.Responder,
) {
r, err := http.NewRequest("GET", "/api/v2/silences", nil)
require.NoError(t, err)

p := runtime.TextProducer()
responder := handlerFunc(silence_ops.GetSilencesParams{
HTTPRequest: r,
Filter: nil,
})
responder.WriteResponse(w, p)
}

func postSilences(
t *testing.T,
w *httptest.ResponseRecorder,
handlerFunc func(params silence_ops.PostSilencesParams) middleware.Responder,
sil open_api_models.PostableSilence,
) {
b, err := json.Marshal(sil)
require.NoError(t, err)

r, err := http.NewRequest("POST", "/api/v2/silences", bytes.NewReader(b))
require.NoError(t, err)

p := runtime.TextProducer()
responder := handlerFunc(silence_ops.PostSilencesParams{
HTTPRequest: r,
Silence: &sil,
})
responder.WriteResponse(w, p)
}

func TestCheckSilenceMatchesFilterLabels(t *testing.T) {
type test struct {
silenceMatchers []*silencepb.Matcher
Expand Down
9 changes: 2 additions & 7 deletions api/v2/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,17 @@
package v2

import (
"encoding/json"
"testing"
"time"

"github.com/go-openapi/strfmt"
"github.com/stretchr/testify/require"

open_api_models "github.com/prometheus/alertmanager/api/v2/models"
"github.com/prometheus/alertmanager/pkg/labels"
"github.com/prometheus/alertmanager/silence/silencepb"
)

func createSilence(t *testing.T, ID, creator string, start, ends time.Time) (open_api_models.PostableSilence, []byte) {
func createSilence(t *testing.T, ID, creator string, start, ends time.Time) open_api_models.PostableSilence {
t.Helper()

comment := "test"
Expand All @@ -46,10 +44,7 @@ func createSilence(t *testing.T, ID, creator string, start, ends time.Time) (ope
Comment: &comment,
},
}
b, err := json.Marshal(&sil)
require.NoError(t, err)

return sil, b
return sil
}

func createSilenceMatcher(t *testing.T, name, pattern string, matcherType silencepb.Matcher_Type) *silencepb.Matcher {
Expand Down
8 changes: 4 additions & 4 deletions notify/notify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -717,11 +717,11 @@ func TestMuteStageWithSilences(t *testing.T) {
if err != nil {
t.Fatal(err)
}
silID, err := silences.Set(&silencepb.Silence{
sil := &silencepb.Silence{
EndsAt: utcNow().Add(time.Hour),
Matchers: []*silencepb.Matcher{{Name: "mute", Pattern: "me"}},
})
if err != nil {
}
if err = silences.Set(sil); err != nil {
t.Fatal(err)
}

Expand Down Expand Up @@ -798,7 +798,7 @@ func TestMuteStageWithSilences(t *testing.T) {
}

// Expire the silence and verify that no alerts are silenced now.
if err := silences.Expire(silID); err != nil {
if err := silences.Expire(sil.Id); err != nil {
t.Fatal(err)
}

Expand Down
Loading
Loading