mirror of
https://github.com/prymitive/karma
synced 2026-05-07 03:26:52 +00:00
refactor(api): deduplicate lables & annotations in API responses
Each alert in a group holds only keys that are unique to that alert instance, everything shared by the entire group is moved to group.shared ns
This commit is contained in:
254
api_test.go
254
api_test.go
@@ -18,6 +18,7 @@ type groupTest struct {
|
||||
alerts []models.Alert
|
||||
hash string
|
||||
id string
|
||||
shared models.APIAlertGroupSharedMaps
|
||||
stateCount map[string]int
|
||||
}
|
||||
|
||||
@@ -34,10 +35,9 @@ var groupTests = []groupTest{
|
||||
models.Annotation{Visible: true, Name: "dashboard", Value: "http://localhost/dashboard.html", IsLink: true},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Memory_Usage_Too_High",
|
||||
"cluster": "prod",
|
||||
"instance": "server2",
|
||||
"job": "node_exporter",
|
||||
"cluster": "prod",
|
||||
"instance": "server2",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -80,10 +80,8 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Memory_Usage_Too_High",
|
||||
"cluster": "prod",
|
||||
"instance": "server2",
|
||||
"job": "node_exporter",
|
||||
"instance": "server2",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
@@ -105,9 +103,7 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
Name: "default",
|
||||
@@ -117,18 +113,13 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "staging",
|
||||
"instance": "server3",
|
||||
"job": "node_ping",
|
||||
"instance": "server3",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
Name: "default",
|
||||
@@ -138,18 +129,13 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "staging",
|
||||
"instance": "server4",
|
||||
"job": "node_ping",
|
||||
"instance": "server4",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
Name: "default",
|
||||
@@ -159,10 +145,7 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "staging",
|
||||
"instance": "server5",
|
||||
"job": "node_ping",
|
||||
"instance": "server5",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
@@ -170,6 +153,14 @@ var groupTests = []groupTest{
|
||||
},
|
||||
hash: "db53e38245a7afe18f923518146326b6fe53109a",
|
||||
id: "2d3f39413b41c873cb72e0b8065aa7b8631e983e",
|
||||
shared: models.APIAlertGroupSharedMaps{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"job": "node_ping",
|
||||
},
|
||||
},
|
||||
stateCount: map[string]int{
|
||||
models.AlertStateActive: 3,
|
||||
models.AlertStateSuppressed: 0,
|
||||
@@ -184,9 +175,7 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
Name: "default",
|
||||
@@ -202,18 +191,13 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "dev",
|
||||
"instance": "server6",
|
||||
"job": "node_ping",
|
||||
"instance": "server6",
|
||||
},
|
||||
State: models.AlertStateSuppressed,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
Name: "default",
|
||||
@@ -232,18 +216,13 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "dev",
|
||||
"instance": "server7",
|
||||
"job": "node_ping",
|
||||
"instance": "server7",
|
||||
},
|
||||
State: models.AlertStateSuppressed,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
Name: "default",
|
||||
@@ -258,10 +237,7 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "dev",
|
||||
"instance": "server8",
|
||||
"job": "node_ping",
|
||||
"instance": "server8",
|
||||
},
|
||||
State: models.AlertStateSuppressed,
|
||||
Receiver: "by-cluster-service",
|
||||
@@ -269,6 +245,14 @@ var groupTests = []groupTest{
|
||||
},
|
||||
hash: "bcb440cdee1d6f818599cf405c40f3382a4b1229",
|
||||
id: "3c09c4156e6784dcf6d5b2e1629253798f82909b",
|
||||
shared: models.APIAlertGroupSharedMaps{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"job": "node_ping",
|
||||
},
|
||||
},
|
||||
stateCount: map[string]int{
|
||||
models.AlertStateActive: 0,
|
||||
models.AlertStateSuppressed: 3,
|
||||
@@ -283,14 +267,11 @@ var groupTests = []groupTest{
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
models.Annotation{Visible: true, Name: "url", Value: "http://localhost/example.html", IsLink: true},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "prod",
|
||||
"instance": "server1",
|
||||
"job": "node_ping",
|
||||
"cluster": "prod",
|
||||
"instance": "server1",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -304,14 +285,10 @@ var groupTests = []groupTest{
|
||||
Receiver: "by-name",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "prod",
|
||||
"instance": "server2",
|
||||
"job": "node_ping",
|
||||
"cluster": "prod",
|
||||
"instance": "server2",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -325,14 +302,10 @@ var groupTests = []groupTest{
|
||||
Receiver: "by-name",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "staging",
|
||||
"instance": "server3",
|
||||
"job": "node_ping",
|
||||
"cluster": "staging",
|
||||
"instance": "server3",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -346,14 +319,10 @@ var groupTests = []groupTest{
|
||||
Receiver: "by-name",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "staging",
|
||||
"instance": "server4",
|
||||
"job": "node_ping",
|
||||
"cluster": "staging",
|
||||
"instance": "server4",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -367,14 +336,10 @@ var groupTests = []groupTest{
|
||||
Receiver: "by-name",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "staging",
|
||||
"instance": "server5",
|
||||
"job": "node_ping",
|
||||
"cluster": "staging",
|
||||
"instance": "server5",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -388,14 +353,10 @@ var groupTests = []groupTest{
|
||||
Receiver: "by-name",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "dev",
|
||||
"instance": "server6",
|
||||
"job": "node_ping",
|
||||
"cluster": "dev",
|
||||
"instance": "server6",
|
||||
},
|
||||
State: models.AlertStateSuppressed,
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -414,14 +375,10 @@ var groupTests = []groupTest{
|
||||
Receiver: "by-name",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "dev",
|
||||
"instance": "server7",
|
||||
"job": "node_ping",
|
||||
"cluster": "dev",
|
||||
"instance": "server7",
|
||||
},
|
||||
State: models.AlertStateSuppressed,
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -444,14 +401,10 @@ var groupTests = []groupTest{
|
||||
Receiver: "by-name",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "dev",
|
||||
"instance": "server8",
|
||||
"job": "node_ping",
|
||||
"cluster": "dev",
|
||||
"instance": "server8",
|
||||
},
|
||||
State: models.AlertStateSuppressed,
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -472,6 +425,14 @@ var groupTests = []groupTest{
|
||||
},
|
||||
id: "58c6a3467cebc53abe68ecbe8643ce478c5a1573",
|
||||
hash: "68d0ac6e27b890e0f854611963b03b51b37242cf",
|
||||
shared: models.APIAlertGroupSharedMaps{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"job": "node_ping",
|
||||
},
|
||||
},
|
||||
stateCount: map[string]int{
|
||||
models.AlertStateActive: 5,
|
||||
models.AlertStateSuppressed: 3,
|
||||
@@ -499,10 +460,8 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Free_Disk_Space_Too_Low",
|
||||
"cluster": "staging",
|
||||
"instance": "server5",
|
||||
"job": "node_exporter",
|
||||
"instance": "server5",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
@@ -525,7 +484,6 @@ var groupTests = []groupTest{
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
models.Annotation{Visible: true, Name: "url", Value: "http://localhost/example.html", IsLink: true},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -537,18 +495,13 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "prod",
|
||||
"instance": "server1",
|
||||
"job": "node_ping",
|
||||
"instance": "server1",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
Name: "default",
|
||||
@@ -558,10 +511,7 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
"cluster": "prod",
|
||||
"instance": "server2",
|
||||
"job": "node_ping",
|
||||
"instance": "server2",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
@@ -569,6 +519,14 @@ var groupTests = []groupTest{
|
||||
},
|
||||
hash: "eee0a9960be86ab7308f50a8ff438caed5cf8540",
|
||||
id: "98c1a53d0f71af9c734c9180697383f3b8aff80f",
|
||||
shared: models.APIAlertGroupSharedMaps{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"job": "node_ping",
|
||||
},
|
||||
},
|
||||
stateCount: map[string]int{
|
||||
models.AlertStateActive: 2,
|
||||
models.AlertStateSuppressed: 0,
|
||||
@@ -584,7 +542,6 @@ var groupTests = []groupTest{
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "help", Value: "Example help annotation"},
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
models.Annotation{Visible: true, Name: "url", Value: "http://localhost/example.html", IsLink: true},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -601,18 +558,13 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "HTTP_Probe_Failed",
|
||||
"cluster": "dev",
|
||||
"instance": "web1",
|
||||
"job": "node_exporter",
|
||||
"instance": "web1",
|
||||
},
|
||||
State: models.AlertStateSuppressed,
|
||||
Receiver: "by-name",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
Name: "default",
|
||||
@@ -622,10 +574,7 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "HTTP_Probe_Failed",
|
||||
"cluster": "dev",
|
||||
"instance": "web2",
|
||||
"job": "node_exporter",
|
||||
"instance": "web2",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-name",
|
||||
@@ -633,6 +582,15 @@ var groupTests = []groupTest{
|
||||
},
|
||||
hash: "cc1b20a6b0ded9265ab96699638d844a4c992614",
|
||||
id: "bc4845fec77585cdfebe946234279d785ca93891",
|
||||
shared: models.APIAlertGroupSharedMaps{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"cluster": "dev",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
},
|
||||
stateCount: map[string]int{
|
||||
models.AlertStateActive: 1,
|
||||
models.AlertStateSuppressed: 1,
|
||||
@@ -659,10 +617,9 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Free_Disk_Space_Too_Low",
|
||||
"cluster": "staging",
|
||||
"instance": "server5",
|
||||
"job": "node_exporter",
|
||||
"cluster": "staging",
|
||||
"instance": "server5",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-name",
|
||||
@@ -686,7 +643,6 @@ var groupTests = []groupTest{
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "help", Value: "Example help annotation"},
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
models.Annotation{Visible: true, Name: "url", Value: "http://localhost/example.html", IsLink: true},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
@@ -703,18 +659,13 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "HTTP_Probe_Failed",
|
||||
"cluster": "dev",
|
||||
"instance": "web1",
|
||||
"job": "node_exporter",
|
||||
"instance": "web1",
|
||||
},
|
||||
State: models.AlertStateSuppressed,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Annotations: models.Annotations{},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
Name: "default",
|
||||
@@ -724,10 +675,7 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "HTTP_Probe_Failed",
|
||||
"cluster": "dev",
|
||||
"instance": "web2",
|
||||
"job": "node_exporter",
|
||||
"instance": "web2",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
@@ -735,6 +683,12 @@ var groupTests = []groupTest{
|
||||
},
|
||||
hash: "1dd655dc8ac8ed51aca51a702e70b1a2f442f434",
|
||||
id: "ecefc3705b1ab4e4c3283c879540be348d2d9dce",
|
||||
shared: models.APIAlertGroupSharedMaps{
|
||||
Annotations: models.Annotations{},
|
||||
Labels: map[string]string{
|
||||
"job": "node_exporter",
|
||||
},
|
||||
},
|
||||
stateCount: map[string]int{
|
||||
models.AlertStateActive: 1,
|
||||
models.AlertStateSuppressed: 1,
|
||||
@@ -783,7 +737,7 @@ var countsMap = models.LabelsCountMap{
|
||||
|
||||
var filtersExpected = []models.Filter{}
|
||||
|
||||
func compareAlertGroups(testCase groupTest, group models.AlertGroup) bool {
|
||||
func compareAlertGroups(testCase groupTest, group models.APIAlertGroup) bool {
|
||||
if testCase.receiver != group.Receiver {
|
||||
return false
|
||||
}
|
||||
@@ -888,7 +842,7 @@ func testAlert(version string, t *testing.T, expectedAlert, gotAlert models.Aler
|
||||
}
|
||||
}
|
||||
|
||||
func testAlertGroup(version string, t *testing.T, testCase groupTest, group models.AlertGroup) {
|
||||
func testAlertGroup(version string, t *testing.T, testCase groupTest, group models.APIAlertGroup) {
|
||||
//if testCase.hash != group.Hash {
|
||||
// FIXME this is different per mock version due to startsAt / endsAt
|
||||
// t.Errorf("[%s] Alert group.Hash mismatch, expected '%s' but got '%s' for group %v",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/prymitive/unsee/internal/slices"
|
||||
)
|
||||
|
||||
// Filter holds returned data on any filter passed by the user as part of the query
|
||||
type Filter struct {
|
||||
Text string `json:"text"`
|
||||
@@ -31,18 +37,139 @@ type LabelsColorMap map[string]map[string]LabelColors
|
||||
// LabelsCountMap is a map of "Label Key" -> "Label Value" -> number of occurence
|
||||
type LabelsCountMap map[string]map[string]int
|
||||
|
||||
// APIAlertGroupSharedMaps defines shared part of APIAlertGroup
|
||||
type APIAlertGroupSharedMaps struct {
|
||||
Annotations Annotations `json:"annotations"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
// APIAlertGroup is how AlertGroup is returned in the API response
|
||||
// All labels and annotations that are shared between all alerts in given group
|
||||
// are moved to Shared namespace, each alert instance only tracks labels and
|
||||
// annotations that are unique to that instance
|
||||
type APIAlertGroup struct {
|
||||
AlertGroup
|
||||
Shared APIAlertGroupSharedMaps `json:"shared"`
|
||||
}
|
||||
|
||||
func (ag *APIAlertGroup) dedupLabels() {
|
||||
totalAlerts := len(ag.Alerts)
|
||||
|
||||
labelCounts := map[string]int{}
|
||||
|
||||
for _, alert := range ag.Alerts {
|
||||
for name, val := range alert.Labels {
|
||||
key := fmt.Sprintf("%s\n%s", name, val)
|
||||
_, found := labelCounts[key]
|
||||
if found {
|
||||
labelCounts[key]++
|
||||
} else {
|
||||
labelCounts[key] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sharedLabels := map[string]string{}
|
||||
|
||||
for i, alert := range ag.Alerts {
|
||||
newAlertLabels := map[string]string{}
|
||||
for name, val := range alert.Labels {
|
||||
key := fmt.Sprintf("%s\n%s", name, val)
|
||||
if labelCounts[key] == totalAlerts {
|
||||
sharedLabels[name] = val
|
||||
} else {
|
||||
newAlertLabels[name] = val
|
||||
}
|
||||
}
|
||||
ag.Alerts[i].Labels = newAlertLabels
|
||||
}
|
||||
|
||||
ag.Shared.Labels = sharedLabels
|
||||
|
||||
}
|
||||
|
||||
func (ag *APIAlertGroup) removeGroupingLabels() {
|
||||
for i, alert := range ag.Alerts {
|
||||
newAlertLabels := map[string]string{}
|
||||
for name, val := range alert.Labels {
|
||||
if _, found := ag.Labels[name]; found {
|
||||
// skip all labels that are used for grouping
|
||||
continue
|
||||
}
|
||||
newAlertLabels[name] = val
|
||||
}
|
||||
ag.Alerts[i].Labels = newAlertLabels
|
||||
}
|
||||
}
|
||||
|
||||
func (ag *APIAlertGroup) dedupAnnotations() {
|
||||
totalAlerts := len(ag.Alerts)
|
||||
|
||||
annotationCount := map[string]int{}
|
||||
|
||||
for _, alert := range ag.Alerts {
|
||||
for _, annotation := range alert.Annotations {
|
||||
key := fmt.Sprintf("%s\n%s", annotation.Name, annotation.Value)
|
||||
_, found := annotationCount[key]
|
||||
if found {
|
||||
annotationCount[key]++
|
||||
} else {
|
||||
annotationCount[key] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sharedAnnotations := Annotations{}
|
||||
sharedKeys := []string{}
|
||||
|
||||
for i, alert := range ag.Alerts {
|
||||
newAlertAnnotations := Annotations{}
|
||||
for _, annotation := range alert.Annotations {
|
||||
key := fmt.Sprintf("%s\n%s", annotation.Name, annotation.Value)
|
||||
if annotationCount[key] == totalAlerts {
|
||||
if !slices.StringInSlice(sharedKeys, key) {
|
||||
sharedAnnotations = append(sharedAnnotations, annotation)
|
||||
sharedKeys = append(sharedKeys, key)
|
||||
}
|
||||
} else {
|
||||
newAlertAnnotations = append(newAlertAnnotations, annotation)
|
||||
}
|
||||
}
|
||||
ag.Alerts[i].Annotations = newAlertAnnotations
|
||||
}
|
||||
|
||||
ag.Shared.Annotations = sharedAnnotations
|
||||
}
|
||||
|
||||
// DedupSharedMaps will find all labels and annotations shared by all alerts
|
||||
// in this group and moved them to Shared namespace
|
||||
func (ag *APIAlertGroup) DedupSharedMaps() {
|
||||
// remove all labels that are used for grouping
|
||||
ag.removeGroupingLabels()
|
||||
// don't dedup if we only have a single alert in this group
|
||||
if len(ag.Alerts) > 1 {
|
||||
ag.dedupLabels()
|
||||
ag.dedupAnnotations()
|
||||
} else {
|
||||
ag.Shared = APIAlertGroupSharedMaps{
|
||||
Labels: map[string]string{},
|
||||
Annotations: Annotations{},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AlertsResponse is the structure of JSON response UI will use to get alert data
|
||||
type AlertsResponse struct {
|
||||
Status string `json:"status"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Version string `json:"version"`
|
||||
Upstreams AlertmanagerAPISummary `json:"upstreams"`
|
||||
AlertGroups map[string]AlertGroup `json:"groups"`
|
||||
TotalAlerts int `json:"totalAlerts"`
|
||||
Colors LabelsColorMap `json:"colors"`
|
||||
Filters []Filter `json:"filters"`
|
||||
Counters LabelsCountMap `json:"counters"`
|
||||
StaticColorLabels []string `json:"staticColorLabels"`
|
||||
Status string `json:"status"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Version string `json:"version"`
|
||||
Upstreams AlertmanagerAPISummary `json:"upstreams"`
|
||||
AlertGroups map[string]APIAlertGroup `json:"groups"`
|
||||
TotalAlerts int `json:"totalAlerts"`
|
||||
Colors LabelsColorMap `json:"colors"`
|
||||
Filters []Filter `json:"filters"`
|
||||
Counters LabelsCountMap `json:"counters"`
|
||||
StaticColorLabels []string `json:"staticColorLabels"`
|
||||
}
|
||||
|
||||
// Autocomplete is the structure of autocomplete object for filter hints
|
||||
|
||||
6
views.go
6
views.go
@@ -68,7 +68,7 @@ func alerts(c *gin.Context) {
|
||||
matchFilters, validFilters := getFiltersFromQuery(c.QueryArray("q"))
|
||||
|
||||
// set pointers for data store objects, need a lock until end of view is reached
|
||||
alerts := map[string]models.AlertGroup{}
|
||||
alerts := map[string]models.APIAlertGroup{}
|
||||
colors := models.LabelsColorMap{}
|
||||
// used for top labels dropdown
|
||||
counters := models.LabelsCountMap{}
|
||||
@@ -138,7 +138,9 @@ func alerts(c *gin.Context) {
|
||||
|
||||
if len(agCopy.Alerts) > 0 {
|
||||
agCopy.Hash = agCopy.ContentFingerprint()
|
||||
alerts[agCopy.ID] = agCopy
|
||||
apiAG := models.APIAlertGroup{AlertGroup: agCopy}
|
||||
apiAG.DedupSharedMaps()
|
||||
alerts[agCopy.ID] = apiAG
|
||||
resp.TotalAlerts += len(agCopy.Alerts)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user