Alerting: Add support for keep_firing_for field from external rulers (#75163)

* Add support for `keep_firing_for` in ruler proxy

* Don't delete `keep_firing_for` when editing a rule with the field set

Co-Authored-By: Sonia Aguilar <33540275+soniaAguilarPeiron@users.noreply.github.com>

---------

Co-authored-by: Sonia Aguilar <33540275+soniaAguilarPeiron@users.noreply.github.com>
This commit is contained in:
William Wernert 2023-09-21 16:02:53 -04:00 committed by GitHub
parent 633605af4e
commit 925f12d0ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 157 additions and 10 deletions

View File

@ -262,6 +262,7 @@ type ApiRuleNode struct {
Alert string `yaml:"alert,omitempty" json:"alert,omitempty"` Alert string `yaml:"alert,omitempty" json:"alert,omitempty"`
Expr string `yaml:"expr" json:"expr"` Expr string `yaml:"expr" json:"expr"`
For *model.Duration `yaml:"for,omitempty" json:"for,omitempty"` For *model.Duration `yaml:"for,omitempty" json:"for,omitempty"`
KeepFiringFor *model.Duration `yaml:"keep_firing_for,omitempty" json:"keep_firing_for,omitempty"`
Labels map[string]string `yaml:"labels,omitempty" json:"labels,omitempty"` Labels map[string]string `yaml:"labels,omitempty" json:"labels,omitempty"`
Annotations map[string]string `yaml:"annotations,omitempty" json:"annotations,omitempty"` Annotations map[string]string `yaml:"annotations,omitempty" json:"annotations,omitempty"`
} }

View File

@ -32,5 +32,7 @@ export interface RuleFormValues {
namespace: string; namespace: string;
forTime: number; forTime: number;
forTimeUnit: string; forTimeUnit: string;
keepFiringForTime?: number;
keepFiringForTimeUnit?: string;
expression: string; expression: string;
} }

View File

@ -59,3 +59,73 @@ exports[`formValuesToRulerGrafanaRuleDTO should not save both instant and range
}, },
} }
`; `;
exports[`formValuesToRulerGrafanaRuleDTO should not set keep_firing_for if values are undefined 1`] = `
{
"alert": "",
"annotations": {
"description": "",
"runbook_url": "",
"summary": "",
},
"expr": "",
"for": "1m",
"keep_firing_for": undefined,
"labels": {
"": "",
},
}
`;
exports[`formValuesToRulerGrafanaRuleDTO should parse keep_firing_for 1`] = `
{
"annotations": [],
"expression": "B",
"forTime": 1,
"forTimeUnit": "m",
"keepFiringForTime": 1,
"keepFiringForTimeUnit": "m",
"labels": [
{
"key": "",
"value": "",
},
],
"name": "A",
}
`;
exports[`formValuesToRulerGrafanaRuleDTO should set keep_firing_for if values are populated 1`] = `
{
"alert": "",
"annotations": {
"description": "",
"runbook_url": "",
"summary": "",
},
"expr": "",
"for": "1m",
"keep_firing_for": "1m",
"labels": {
"": "",
},
}
`;
exports[`formValuesToRulerGrafanaRuleDTO should set keepFiringForTime and keepFiringForTimeUnit to undefined if keep_firing_for not set 1`] = `
{
"annotations": [],
"expression": "B",
"forTime": 1,
"forTimeUnit": "m",
"keepFiringForTime": undefined,
"keepFiringForTimeUnit": undefined,
"labels": [
{
"key": "",
"value": "",
},
],
"name": "A",
}
`;

View File

@ -1,8 +1,14 @@
import { PromQuery } from 'app/plugins/datasource/prometheus/types'; import { PromQuery } from 'app/plugins/datasource/prometheus/types';
import { RulerAlertingRuleDTO } from 'app/types/unified-alerting-dto';
import { RuleFormValues } from '../types/rule-form'; import { RuleFormType, RuleFormValues } from '../types/rule-form';
import { formValuesToRulerGrafanaRuleDTO, getDefaultFormValues } from './rule-form'; import {
alertingRulerRuleToRuleForm,
formValuesToRulerGrafanaRuleDTO,
formValuesToRulerRuleDTO,
getDefaultFormValues,
} from './rule-form';
describe('formValuesToRulerGrafanaRuleDTO', () => { describe('formValuesToRulerGrafanaRuleDTO', () => {
it('should correctly convert rule form values', () => { it('should correctly convert rule form values', () => {
@ -33,4 +39,49 @@ describe('formValuesToRulerGrafanaRuleDTO', () => {
expect(formValuesToRulerGrafanaRuleDTO(values)).toMatchSnapshot(); expect(formValuesToRulerGrafanaRuleDTO(values)).toMatchSnapshot();
}); });
it('should set keep_firing_for if values are populated', () => {
const formValues: RuleFormValues = {
...getDefaultFormValues(),
type: RuleFormType.cloudAlerting,
condition: 'A',
keepFiringForTime: 1,
keepFiringForTimeUnit: 'm',
};
expect(formValuesToRulerRuleDTO(formValues)).toMatchSnapshot();
});
it('should not set keep_firing_for if values are undefined', () => {
const formValues: RuleFormValues = {
...getDefaultFormValues(),
type: RuleFormType.cloudAlerting,
condition: 'A',
};
expect(formValuesToRulerRuleDTO(formValues)).toMatchSnapshot();
});
it('should parse keep_firing_for', () => {
const rule: RulerAlertingRuleDTO = {
alert: 'A',
expr: 'B',
for: '1m',
keep_firing_for: '1m',
labels: {},
};
expect(alertingRulerRuleToRuleForm(rule)).toMatchSnapshot();
});
it('should set keepFiringForTime and keepFiringForTimeUnit to undefined if keep_firing_for not set', () => {
const rule: RulerAlertingRuleDTO = {
alert: 'A',
expr: 'B',
for: '1m',
labels: {},
};
expect(alertingRulerRuleToRuleForm(rule)).toMatchSnapshot();
});
}); });

View File

@ -75,11 +75,17 @@ export const getDefaultFormValues = (): RuleFormValues => {
}; };
export function formValuesToRulerRuleDTO(values: RuleFormValues): RulerRuleDTO { export function formValuesToRulerRuleDTO(values: RuleFormValues): RulerRuleDTO {
const { name, expression, forTime, forTimeUnit, type } = values; const { name, expression, forTime, forTimeUnit, keepFiringForTime, keepFiringForTimeUnit, type } = values;
if (type === RuleFormType.cloudAlerting) { if (type === RuleFormType.cloudAlerting) {
let keepFiringFor: string | undefined;
if (keepFiringForTime && keepFiringForTimeUnit) {
keepFiringFor = `${keepFiringForTime}${keepFiringForTimeUnit}`;
}
return { return {
alert: name, alert: name,
for: `${forTime}${forTimeUnit}`, for: `${forTime}${forTimeUnit}`,
keep_firing_for: keepFiringFor,
annotations: arrayToRecord(values.annotations || []), annotations: arrayToRecord(values.annotations || []),
labels: arrayToRecord(values.labels || []), labels: arrayToRecord(values.labels || []),
expr: expression, expr: expression,
@ -220,18 +226,34 @@ export function rulerRuleToFormValues(ruleWithLocation: RuleWithLocation): RuleF
export function alertingRulerRuleToRuleForm( export function alertingRulerRuleToRuleForm(
rule: RulerAlertingRuleDTO rule: RulerAlertingRuleDTO
): Pick<RuleFormValues, 'name' | 'forTime' | 'forTimeUnit' | 'expression' | 'annotations' | 'labels'> { ): Pick<
RuleFormValues,
| 'name'
| 'forTime'
| 'forTimeUnit'
| 'keepFiringForTime'
| 'keepFiringForTimeUnit'
| 'expression'
| 'annotations'
| 'labels'
> {
const defaultFormValues = getDefaultFormValues(); const defaultFormValues = getDefaultFormValues();
const [forTime, forTimeUnit] = rule.for const [forTime, forTimeUnit] = rule.for
? parseInterval(rule.for) ? parseInterval(rule.for)
: [defaultFormValues.forTime, defaultFormValues.forTimeUnit]; : [defaultFormValues.forTime, defaultFormValues.forTimeUnit];
const [keepFiringForTime, keepFiringForTimeUnit] = rule.keep_firing_for
? parseInterval(rule.keep_firing_for)
: [defaultFormValues.keepFiringForTime, defaultFormValues.keepFiringForTimeUnit];
return { return {
name: rule.alert, name: rule.alert,
expression: rule.expr, expression: rule.expr,
forTime, forTime,
forTimeUnit, forTimeUnit,
keepFiringForTime,
keepFiringForTimeUnit,
annotations: listifyLabelsOrAnnotations(rule.annotations, false), annotations: listifyLabelsOrAnnotations(rule.annotations, false),
labels: listifyLabelsOrAnnotations(rule.labels, true), labels: listifyLabelsOrAnnotations(rule.labels, true),
}; };

View File

@ -174,6 +174,7 @@ export interface RulerRecordingRuleDTO extends RulerRuleBaseDTO {
export interface RulerAlertingRuleDTO extends RulerRuleBaseDTO { export interface RulerAlertingRuleDTO extends RulerRuleBaseDTO {
alert: string; alert: string;
for?: string; for?: string;
keep_firing_for?: string;
annotations?: Annotations; annotations?: Annotations;
} }