206 lines
7.0 KiB
TypeScript
206 lines
7.0 KiB
TypeScript
import { isEqual, uniqWith } from 'lodash';
|
|
|
|
import { SelectableValue } from '@grafana/data';
|
|
import {
|
|
AlertManagerCortexConfig,
|
|
Matcher,
|
|
MatcherOperator,
|
|
ObjectMatcher,
|
|
Route,
|
|
TimeInterval,
|
|
TimeRange,
|
|
} from 'app/plugins/datasource/alertmanager/types';
|
|
import { Labels } from 'app/types/unified-alerting-dto';
|
|
|
|
import { MatcherFieldValue } from '../types/silence-form';
|
|
|
|
import { getAllDataSources } from './config';
|
|
import { DataSourceType, GRAFANA_RULES_SOURCE_NAME } from './datasource';
|
|
import { objectLabelsToArray } from './labels';
|
|
import { MatcherFormatter, matchLabelsSet, parsePromQLStyleMatcherLooseSafe, unquoteWithUnescape } from './matchers';
|
|
|
|
export function addDefaultsToAlertmanagerConfig(config: AlertManagerCortexConfig): AlertManagerCortexConfig {
|
|
// add default receiver if it does not exist
|
|
if (!config.alertmanager_config.receivers) {
|
|
config.alertmanager_config.receivers = [{ name: 'default ' }];
|
|
}
|
|
// add default route if it does not exists
|
|
if (!config.alertmanager_config.route) {
|
|
config.alertmanager_config.route = {
|
|
receiver: config.alertmanager_config.receivers![0].name,
|
|
};
|
|
}
|
|
if (!config.template_files) {
|
|
config.template_files = {};
|
|
}
|
|
return config;
|
|
}
|
|
|
|
export function removeTimeIntervalFromRoute(muteTiming: string, route: Route): Route {
|
|
const newRoute: Route = {
|
|
...route,
|
|
mute_time_intervals: route.mute_time_intervals?.filter((muteName) => muteName !== muteTiming) ?? [],
|
|
active_time_intervals: route.active_time_intervals?.filter((muteName) => muteName !== muteTiming) ?? [],
|
|
routes: route.routes?.map((subRoute) => removeTimeIntervalFromRoute(muteTiming, subRoute)),
|
|
};
|
|
return newRoute;
|
|
}
|
|
|
|
export function renameTimeInterval(newName: string, oldName: string, route: Route): Route {
|
|
return {
|
|
...route,
|
|
mute_time_intervals: route.mute_time_intervals?.map((name) => (name === oldName ? newName : name)),
|
|
active_time_intervals: route.active_time_intervals?.map((name) => (name === oldName ? newName : name)),
|
|
routes: route.routes?.map((subRoute) => renameTimeInterval(newName, oldName, subRoute)),
|
|
};
|
|
}
|
|
|
|
export function unescapeObjectMatchers(matchers: ObjectMatcher[]): ObjectMatcher[] {
|
|
return matchers.map(([name, operator, value]) => [name, operator, unquoteWithUnescape(value)]);
|
|
}
|
|
|
|
export function matcherToOperator(matcher: Matcher): MatcherOperator {
|
|
if (matcher.isEqual) {
|
|
if (matcher.isRegex) {
|
|
return MatcherOperator.regex;
|
|
} else {
|
|
return MatcherOperator.equal;
|
|
}
|
|
} else if (matcher.isRegex) {
|
|
return MatcherOperator.notRegex;
|
|
} else {
|
|
return MatcherOperator.notEqual;
|
|
}
|
|
}
|
|
|
|
export function matcherOperatorToValue(operator: MatcherOperator) {
|
|
switch (operator) {
|
|
case MatcherOperator.equal:
|
|
return { isEqual: true, isRegex: false };
|
|
case MatcherOperator.notEqual:
|
|
return { isEqual: false, isRegex: false };
|
|
case MatcherOperator.regex:
|
|
return { isEqual: true, isRegex: true };
|
|
case MatcherOperator.notRegex:
|
|
return { isEqual: false, isRegex: true };
|
|
}
|
|
}
|
|
|
|
export function matcherToMatcherField(matcher: Matcher): MatcherFieldValue {
|
|
return {
|
|
name: matcher.name,
|
|
value: matcher.value,
|
|
operator: matcherToOperator(matcher),
|
|
};
|
|
}
|
|
|
|
export function matcherFieldToMatcher(field: MatcherFieldValue): Matcher {
|
|
return {
|
|
name: field.name,
|
|
value: field.value,
|
|
...matcherOperatorToValue(field.operator),
|
|
};
|
|
}
|
|
|
|
export function matchersToString(matchers: Matcher[]) {
|
|
const matcherFields = matchers.map(matcherToMatcherField);
|
|
|
|
const combinedMatchers = matcherFields.reduce((acc, current) => {
|
|
const currentMatcherString = `${current.name}${current.operator}"${current.value}"`;
|
|
return acc ? `${acc}, ${currentMatcherString}` : currentMatcherString;
|
|
}, '');
|
|
|
|
return `{ ${combinedMatchers} }`;
|
|
}
|
|
|
|
export const matcherFieldOptions: SelectableValue[] = [
|
|
{ label: MatcherOperator.equal, description: 'Equals', value: MatcherOperator.equal },
|
|
{ label: MatcherOperator.notEqual, description: 'Does not equal', value: MatcherOperator.notEqual },
|
|
{ label: MatcherOperator.regex, description: 'Matches regex', value: MatcherOperator.regex },
|
|
{ label: MatcherOperator.notRegex, description: 'Does not match regex', value: MatcherOperator.notRegex },
|
|
];
|
|
|
|
export function matcherToObjectMatcher(matcher: Matcher): ObjectMatcher {
|
|
const operator = matcherToOperator(matcher);
|
|
return [matcher.name, operator, matcher.value];
|
|
}
|
|
|
|
export function labelsMatchMatchers(labels: Labels, matchers: Matcher[]): boolean {
|
|
const labelsArray = objectLabelsToArray(labels);
|
|
const objectMatchers = matchers.map(matcherToObjectMatcher);
|
|
|
|
return matchLabelsSet(objectMatchers, labelsArray);
|
|
}
|
|
|
|
export function combineMatcherStrings(...matcherStrings: string[]): string {
|
|
const matchers = matcherStrings.map(parsePromQLStyleMatcherLooseSafe).flat();
|
|
const uniqueMatchers = uniqWith(matchers, isEqual);
|
|
return matchersToString(uniqueMatchers);
|
|
}
|
|
|
|
export function getAmMatcherFormatter(alertmanagerSourceName?: string): MatcherFormatter {
|
|
return alertmanagerSourceName === GRAFANA_RULES_SOURCE_NAME ? 'default' : 'unquote';
|
|
}
|
|
|
|
export function getAllAlertmanagerDataSources() {
|
|
return getAllDataSources().filter((ds) => ds.type === DataSourceType.Alertmanager);
|
|
}
|
|
|
|
export function getAlertmanagerByUid(uid?: string) {
|
|
return getAllAlertmanagerDataSources().find((ds) => uid === ds.uid);
|
|
}
|
|
|
|
export function timeIntervalToString(timeInterval: TimeInterval): string {
|
|
const { times, weekdays, days_of_month, months, years, location } = timeInterval;
|
|
const timeString = getTimeString(times, location);
|
|
const weekdayString = getWeekdayString(weekdays);
|
|
const daysString = getDaysOfMonthString(days_of_month);
|
|
const monthsString = getMonthsString(months);
|
|
const yearsString = getYearsString(years);
|
|
|
|
return [timeString, weekdayString, daysString, monthsString, yearsString].join(', ');
|
|
}
|
|
|
|
export function getTimeString(times?: TimeRange[], location?: string): string {
|
|
return (
|
|
'Times: ' +
|
|
(times
|
|
? times?.map(({ start_time, end_time }) => `${start_time} - ${end_time} [${location ?? 'UTC'}]`).join(' and ')
|
|
: 'All')
|
|
);
|
|
}
|
|
|
|
export function getWeekdayString(weekdays?: string[]): string {
|
|
return (
|
|
'Weekdays: ' +
|
|
(weekdays
|
|
?.map((day) => {
|
|
if (day.includes(':')) {
|
|
return day
|
|
.split(':')
|
|
.map((d) => {
|
|
const abbreviated = d.slice(0, 3);
|
|
return abbreviated[0].toLocaleUpperCase() + abbreviated.slice(1);
|
|
})
|
|
.join('-');
|
|
} else {
|
|
const abbreviated = day.slice(0, 3);
|
|
return abbreviated[0].toLocaleUpperCase() + abbreviated.slice(1);
|
|
}
|
|
})
|
|
.join(', ') ?? 'All')
|
|
);
|
|
}
|
|
|
|
export function getDaysOfMonthString(daysOfMonth?: string[]): string {
|
|
return 'Days of the month: ' + (daysOfMonth?.join(', ') ?? 'All');
|
|
}
|
|
|
|
export function getMonthsString(months?: string[]): string {
|
|
return 'Months: ' + (months?.join(', ') ?? 'All');
|
|
}
|
|
|
|
export function getYearsString(years?: string[]): string {
|
|
return 'Years: ' + (years?.join(', ') ?? 'All');
|
|
}
|