mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
committed by
Łukasz Mierzwa
parent
8d41c67681
commit
ce4a9c3e67
@@ -6,6 +6,7 @@
|
||||
|
||||
- Messages are now logged correctly when both `--log.format=json` and
|
||||
`--log.timestamp=true` flags are set #3822.
|
||||
- Escape label values in silence form #3866.
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -557,6 +557,26 @@ class RichAnnotations(AlertGenerator):
|
||||
]
|
||||
|
||||
|
||||
class RegexEscapeValue(AlertGenerator):
|
||||
name = "Labels with rich values"
|
||||
comment = "This alert will have rich labels"
|
||||
|
||||
def alerts(self):
|
||||
return [
|
||||
newAlert(
|
||||
self._labels(
|
||||
instance="server{}".format(i),
|
||||
cluster="staging",
|
||||
job="textfile_exporter",
|
||||
region="SA",
|
||||
device="Device {} (main)".format(i % 2),
|
||||
regex="^device{}(.+)bar\\$".format(i),
|
||||
),
|
||||
)
|
||||
for i in range(0, 10)
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generators = [
|
||||
AlwaysOnAlert(MAX_INTERVAL),
|
||||
@@ -573,6 +593,7 @@ if __name__ == "__main__":
|
||||
SilencedAlertWithJiraLink(MAX_INTERVAL),
|
||||
PaginationTest(MAX_INTERVAL),
|
||||
RichAnnotations(MAX_INTERVAL),
|
||||
RegexEscapeValue(MAX_INTERVAL),
|
||||
]
|
||||
while True:
|
||||
for g in generators:
|
||||
|
||||
@@ -5,6 +5,7 @@ export const NewLabelValue = (v: string): string => `New value: ${v}`;
|
||||
export interface OptionT {
|
||||
label: string;
|
||||
value: string;
|
||||
wasCreated: boolean;
|
||||
}
|
||||
|
||||
export interface MultiValueOptionT {
|
||||
@@ -15,4 +16,5 @@ export interface MultiValueOptionT {
|
||||
export const StringToOption = (value: string): OptionT => ({
|
||||
label: value,
|
||||
value: value,
|
||||
wasCreated: false,
|
||||
});
|
||||
|
||||
@@ -27,10 +27,10 @@ import { ThemeContext } from "Components/Theme";
|
||||
import { useOnClickOutside } from "Hooks/useOnClickOutside";
|
||||
|
||||
const specialLabels: OptionT[] = [
|
||||
{ label: "Automatic selection", value: "@auto" },
|
||||
{ label: "@alertmanager", value: "@alertmanager" },
|
||||
{ label: "@cluster", value: "@cluster" },
|
||||
{ label: "@receiver", value: "@receiver" },
|
||||
{ label: "Automatic selection", value: "@auto", wasCreated: false },
|
||||
{ label: "@alertmanager", value: "@alertmanager", wasCreated: false },
|
||||
{ label: "@cluster", value: "@cluster", wasCreated: false },
|
||||
{ label: "@receiver", value: "@receiver", wasCreated: false },
|
||||
];
|
||||
|
||||
const NullContainer: FC = () => null;
|
||||
|
||||
@@ -24,6 +24,7 @@ const AlertGroupCollapseConfiguration: FC<{
|
||||
return {
|
||||
label: settingsStore.alertGroupConfig.options[val].label,
|
||||
value: val,
|
||||
wasCreated: false,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -30,7 +30,11 @@ const AlertGroupSortConfiguration: FC<{
|
||||
};
|
||||
|
||||
const valueToOption = (val: SortOrderT): OptionT => {
|
||||
return { label: settingsStore.gridConfig.options[val].label, value: val };
|
||||
return {
|
||||
label: settingsStore.gridConfig.options[val].label,
|
||||
value: val,
|
||||
wasCreated: false,
|
||||
};
|
||||
};
|
||||
|
||||
const hideReverse =
|
||||
|
||||
@@ -14,14 +14,15 @@ const disabledLabel = "Disable multi-grid";
|
||||
const valueToOption = (v: string) => ({
|
||||
label: v ? v : disabledLabel,
|
||||
value: v,
|
||||
wasCreated: false,
|
||||
});
|
||||
|
||||
const staticValues = [
|
||||
{ label: disabledLabel, value: "" },
|
||||
{ label: "Automatic selection", value: "@auto" },
|
||||
{ label: "@alertmanager", value: "@alertmanager" },
|
||||
{ label: "@cluster", value: "@cluster" },
|
||||
{ label: "@receiver", value: "@receiver" },
|
||||
{ label: disabledLabel, value: "", wasCreated: false },
|
||||
{ label: "Automatic selection", value: "@auto", wasCreated: false },
|
||||
{ label: "@alertmanager", value: "@alertmanager", wasCreated: false },
|
||||
{ label: "@cluster", value: "@cluster", wasCreated: false },
|
||||
{ label: "@receiver", value: "@receiver", wasCreated: false },
|
||||
];
|
||||
|
||||
const GridLabelName: FC<{
|
||||
@@ -35,7 +36,7 @@ const GridLabelName: FC<{
|
||||
|
||||
const defaultValue =
|
||||
settingsStore.multiGridConfig.config.gridLabel === "@auto"
|
||||
? { label: "Automatic selection", value: "@auto" }
|
||||
? { label: "Automatic selection", value: "@auto", wasCreated: false }
|
||||
: valueToOption(settingsStore.multiGridConfig.config.gridLabel);
|
||||
|
||||
return (
|
||||
|
||||
@@ -24,6 +24,7 @@ const ThemeConfiguration: FC<{
|
||||
return {
|
||||
label: settingsStore.themeConfig.options[val].label,
|
||||
value: val,
|
||||
wasCreated: false,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
import { FormatQuery, QueryOperators, StaticLabels } from "Common/Query";
|
||||
import type { MultiValueOptionT } from "Common/Select";
|
||||
import { MatcherT, MatcherToOperator } from "Stores/SilenceFormStore";
|
||||
import {
|
||||
MatcherT,
|
||||
MatcherToOperator,
|
||||
EscapeRegex,
|
||||
} from "Stores/SilenceFormStore";
|
||||
|
||||
const MatcherToFilter = (matcher: MatcherT): string => {
|
||||
const values = matcher.values.map((v) =>
|
||||
v.wasCreated
|
||||
? v
|
||||
: matcher.isRegex
|
||||
? { ...v, value: EscapeRegex(v.value) }
|
||||
: v
|
||||
);
|
||||
const value =
|
||||
matcher.values.length > 1
|
||||
? `(${matcher.values.map((v) => v.value).join("|")})`
|
||||
: matcher.values[0].value;
|
||||
values.length > 1
|
||||
? `(${values.map((v) => v.value).join("|")})`
|
||||
: values[0].value;
|
||||
return FormatQuery(
|
||||
matcher.name,
|
||||
MatcherToOperator(matcher),
|
||||
|
||||
@@ -110,6 +110,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "alertnameEqual",
|
||||
value: "alertnameEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -122,6 +123,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "alertnameNotEqual",
|
||||
value: "alertnameNotEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -134,6 +136,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*alertnameRegex.*",
|
||||
value: ".*alertnameRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -146,6 +149,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*alertnameNegativeRegex.*",
|
||||
value: ".*alertnameNegativeRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -158,6 +162,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "clusterEqual",
|
||||
value: "clusterEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -170,6 +175,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "clusterNotEqual",
|
||||
value: "clusterNotEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -182,6 +188,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*clusterRegex.*",
|
||||
value: ".*clusterRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -194,6 +201,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*clusterNegativeRegex.*",
|
||||
value: ".*clusterNegativeRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -206,6 +214,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "fooEqual",
|
||||
value: "fooEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -218,6 +227,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "fooNotEqual",
|
||||
value: "fooNotEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -230,6 +240,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*fooRegex.*",
|
||||
value: ".*fooRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -242,6 +253,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*fooNegativeRegex.*",
|
||||
value: ".*fooNegativeRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -288,6 +300,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "alertnameEqual",
|
||||
value: "alertnameEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -300,6 +313,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "alertnameNotEqual",
|
||||
value: "alertnameNotEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -312,6 +326,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*alertnameRegex.*",
|
||||
value: ".*alertnameRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -324,6 +339,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*alertnameNegativeRegex.*",
|
||||
value: ".*alertnameNegativeRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -336,6 +352,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "clusterEqual",
|
||||
value: "clusterEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -348,6 +365,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "clusterNotEqual",
|
||||
value: "clusterNotEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -360,6 +378,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*clusterRegex.*",
|
||||
value: ".*clusterRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -372,6 +391,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*clusterNegativeRegex.*",
|
||||
value: ".*clusterNegativeRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -384,6 +404,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "fooEqual",
|
||||
value: "fooEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -396,6 +417,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: "fooNotEqual",
|
||||
value: "fooNotEqual",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -408,6 +430,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*fooRegex.*",
|
||||
value: ".*fooRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -420,6 +443,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
{
|
||||
label: ".*fooNegativeRegex.*",
|
||||
value: ".*fooNegativeRegex.*",
|
||||
wasCreated: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -531,7 +555,9 @@ describe("<SilenceForm /> preview", () => {
|
||||
it("clicking on the copy button copies form link to the clipboard", () => {
|
||||
const matcher = NewEmptyMatcher();
|
||||
matcher.name = "job";
|
||||
matcher.values = [{ label: "node_exporter", value: "node_exporter" }];
|
||||
matcher.values = [
|
||||
{ label: "node_exporter", value: "node_exporter", wasCreated: false },
|
||||
];
|
||||
silenceFormStore.data.setMatchers([matcher]);
|
||||
silenceFormStore.data.setAlertmanagers([{ label: "am1", value: ["am1"] }]);
|
||||
silenceFormStore.data.setAuthor("me@example.com");
|
||||
@@ -550,7 +576,9 @@ describe("<SilenceForm /> preview", () => {
|
||||
it("silence form share link doesn't change on new input", () => {
|
||||
const matcher = NewEmptyMatcher();
|
||||
matcher.name = "job";
|
||||
matcher.values = [{ label: "node_exporter", value: "node_exporter" }];
|
||||
matcher.values = [
|
||||
{ label: "node_exporter", value: "node_exporter", wasCreated: false },
|
||||
];
|
||||
silenceFormStore.data.setMatchers([matcher]);
|
||||
silenceFormStore.data.setAlertmanagers([{ label: "am1", value: ["am1"] }]);
|
||||
silenceFormStore.data.setAuthor("me@example.com");
|
||||
@@ -620,7 +648,9 @@ describe("<SilenceForm />", () => {
|
||||
it("calling submit move form to the 'Preview' stage when form is valid", () => {
|
||||
const matcher = NewEmptyMatcher();
|
||||
matcher.name = "job";
|
||||
matcher.values = [{ label: "node_exporter", value: "node_exporter" }];
|
||||
matcher.values = [
|
||||
{ label: "node_exporter", value: "node_exporter", wasCreated: false },
|
||||
];
|
||||
silenceFormStore.data.setMatchers([matcher]);
|
||||
silenceFormStore.data.setAlertmanagers([{ label: "am1", value: ["am1"] }]);
|
||||
silenceFormStore.data.setAuthor("me@example.com");
|
||||
|
||||
@@ -10,8 +10,9 @@ import {
|
||||
MatcherWithIDT,
|
||||
} from "Stores/SilenceFormStore";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { StringToOption } from "Common/Select";
|
||||
import { OptionT, StringToOption } from "Common/Select";
|
||||
import { LabelValueInput } from "./LabelValueInput";
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
let silenceFormStore: SilenceFormStore;
|
||||
let matcher: MatcherWithIDT;
|
||||
@@ -123,6 +124,22 @@ describe("<LabelValueInput />", () => {
|
||||
expect(matcher.isRegex).toBe(true);
|
||||
});
|
||||
|
||||
it("creating a manual option sets wasCreated=true", () => {
|
||||
const tree = MountedLabelValueInput(true);
|
||||
const input = tree.find("Select").instance();
|
||||
const options: OptionT[] = [
|
||||
{ label: "foo", value: "foo", wasCreated: false },
|
||||
];
|
||||
act(() => {
|
||||
(input.props as any).onChange(options, { action: "create-option" });
|
||||
});
|
||||
expect(matcher.values[0]).toStrictEqual({
|
||||
label: "foo",
|
||||
value: "foo",
|
||||
wasCreated: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("removing last value sets matcher.values to []", () => {
|
||||
matcher.values = [StringToOption("dev"), StringToOption("staging")];
|
||||
const tree = MountedLabelValueInput(true);
|
||||
|
||||
@@ -90,13 +90,16 @@ const LabelValueInput: FC<{
|
||||
placeholder={isValid ? "Label value" : <ValidationError />}
|
||||
onChange={(
|
||||
newValue: OnChangeValue<OptionT, true>,
|
||||
_: ActionMeta<OptionT>
|
||||
meta: ActionMeta<OptionT>
|
||||
) => {
|
||||
matcher.values = newValue as OptionT[];
|
||||
// force regex if we have multiple values
|
||||
if (matcher.values.length > 1 && matcher.isRegex === false) {
|
||||
matcher.isRegex = true;
|
||||
}
|
||||
if (meta.action === "create-option") {
|
||||
matcher.values[matcher.values.length - 1].wasCreated = true;
|
||||
}
|
||||
}}
|
||||
hideSelectedOptions
|
||||
isMulti
|
||||
|
||||
@@ -145,6 +145,50 @@ describe("<MatchCounter />", () => {
|
||||
).toBe("./alertList.json?q=foo%3D~%5Ebar%24");
|
||||
});
|
||||
|
||||
it("sends correct query string for a 'foo=(x)' matcher with wasCreated=true & isRegex=false", () => {
|
||||
const v = StringToOption("(x)");
|
||||
v.wasCreated = true;
|
||||
matcher.values = [v];
|
||||
matcher.isRegex = false;
|
||||
MountedMatchCounter();
|
||||
expect(
|
||||
(useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0]
|
||||
).toBe("./alertList.json?q=foo%3D%28x%29");
|
||||
});
|
||||
|
||||
it("sends correct query string for a 'foo=(x)' matcher with wasCreated=true & isRegex=true", () => {
|
||||
const v = StringToOption("(x)");
|
||||
v.wasCreated = true;
|
||||
matcher.values = [v];
|
||||
matcher.isRegex = true;
|
||||
MountedMatchCounter();
|
||||
expect(
|
||||
(useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0]
|
||||
).toBe("./alertList.json?q=foo%3D~%5E%28x%29%24");
|
||||
});
|
||||
|
||||
it("sends correct query string for a 'foo=(x)' matcher with wasCreated=false & isRegex=false", () => {
|
||||
const v = StringToOption("(x)");
|
||||
v.wasCreated = false;
|
||||
matcher.values = [v];
|
||||
matcher.isRegex = false;
|
||||
MountedMatchCounter();
|
||||
expect(
|
||||
(useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0]
|
||||
).toBe("./alertList.json?q=foo%3D%28x%29");
|
||||
});
|
||||
|
||||
it("sends correct query string for a 'foo=(x)' matcher with wasCreated=false & isRegex=true", () => {
|
||||
const v = StringToOption("(x)");
|
||||
v.wasCreated = false;
|
||||
matcher.values = [v];
|
||||
matcher.isRegex = true;
|
||||
MountedMatchCounter();
|
||||
expect(
|
||||
(useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0]
|
||||
).toBe("./alertList.json?q=foo%3D~%5E%5C%28x%5C%29%24");
|
||||
});
|
||||
|
||||
it("sends correct query string for a 'foo=~(bar|baz)' matcher", () => {
|
||||
matcher.values = [StringToOption("bar"), StringToOption("baz")];
|
||||
matcher.isRegex = true;
|
||||
|
||||
@@ -59,12 +59,21 @@ interface AlertGroupConfigStorage {
|
||||
}
|
||||
class AlertGroupConfig {
|
||||
options = Object.freeze({
|
||||
expanded: { label: "Always expanded", value: "expanded" },
|
||||
expanded: {
|
||||
label: "Always expanded",
|
||||
value: "expanded",
|
||||
wasCreated: false,
|
||||
},
|
||||
collapsedOnMobile: {
|
||||
label: "Collapse on mobile",
|
||||
value: "collapsedOnMobile",
|
||||
wasCreated: false,
|
||||
},
|
||||
collapsed: {
|
||||
label: "Always collapsed",
|
||||
value: "collapsed",
|
||||
wasCreated: false,
|
||||
},
|
||||
collapsed: { label: "Always collapsed", value: "collapsed" },
|
||||
});
|
||||
|
||||
config: AlertGroupConfigStorage;
|
||||
@@ -128,10 +137,18 @@ interface GridConfigStorage {
|
||||
}
|
||||
class GridConfig {
|
||||
options = Object.freeze({
|
||||
default: { label: "Use defaults from karma config file", value: "default" },
|
||||
disabled: { label: "No sorting", value: "disabled" },
|
||||
startsAt: { label: "Sort by alert timestamp", value: "startsAt" },
|
||||
label: { label: "Sort by alert label", value: "label" },
|
||||
default: {
|
||||
label: "Use defaults from karma config file",
|
||||
value: "default",
|
||||
wasCreated: false,
|
||||
},
|
||||
disabled: { label: "No sorting", value: "disabled", wasCreated: false },
|
||||
startsAt: {
|
||||
label: "Sort by alert timestamp",
|
||||
value: "startsAt",
|
||||
wasCreated: false,
|
||||
},
|
||||
label: { label: "Sort by alert label", value: "label", wasCreated: false },
|
||||
});
|
||||
|
||||
config: GridConfigStorage;
|
||||
@@ -206,9 +223,10 @@ class ThemeConfig {
|
||||
auto: {
|
||||
label: "Automatic theme, follow browser preference",
|
||||
value: "auto",
|
||||
wasCreated: false,
|
||||
},
|
||||
light: { label: "Light theme", value: "light" },
|
||||
dark: { label: "Dark theme", value: "dark" },
|
||||
light: { label: "Light theme", value: "light", wasCreated: false },
|
||||
dark: { label: "Dark theme", value: "dark", wasCreated: false },
|
||||
});
|
||||
|
||||
this.config = localStored(
|
||||
|
||||
@@ -174,14 +174,14 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "alertname",
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert" }],
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "job",
|
||||
values: [{ label: "mock", value: "mock" }],
|
||||
values: [{ label: "mock", value: "mock", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
@@ -189,12 +189,12 @@ describe("SilenceFormStore.data", () => {
|
||||
expect.objectContaining({
|
||||
name: "instance",
|
||||
values: [
|
||||
{ label: "dev1", value: "dev1" },
|
||||
{ label: "prod1", value: "prod1" },
|
||||
{ label: "prod2", value: "prod2" },
|
||||
{ label: "dev2", value: "dev2" },
|
||||
{ label: "dev3", value: "dev3" },
|
||||
{ label: "dev4", value: "dev4" },
|
||||
{ label: "dev1", value: "dev1", wasCreated: false },
|
||||
{ label: "prod1", value: "prod1", wasCreated: false },
|
||||
{ label: "prod2", value: "prod2", wasCreated: false },
|
||||
{ label: "dev2", value: "dev2", wasCreated: false },
|
||||
{ label: "dev3", value: "dev3", wasCreated: false },
|
||||
{ label: "dev4", value: "dev4", wasCreated: false },
|
||||
],
|
||||
isRegex: true,
|
||||
})
|
||||
@@ -203,8 +203,8 @@ describe("SilenceFormStore.data", () => {
|
||||
expect.objectContaining({
|
||||
name: "cluster",
|
||||
values: [
|
||||
{ label: "dev", value: "dev" },
|
||||
{ label: "prod", value: "prod" },
|
||||
{ label: "dev", value: "dev", wasCreated: false },
|
||||
{ label: "prod", value: "prod", wasCreated: false },
|
||||
],
|
||||
isRegex: true,
|
||||
})
|
||||
@@ -232,7 +232,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "alertname",
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert" }],
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
@@ -240,8 +240,8 @@ describe("SilenceFormStore.data", () => {
|
||||
expect.objectContaining({
|
||||
name: "instance",
|
||||
values: [
|
||||
{ label: "prod1", value: "prod1" },
|
||||
{ label: "prod2", value: "prod2" },
|
||||
{ label: "prod1", value: "prod1", wasCreated: false },
|
||||
{ label: "prod2", value: "prod2", wasCreated: false },
|
||||
],
|
||||
isRegex: true,
|
||||
})
|
||||
@@ -249,7 +249,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "cluster",
|
||||
values: [{ label: "prod", value: "prod" }],
|
||||
values: [{ label: "prod", value: "prod", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
@@ -277,7 +277,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "alertname",
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert" }],
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
@@ -285,8 +285,8 @@ describe("SilenceFormStore.data", () => {
|
||||
expect.objectContaining({
|
||||
name: "instance",
|
||||
values: [
|
||||
{ label: "dev1", value: "dev1" },
|
||||
{ label: "prod1", value: "prod1" },
|
||||
{ label: "dev1", value: "dev1", wasCreated: false },
|
||||
{ label: "prod1", value: "prod1", wasCreated: false },
|
||||
],
|
||||
isRegex: true,
|
||||
})
|
||||
@@ -295,8 +295,8 @@ describe("SilenceFormStore.data", () => {
|
||||
expect.objectContaining({
|
||||
name: "cluster",
|
||||
values: [
|
||||
{ label: "dev", value: "dev" },
|
||||
{ label: "prod", value: "prod" },
|
||||
{ label: "dev", value: "dev", wasCreated: false },
|
||||
{ label: "prod", value: "prod", wasCreated: false },
|
||||
],
|
||||
isRegex: true,
|
||||
})
|
||||
@@ -348,7 +348,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "alertname",
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert" }],
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
@@ -356,8 +356,8 @@ describe("SilenceFormStore.data", () => {
|
||||
expect.objectContaining({
|
||||
name: "instance",
|
||||
values: [
|
||||
{ label: "1", value: "1" },
|
||||
{ label: "3", value: "3" },
|
||||
{ label: "1", value: "1", wasCreated: false },
|
||||
{ label: "3", value: "3", wasCreated: false },
|
||||
],
|
||||
isRegex: true,
|
||||
})
|
||||
@@ -394,7 +394,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "alertname",
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert" }],
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
@@ -415,28 +415,28 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "alertname",
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert" }],
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "job",
|
||||
values: [{ label: "mock", value: "mock" }],
|
||||
values: [{ label: "mock", value: "mock", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "instance",
|
||||
values: [{ label: "prod1", value: "prod1" }],
|
||||
values: [{ label: "prod1", value: "prod1", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "cluster",
|
||||
values: [{ label: "prod", value: "prod" }],
|
||||
values: [{ label: "prod", value: "prod", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
@@ -454,7 +454,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "alertname",
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert" }],
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
@@ -505,7 +505,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "region",
|
||||
values: [{ label: "AF", value: "AF" }],
|
||||
values: [{ label: "AF", value: "AF", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
@@ -513,9 +513,9 @@ describe("SilenceFormStore.data", () => {
|
||||
expect.objectContaining({
|
||||
name: "alertname",
|
||||
values: [
|
||||
{ label: "Alert1", value: "Alert1" },
|
||||
{ label: "Alert2", value: "Alert2" },
|
||||
{ label: "Alert3", value: "Alert3" },
|
||||
{ label: "Alert1", value: "Alert1", wasCreated: false },
|
||||
{ label: "Alert2", value: "Alert2", wasCreated: false },
|
||||
{ label: "Alert3", value: "Alert3", wasCreated: false },
|
||||
],
|
||||
isRegex: true,
|
||||
})
|
||||
@@ -523,7 +523,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "job",
|
||||
values: [{ label: "mock", value: "mock" }],
|
||||
values: [{ label: "mock", value: "mock", wasCreated: false }],
|
||||
isRegex: false,
|
||||
})
|
||||
);
|
||||
@@ -558,7 +558,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "regex",
|
||||
values: [{ label: "equal", value: "equal" }],
|
||||
values: [{ label: "equal", value: "equal", wasCreated: false }],
|
||||
isRegex: true,
|
||||
isEqual: true,
|
||||
})
|
||||
@@ -566,7 +566,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "regex",
|
||||
values: [{ label: "notEqual", value: "notEqual" }],
|
||||
values: [{ label: "notEqual", value: "notEqual", wasCreated: false }],
|
||||
isRegex: true,
|
||||
isEqual: false,
|
||||
})
|
||||
@@ -574,7 +574,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "notRegex",
|
||||
values: [{ label: "equal", value: "equal" }],
|
||||
values: [{ label: "equal", value: "equal", wasCreated: false }],
|
||||
isRegex: false,
|
||||
isEqual: true,
|
||||
})
|
||||
@@ -582,7 +582,7 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "notRegex",
|
||||
values: [{ label: "notEqual", value: "notEqual" }],
|
||||
values: [{ label: "notEqual", value: "notEqual", wasCreated: false }],
|
||||
isRegex: false,
|
||||
isEqual: false,
|
||||
})
|
||||
@@ -694,7 +694,11 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(silenceFormStorestore.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: t.result.name,
|
||||
values: t.result.values.map((v) => ({ label: v, value: v })),
|
||||
values: t.result.values.map((v) => ({
|
||||
label: v,
|
||||
value: v,
|
||||
wasCreated: false,
|
||||
})),
|
||||
isRegex: t.matcher.isRegex,
|
||||
isEqual: t.matcher.isEqual,
|
||||
})
|
||||
@@ -732,6 +736,112 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.toAlertmanagerPayload).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("toAlertmanagerPayload creates payload that matches snapshot with regex values", () => {
|
||||
store.data.setMatchers([
|
||||
{
|
||||
id: "1",
|
||||
name: "notEqualRegexAuto",
|
||||
values: [{ label: "foo", value: "^(.+)$", wasCreated: false }],
|
||||
isEqual: false,
|
||||
isRegex: true,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "equalRegexAuto",
|
||||
values: [{ label: "foo", value: "^(.+)$", wasCreated: false }],
|
||||
isEqual: true,
|
||||
isRegex: true,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "equalNotRegexAuto",
|
||||
values: [{ label: "foo", value: "^(.+)$", wasCreated: false }],
|
||||
isEqual: true,
|
||||
isRegex: false,
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "notEqualnotRegexAuto",
|
||||
values: [{ label: "foo", value: "^(.+)$", wasCreated: false }],
|
||||
isEqual: false,
|
||||
isRegex: false,
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
name: "notEqualRegexCreated",
|
||||
values: [{ label: "foo", value: "^(.+)$", wasCreated: true }],
|
||||
isEqual: false,
|
||||
isRegex: true,
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
name: "equalRegexCreated",
|
||||
values: [{ label: "foo", value: "^(.+)$", wasCreated: true }],
|
||||
isEqual: true,
|
||||
isRegex: true,
|
||||
},
|
||||
{
|
||||
id: "7",
|
||||
name: "equalNotRegexCreated",
|
||||
values: [{ label: "foo", value: "^(.+)$", wasCreated: true }],
|
||||
isEqual: true,
|
||||
isRegex: false,
|
||||
},
|
||||
{
|
||||
id: "8",
|
||||
name: "notEqualnotRegexAuto",
|
||||
values: [{ label: "foo", value: "^(.+)$", wasCreated: true }],
|
||||
isEqual: false,
|
||||
isRegex: false,
|
||||
},
|
||||
{
|
||||
id: "9",
|
||||
name: "notEqualRegexCreatedMulti",
|
||||
values: [
|
||||
{ label: "foo", value: "^(.+)$", wasCreated: true },
|
||||
{ label: "bar", value: "\\", wasCreated: true },
|
||||
],
|
||||
isEqual: false,
|
||||
isRegex: true,
|
||||
},
|
||||
{
|
||||
id: "10",
|
||||
name: "equalRegexCreatedMulti",
|
||||
values: [
|
||||
{ label: "foo", value: "^(.+)$", wasCreated: true },
|
||||
{ label: "bar", value: "\\", wasCreated: true },
|
||||
],
|
||||
isEqual: true,
|
||||
isRegex: true,
|
||||
},
|
||||
{
|
||||
id: "11",
|
||||
name: "notEqualRegexAuto",
|
||||
values: [
|
||||
{ label: "foo", value: "^(.+)$", wasCreated: false },
|
||||
{ label: "bar", value: "\\", wasCreated: true },
|
||||
],
|
||||
isEqual: false,
|
||||
isRegex: true,
|
||||
},
|
||||
{
|
||||
id: "12",
|
||||
name: "equalRegexAuto",
|
||||
values: [
|
||||
{ label: "foo", value: "^(.+)$", wasCreated: false },
|
||||
{ label: "bar", value: "\\", wasCreated: true },
|
||||
],
|
||||
isEqual: true,
|
||||
isRegex: true,
|
||||
},
|
||||
]);
|
||||
store.data.setStart(new Date(Date.UTC(2000, 1, 1, 0, 0, 0)));
|
||||
store.data.setEnd(new Date(Date.UTC(2000, 1, 1, 1, 0, 0)));
|
||||
store.data.setAuthor("me@example.com");
|
||||
store.data.setComment("toAlertmanagerPayload test");
|
||||
expect(store.data.toAlertmanagerPayload).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("dumps to base64 and back", () => {
|
||||
store.data.setMatchers([
|
||||
MockMatcher("foo", [StringToOption("bar")]),
|
||||
@@ -793,7 +903,7 @@ describe("SilenceFormStore.data", () => {
|
||||
describe("SilenceFormStore.data.isValid", () => {
|
||||
it("isValid returns 'false' if alertmanagers list is empty", () => {
|
||||
store.data.setMatchers([
|
||||
MockMatcher("foo", [{ label: "bar", value: "bar" }]),
|
||||
MockMatcher("foo", [{ label: "bar", value: "bar", wasCreated: false }]),
|
||||
]);
|
||||
store.data.setAuthor("me@example.com");
|
||||
store.data.setComment("fake silence");
|
||||
@@ -810,7 +920,9 @@ describe("SilenceFormStore.data.isValid", () => {
|
||||
|
||||
it("isValid returns 'false' if matchers list is pupulated when a matcher without any name", () => {
|
||||
store.data.setAlertmanagers([MockAlertmanagerOption()]);
|
||||
store.data.setMatchers([MockMatcher("", [{ label: "bar", value: "bar" }])]);
|
||||
store.data.setMatchers([
|
||||
MockMatcher("", [{ label: "bar", value: "bar", wasCreated: false }]),
|
||||
]);
|
||||
store.data.setAuthor("me@example.com");
|
||||
store.data.setComment("fake silence");
|
||||
expect(store.data.isValid).toBe(false);
|
||||
@@ -835,7 +947,7 @@ describe("SilenceFormStore.data.isValid", () => {
|
||||
it("isValid returns 'false' if author is empty", () => {
|
||||
store.data.setAlertmanagers([MockAlertmanagerOption()]);
|
||||
store.data.setMatchers([
|
||||
MockMatcher("foo", [{ label: "bar", value: "bar" }]),
|
||||
MockMatcher("foo", [{ label: "bar", value: "bar", wasCreated: false }]),
|
||||
]);
|
||||
store.data.setAuthor("");
|
||||
store.data.setComment("fake silence");
|
||||
@@ -845,7 +957,7 @@ describe("SilenceFormStore.data.isValid", () => {
|
||||
it("isValid returns 'false' if comment is empty", () => {
|
||||
store.data.setAlertmanagers([MockAlertmanagerOption()]);
|
||||
store.data.setMatchers([
|
||||
MockMatcher("foo", [{ label: "bar", value: "bar" }]),
|
||||
MockMatcher("foo", [{ label: "bar", value: "bar", wasCreated: false }]),
|
||||
]);
|
||||
store.data.setAuthor("me@example.com");
|
||||
store.data.setComment("");
|
||||
@@ -855,7 +967,7 @@ describe("SilenceFormStore.data.isValid", () => {
|
||||
it("isValid returns 'true' if all fileds are set", () => {
|
||||
store.data.setAlertmanagers([MockAlertmanagerOption()]);
|
||||
store.data.setMatchers([
|
||||
MockMatcher("foo", [{ label: "bar", value: "bar" }]),
|
||||
MockMatcher("foo", [{ label: "bar", value: "bar", wasCreated: false }]),
|
||||
]);
|
||||
store.data.setAuthor("me@example.com");
|
||||
store.data.setComment("fake silence");
|
||||
|
||||
@@ -77,6 +77,10 @@ const AlertmanagerClustersToOption = (clusterDict: {
|
||||
value: clusterMembers,
|
||||
}));
|
||||
|
||||
export const EscapeRegex = (v: string): string => {
|
||||
return v.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||||
};
|
||||
|
||||
const MatchersFromGroup = (
|
||||
group: APIAlertGroupT,
|
||||
stripLabels: string[],
|
||||
@@ -218,9 +222,13 @@ const GenerateAlertmanagerSilenceData = (
|
||||
name: m.name,
|
||||
value:
|
||||
m.values.length > 1
|
||||
? `(${m.values.map((v) => v.value).join("|")})`
|
||||
? `(${m.values
|
||||
.map((v) => (v.wasCreated ? v.value : EscapeRegex(v.value)))
|
||||
.join("|")})`
|
||||
: m.values.length === 1
|
||||
? m.values[0].value
|
||||
? m.values[0].wasCreated
|
||||
? m.values[0].value
|
||||
: EscapeRegex(m.values[0].value)
|
||||
: "",
|
||||
isRegex: m.isRegex,
|
||||
isEqual: m.isEqual,
|
||||
|
||||
@@ -40,3 +40,86 @@ Object {
|
||||
"startsAt": "2000-02-01T00:00:00.000Z",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`SilenceFormStore.data toAlertmanagerPayload creates payload that matches snapshot with regex values 1`] = `
|
||||
Object {
|
||||
"comment": "toAlertmanagerPayload test",
|
||||
"createdBy": "me@example.com",
|
||||
"endsAt": "2000-02-01T01:00:00.000Z",
|
||||
"matchers": Array [
|
||||
Object {
|
||||
"isEqual": false,
|
||||
"isRegex": true,
|
||||
"name": "notEqualRegexAuto",
|
||||
"value": "\\\\^\\\\(\\\\.\\\\+\\\\)\\\\$",
|
||||
},
|
||||
Object {
|
||||
"isEqual": true,
|
||||
"isRegex": true,
|
||||
"name": "equalRegexAuto",
|
||||
"value": "\\\\^\\\\(\\\\.\\\\+\\\\)\\\\$",
|
||||
},
|
||||
Object {
|
||||
"isEqual": true,
|
||||
"isRegex": false,
|
||||
"name": "equalNotRegexAuto",
|
||||
"value": "\\\\^\\\\(\\\\.\\\\+\\\\)\\\\$",
|
||||
},
|
||||
Object {
|
||||
"isEqual": false,
|
||||
"isRegex": false,
|
||||
"name": "notEqualnotRegexAuto",
|
||||
"value": "\\\\^\\\\(\\\\.\\\\+\\\\)\\\\$",
|
||||
},
|
||||
Object {
|
||||
"isEqual": false,
|
||||
"isRegex": true,
|
||||
"name": "notEqualRegexCreated",
|
||||
"value": "^(.+)$",
|
||||
},
|
||||
Object {
|
||||
"isEqual": true,
|
||||
"isRegex": true,
|
||||
"name": "equalRegexCreated",
|
||||
"value": "^(.+)$",
|
||||
},
|
||||
Object {
|
||||
"isEqual": true,
|
||||
"isRegex": false,
|
||||
"name": "equalNotRegexCreated",
|
||||
"value": "^(.+)$",
|
||||
},
|
||||
Object {
|
||||
"isEqual": false,
|
||||
"isRegex": false,
|
||||
"name": "notEqualnotRegexAuto",
|
||||
"value": "^(.+)$",
|
||||
},
|
||||
Object {
|
||||
"isEqual": false,
|
||||
"isRegex": true,
|
||||
"name": "notEqualRegexCreatedMulti",
|
||||
"value": "(^(.+)$|\\\\)",
|
||||
},
|
||||
Object {
|
||||
"isEqual": true,
|
||||
"isRegex": true,
|
||||
"name": "equalRegexCreatedMulti",
|
||||
"value": "(^(.+)$|\\\\)",
|
||||
},
|
||||
Object {
|
||||
"isEqual": false,
|
||||
"isRegex": true,
|
||||
"name": "notEqualRegexAuto",
|
||||
"value": "(\\\\^\\\\(\\\\.\\\\+\\\\)\\\\$|\\\\)",
|
||||
},
|
||||
Object {
|
||||
"isEqual": true,
|
||||
"isRegex": true,
|
||||
"name": "equalRegexAuto",
|
||||
"value": "(\\\\^\\\\(\\\\.\\\\+\\\\)\\\\$|\\\\)",
|
||||
},
|
||||
],
|
||||
"startsAt": "2000-02-01T00:00:00.000Z",
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -98,6 +98,10 @@ const useFetchGetMock = (
|
||||
uri: "./labelValues.json?name=cluster",
|
||||
response: ["dev", "staging", "prod"],
|
||||
},
|
||||
{
|
||||
uri: "./labelValues.json?name=regex",
|
||||
response: ["(dev)", "staging (.+)", "\\prod\\"],
|
||||
},
|
||||
// matcher value counters
|
||||
{
|
||||
re: /^\.\/alerts\.json\?q=/,
|
||||
|
||||
Reference in New Issue
Block a user