Skip to content
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

stevesg/hairyhenderson otel tracing support #4036

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
5a69ba9
Capture and expose notification delivery errors (#31)
santihernandezc Jan 19, 2023
ce256a2
regen swagger
alexweav Oct 27, 2023
e020189
Add Upsert method to Silences
santihernandezc Mar 18, 2024
ee47641
call mtx.Lock() only once
santihernandezc Mar 19, 2024
c33c6b5
Merge pull request #78 from grafana/grobinson/update-am
grobinson-grafana Apr 22, 2024
ce9fd63
remove changes related to capturing and exposing errors in notificati…
santihernandezc May 23, 2024
8090d88
fix tests
santihernandezc May 24, 2024
11482fe
go back to using pointers for integrations
santihernandezc May 27, 2024
b0de8f9
Add limits for silences (#3852)
grobinson-grafana May 31, 2024
4362a30
Merge pull request #84 from grafana/grobinson/enforce-limits-on-silences
grobinson-grafana May 31, 2024
5121fbc
Merge branch 'main' of github.com:grafana/prometheus-alertmanager int…
santihernandezc Jun 3, 2024
1ebcebd
Limits should include expired silences (#3862)
grobinson-grafana Jun 3, 2024
2798416
Merge pull request #85 from grafana/santihernandezc/limits_should_inc…
santihernandezc Jun 4, 2024
c854031
Merge pull request #83 from grafana/santihernandezc/remove_changes_in…
santihernandezc Jun 4, 2024
1647c6f
Fix TestSilenceLimits tests (#3866)
grobinson-grafana Jun 5, 2024
70d9d63
Merge pull request #86 from grafana/santihernandezc/cherry_pick_f9d5a08
santihernandezc Jun 5, 2024
c3a1f54
Replace incorrect use of fmt.Errorf (#3883)
grobinson-grafana Jun 20, 2024
b767568
Silence limits as functions (#3885)
grobinson-grafana Jun 20, 2024
edab848
Rename silence limit to max-silence-size-bytes (#3886)
grobinson-grafana Jun 20, 2024
d75ea57
Merge pull request #87 from grobinson-grafana/grobinson/update-silenc…
santihernandezc Jun 21, 2024
26fa900
Remove Id return from silences.Set(*pb.Silence)
grobinson-grafana Jun 17, 2024
a03a719
Fix MaxSilences limit causes incomplete updates of existing silences …
grobinson-grafana Jun 24, 2024
3c44657
Improve test coverage for silences (#3896)
grobinson-grafana Jun 24, 2024
b43b86c
Update Upsert to return error
grobinson-grafana Jun 24, 2024
1727721
Add tests for create and then update silences (#3899)
grobinson-grafana Jun 25, 2024
050a29e
Fix invalid silence causes incomplete updates (#3898)
grobinson-grafana Jun 25, 2024
dcd07f1
Fix MaxSilenceSizeBytes limit causes incomplete updates of existing s…
grobinson-grafana Jun 25, 2024
700c04e
Update Upsert to use latest fixes
grobinson-grafana Jun 25, 2024
5c35126
Merge pull request #88 from grobinson-grafana/grobinson/pick-silence-…
yuri-tceretian Jun 25, 2024
d812a96
Improve test coverage of Upsert
grobinson-grafana Jun 25, 2024
66ec17e
Merge pull request #89 from grobinson-grafana/grobinson/improve-test-…
yuri-tceretian Jun 25, 2024
aac04c9
Use Adaptive Cards in MS Teams integration
santihernandezc Aug 30, 2024
ca3d9ad
license header
santihernandezc Aug 30, 2024
bf5d7a3
Remove unused constants
alexander-akhmetov Sep 2, 2024
b2665b0
Test MS Teams adaptive cards
alexander-akhmetov Sep 2, 2024
60521fa
Merge pull request #90 from grafana/santihernandezc/use_adaptive_card…
santihernandezc Sep 6, 2024
aafa445
Add tracing to webhook receiver.
stevesg Sep 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@
!/.promu.yml
!/api/v2/openapi.yaml
!.github/workflows/*.yml

# Editor
.vscode
.DS_Store
13 changes: 7 additions & 6 deletions api/v2/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import (
"github.com/prometheus/common/version"
"github.com/rs/cors"

"github.com/prometheus/alertmanager/api/metrics"
open_api_models "github.com/prometheus/alertmanager/api/v2/models"
"github.com/prometheus/alertmanager/api/v2/restapi"
"github.com/prometheus/alertmanager/api/v2/restapi/operations"
Expand All @@ -42,6 +41,8 @@ import (
general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general"
receiver_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/receiver"
silence_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/silence"

"github.com/prometheus/alertmanager/api/metrics"
"github.com/prometheus/alertmanager/cluster"
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/dispatch"
Expand Down Expand Up @@ -225,8 +226,9 @@ func (api *API) getReceiversHandler(params receiver_ops.GetReceiversParams) midd
defer api.mtx.RUnlock()

receivers := make([]*open_api_models.Receiver, 0, len(api.alertmanagerConfig.Receivers))
for i := range api.alertmanagerConfig.Receivers {
receivers = append(receivers, &open_api_models.Receiver{Name: &api.alertmanagerConfig.Receivers[i].Name})
for _, r := range api.alertmanagerConfig.Receivers {
name := r.Name
receivers = append(receivers, &open_api_models.Receiver{Name: &name})
}

return receiver_ops.NewGetReceiversOK().WithPayload(receivers)
Expand Down Expand Up @@ -660,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 @@ -670,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
20 changes: 13 additions & 7 deletions cmd/alertmanager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ func run() int {
dataDir = kingpin.Flag("storage.path", "Base path for data storage.").Default("data/").String()
retention = kingpin.Flag("data.retention", "How long to keep data for.").Default("120h").Duration()
maintenanceInterval = kingpin.Flag("data.maintenance-interval", "Interval between garbage collection and snapshotting to disk of the silences and the notification logs.").Default("15m").Duration()
maxSilences = kingpin.Flag("silences.max-silences", "Maximum number of silences, including expired silences. If negative or zero, no limit is set.").Default("0").Int()
maxSilenceSizeBytes = kingpin.Flag("silences.max-silence-size-bytes", "Maximum silence size in bytes. If negative or zero, no limit is set.").Default("0").Int()
alertGCInterval = kingpin.Flag("alerts.gc-interval", "Interval between alert GC.").Default("30m").Duration()

webConfig = webflag.AddFlags(kingpin.CommandLine, ":9093")
Expand Down Expand Up @@ -258,8 +260,12 @@ func run() int {
silenceOpts := silence.Options{
SnapshotFile: filepath.Join(*dataDir, "silences"),
Retention: *retention,
Logger: log.With(logger, "component", "silences"),
Metrics: prometheus.DefaultRegisterer,
Limits: silence.Limits{
MaxSilences: func() int { return *maxSilences },
MaxSilenceSizeBytes: func() int { return *maxSilenceSizeBytes },
},
Logger: log.With(logger, "component", "silences"),
Metrics: prometheus.DefaultRegisterer,
}

silences, err := silence.New(silenceOpts)
Expand Down Expand Up @@ -383,16 +389,16 @@ func run() int {

// Build the routing tree and record which receivers are used.
routes := dispatch.NewRoute(conf.Route, nil)
activeReceivers := make(map[string]struct{})
activeReceiversMap := make(map[string]struct{})
routes.Walk(func(r *dispatch.Route) {
activeReceivers[r.RouteOpts.Receiver] = struct{}{}
activeReceiversMap[r.RouteOpts.Receiver] = struct{}{}
})

// Build the map of receiver to integrations.
receivers := make(map[string][]notify.Integration, len(activeReceivers))
receivers := make(map[string][]*notify.Integration, len(activeReceiversMap))
var integrationsNum int
for _, rcv := range conf.Receivers {
if _, found := activeReceivers[rcv.Name]; !found {
if _, found := activeReceiversMap[rcv.Name]; !found {
// No need to build a receiver if no route is using it.
level.Info(configLogger).Log("msg", "skipping creation of receiver not referenced by any route", "receiver", rcv.Name)
continue
Expand Down Expand Up @@ -442,7 +448,7 @@ func run() int {
pipelinePeer,
)

configuredReceivers.Set(float64(len(activeReceivers)))
configuredReceivers.Set(float64(len(activeReceiversMap)))
configuredIntegrations.Set(float64(integrationsNum))
configuredInhibitionRules.Set(float64(len(conf.InhibitRules)))

Expand Down
4 changes: 2 additions & 2 deletions config/receiver/receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ import (

// BuildReceiverIntegrations builds a list of integration notifiers off of a
// receiver config.
func BuildReceiverIntegrations(nc config.Receiver, tmpl *template.Template, logger log.Logger, httpOpts ...commoncfg.HTTPClientOption) ([]notify.Integration, error) {
func BuildReceiverIntegrations(nc config.Receiver, tmpl *template.Template, logger log.Logger, httpOpts ...commoncfg.HTTPClientOption) ([]*notify.Integration, error) {
var (
errs types.MultiError
integrations []notify.Integration
integrations []*notify.Integration
add = func(name string, i int, rs notify.ResolvedSender, f func(l log.Logger) (notify.Notifier, error)) {
n, err := f(log.With(logger, "integration", name))
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions config/receiver/receiver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestBuildReceiverIntegrations(t *testing.T) {
for _, tc := range []struct {
receiver config.Receiver
err bool
exp []notify.Integration
exp []*notify.Integration
}{
{
receiver: config.Receiver{
Expand All @@ -48,7 +48,7 @@ func TestBuildReceiverIntegrations(t *testing.T) {
},
},
},
exp: []notify.Integration{
exp: []*notify.Integration{
notify.NewIntegration(nil, sendResolved(false), "webhook", 0, "foo"),
notify.NewIntegration(nil, sendResolved(true), "webhook", 1, "foo"),
},
Expand Down
Loading