feat(api): expose alert fingerprint in the API response

This commit is contained in:
Łukasz Mierzwa
2020-06-30 20:56:41 +01:00
committed by Łukasz Mierzwa
parent 018e4486f8
commit 760acee8d7
8 changed files with 145 additions and 11 deletions

View File

@@ -244,6 +244,7 @@ func (am *Alertmanager) pullAlerts(version string) error {
alert.Alertmanager = []models.AlertmanagerInstance{
{
Fingerprint: alert.Fingerprint,
Name: am.Name,
Cluster: am.ClusterName(),
State: alert.State,

View File

@@ -0,0 +1,39 @@
package filters
import (
"fmt"
"github.com/prymitive/karma/internal/models"
)
type fingerprintFilter struct {
alertFilter
}
func (filter *fingerprintFilter) Match(alert *models.Alert, matches int) bool {
if filter.IsValid {
var isMatch bool
for _, am := range alert.Alertmanager {
m := filter.Matcher.Compare(am.Fingerprint, filter.Value)
if m {
isMatch = m
}
}
if isMatch {
filter.Hits++
}
return isMatch
}
e := fmt.Sprintf("Match() called on invalid filter %#v", filter)
panic(e)
}
func (filter *fingerprintFilter) MatchAlertmanager(am *models.AlertmanagerInstance) bool {
return filter.Matcher.Compare(am.Fingerprint, filter.Value)
}
func newFingerprintFilter() FilterT {
f := fingerprintFilter{}
f.IsAlertmanagerFilter = true
return &f
}

View File

@@ -114,6 +114,83 @@ var tests = []filterTest{
IsValid: false,
},
{
Expression: "@fingerprint=",
IsValid: false,
},
{
Expression: "@fingerprint==",
IsValid: false,
},
{
Expression: "@fingerprint<=active",
IsValid: false,
},
{
Expression: "@fingerprint=123",
IsValid: true,
Alert: models.Alert{},
IsMatch: false,
IsAlertmanagerMatch: true,
},
{
Expression: "@fingerprint!=123",
IsValid: true,
Alert: models.Alert{},
IsMatch: true,
IsAlertmanagerMatch: true,
},
{
Expression: "@fingerprint=1234",
IsValid: true,
Alert: models.Alert{},
Alertmanagers: []models.AlertmanagerInstance{
{Fingerprint: "1234"},
},
IsMatch: true,
IsAlertmanagerMatch: true,
},
{
Expression: "@fingerprint=~123",
IsValid: true,
Alert: models.Alert{},
Alertmanagers: []models.AlertmanagerInstance{
{Fingerprint: "01234"},
},
IsMatch: true,
IsAlertmanagerMatch: true,
},
{
Expression: "@fingerprint=abc",
IsValid: true,
Alert: models.Alert{},
Alertmanagers: []models.AlertmanagerInstance{
{Fingerprint: "12345"},
},
IsMatch: false,
IsAlertmanagerMatch: false,
},
{
Expression: "@fingerprint!=1a1",
IsValid: true,
Alert: models.Alert{},
Alertmanagers: []models.AlertmanagerInstance{
{Fingerprint: "1a1"},
},
IsMatch: false,
IsAlertmanagerMatch: false,
},
{
Expression: "@fingerprint!=cde",
IsValid: true,
Alert: models.Alert{},
Alertmanagers: []models.AlertmanagerInstance{
{Fingerprint: "abc"},
},
IsMatch: true,
IsAlertmanagerMatch: true,
},
{
Expression: "@silence_id=abcdef",
IsValid: true,
@@ -699,7 +776,7 @@ func TestFilters(t *testing.T) {
}
if f.GetIsValid() {
isAlertmanagerFilter := slices.StringInSlice(
[]string{"@age", "@alertmanager", "@cluster", "@state", "@silence_id", "@silence_ticket", "@silence_author"},
[]string{"@age", "@alertmanager", "@cluster", "@state", "@silence_id", "@silence_ticket", "@silence_author", "@fingerprint"},
f.GetName())
if isAlertmanagerFilter != f.GetIsAlertmanagerFilter() {
t.Errorf("[%s] GetIsAlertmanagerFilter() returned %#v while %#v was expected", ft.Expression, f.GetIsAlertmanagerFilter(), isAlertmanagerFilter)

View File

@@ -61,6 +61,12 @@ var AllFilters = []filterConfig{
Factory: newStateFilter,
Autocomplete: stateAutocomplete,
},
{
Label: "@fingerprint",
LabelRe: regexp.MustCompile("^@fingerprint$"),
SupportedOperators: []string{regexpOperator, equalOperator, notEqualOperator},
Factory: newFingerprintFilter,
},
{
Label: "@receiver",
LabelRe: regexp.MustCompile("^@receiver$"),

View File

@@ -56,6 +56,7 @@ func groups(c *client.Alertmanager, timeout time.Duration) ([]models.AlertGroup,
}
for _, alert := range group.Alerts {
a := models.Alert{
Fingerprint: *alert.Fingerprint,
Receiver: *group.Receiver.Name,
Annotations: models.AnnotationsFromMap(alert.Annotations),
Labels: alert.Labels,

View File

@@ -37,6 +37,7 @@ type Alert struct {
// those are not exposed in JSON, Alertmanager specific value will be in kept
// in the Alertmanager slice
// skip those when generating alert fingerprint too
Fingerprint string `json:"-" hash:"-"`
GeneratorURL string `json:"-" hash:"-"`
SilencedBy []string `json:"-" hash:"-"`
InhibitedBy []string `json:"-" hash:"-"`

View File

@@ -5,8 +5,9 @@ import "time"
// AlertmanagerInstance describes the Alertmanager instance alert was collected
// from
type AlertmanagerInstance struct {
Name string `json:"name"`
Cluster string `json:"cluster"`
Fingerprint string `json:"fingerprint"`
Name string `json:"name"`
Cluster string `json:"cluster"`
// per instance alert state
State string `json:"state"`
// timestamp collected from this instance, those on the alert itself

View File

@@ -12,8 +12,10 @@ import (
func TestDedupSharedMaps(t *testing.T) {
am := models.AlertmanagerInstance{
Cluster: "fakeCluster",
SilencedBy: []string{"fakeSilence1", "fakeSilence2"},
Fingerprint: "1",
Name: "am",
Cluster: "fakeCluster",
SilencedBy: []string{"fakeSilence1", "fakeSilence2"},
}
ag := models.APIAlertGroup{
AlertGroup: models.AlertGroup{
@@ -98,7 +100,8 @@ func TestDedupSharedMaps(t *testing.T) {
"state": "suppressed",
"alertmanager": [
{
"name": "",
"fingerprint": "1",
"name": "am",
"cluster": "fakeCluster",
"state": "",
"startsAt": "0001-01-01T00:00:00Z",
@@ -110,7 +113,8 @@ func TestDedupSharedMaps(t *testing.T) {
"inhibitedBy": null
},
{
"name": "",
"fingerprint": "1",
"name": "am",
"cluster": "fakeCluster",
"state": "",
"startsAt": "0001-01-01T00:00:00Z",
@@ -135,7 +139,8 @@ func TestDedupSharedMaps(t *testing.T) {
"state": "suppressed",
"alertmanager": [
{
"name": "",
"fingerprint": "1",
"name": "am",
"cluster": "fakeCluster",
"state": "",
"startsAt": "0001-01-01T00:00:00Z",
@@ -147,7 +152,8 @@ func TestDedupSharedMaps(t *testing.T) {
"inhibitedBy": null
},
{
"name": "",
"fingerprint": "1",
"name": "am",
"cluster": "fakeCluster",
"state": "",
"startsAt": "0001-01-01T00:00:00Z",
@@ -172,7 +178,8 @@ func TestDedupSharedMaps(t *testing.T) {
"state": "suppressed",
"alertmanager": [
{
"name": "",
"fingerprint": "1",
"name": "am",
"cluster": "fakeCluster",
"state": "",
"startsAt": "0001-01-01T00:00:00Z",
@@ -184,7 +191,8 @@ func TestDedupSharedMaps(t *testing.T) {
"inhibitedBy": null
},
{
"name": "",
"fingerprint": "1",
"name": "am",
"cluster": "fakeCluster",
"state": "",
"startsAt": "0001-01-01T00:00:00Z",