mirror of
https://github.com/prymitive/karma
synced 2026-02-13 20:59:53 +00:00
feat(api): expose common label map per group
This commit is contained in:
committed by
Łukasz Mierzwa
parent
121e8698f9
commit
a2ee4812f7
190
internal/models/__snapshots__/models.snapshot
Normal file
190
internal/models/__snapshots__/models.snapshot
Normal file
@@ -0,0 +1,190 @@
|
||||
/* snapshot: SharedMaps */
|
||||
{
|
||||
"receiver": "",
|
||||
"labels": {
|
||||
"alertname": "FakeAlert"
|
||||
},
|
||||
"alerts": [
|
||||
{
|
||||
"annotations": [
|
||||
{
|
||||
"name": "foo",
|
||||
"value": "bar",
|
||||
"visible": false,
|
||||
"isLink": false,
|
||||
"isAction": false
|
||||
}
|
||||
],
|
||||
"labels": {
|
||||
"instance": "1",
|
||||
"job": "node_exporter"
|
||||
},
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"state": "suppressed",
|
||||
"alertmanager": [
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
},
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
}
|
||||
],
|
||||
"receiver": "",
|
||||
"id": ""
|
||||
},
|
||||
{
|
||||
"annotations": [],
|
||||
"labels": {
|
||||
"instance": "2",
|
||||
"job": "node_exporter"
|
||||
},
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"state": "active",
|
||||
"alertmanager": [
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
},
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
}
|
||||
],
|
||||
"receiver": "",
|
||||
"id": ""
|
||||
},
|
||||
{
|
||||
"annotations": [],
|
||||
"labels": {
|
||||
"extra": "ignore",
|
||||
"instance": "3",
|
||||
"job": "blackbox"
|
||||
},
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"state": "suppressed",
|
||||
"alertmanager": [
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
},
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
}
|
||||
],
|
||||
"receiver": "",
|
||||
"id": ""
|
||||
}
|
||||
],
|
||||
"id": "",
|
||||
"alertmanagerCount": null,
|
||||
"stateCount": null,
|
||||
"totalAlerts": 0,
|
||||
"shared": {
|
||||
"annotations": [
|
||||
{
|
||||
"name": "summary",
|
||||
"value": "this is summary",
|
||||
"visible": false,
|
||||
"isLink": false,
|
||||
"isAction": false
|
||||
}
|
||||
],
|
||||
"labels": {},
|
||||
"silences": {
|
||||
"fakeCluster": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
]
|
||||
},
|
||||
"sources": [
|
||||
"https://am.example.com"
|
||||
],
|
||||
"clusters": [
|
||||
"fakeCluster"
|
||||
]
|
||||
},
|
||||
"allLabels": {
|
||||
"active": {
|
||||
"alertname": [
|
||||
"FakeAlert"
|
||||
],
|
||||
"instance": [
|
||||
"2"
|
||||
],
|
||||
"job": [
|
||||
"node_exporter"
|
||||
]
|
||||
},
|
||||
"suppressed": {
|
||||
"alertname": [
|
||||
"FakeAlert"
|
||||
],
|
||||
"instance": [
|
||||
"1",
|
||||
"3"
|
||||
],
|
||||
"job": [
|
||||
"blackbox",
|
||||
"node_exporter"
|
||||
]
|
||||
},
|
||||
"unprocessed": {}
|
||||
}
|
||||
}
|
||||
@@ -105,8 +105,9 @@ type APIAlertGroupSharedMaps struct {
|
||||
// annotations that are unique to that instance
|
||||
type APIAlertGroup struct {
|
||||
AlertGroup
|
||||
TotalAlerts int `json:"totalAlerts"`
|
||||
Shared APIAlertGroupSharedMaps `json:"shared"`
|
||||
TotalAlerts int `json:"totalAlerts"`
|
||||
Shared APIAlertGroupSharedMaps `json:"shared"`
|
||||
AllLabels map[string]map[string][]string `json:"allLabels"`
|
||||
}
|
||||
|
||||
func (ag *APIAlertGroup) dedupLabels() {
|
||||
@@ -293,9 +294,55 @@ func (ag *APIAlertGroup) dedupClusters() {
|
||||
sort.Strings(ag.Shared.Clusters)
|
||||
}
|
||||
|
||||
func (ag *APIAlertGroup) populateAllLabels() {
|
||||
ag.AllLabels = map[string]map[string][]string{
|
||||
AlertStateActive: {},
|
||||
AlertStateSuppressed: {},
|
||||
AlertStateUnprocessed: {},
|
||||
}
|
||||
|
||||
labels := map[string]int{}
|
||||
for _, alert := range ag.Alerts {
|
||||
for k := range alert.Labels {
|
||||
if _, ok := labels[k]; !ok {
|
||||
labels[k] = 0
|
||||
}
|
||||
labels[k]++
|
||||
}
|
||||
}
|
||||
|
||||
labelNames := map[string]struct{}{}
|
||||
totalAlerts := len(ag.Alerts)
|
||||
for k, totalValues := range labels {
|
||||
if totalValues == totalAlerts {
|
||||
labelNames[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for _, alert := range ag.Alerts {
|
||||
for k, v := range alert.Labels {
|
||||
if _, ok := labelNames[k]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := ag.AllLabels[alert.State][k]; !ok {
|
||||
ag.AllLabels[alert.State][k] = []string{}
|
||||
}
|
||||
if !slices.StringInSlice(ag.AllLabels[alert.State][k], v) {
|
||||
ag.AllLabels[alert.State][k] = append(ag.AllLabels[alert.State][k], v)
|
||||
}
|
||||
}
|
||||
}
|
||||
for state := range ag.AllLabels {
|
||||
for k := range ag.AllLabels[state] {
|
||||
sort.Strings(ag.AllLabels[state][k])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DedupSharedMaps will find all labels and annotations shared by all alerts
|
||||
// in this group and moved them to Shared namespace
|
||||
func (ag *APIAlertGroup) DedupSharedMaps() {
|
||||
ag.populateAllLabels()
|
||||
// remove all labels that are used for grouping
|
||||
ag.removeGroupingLabels()
|
||||
// don't dedup if we only have a single alert in this group
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package models_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
"github.com/beme/abide"
|
||||
|
||||
"github.com/prymitive/karma/internal/models"
|
||||
)
|
||||
@@ -44,7 +45,7 @@ func TestDedupSharedMaps(t *testing.T) {
|
||||
Alertmanager: []models.AlertmanagerInstance{am, am},
|
||||
},
|
||||
models.Alert{
|
||||
State: models.AlertStateSuppressed,
|
||||
State: models.AlertStateActive,
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{
|
||||
Name: "summary",
|
||||
@@ -70,6 +71,7 @@ func TestDedupSharedMaps(t *testing.T) {
|
||||
"alertname": "FakeAlert",
|
||||
"job": "blackbox",
|
||||
"instance": "3",
|
||||
"extra": "ignore",
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{am, am},
|
||||
},
|
||||
@@ -78,191 +80,16 @@ func TestDedupSharedMaps(t *testing.T) {
|
||||
}
|
||||
ag.DedupSharedMaps()
|
||||
|
||||
expectedJSON := `{
|
||||
"receiver": "",
|
||||
"labels": {
|
||||
"alertname": "FakeAlert"
|
||||
},
|
||||
"alerts": [
|
||||
{
|
||||
"annotations": [
|
||||
{
|
||||
"name": "foo",
|
||||
"value": "bar",
|
||||
"visible": false,
|
||||
"isLink": false,
|
||||
"isAction": false
|
||||
}
|
||||
],
|
||||
"labels": {
|
||||
"instance": "1",
|
||||
"job": "node_exporter"
|
||||
},
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"state": "suppressed",
|
||||
"alertmanager": [
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
},
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
}
|
||||
],
|
||||
"receiver": "",
|
||||
"id": ""
|
||||
},
|
||||
{
|
||||
"annotations": [],
|
||||
"labels": {
|
||||
"instance": "2",
|
||||
"job": "node_exporter"
|
||||
},
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"state": "suppressed",
|
||||
"alertmanager": [
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
},
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
}
|
||||
],
|
||||
"receiver": "",
|
||||
"id": ""
|
||||
},
|
||||
{
|
||||
"annotations": [],
|
||||
"labels": {
|
||||
"instance": "3",
|
||||
"job": "blackbox"
|
||||
},
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"state": "suppressed",
|
||||
"alertmanager": [
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
},
|
||||
{
|
||||
"fingerprint": "1",
|
||||
"name": "am",
|
||||
"cluster": "fakeCluster",
|
||||
"state": "",
|
||||
"startsAt": "0001-01-01T00:00:00Z",
|
||||
"source": "https://am.example.com/graph",
|
||||
"silencedBy": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
],
|
||||
"inhibitedBy": null
|
||||
}
|
||||
],
|
||||
"receiver": "",
|
||||
"id": ""
|
||||
}
|
||||
],
|
||||
"id": "",
|
||||
"alertmanagerCount": null,
|
||||
"stateCount": null,
|
||||
"totalAlerts": 0,
|
||||
"shared": {
|
||||
"annotations": [
|
||||
{
|
||||
"name": "summary",
|
||||
"value": "this is summary",
|
||||
"visible": false,
|
||||
"isLink": false,
|
||||
"isAction": false
|
||||
}
|
||||
],
|
||||
"labels": {},
|
||||
"silences": {
|
||||
"fakeCluster": [
|
||||
"fakeSilence1",
|
||||
"fakeSilence2"
|
||||
]
|
||||
},
|
||||
"sources": [
|
||||
"https://am.example.com"
|
||||
],
|
||||
"clusters": [
|
||||
"fakeCluster"
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
||||
agJSON, _ := json.MarshalIndent(ag, "", " ")
|
||||
if string(agJSON) != expectedJSON {
|
||||
diff := difflib.UnifiedDiff{
|
||||
A: difflib.SplitLines(expectedJSON),
|
||||
B: difflib.SplitLines(string(agJSON)),
|
||||
FromFile: "Expected",
|
||||
ToFile: "Current",
|
||||
Context: 3,
|
||||
}
|
||||
text, err := difflib.GetUnifiedDiffString(diff)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Errorf("JSON mismatch:\n%s", text)
|
||||
}
|
||||
abide.AssertReader(t, "SharedMaps", bytes.NewReader(agJSON))
|
||||
}
|
||||
|
||||
func TestDedupSharedMapsSingleGroup(t *testing.T) {
|
||||
ag := models.APIAlertGroup{
|
||||
AlertGroup: models.AlertGroup{
|
||||
Alerts: models.AlertList{
|
||||
models.Alert{Labels: map[string]string{"foo": "bar"}},
|
||||
models.Alert{Labels: map[string]string{"foo": "bar"}},
|
||||
models.Alert{State: models.AlertStateActive, Labels: map[string]string{"foo": "bar"}},
|
||||
models.Alert{State: models.AlertStateUnprocessed, Labels: map[string]string{"foo": "bar"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user