fix(ui): only select alertmanagers from alert/group

Fixes #1904
This commit is contained in:
Łukasz Mierzwa
2020-06-29 12:35:02 +01:00
committed by Łukasz Mierzwa
parent 2d3458b688
commit 32f249f2d8
6 changed files with 116 additions and 8 deletions

View File

@@ -12,17 +12,31 @@ import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons/faExternalL
import { APIAlert, APIGroup } from "Models/API";
import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore, SilenceTabNames } from "Stores/SilenceFormStore";
import {
SilenceFormStore,
SilenceTabNames,
AlertmanagerClustersToOption,
} from "Stores/SilenceFormStore";
import { FetchPauser } from "Components/FetchPauser";
import { DropdownSlide } from "Components/Animations/DropdownSlide";
import { DateFromNow } from "Components/DateFromNow";
import { useOnClickOutside } from "Hooks/useOnClickOutside";
const onSilenceClick = (alertStore, silenceFormStore, group, alert) => {
let clusters = {};
Object.entries(alertStore.data.clustersWithoutReadOnly).forEach(
([cluster, members]) => {
if (alert.alertmanager.map((am) => am.cluster).includes(cluster)) {
clusters[cluster] = members;
}
}
);
silenceFormStore.data.resetProgress();
silenceFormStore.data.fillMatchersFromGroup(
group,
alertStore.settings.values.silenceForm.strip.labels,
AlertmanagerClustersToOption(clusters),
[alert]
);
silenceFormStore.tab.setTab(SilenceTabNames.Editor);

View File

@@ -31,7 +31,7 @@ beforeEach(() => {
group = MockAlertGroup({ alertname: "Fake Alert" }, [alert], [], {}, {});
alertStore.data.upstreams = {
clusters: { default: ["am1"] },
clusters: { default: ["am1"], ro: ["ro"], am2: ["am2"] },
instances: [
{
name: "am1",
@@ -45,6 +45,30 @@ beforeEach(() => {
cluster: "default",
clusterMembers: ["am1"],
},
{
name: "ro",
uri: "http://localhost:8080",
publicURI: "http://example.com",
readonly: true,
headers: {},
corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ro",
clusterMembers: ["ro"],
},
{
name: "am2",
uri: "http://localhost:8080",
publicURI: "http://example.com",
readonly: false,
headers: {},
corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "am2",
clusterMembers: ["am2"],
},
],
};
});
@@ -131,14 +155,19 @@ const MountedMenuContent = (group) => {
describe("<MenuContent />", () => {
it("clicking on 'Silence' icon opens the silence form modal", () => {
group.alertmanagerCount = { am1: 1, ro: 1 };
const tree = MountedMenuContent(group);
const button = tree.find(".dropdown-item").at(1);
button.simulate("click");
expect(silenceFormStore.toggle.visible).toBe(true);
expect(silenceFormStore.data.alertmanagers).toMatchObject([
{ label: "am1", value: ["am1"] },
]);
});
it("'Silence' menu entry is disabled when all Alertmanager instances are read-only", () => {
alertStore.data.upstreams.instances[0].readonly = true;
alertStore.data.upstreams.instances[2].readonly = true;
const tree = MountedMenuContent(group);
const button = tree.find(".dropdown-item").at(1);
expect(button.hasClass("disabled")).toBe(true);

View File

@@ -13,17 +13,33 @@ import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
import { APIGroup } from "Models/API";
import { FormatAlertsQ } from "Stores/AlertStore";
import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore, SilenceTabNames } from "Stores/SilenceFormStore";
import {
SilenceFormStore,
SilenceTabNames,
AlertmanagerClustersToOption,
} from "Stores/SilenceFormStore";
import { QueryOperators, StaticLabels, FormatQuery } from "Common/Query";
import { DropdownSlide } from "Components/Animations/DropdownSlide";
import { FetchPauser } from "Components/FetchPauser";
import { useOnClickOutside } from "Hooks/useOnClickOutside";
const onSilenceClick = (alertStore, silenceFormStore, group) => {
let clusters = {};
Object.entries(alertStore.data.clustersWithoutReadOnly).forEach(
([cluster, members]) => {
members.forEach((member) => {
if (Object.keys(group.alertmanagerCount).includes(member)) {
clusters[cluster] = members;
}
});
}
);
silenceFormStore.data.resetProgress();
silenceFormStore.data.fillMatchersFromGroup(
group,
alertStore.settings.values.silenceForm.strip.labels
alertStore.settings.values.silenceForm.strip.labels,
AlertmanagerClustersToOption(clusters)
);
silenceFormStore.tab.setTab(SilenceTabNames.Editor);
silenceFormStore.toggle.show();

View File

@@ -29,7 +29,7 @@ beforeEach(() => {
MockSetIsMenuOpen = jest.fn();
alertStore.data.upstreams = {
clusters: { default: ["am1"] },
clusters: { default: ["am1"], ro: ["ro"], am2: ["am2"] },
instances: [
{
name: "am1",
@@ -43,6 +43,30 @@ beforeEach(() => {
cluster: "default",
clusterMembers: ["am1"],
},
{
name: "ro",
uri: "http://localhost:8080",
publicURI: "http://example.com",
readonly: true,
headers: {},
corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ro",
clusterMembers: ["ro"],
},
{
name: "am2",
uri: "http://localhost:8080",
publicURI: "http://example.com",
readonly: false,
headers: {},
corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "am2",
clusterMembers: ["am2"],
},
],
};
});
@@ -141,14 +165,19 @@ describe("<MenuContent />", () => {
it("clicking on 'Silence' icon opens the silence form modal", () => {
const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {}, {});
group.alertmanagerCount = { am1: 1, ro: 1 };
const tree = MountedMenuContent(group);
const button = tree.find(".dropdown-item").at(1);
button.simulate("click");
expect(silenceFormStore.toggle.visible).toBe(true);
expect(silenceFormStore.data.alertmanagers).toMatchObject([
{ label: "am1", value: ["am1"] },
]);
});
it("'Silence' menu entry is disabled when all Alertmanager instances are read-only", () => {
alertStore.data.upstreams.instances[0].readonly = true;
alertStore.data.upstreams.instances[2].readonly = true;
const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {}, {});
const tree = MountedMenuContent(group);

View File

@@ -316,13 +316,17 @@ class SilenceFormStore {
},
// if alerts argument is not passed all group alerts will be used
fillMatchersFromGroup(group, stripLabels, alerts) {
fillMatchersFromGroup(group, stripLabels, alertmanagers, alerts) {
this.alertmanagers = alertmanagers;
this.matchers = MatchersFromGroup(group, stripLabels, alerts);
// ensure that silenceID is nulled, since it's used to edit silences
// and this is used to silence groups
this.silenceID = null;
// disable matcher autofill
this.autofillMatchers = false;
// disable alertmanager input reset
this.resetInputs = false;
},
fillFormFromSilence(alertmanager, silence) {

View File

@@ -16,6 +16,7 @@ import {
NewEmptyMatcher,
SilenceTabNames,
MatcherValueToObject,
AlertmanagerClustersToOption,
} from "./SilenceFormStore";
let store;
@@ -126,7 +127,14 @@ describe("SilenceFormStore.data", () => {
it("fillMatchersFromGroup() creates correct matcher object for a group", () => {
const group = MockGroup();
store.data.fillMatchersFromGroup(group, []);
store.data.fillMatchersFromGroup(
group,
[],
AlertmanagerClustersToOption({ ha: ["am1", "am2"] })
);
expect(store.data.alertmanagers).toMatchObject([
{ label: "Cluster: ha", value: ["am1", "am2"] },
]);
expect(store.data.matchers).toHaveLength(4);
expect(store.data.matchers).toContainEqual(
expect.objectContaining({
@@ -167,7 +175,15 @@ describe("SilenceFormStore.data", () => {
it("fillMatchersFromGroup() creates correct matcher object for a group with only a subset of alerts passed", () => {
const group = MockGroup();
store.data.fillMatchersFromGroup(group, [], [group.alerts[0]]);
store.data.fillMatchersFromGroup(
group,
[],
AlertmanagerClustersToOption({ ha: ["am1", "am2"] }),
[group.alerts[0]]
);
expect(store.data.alertmanagers).toMatchObject([
{ label: "Cluster: ha", value: ["am1", "am2"] },
]);
expect(store.data.matchers).toHaveLength(4);
expect(store.data.matchers).toContainEqual(
expect.objectContaining({