Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions models/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,23 @@ const (
ValuesAnnotation = "__values__"
ValueStringAnnotation = "__value_string__"
)

// filterAlertmanagerKV returns true if a label or annotation should be excluded
// because its name or value is empty.
func filterAlertmanagerKV(name, value string) bool {
return name == "" || value == ""
}

// FilterAlertmanagerLabel returns true if the label should be excluded from
// Alertmanager payloads. Labels are filtered when the name is empty,
// the value is empty, or the name matches NamespaceUIDLabel.
func FilterAlertmanagerLabel(name, value string) bool {
return filterAlertmanagerKV(name, value) || name == NamespaceUIDLabel
}

// FilterAlertmanagerAnnotation returns true if the annotation should be
// excluded from Alertmanager payloads. Annotations are filtered when the
// name or value is empty.
func FilterAlertmanagerAnnotation(name, value string) bool {
return filterAlertmanagerKV(name, value)
}
95 changes: 95 additions & 0 deletions models/labels_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package models

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestFilterAlertmanagerLabel(t *testing.T) {
cases := []struct {
name string
label string
value string
want bool
}{
{
name: "empty label name",
label: "",
value: "v",
want: true,
},
{
name: "empty label value",
label: "alertname",
value: "",
want: true,
},
{
name: "NamespaceUIDLabel",
label: NamespaceUIDLabel,
value: "some-uid",
want: true,
},
{
name: "valid label",
label: "severity",
value: "critical",
want: false,
},
{
name: "both empty",
label: "",
value: "",
want: true,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := FilterAlertmanagerLabel(tc.label, tc.value)
require.Equal(t, tc.want, got)
})
}
}

func TestFilterAlertmanagerAnnotation(t *testing.T) {
cases := []struct {
name string
annotation string
value string
want bool
}{
{
name: "empty annotation name",
annotation: "",
value: "v",
want: true,
},
{
name: "empty annotation value",
annotation: "summary",
value: "",
want: true,
},
{
name: "valid annotation",
annotation: "summary",
value: "alert fired",
want: false,
},
{
name: "NamespaceUIDLabel is not filtered for annotations",
annotation: NamespaceUIDLabel,
value: "some-uid",
want: false,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := FilterAlertmanagerAnnotation(tc.annotation, tc.value)
require.Equal(t, tc.want, got)
})
}
}
4 changes: 2 additions & 2 deletions notify/grafana_alertmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -908,15 +908,15 @@ func PostableAlertsToAlertmanagerAlerts(postableAlerts amv2.PostableAlerts, now
}

for k, v := range a.Labels {
if len(v) == 0 || k == models.NamespaceUIDLabel { // Skip empty and namespace UID labels.
if models.FilterAlertmanagerLabel(k, v) {
continue
}

alert.Labels[model.LabelName(k)] = model.LabelValue(v)
}

for k, v := range a.Annotations {
if len(v) == 0 { // Skip empty annotation.
if models.FilterAlertmanagerAnnotation(k, v) {
continue
}
alert.Annotations[model.LabelName(k)] = model.LabelValue(v)
Expand Down
79 changes: 79 additions & 0 deletions notify/grafana_alertmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,85 @@ func TestPutAlert(t *testing.T) {
},
}
},
}, {
title: "Removing NamespaceUIDLabel",
postableAlerts: amv2.PostableAlerts{
{
Alert: amv2.Alert{
Labels: amv2.LabelSet{"alertname": "Alert1", models.NamespaceUIDLabel: "some-uid"},
},
},
},
expAlerts: func(now time.Time) []*types.Alert {
return []*types.Alert{
{
Alert: model.Alert{
Annotations: model.LabelSet{},
Labels: model.LabelSet{"alertname": "Alert1"},
StartsAt: now,
EndsAt: now.Add(defaultResolveTimeout),
GeneratorURL: "",
},
UpdatedAt: now,
Timeout: true,
},
}
},
}, {
title: "Removing labels with empty name",
postableAlerts: amv2.PostableAlerts{
{
Alert: amv2.Alert{
Labels: amv2.LabelSet{"alertname": "Alert1", "": "empty-name-value"},
GeneratorURL: "http://localhost/url1",
},
StartsAt: strfmt.DateTime{},
EndsAt: strfmt.DateTime{},
},
},
expAlerts: func(now time.Time) []*types.Alert {
return []*types.Alert{
{
Alert: model.Alert{
Annotations: model.LabelSet{},
Labels: model.LabelSet{"alertname": "Alert1"},
StartsAt: now,
EndsAt: now.Add(defaultResolveTimeout),
GeneratorURL: "http://localhost/url1",
},
UpdatedAt: now,
Timeout: true,
},
}
},
}, {
title: "Removing annotations with empty name",
postableAlerts: amv2.PostableAlerts{
{
Annotations: amv2.LabelSet{"summary": "valid", "": "empty-name-annotation"},
Alert: amv2.Alert{
Labels: amv2.LabelSet{"alertname": "Alert1"},
GeneratorURL: "http://localhost/url1",
},
StartsAt: strfmt.DateTime{},
EndsAt: strfmt.DateTime{},
},
},
expAlerts: func(now time.Time) []*types.Alert {
return []*types.Alert{
{
Alert: model.Alert{
Annotations: model.LabelSet{"summary": "valid"},
Labels: model.LabelSet{"alertname": "Alert1"},
StartsAt: now,
EndsAt: now.Add(defaultResolveTimeout),
GeneratorURL: "http://localhost/url1",
},
UpdatedAt: now,
Timeout: true,
},
}
},
}, {
title: "Allow spaces in label and annotation name",
postableAlerts: amv2.PostableAlerts{
Expand Down