Skip to content

Commit

Permalink
Merge pull request #88 from grobinson-grafana/grobinson/pick-silence-…
Browse files Browse the repository at this point in the history
…fixes

Bring in upstream fixes to silences
  • Loading branch information
yuri-tceretian authored Jun 25, 2024
2 parents d75ea57 + 700c04e commit 5c35126
Show file tree
Hide file tree
Showing 8 changed files with 368 additions and 254 deletions.
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

0 comments on commit 5c35126

Please sign in to comment.