Alerting: Allow disabling override timings for notification policies (#48648)

This commit is contained in:
Gilles De Mey 2022-05-16 11:46:14 +02:00 committed by GitHub
parent 1a7ca3f0de
commit fa37c6c9d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 52 additions and 47 deletions

View File

@ -646,6 +646,7 @@ const clickSelectOption = async (selectElement: HTMLElement, optionText: string)
const updateTiming = async (selectElement: HTMLElement, value: string, timeUnit: string): Promise<void> => {
const input = byRole('textbox').get(selectElement);
const select = byRole('combobox').get(selectElement);
await userEvent.clear(input);
await userEvent.type(input, value);
await userEvent.click(select);
await selectOptionInTest(selectElement, timeUnit);

View File

@ -36,7 +36,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
const [groupByOptions, setGroupByOptions] = useState(stringsToSelectableValues(routes.groupBy));
return (
<Form defaultValues={routes} onSubmit={onSave}>
<Form defaultValues={{ ...routes, overrideTimings: true }} onSubmit={onSave}>
{({ control, errors, setValue }) => (
<>
<Field label="Default contact point" invalid={!!errors.receiver} error={errors.receiver?.message}>
@ -111,12 +111,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
<div className={cx(styles.container, styles.timingContainer)}>
<InputControl
render={({ field, fieldState: { invalid } }) => (
<Input
{...field}
className={styles.smallInput}
invalid={invalid}
placeholder={'Default 30 seconds'}
/>
<Input {...field} className={styles.smallInput} invalid={invalid} placeholder={'30'} />
)}
control={control}
name="groupWaitValue"
@ -151,12 +146,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
<div className={cx(styles.container, styles.timingContainer)}>
<InputControl
render={({ field, fieldState: { invalid } }) => (
<Input
{...field}
className={styles.smallInput}
invalid={invalid}
placeholder={'Default 5 minutes'}
/>
<Input {...field} className={styles.smallInput} invalid={invalid} placeholder={'5'} />
)}
control={control}
name="groupIntervalValue"
@ -191,7 +181,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
<div className={cx(styles.container, styles.timingContainer)}>
<InputControl
render={({ field, fieldState: { invalid } }) => (
<Input {...field} className={styles.smallInput} invalid={invalid} placeholder="Default 4 hours" />
<Input {...field} className={styles.smallInput} invalid={invalid} placeholder="4" />
)}
control={control}
name="repeatIntervalValue"

View File

@ -45,15 +45,12 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
const styles = useStyles2(getStyles);
const formStyles = useStyles2(getFormStyles);
const [overrideGrouping, setOverrideGrouping] = useState(routes.groupBy.length > 0);
const [overrideTimings, setOverrideTimings] = useState(
!!routes.groupWaitValue || !!routes.groupIntervalValue || !!routes.repeatIntervalValue
);
const [groupByOptions, setGroupByOptions] = useState(stringsToSelectableValues(routes.groupBy));
const muteTimingOptions = useMuteTimingOptions();
return (
<Form defaultValues={routes} onSubmit={onSave}>
{({ control, register, errors, setValue }) => (
{({ control, register, errors, setValue, watch }) => (
<>
{/* @ts-ignore-check: react-hook-form made me do this */}
<input type="hidden" {...register('id')} />
@ -169,7 +166,10 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
/>
</Field>
{overrideGrouping && (
<Field label="Group by" description="Group alerts when you receive a notification based on labels.">
<Field
label="Group by"
description="Group alerts when you receive a notification based on labels. If empty it will be inherited from the parent policy."
>
<InputControl
render={({ field: { onChange, ref, ...field } }) => (
<MultiSelect
@ -193,17 +193,13 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
</Field>
)}
<Field label="Override general timings">
<Switch
id="override-timings-toggle"
value={overrideTimings}
onChange={() => setOverrideTimings((overrideTimings) => !overrideTimings)}
/>
<Switch id="override-timings-toggle" {...register('overrideTimings')} />
</Field>
{overrideTimings && (
{watch().overrideTimings && (
<>
<Field
label="Group wait"
description="The waiting time until the initial notification is sent for a new group created by an incoming alert."
description="The waiting time until the initial notification is sent for a new group created by an incoming alert. If empty it will be inherited from the parent policy."
invalid={!!errors.groupWaitValue}
error={errors.groupWaitValue?.message}
>
@ -215,7 +211,6 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
{...field}
className={formStyles.smallInput}
invalid={invalid}
placeholder="Time"
aria-label="Group wait value"
/>
)}
@ -243,7 +238,7 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
</Field>
<Field
label="Group interval"
description="The waiting time to send a batch of new alerts for that group after the first notification was sent."
description="The waiting time to send a batch of new alerts for that group after the first notification was sent. If empty it will be inherited from the parent policy."
invalid={!!errors.groupIntervalValue}
error={errors.groupIntervalValue?.message}
>
@ -255,7 +250,6 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
{...field}
className={formStyles.smallInput}
invalid={invalid}
placeholder="Time"
aria-label="Group interval value"
/>
)}
@ -295,7 +289,6 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
{...field}
className={formStyles.smallInput}
invalid={invalid}
placeholder="Time"
aria-label="Repeat interval value"
/>
)}

View File

@ -11,6 +11,7 @@ const defaultAmRoute: FormAmRoute = {
continue: false,
receiver: '',
groupBy: [],
overrideTimings: false,
groupWaitValue: '',
groupWaitValueType: '',
groupIntervalValue: '',

View File

@ -6,6 +6,7 @@ export interface FormAmRoute {
continue: boolean;
receiver: string;
groupBy: string[];
overrideTimings: boolean;
groupWaitValue: string;
groupWaitValueType: string;
groupIntervalValue: string;

View File

@ -11,7 +11,7 @@ import { matcherToMatcherField, parseMatcher } from './alertmanager';
import { GRAFANA_RULES_SOURCE_NAME } from './datasource';
import { parseInterval, timeOptions } from './time';
const defaultValueAndType: [string, string] = ['', timeOptions[0].value];
const defaultValueAndType: [string, string] = ['', ''];
const matchersToArrayFieldMatchers = (
matchers: Record<string, string> | undefined,
@ -29,9 +29,12 @@ const matchersToArrayFieldMatchers = (
[] as MatcherFieldValue[]
);
const intervalToValueAndType = (strValue: string | undefined): [string, string] => {
const intervalToValueAndType = (
strValue: string | undefined,
defaultValue?: typeof defaultValueAndType
): [string, string] => {
if (!strValue) {
return defaultValueAndType;
return defaultValue ?? defaultValueAndType;
}
const [value, valueType] = strValue ? parseInterval(strValue) : [undefined, undefined];
@ -63,6 +66,7 @@ export const emptyRoute: FormAmRoute = {
routes: [],
continue: false,
receiver: '',
overrideTimings: false,
groupWaitValue: '',
groupWaitValueType: timeOptions[0].value,
groupIntervalValue: '',
@ -78,9 +82,9 @@ export const amRouteToFormAmRoute = (route: Route | undefined): [FormAmRoute, Re
return [emptyRoute, {}];
}
const [groupWaitValue, groupWaitValueType] = intervalToValueAndType(route.group_wait);
const [groupIntervalValue, groupIntervalValueType] = intervalToValueAndType(route.group_interval);
const [repeatIntervalValue, repeatIntervalValueType] = intervalToValueAndType(route.repeat_interval);
const [groupWaitValue, groupWaitValueType] = intervalToValueAndType(route.group_wait, ['', 's']);
const [groupIntervalValue, groupIntervalValueType] = intervalToValueAndType(route.group_interval, ['', 'm']);
const [repeatIntervalValue, repeatIntervalValueType] = intervalToValueAndType(route.repeat_interval, ['', 'h']);
const id = String(Math.random());
const id2route = {
@ -111,6 +115,7 @@ export const amRouteToFormAmRoute = (route: Route | undefined): [FormAmRoute, Re
continue: route.continue ?? false,
receiver: route.receiver ?? '',
groupBy: route.group_by ?? [],
overrideTimings: [groupWaitValue, groupIntervalValue, repeatIntervalValue].some(Boolean),
groupWaitValue,
groupWaitValueType,
groupIntervalValue,
@ -130,6 +135,26 @@ export const formAmRouteToAmRoute = (
id2ExistingRoute: Record<string, Route>
): Route => {
const existing: Route | undefined = id2ExistingRoute[formAmRoute.id];
const {
overrideTimings,
groupWaitValue,
groupWaitValueType,
groupIntervalValue,
groupIntervalValueType,
repeatIntervalValue,
repeatIntervalValueType,
} = formAmRoute;
const overrideGroupWait = overrideTimings && groupWaitValue;
const group_wait = overrideGroupWait ? `${groupWaitValue}${groupWaitValueType}` : undefined;
const overrideGroupInterval = overrideTimings && groupIntervalValue;
const group_interval = overrideGroupInterval ? `${groupIntervalValue}${groupIntervalValueType}` : undefined;
const overrideRepeatInterval = overrideTimings && repeatIntervalValue;
const repeat_interval = overrideRepeatInterval ? `${repeatIntervalValue}${repeatIntervalValueType}` : undefined;
const amRoute: Route = {
...(existing ?? {}),
continue: formAmRoute.continue,
@ -137,17 +162,11 @@ export const formAmRouteToAmRoute = (
object_matchers: formAmRoute.object_matchers.length
? formAmRoute.object_matchers.map((matcher) => [matcher.name, matcher.operator, matcher.value])
: undefined,
match: undefined,
match_re: undefined,
group_wait: formAmRoute.groupWaitValue
? `${formAmRoute.groupWaitValue}${formAmRoute.groupWaitValueType}`
: undefined,
group_interval: formAmRoute.groupIntervalValue
? `${formAmRoute.groupIntervalValue}${formAmRoute.groupIntervalValueType}`
: undefined,
repeat_interval: formAmRoute.repeatIntervalValue
? `${formAmRoute.repeatIntervalValue}${formAmRoute.repeatIntervalValueType}`
: undefined,
match: undefined, // DEPRECATED: Use matchers
match_re: undefined, // DEPRECATED: Use matchers
group_wait,
group_interval,
repeat_interval,
routes: formAmRoute.routes.map((subRoute) =>
formAmRouteToAmRoute(alertManagerSourceName, subRoute, id2ExistingRoute)
),