mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
feat(api): expose alert fingerprint in the API response
This commit is contained in:
committed by
Łukasz Mierzwa
parent
018e4486f8
commit
760acee8d7
@@ -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,
|
||||
|
||||
39
internal/filters/filter_fingerprint.go
Normal file
39
internal/filters/filter_fingerprint.go
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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$"),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:"-"`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user