diff --git a/config/config.go b/config/config.go index bf236aefe0..65484bb3e0 100644 --- a/config/config.go +++ b/config/config.go @@ -866,6 +866,8 @@ type InhibitRule struct { // A set of labels that must be equal between the source and target alert // for them to be a match. Equal model.LabelNames `yaml:"equal,omitempty" json:"equal,omitempty"` + // EqualPairs defines a set of pairs that have to be equal in the source and target alert + EqualPairs []LabelPair `yaml:"equal_pairs,omitempty" json:"equal_pairs,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface for InhibitRule. @@ -890,6 +892,12 @@ func (r *InhibitRule) UnmarshalYAML(unmarshal func(interface{}) error) error { return nil } +// LabelPair defines a source / target label pair +type LabelPair struct { + SourceLabel model.LabelName `yaml:"source_label,omitempty" json:"source_label,omitempty"` + TargetLabel model.LabelName `yaml:"target_label,omitempty" json:"target_label,omitempty"` +} + // Receiver configuration provides configuration on how to contact a receiver. type Receiver struct { // A unique identifier for this receiver. diff --git a/docs/configuration.md b/docs/configuration.md index 13bc3bc4c8..5da0e2910e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -413,6 +413,17 @@ source_matchers: # alert for the inhibition to take effect. [ equal: '[' , ... ']' ] +# Label pairs that must have an equal value in the source and target +# alert for the inhibition to take effect. +equal_pairs: + [ - ... ] + +``` + +#### `` +```yaml +source_label: +target_label: ``` ## Label matchers diff --git a/inhibit/inhibit.go b/inhibit/inhibit.go index a8f96f28aa..8c2d7fb061 100644 --- a/inhibit/inhibit.go +++ b/inhibit/inhibit.go @@ -157,19 +157,29 @@ type InhibitRule struct { // The set of Filters which define the group of target alerts (which are // inhibited by the source alerts). TargetMatchers labels.Matchers - // A set of label names whose label values need to be identical in source and + // A set of label name pairs whose label values need to be identical in source and // target alerts in order for the inhibition to take effect. - Equal map[model.LabelName]struct{} + EqualPairs LabelPairs // Cache of alerts matching source labels. scache *store.Alerts } +// LabelPairs defines a set of source / target label pairs +type LabelPairs []LabelPair + +// LabelPair defines a source / target label pair +type LabelPair struct { + SourceLabel model.LabelName + TargetLabel model.LabelName +} + // NewInhibitRule returns a new InhibitRule based on a configuration definition. func NewInhibitRule(cr config.InhibitRule) *InhibitRule { var ( sourcem labels.Matchers targetm labels.Matchers + pairs LabelPairs ) // cr.SourceMatch will be deprecated. This for loop appends regex matchers. for ln, lv := range cr.SourceMatch { @@ -213,15 +223,26 @@ func NewInhibitRule(cr config.InhibitRule) *InhibitRule { // We append the new-style matchers. This can be simplified once the deprecated matcher syntax is removed. targetm = append(targetm, cr.TargetMatchers...) - equal := map[model.LabelName]struct{}{} for _, ln := range cr.Equal { - equal[ln] = struct{}{} + pair := LabelPair{ + SourceLabel: ln, + TargetLabel: ln, + } + pairs = append(pairs, pair) + } + + for _, p := range cr.EqualPairs { + pair := LabelPair{ + SourceLabel: p.SourceLabel, + TargetLabel: p.TargetLabel, + } + pairs = append(pairs, pair) } return &InhibitRule{ SourceMatchers: sourcem, TargetMatchers: targetm, - Equal: equal, + EqualPairs: pairs, scache: store.NewAlerts(), } } @@ -237,8 +258,8 @@ Outer: if a.Resolved() { continue } - for n := range r.Equal { - if a.Labels[n] != lset[n] { + for _, p := range r.EqualPairs { + if a.Labels[p.SourceLabel] != lset[p.TargetLabel] { continue Outer } } diff --git a/inhibit/inhibit_test.go b/inhibit/inhibit_test.go index 24089a671d..8eaf478a15 100644 --- a/inhibit/inhibit_test.go +++ b/inhibit/inhibit_test.go @@ -35,10 +35,11 @@ func TestInhibitRuleHasEqual(t *testing.T) { now := time.Now() cases := []struct { - initial map[model.Fingerprint]*types.Alert - equal model.LabelNames - input model.LabelSet - result bool + initial map[model.Fingerprint]*types.Alert + equal model.LabelNames + equalPairs LabelPairs + input model.LabelSet + result bool }{ { // No source alerts at all. @@ -118,16 +119,62 @@ func TestInhibitRuleHasEqual(t *testing.T) { input: model.LabelSet{"a": "b"}, result: false, }, + { + // Equal label pair does match + initial: map[model.Fingerprint]*types.Alert{ + 1: { + Alert: model.Alert{ + Labels: model.LabelSet{"a": "b"}, + StartsAt: now.Add(-time.Minute), + EndsAt: now.Add(time.Hour), + }, + }, + }, + equalPairs: LabelPairs{ + { + SourceLabel: "a", + TargetLabel: "x", + }, + }, + input: model.LabelSet{"x": "b"}, + result: true, + }, + { + // Equal label pair does not match + initial: map[model.Fingerprint]*types.Alert{ + 1: { + Alert: model.Alert{ + Labels: model.LabelSet{"a": "b"}, + StartsAt: now.Add(-time.Minute), + EndsAt: now.Add(time.Hour), + }, + }, + }, + equalPairs: LabelPairs{ + { + SourceLabel: "a", + TargetLabel: "x", + }, + }, + input: model.LabelSet{"x": "c"}, + result: false, + }, } for _, c := range cases { r := &InhibitRule{ - Equal: map[model.LabelName]struct{}{}, - scache: store.NewAlerts(), + EqualPairs: LabelPairs{}, + scache: store.NewAlerts(), } for _, ln := range c.equal { - r.Equal[ln] = struct{}{} + pair := LabelPair{ + SourceLabel: ln, + TargetLabel: ln, + } + r.EqualPairs = append(r.EqualPairs, pair) } + r.EqualPairs = append(r.EqualPairs, c.equalPairs...) + for _, v := range c.initial { r.scache.Set(v) }