diff --git a/ui/src/Stores/SilenceFormStore.js b/ui/src/Stores/SilenceFormStore.js index 6e13963c7..8fb755850 100644 --- a/ui/src/Stores/SilenceFormStore.js +++ b/ui/src/Stores/SilenceFormStore.js @@ -51,18 +51,41 @@ const MatchersFromGroup = (group, stripLabels, alerts, onlyActive) => { } } + // this is the list of alerts we'll use to generate matchers + const filteredAlerts = (alerts ? alerts : group.alerts).filter( + (alert) => !onlyActive || alert.state === "active" + ); + + // array of arrays with label keys for each alert + const allLabelKeys = filteredAlerts + .map((alert) => Object.keys(alert.labels)) + .filter((a) => a.length > 0); + + // this is the list of label key that are shared across all alerts in the group + // https://stackoverflow.com/a/34498210/1154047 + const sharedLabelKeys = allLabelKeys.length + ? allLabelKeys.reduce(function (r, a) { + var last = {}; + return r.filter(function (b) { + var p = a.indexOf(b, last[b] || 0); + if (~p) { + last[b] = p + 1; + return true; + } + return false; + }); + }) + : []; + // add matchers for all unique labels in this group let labels = {}; - const allAlerts = alerts ? alerts : group.alerts; - for (const alert of allAlerts) { - if (!onlyActive || alert.state === "active") { - for (const [key, value] of Object.entries(alert.labels)) { - if (!stripLabels.includes(key)) { - if (!labels[key]) { - labels[key] = new Set(); - } - labels[key].add(value); + for (const alert of filteredAlerts) { + for (const [key, value] of Object.entries(alert.labels)) { + if (sharedLabelKeys.includes(key) && !stripLabels.includes(key)) { + if (!labels[key]) { + labels[key] = new Set(); } + labels[key].add(value); } } } diff --git a/ui/src/Stores/SilenceFormStore.test.js b/ui/src/Stores/SilenceFormStore.test.js index f3ea0f241..34555486a 100644 --- a/ui/src/Stores/SilenceFormStore.test.js +++ b/ui/src/Stores/SilenceFormStore.test.js @@ -211,6 +211,57 @@ describe("SilenceFormStore.data", () => { ); }); + it("fillMatchersFromGroup() handles alerts with different label sets", () => { + const group = MockAlertGroup( + { region: "AF" }, + [ + MockAlert([], { + alertname: "Alert1", + cluster: "prod", + foo: "bar", + }), + MockAlert([], { + alertname: "Alert2", + instance: "prod2", + cluster: "prod", + }), + MockAlert([], { alertname: "Alert3", instance: "dev1" }), + ], + [], + { + job: "mock", + }, + {} + ); + store.data.fillMatchersFromGroup(group, []); + expect(store.data.matchers).toHaveLength(3); + expect(store.data.matchers).toContainEqual( + expect.objectContaining({ + name: "region", + values: [{ label: "AF", value: "AF" }], + isRegex: false, + }) + ); + expect(store.data.matchers).toContainEqual( + expect.objectContaining({ + name: "alertname", + values: [ + { label: "Alert1", value: "Alert1" }, + { label: "Alert2", value: "Alert2" }, + { label: "Alert3", value: "Alert3" }, + ], + isRegex: true, + }) + ); + expect(store.data.matchers).toContainEqual( + expect.objectContaining({ + name: "job", + values: [{ label: "mock", value: "mock" }], + isRegex: false, + }) + ); + }); + it("fillMatchersFromGroup() resets silenceID if set", () => { store.data.silenceID = "12345"; const group = MockGroup();