Alerting: Hook up GMA silence APIs to new authentication handler (#86625)

This PR connects the new RBAC authentication service to existing alertmanager API silence endpoints.
This commit is contained in:
Matthew Jacobson
2024-05-03 15:32:30 -04:00
committed by GitHub
parent e9b932c8f6
commit babfa2beac
25 changed files with 1192 additions and 230 deletions

View File

@@ -1,11 +1,37 @@
package models
// Silence is the model-layer representation of an alertmanager silence.
type Silence struct { // TODO implement using matchers
ID *string
RuleUID *string
import (
amv2 "github.com/prometheus/alertmanager/api/v2/models"
alertingModels "github.com/grafana/alerting/models"
"github.com/grafana/alerting/notify"
)
// Silence is the model-layer representation of an alertmanager silence. Currently just a wrapper around the
// alerting notify.Silence.
type Silence notify.GettableSilence
// GetRuleUID returns the rule UID of the silence if the silence is associated with a rule, otherwise nil.
// Currently, this works by looking for a matcher with the RuleUIDLabel name and returning its value.
func (s Silence) GetRuleUID() *string {
return getRuleUIDLabelValue(s.Silence)
}
func (s *Silence) GetRuleUID() *string {
return s.RuleUID
// getRuleUIDLabelValue returns the value of the RuleUIDLabel matcher in the given silence, if it exists.
func getRuleUIDLabelValue(silence notify.Silence) *string {
for _, m := range silence.Matchers {
if m != nil && isRuleUIDMatcher(*m) {
return m.Value
}
}
return nil
}
func isRuleUIDMatcher(m amv2.Matcher) bool {
return isEqualMatcher(m) && m.Name != nil && *m.Name == alertingModels.RuleUIDLabel
}
func isEqualMatcher(m amv2.Matcher) bool {
// If IsEqual is nil, it is considered to be true.
return (m.IsEqual == nil || *m.IsEqual) && (m.IsRegex == nil || !*m.IsRegex)
}

View File

@@ -0,0 +1,50 @@
package models
import (
"testing"
"github.com/prometheus/alertmanager/pkg/labels"
"github.com/stretchr/testify/assert"
"github.com/grafana/alerting/models"
"github.com/grafana/grafana/pkg/util"
)
func TestSilenceGetRuleUID(t *testing.T) {
testCases := []struct {
name string
silence Silence
expectedRuleUID *string
}{
{
name: "silence with no rule UID",
silence: SilenceGen()(),
expectedRuleUID: nil,
},
{
name: "silence with rule UID",
silence: SilenceGen(SilenceMuts.WithMatcher(models.RuleUIDLabel, "someuid", labels.MatchEqual))(),
expectedRuleUID: util.Pointer("someuid"),
},
{
name: "silence with rule UID Matcher but MatchNotEqual",
silence: SilenceGen(SilenceMuts.WithMatcher(models.RuleUIDLabel, "someuid", labels.MatchNotEqual))(),
expectedRuleUID: nil,
},
{
name: "silence with rule UID Matcher but MatchRegexp",
silence: SilenceGen(SilenceMuts.WithMatcher(models.RuleUIDLabel, "someuid", labels.MatchRegexp))(),
expectedRuleUID: nil,
},
{
name: "silence with rule UID Matcher but MatchNotRegexp",
silence: SilenceGen(SilenceMuts.WithMatcher(models.RuleUIDLabel, "someuid", labels.MatchNotRegexp))(),
expectedRuleUID: nil,
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expectedRuleUID, tt.silence.GetRuleUID(), "unexpected rule UID")
})
}
}

View File

@@ -9,7 +9,10 @@ import (
"testing"
"time"
"github.com/go-openapi/strfmt"
"github.com/grafana/grafana-plugin-sdk-go/data"
amv2 "github.com/prometheus/alertmanager/api/v2/models"
"github.com/prometheus/alertmanager/pkg/labels"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
@@ -903,3 +906,117 @@ func (n NotificationSettingsMutators) WithMuteTimeIntervals(muteTimeIntervals ..
ns.MuteTimeIntervals = muteTimeIntervals
}
}
// Silences
// CopySilenceWith creates a deep copy of Silence and then applies mutators to it.
func CopySilenceWith(s Silence, mutators ...Mutator[Silence]) Silence {
c := CopySilence(s)
for _, mutator := range mutators {
mutator(&c)
}
return c
}
// CopySilence creates a deep copy of Silence.
func CopySilence(s Silence) Silence {
c := Silence{
Silence: amv2.Silence{},
}
if s.ID != nil {
c.ID = util.Pointer(*s.ID)
}
if s.Status != nil {
c.Status = util.Pointer(*s.Status)
}
if s.UpdatedAt != nil {
c.UpdatedAt = util.Pointer(*s.UpdatedAt)
}
if s.Silence.Comment != nil {
c.Silence.Comment = util.Pointer(*s.Silence.Comment)
}
if s.Silence.CreatedBy != nil {
c.Silence.CreatedBy = util.Pointer(*s.Silence.CreatedBy)
}
if s.Silence.EndsAt != nil {
c.Silence.EndsAt = util.Pointer(*s.Silence.EndsAt)
}
if s.Silence.StartsAt != nil {
c.Silence.StartsAt = util.Pointer(*s.Silence.StartsAt)
}
if s.Silence.Matchers != nil {
c.Silence.Matchers = CopyMatchers(s.Silence.Matchers)
}
return c
}
// CopyMatchers creates a deep copy of Matchers.
func CopyMatchers(matchers []*amv2.Matcher) []*amv2.Matcher {
copies := make([]*amv2.Matcher, len(matchers))
for i, m := range matchers {
c := amv2.Matcher{}
if m.IsEqual != nil {
c.IsEqual = util.Pointer(*m.IsEqual)
}
if m.IsRegex != nil {
c.IsRegex = util.Pointer(*m.IsRegex)
}
if m.Name != nil {
c.Name = util.Pointer(*m.Name)
}
if m.Value != nil {
c.Value = util.Pointer(*m.Value)
}
copies[i] = &c
}
return copies
}
// SilenceGen generates Silence using a base and mutators.
func SilenceGen(mutators ...Mutator[Silence]) func() Silence {
return func() Silence {
now := time.Now()
c := Silence{
ID: util.Pointer(util.GenerateShortUID()),
Status: util.Pointer(amv2.SilenceStatus{State: util.Pointer(amv2.SilenceStatusStateActive)}),
UpdatedAt: util.Pointer(strfmt.DateTime(now.Add(time.Minute))),
Silence: amv2.Silence{
Comment: util.Pointer(util.GenerateShortUID()),
CreatedBy: util.Pointer(util.GenerateShortUID()),
StartsAt: util.Pointer(strfmt.DateTime(now.Add(-time.Minute))),
EndsAt: util.Pointer(strfmt.DateTime(now.Add(time.Minute))),
Matchers: []*amv2.Matcher{{Name: util.Pointer(util.GenerateShortUID()), Value: util.Pointer(util.GenerateShortUID()), IsRegex: util.Pointer(false), IsEqual: util.Pointer(true)}},
},
}
for _, mutator := range mutators {
mutator(&c)
}
return c
}
}
var (
SilenceMuts = SilenceMutators{}
)
type SilenceMutators struct{}
func (n SilenceMutators) WithMatcher(name, value string, matchType labels.MatchType) Mutator[Silence] {
return func(s *Silence) {
m := amv2.Matcher{
Name: &name,
Value: &value,
IsRegex: util.Pointer(matchType == labels.MatchRegexp || matchType == labels.MatchNotRegexp),
IsEqual: util.Pointer(matchType == labels.MatchRegexp || matchType == labels.MatchEqual),
}
s.Silence.Matchers = append(s.Silence.Matchers, &m)
}
}
func (n SilenceMutators) WithEmptyId() Mutator[Silence] {
return func(s *Silence) {
s.ID = util.Pointer("")
}
}