Skip to content

Commit 35be6d7

Browse files
committed
add multiple sources for inhibition
Signed-off-by: Coleen Iona Quadros <[email protected]>
1 parent 64a7edf commit 35be6d7

File tree

5 files changed

+176
-61
lines changed

5 files changed

+176
-61
lines changed

config/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,11 @@ func (r *Route) UnmarshalYAML(unmarshal func(any) error) error {
959959
return nil
960960
}
961961

962+
type Source struct {
963+
SrcMatchers Matchers `yaml:"matchers,omitempty" json:"matchers,omitempty"`
964+
Equal []string `yaml:"equal,omitempty" json:"equal,omitempty"`
965+
}
966+
962967
// InhibitRule defines an inhibition rule that mutes alerts that match the
963968
// target labels if an alert matching the source labels exists.
964969
// Both alerts have to have a set of labels being equal.
@@ -973,6 +978,8 @@ type InhibitRule struct {
973978
SourceMatchRE MatchRegexps `yaml:"source_match_re,omitempty" json:"source_match_re,omitempty"`
974979
// SourceMatchers defines a set of label matchers that have to be fulfilled for source alerts.
975980
SourceMatchers Matchers `yaml:"source_matchers,omitempty" json:"source_matchers,omitempty"`
981+
// Sources defines a set of source matchers and equal labels.
982+
Sources []Source `yaml:"source,omitempty" json:"source,omitempty"`
976983
// TargetMatch defines a set of labels that have to equal the given
977984
// value for target alerts. Deprecated. Remove before v1.0 release.
978985
TargetMatch map[string]string `yaml:"target_match,omitempty" json:"target_match,omitempty"`

inhibit/inhibit.go

Lines changed: 103 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package inhibit
1616
import (
1717
"context"
1818
"log/slog"
19+
"strings"
1920
"sync"
2021
"time"
2122

@@ -87,14 +88,31 @@ func (ih *Inhibitor) run(ctx context.Context) {
8788
cachedSum := 0
8889
indexedSum := 0
8990
for _, r := range ih.rules {
90-
if r.SourceMatchers.Matches(a.Labels) {
91-
if err := r.scache.Set(a); err != nil {
92-
ih.logger.Error("error on set alert", "err", err)
93-
continue
91+
if len(r.Sources) > 0 {
92+
allSrcMatched := true
93+
for _, src := range r.Sources {
94+
if !src.SrcMatchers.Matches(a.Labels) {
95+
allSrcMatched = false
96+
break
97+
}
98+
}
99+
if allSrcMatched {
100+
if err := r.scache.Set(a); err != nil {
101+
ih.logger.Error("error on set alert", "err", err)
102+
continue
103+
}
104+
r.updateIndex(a)
105+
}
106+
} else {
107+
if r.SourceMatchers.Matches(a.Labels) {
108+
if err := r.scache.Set(a); err != nil {
109+
ih.logger.Error("error on set alert", "err", err)
110+
continue
111+
}
112+
r.updateIndex(a)
94113
}
95-
r.updateIndex(a)
96-
97114
}
115+
98116
cached := r.scache.Len()
99117
indexed := r.sindex.Len()
100118

@@ -169,23 +187,58 @@ func (ih *Inhibitor) Mutes(lset model.LabelSet) bool {
169187
r.metrics.matchesDurationMatched.Observe(time.Since(ruleStart).Seconds())
170188
// If we are here, the target side matches. If the source side matches, too, we
171189
// need to exclude inhibiting alerts for which the same is true.
172-
if inhibitedByFP, eq := r.hasEqual(lset, r.SourceMatchers.Matches(lset), ruleStart); eq {
173-
ih.marker.SetInhibited(fp, inhibitedByFP.String())
174-
now := time.Now()
175-
sinceStart := now.Sub(start)
176-
sinceRuleStart := now.Sub(ruleStart)
177-
ih.metrics.mutesDurationMuted.Observe(sinceStart.Seconds())
178-
r.metrics.mutesDurationMuted.Observe(sinceRuleStart.Seconds())
179-
return true
190+
191+
if len(r.Sources) > 0 {
192+
allSourcesMatched := true
193+
var inhibitorIDs []string
194+
for _, source := range r.Sources {
195+
if inhibitedByFP, eq := r.hasEqual(lset, source.SrcMatchers.Matches(lset), ruleStart); eq {
196+
inhibitorIDs = append(inhibitorIDs, inhibitedByFP.String())
197+
} else {
198+
allSourcesMatched = false
199+
break
200+
}
201+
}
202+
if allSourcesMatched {
203+
compositeInhibitorID := strings.Join(inhibitorIDs, ",")
204+
ih.marker.SetInhibited(fp, compositeInhibitorID)
205+
now := time.Now()
206+
sinceStart := now.Sub(start)
207+
sinceRuleStart := now.Sub(ruleStart)
208+
ih.metrics.mutesDurationMuted.Observe(sinceStart.Seconds())
209+
r.metrics.mutesDurationMuted.Observe(sinceRuleStart.Seconds())
210+
return true
211+
}
212+
} else {
213+
if inhibitedByFP, eq := r.hasEqual(lset, r.SourceMatchers.Matches(lset), ruleStart); eq {
214+
ih.marker.SetInhibited(fp, inhibitedByFP.String())
215+
now := time.Now()
216+
sinceStart := now.Sub(start)
217+
sinceRuleStart := now.Sub(ruleStart)
218+
ih.metrics.mutesDurationMuted.Observe(sinceStart.Seconds())
219+
r.metrics.mutesDurationMuted.Observe(sinceRuleStart.Seconds())
220+
return true
221+
}
222+
180223
}
181224
r.metrics.mutesDurationNotMuted.Observe(time.Since(ruleStart).Seconds())
182225
}
226+
183227
ih.marker.SetInhibited(fp)
184228
ih.metrics.mutesDurationNotMuted.Observe(time.Since(start).Seconds())
185229

186230
return false
187231
}
188232

233+
type Source struct {
234+
// The set of Filters which define the group of source alerts (which inhibit
235+
// the target alerts).
236+
SrcMatchers labels.Matchers
237+
// A set of label names whose label values need to be identical in source and
238+
// target alerts in order for the inhibition to take effect.
239+
Equal map[model.LabelName]struct{}
240+
}
241+
189242
// An InhibitRule specifies that a class of (source) alerts should inhibit
190243
// notifications for another class of (target) alerts if all specified matching
191244
// labels are equal between the two alerts. This may be used to inhibit alerts
@@ -197,6 +250,7 @@ type InhibitRule struct {
197250
// The set of Filters which define the group of source alerts (which inhibit
198251
// the target alerts).
199252
SourceMatchers labels.Matchers
253+
Sources []Source
200254
// The set of Filters which define the group of target alerts (which are
201255
// inhibited by the source alerts).
202256
TargetMatchers labels.Matchers
@@ -219,30 +273,46 @@ type InhibitRule struct {
219273
// NewInhibitRule returns a new InhibitRule based on a configuration definition.
220274
func NewInhibitRule(cr config.InhibitRule, metrics *RuleMetrics) *InhibitRule {
221275
var (
276+
sources []Source
222277
sourcem labels.Matchers
223278
targetm labels.Matchers
224279
)
225280

226-
// cr.SourceMatch will be deprecated. This for loop appends regex matchers.
227-
for ln, lv := range cr.SourceMatch {
228-
matcher, err := labels.NewMatcher(labels.MatchEqual, ln, lv)
229-
if err != nil {
230-
// This error must not happen because the config already validates the yaml.
231-
panic(err)
281+
if len(cr.Sources) > 0 {
282+
for _, sm := range cr.Sources {
283+
sourcem = append(sourcem, sm.SrcMatchers...)
284+
equal := map[model.LabelName]struct{}{}
285+
for _, ln := range sm.Equal {
286+
equal[model.LabelName(ln)] = struct{}{}
287+
}
288+
sources = append(sources, Source{
289+
SrcMatchers: sourcem,
290+
Equal: equal,
291+
})
232292
}
233-
sourcem = append(sourcem, matcher)
234-
}
235-
// cr.SourceMatchRE will be deprecated. This for loop appends regex matchers.
236-
for ln, lv := range cr.SourceMatchRE {
237-
matcher, err := labels.NewMatcher(labels.MatchRegexp, ln, lv.String())
238-
if err != nil {
239-
// This error must not happen because the config already validates the yaml.
240-
panic(err)
293+
} else {
294+
295+
// cr.SourceMatch will be deprecated. This for loop appends regex matchers.
296+
for ln, lv := range cr.SourceMatch {
297+
matcher, err := labels.NewMatcher(labels.MatchEqual, ln, lv)
298+
if err != nil {
299+
// This error must not happen because the config already validates the yaml.
300+
panic(err)
301+
}
302+
sourcem = append(sourcem, matcher)
241303
}
242-
sourcem = append(sourcem, matcher)
304+
// cr.SourceMatchRE will be deprecated. This for loop appends regex matchers.
305+
for ln, lv := range cr.SourceMatchRE {
306+
matcher, err := labels.NewMatcher(labels.MatchRegexp, ln, lv.String())
307+
if err != nil {
308+
// This error must not happen because the config already validates the yaml.
309+
panic(err)
310+
}
311+
sourcem = append(sourcem, matcher)
312+
}
313+
// We append the new-style matchers. This can be simplified once the deprecated matcher syntax is removed.
314+
sourcem = append(sourcem, cr.SourceMatchers...)
243315
}
244-
// We append the new-style matchers. This can be simplified once the deprecated matcher syntax is removed.
245-
sourcem = append(sourcem, cr.SourceMatchers...)
246316

247317
// cr.TargetMatch will be deprecated. This for loop appends regex matchers.
248318
for ln, lv := range cr.TargetMatch {
@@ -278,6 +348,7 @@ func NewInhibitRule(cr config.InhibitRule, metrics *RuleMetrics) *InhibitRule {
278348
scache: store.NewAlerts(),
279349
sindex: newIndex(),
280350
metrics: metrics,
351+
Sources: sources,
281352
}
282353

283354
rule.scache.SetGCCallback(rule.gcCallback)
@@ -288,6 +359,7 @@ func NewInhibitRule(cr config.InhibitRule, metrics *RuleMetrics) *InhibitRule {
288359
// fingerprintEquals returns the fingerprint of the equal labels of the given label set.
289360
func (r *InhibitRule) fingerprintEquals(lset model.LabelSet) model.Fingerprint {
290361
equalSet := model.LabelSet{}
362+
291363
for n := range r.Equal {
292364
equalSet[n] = lset[n]
293365
}

inhibit/inhibit_bench_test.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,12 @@ func allRulesMatchBenchmark(b *testing.B, numInhibitionRules, numInhibitingAlert
109109
n: numInhibitionRules,
110110
newRuleFunc: func(idx int) config.InhibitRule {
111111
return config.InhibitRule{
112-
SourceMatchers: config.Matchers{
113-
mustNewMatcher(b, labels.MatchEqual, "src", strconv.Itoa(idx)),
112+
Sources: []config.Source{
113+
{
114+
SrcMatchers: config.Matchers{
115+
mustNewMatcher(b, labels.MatchEqual, "src", strconv.Itoa(idx)),
116+
},
117+
},
114118
},
115119
TargetMatchers: config.Matchers{
116120
mustNewMatcher(b, labels.MatchEqual, "dst", "0"),
@@ -152,8 +156,12 @@ func lastRuleMatchesBenchmark(b *testing.B, n int) benchmarkOptions {
152156
n: n,
153157
newRuleFunc: func(idx int) config.InhibitRule {
154158
return config.InhibitRule{
155-
SourceMatchers: config.Matchers{
156-
mustNewMatcher(b, labels.MatchEqual, "src", strconv.Itoa(idx)),
159+
Sources: []config.Source{
160+
{
161+
SrcMatchers: config.Matchers{
162+
mustNewMatcher(b, labels.MatchEqual, "src", strconv.Itoa(idx)),
163+
},
164+
},
157165
},
158166
TargetMatchers: config.Matchers{
159167
mustNewMatcher(b, labels.MatchEqual, "dst", "0"),

inhibit/inhibit_test.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,20 @@ func TestInhibitRuleMatchers(t *testing.T) {
250250
t.Parallel()
251251

252252
rule1 := config.InhibitRule{
253-
SourceMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchEqual, Name: "s1", Value: "1"}},
253+
Sources: []config.Source{
254+
{
255+
SrcMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchEqual, Name: "s1", Value: "1"}},
256+
},
257+
},
254258
TargetMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchNotEqual, Name: "t1", Value: "1"}},
255259
Equal: []string{"e"},
256260
}
257261
rule2 := config.InhibitRule{
258-
SourceMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchEqual, Name: "s2", Value: "1"}},
262+
Sources: []config.Source{
263+
{
264+
SrcMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchEqual, Name: "s2", Value: "1"}},
265+
},
266+
},
259267
TargetMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchEqual, Name: "t2", Value: "1"}},
260268
Equal: []string{"e"},
261269
}
@@ -352,17 +360,21 @@ func TestInhibitRuleName(t *testing.T) {
352360

353361
config1 := config.InhibitRule{
354362
Name: "test-rule",
355-
SourceMatchers: []*labels.Matcher{
356-
{Type: labels.MatchEqual, Name: "severity", Value: "critical"},
363+
Sources: []config.Source{
364+
{
365+
SrcMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchEqual, Name: "severity", Value: "critical"}},
366+
},
357367
},
358368
TargetMatchers: []*labels.Matcher{
359369
{Type: labels.MatchEqual, Name: "severity", Value: "warning"},
360370
},
361371
Equal: []string{"instance"},
362372
}
363373
config2 := config.InhibitRule{
364-
SourceMatchers: []*labels.Matcher{
365-
{Type: labels.MatchEqual, Name: "severity", Value: "critical"},
374+
Sources: []config.Source{
375+
{
376+
SrcMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchEqual, Name: "severity", Value: "critical"}},
377+
},
366378
},
367379
TargetMatchers: []*labels.Matcher{
368380
{Type: labels.MatchEqual, Name: "severity", Value: "warning"},

0 commit comments

Comments
 (0)