mirror of https://github.com/grafana/grafana
Alerting: fix route, silence matchers (#34372)
parent
97cf00ab33
commit
538b1b196b
@ -1,30 +0,0 @@ |
||||
import { AlertManagerCortexConfig, Route } from 'app/plugins/datasource/alertmanager/types'; |
||||
|
||||
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; |
||||
} |
||||
|
||||
function isReceiverUsedInRoute(receiver: string, route: Route): boolean { |
||||
return ( |
||||
(route.receiver === receiver || route.routes?.some((route) => isReceiverUsedInRoute(receiver, route))) ?? false |
||||
); |
||||
} |
||||
|
||||
export function isReceiverUsed(receiver: string, config: AlertManagerCortexConfig): boolean { |
||||
return ( |
||||
(config.alertmanager_config.route && isReceiverUsedInRoute(receiver, config.alertmanager_config.route)) ?? false |
||||
); |
||||
} |
@ -0,0 +1,69 @@ |
||||
import { Matcher } from 'app/plugins/datasource/alertmanager/types'; |
||||
import { parseMatcher, stringifyMatcher } from './alertmanager'; |
||||
|
||||
describe('Alertmanager utils', () => { |
||||
describe('parseMatcher', () => { |
||||
it('should parse operators correctly', () => { |
||||
expect(parseMatcher('foo=bar')).toEqual<Matcher>({ |
||||
name: 'foo', |
||||
value: 'bar', |
||||
isRegex: false, |
||||
isEqual: true, |
||||
}); |
||||
expect(parseMatcher('foo!=bar')).toEqual<Matcher>({ |
||||
name: 'foo', |
||||
value: 'bar', |
||||
isRegex: false, |
||||
isEqual: false, |
||||
}); |
||||
expect(parseMatcher('foo =~bar')).toEqual<Matcher>({ |
||||
name: 'foo', |
||||
value: 'bar', |
||||
isRegex: true, |
||||
isEqual: true, |
||||
}); |
||||
expect(parseMatcher('foo!~ bar')).toEqual<Matcher>({ |
||||
name: 'foo', |
||||
value: 'bar', |
||||
isRegex: true, |
||||
isEqual: false, |
||||
}); |
||||
}); |
||||
|
||||
it('should parse escaped values correctly', () => { |
||||
expect(parseMatcher('foo=~"bar\\"baz\\""')).toEqual<Matcher>({ |
||||
name: 'foo', |
||||
value: 'bar"baz"', |
||||
isRegex: true, |
||||
isEqual: true, |
||||
}); |
||||
expect(parseMatcher('foo=~bar\\"baz\\"')).toEqual<Matcher>({ |
||||
name: 'foo', |
||||
value: 'bar"baz"', |
||||
isRegex: true, |
||||
isEqual: true, |
||||
}); |
||||
}); |
||||
it('should parse multiple operators values correctly', () => { |
||||
expect(parseMatcher('foo=~bar=baz!=bad!~br')).toEqual<Matcher>({ |
||||
name: 'foo', |
||||
value: 'bar=baz!=bad!~br', |
||||
isRegex: true, |
||||
isEqual: true, |
||||
}); |
||||
}); |
||||
}); |
||||
|
||||
describe('stringifyMatcher', () => { |
||||
it('should stringify matcher correctly', () => { |
||||
expect( |
||||
stringifyMatcher({ |
||||
name: 'foo', |
||||
value: 'boo="bar"', |
||||
isRegex: true, |
||||
isEqual: false, |
||||
}) |
||||
).toEqual('foo!~"boo=\\"bar\\""'); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,95 @@ |
||||
import { AlertManagerCortexConfig, MatcherOperator, Route, Matcher } from 'app/plugins/datasource/alertmanager/types'; |
||||
|
||||
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; |
||||
} |
||||
|
||||
function isReceiverUsedInRoute(receiver: string, route: Route): boolean { |
||||
return ( |
||||
(route.receiver === receiver || route.routes?.some((route) => isReceiverUsedInRoute(receiver, route))) ?? false |
||||
); |
||||
} |
||||
|
||||
export function isReceiverUsed(receiver: string, config: AlertManagerCortexConfig): boolean { |
||||
return ( |
||||
(config.alertmanager_config.route && isReceiverUsedInRoute(receiver, config.alertmanager_config.route)) ?? false |
||||
); |
||||
} |
||||
|
||||
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; |
||||
} |
||||
} |
||||
|
||||
const matcherOperators = [ |
||||
MatcherOperator.regex, |
||||
MatcherOperator.notRegex, |
||||
MatcherOperator.notEqual, |
||||
MatcherOperator.equal, |
||||
]; |
||||
|
||||
function unescapeMatcherValue(value: string) { |
||||
let trimmed = value.trim().replace(/\\"/g, '"'); |
||||
if (trimmed.startsWith('"') && trimmed.endsWith('"') && !trimmed.endsWith('\\"')) { |
||||
trimmed = trimmed.substr(1, trimmed.length - 2); |
||||
} |
||||
return trimmed.replace(/\\"/g, '"'); |
||||
} |
||||
|
||||
function escapeMatcherValue(value: string) { |
||||
return '"' + value.replace(/"/g, '\\"') + '"'; |
||||
} |
||||
|
||||
export function stringifyMatcher(matcher: Matcher): string { |
||||
return `${matcher.name}${matcherToOperator(matcher)}${escapeMatcherValue(matcher.value)}`; |
||||
} |
||||
|
||||
export function parseMatcher(matcher: string): Matcher { |
||||
const trimmed = matcher.trim(); |
||||
if (trimmed.startsWith('{') && trimmed.endsWith('}')) { |
||||
throw new Error(`PromQL matchers not supported yet, sorry! PromQL matcher found: ${trimmed}`); |
||||
} |
||||
const operatorsFound = matcherOperators |
||||
.map((op): [MatcherOperator, number] => [op, trimmed.indexOf(op)]) |
||||
.filter(([_, idx]) => idx > -1) |
||||
.sort((a, b) => a[1] - b[1]); |
||||
|
||||
if (!operatorsFound.length) { |
||||
throw new Error(`Invalid matcher: ${trimmed}`); |
||||
} |
||||
const [operator, idx] = operatorsFound[0]; |
||||
const name = trimmed.substr(0, idx).trim(); |
||||
const value = unescapeMatcherValue(trimmed.substr(idx + operator.length).trim()); |
||||
if (!name) { |
||||
throw new Error(`Invalid matcher: ${trimmed}`); |
||||
} |
||||
|
||||
return { |
||||
name, |
||||
value, |
||||
isRegex: operator === MatcherOperator.regex || operator === MatcherOperator.notRegex, |
||||
isEqual: operator === MatcherOperator.equal || operator === MatcherOperator.regex, |
||||
}; |
||||
} |
Loading…
Reference in new issue