diff --git a/internal/models/api.go b/internal/models/api.go
index ff2d40ba3..1805da55d 100644
--- a/internal/models/api.go
+++ b/internal/models/api.go
@@ -36,9 +36,9 @@ type LabelsColorMap map[string]map[string]LabelColors
// APIAlertGroupSharedMaps defines shared part of APIAlertGroup
type APIAlertGroupSharedMaps struct {
- Annotations Annotations `json:"annotations"`
- Labels map[string]string `json:"labels"`
- Silences map[string]string `json:"silences"`
+ Annotations Annotations `json:"annotations"`
+ Labels map[string]string `json:"labels"`
+ Silences map[string][]string `json:"silences"`
}
// APIAlertGroup is how AlertGroup is returned in the API response
@@ -140,42 +140,47 @@ func (ag *APIAlertGroup) dedupAnnotations() {
}
func (ag *APIAlertGroup) dedupSilences() {
- ag.Shared.Silences = map[string]string{}
+ ag.Shared.Silences = map[string][]string{}
- silencesByCluster := map[string][]string{}
+ silencesByCluster := map[string]map[string]int{}
for _, alert := range ag.Alerts {
if alert.State != AlertStateSuppressed {
// if we find any alert that's not silenced then we can break early
return
}
+ // process each cluster only once, rather than each alertmanager instance
+ clusters := []string{}
for _, am := range alert.Alertmanager {
+ if slices.StringInSlice(clusters, am.Cluster) {
+ continue
+ }
+ clusters = append(clusters, am.Cluster)
for _, silenceID := range am.SilencedBy {
_, ok := silencesByCluster[am.Cluster]
if !ok {
- silencesByCluster[am.Cluster] = []string{}
+ silencesByCluster[am.Cluster] = map[string]int{}
}
- if !slices.StringInSlice(silencesByCluster[am.Cluster], silenceID) {
- silencesByCluster[am.Cluster] = append(silencesByCluster[am.Cluster], silenceID)
+ _, ok = silencesByCluster[am.Cluster][silenceID]
+ if !ok {
+ silencesByCluster[am.Cluster][silenceID] = 0
}
+ silencesByCluster[am.Cluster][silenceID]++
}
}
}
- // only deduplicate if all alerts are silenced with the same silence from a
- // single cluster
- if len(silencesByCluster) != 1 {
- return
- }
-
- // now check that all alerts are silenced with the same silenceID
- for cluster, silences := range silencesByCluster {
- for _, silenceID := range silences {
- if silenceID != silences[0] {
- return
+ totalAlerts := len(ag.Alerts)
+ for cluster, silenceCountMap := range silencesByCluster {
+ for silenceID, affectedAlertsCount := range silenceCountMap {
+ if affectedAlertsCount == totalAlerts {
+ _, ok := ag.Shared.Silences[cluster]
+ if !ok {
+ ag.Shared.Silences[cluster] = []string{}
+ }
+ ag.Shared.Silences[cluster] = append(ag.Shared.Silences[cluster], silenceID)
}
}
- ag.Shared.Silences[cluster] = silences[0]
}
}
@@ -193,7 +198,7 @@ func (ag *APIAlertGroup) DedupSharedMaps() {
ag.Shared = APIAlertGroupSharedMaps{
Labels: map[string]string{},
Annotations: Annotations{},
- Silences: map[string]string{},
+ Silences: map[string][]string{},
}
}
}
diff --git a/internal/models/api_test.go b/internal/models/api_test.go
index ef42a2d71..8e1797798 100644
--- a/internal/models/api_test.go
+++ b/internal/models/api_test.go
@@ -12,7 +12,7 @@ import (
func TestDedupSharedMaps(t *testing.T) {
am := models.AlertmanagerInstance{
Cluster: "fakeCluster",
- SilencedBy: []string{"fakeSilenceID"},
+ SilencedBy: []string{"fakeSilence1", "fakeSilence2"},
}
ag := models.APIAlertGroup{
AlertGroup: models.AlertGroup{
@@ -37,7 +37,7 @@ func TestDedupSharedMaps(t *testing.T) {
"job": "node_exporter",
"instance": "1",
},
- Alertmanager: []models.AlertmanagerInstance{am},
+ Alertmanager: []models.AlertmanagerInstance{am, am},
},
models.Alert{
State: models.AlertStateSuppressed,
@@ -52,7 +52,7 @@ func TestDedupSharedMaps(t *testing.T) {
"job": "node_exporter",
"instance": "2",
},
- Alertmanager: []models.AlertmanagerInstance{am},
+ Alertmanager: []models.AlertmanagerInstance{am, am},
},
models.Alert{
State: models.AlertStateSuppressed,
@@ -67,7 +67,7 @@ func TestDedupSharedMaps(t *testing.T) {
"job": "blackbox",
"instance": "3",
},
- Alertmanager: []models.AlertmanagerInstance{am},
+ Alertmanager: []models.AlertmanagerInstance{am, am},
},
},
},
@@ -105,7 +105,21 @@ func TestDedupSharedMaps(t *testing.T) {
"endsAt": "0001-01-01T00:00:00Z",
"source": "",
"silencedBy": [
- "fakeSilenceID"
+ "fakeSilence1",
+ "fakeSilence2"
+ ],
+ "inhibitedBy": null
+ },
+ {
+ "name": "",
+ "cluster": "fakeCluster",
+ "state": "",
+ "startsAt": "0001-01-01T00:00:00Z",
+ "endsAt": "0001-01-01T00:00:00Z",
+ "source": "",
+ "silencedBy": [
+ "fakeSilence1",
+ "fakeSilence2"
],
"inhibitedBy": null
}
@@ -130,7 +144,21 @@ func TestDedupSharedMaps(t *testing.T) {
"endsAt": "0001-01-01T00:00:00Z",
"source": "",
"silencedBy": [
- "fakeSilenceID"
+ "fakeSilence1",
+ "fakeSilence2"
+ ],
+ "inhibitedBy": null
+ },
+ {
+ "name": "",
+ "cluster": "fakeCluster",
+ "state": "",
+ "startsAt": "0001-01-01T00:00:00Z",
+ "endsAt": "0001-01-01T00:00:00Z",
+ "source": "",
+ "silencedBy": [
+ "fakeSilence1",
+ "fakeSilence2"
],
"inhibitedBy": null
}
@@ -155,7 +183,21 @@ func TestDedupSharedMaps(t *testing.T) {
"endsAt": "0001-01-01T00:00:00Z",
"source": "",
"silencedBy": [
- "fakeSilenceID"
+ "fakeSilence1",
+ "fakeSilence2"
+ ],
+ "inhibitedBy": null
+ },
+ {
+ "name": "",
+ "cluster": "fakeCluster",
+ "state": "",
+ "startsAt": "0001-01-01T00:00:00Z",
+ "endsAt": "0001-01-01T00:00:00Z",
+ "source": "",
+ "silencedBy": [
+ "fakeSilence1",
+ "fakeSilence2"
],
"inhibitedBy": null
}
@@ -178,7 +220,10 @@ func TestDedupSharedMaps(t *testing.T) {
],
"labels": {},
"silences": {
- "fakeCluster": "fakeSilenceID"
+ "fakeCluster": [
+ "fakeSilence1",
+ "fakeSilence2"
+ ]
}
}
}`
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.js b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.js
index 04bbdf4fd..72db47a43 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.js
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.js
@@ -49,22 +49,24 @@ const Alert = observer(
BorderClassMap[alert.state] || "border-warning"
];
- let silences = {};
- for (let am of alert.alertmanager) {
+ const silences = {};
+ for (const am of alert.alertmanager) {
if (!silences[am.cluster]) {
silences[am.cluster] = {
alertmanager: am,
- silences: []
+ silences: [
+ ...new Set(
+ am.silencedBy.filter(
+ silenceID =>
+ !(
+ group.shared.silences[am.cluster] &&
+ group.shared.silences[am.cluster].includes(silenceID)
+ )
+ )
+ )
+ ]
};
}
- for (let silenceID of am.silencedBy) {
- if (
- !silences[am.cluster].silences.includes(silenceID) &&
- !(group.shared.silences[am.cluster] === silenceID)
- ) {
- silences[am.cluster].silences.push(silenceID);
- }
- }
}
return (
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.test.js b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.test.js
index 158d53c64..047ed8aba 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.test.js
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.test.js
@@ -105,7 +105,7 @@ describe("