alertmanager.test.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import { Matcher, MatcherOperator, Route } from 'app/plugins/datasource/alertmanager/types';
  2. import { Labels } from 'app/types/unified-alerting-dto';
  3. import {
  4. parseMatcher,
  5. parseMatchers,
  6. labelsMatchMatchers,
  7. removeMuteTimingFromRoute,
  8. matchersToString,
  9. } from './alertmanager';
  10. describe('Alertmanager utils', () => {
  11. describe('parseMatcher', () => {
  12. it('should parse operators correctly', () => {
  13. expect(parseMatcher('foo=bar')).toEqual<Matcher>({
  14. name: 'foo',
  15. value: 'bar',
  16. isRegex: false,
  17. isEqual: true,
  18. });
  19. expect(parseMatcher('foo!=bar')).toEqual<Matcher>({
  20. name: 'foo',
  21. value: 'bar',
  22. isRegex: false,
  23. isEqual: false,
  24. });
  25. expect(parseMatcher('foo =~bar')).toEqual<Matcher>({
  26. name: 'foo',
  27. value: 'bar',
  28. isRegex: true,
  29. isEqual: true,
  30. });
  31. expect(parseMatcher('foo!~ bar')).toEqual<Matcher>({
  32. name: 'foo',
  33. value: 'bar',
  34. isRegex: true,
  35. isEqual: false,
  36. });
  37. });
  38. // Alertmanager has some strict requirements for label values;
  39. // we should not automatically encode or decode any values sent
  40. // and instead let AM return any errors like (matcher value contains unescaped double quote: bar"baz")
  41. // and allow the user to update the values to the correct format
  42. //
  43. // see https://github.com/prometheus/alertmanager/blob/4030e3670b359b8814aa8340ea1144f32b1f5ab3/pkg/labels/parse.go#L55-L99
  44. // and https://github.com/prometheus/alertmanager/blob/4030e3670b359b8814aa8340ea1144f32b1f5ab3/pkg/labels/parse.go#L101-L178
  45. it('should not parse escaped values', () => {
  46. expect(parseMatcher('foo="^[a-z0-9-]{1}[a-z0-9-]{0,30}$"')).toEqual<Matcher>({
  47. name: 'foo',
  48. value: '"^[a-z0-9-]{1}[a-z0-9-]{0,30}$"',
  49. isRegex: false,
  50. isEqual: true,
  51. });
  52. expect(parseMatcher('foo=~bar\\"baz\\"')).toEqual<Matcher>({
  53. name: 'foo',
  54. value: 'bar\\"baz\\"',
  55. isRegex: true,
  56. isEqual: true,
  57. });
  58. });
  59. it('should parse multiple operators values correctly', () => {
  60. expect(parseMatcher('foo=~bar=baz!=bad!~br')).toEqual<Matcher>({
  61. name: 'foo',
  62. value: 'bar=baz!=bad!~br',
  63. isRegex: true,
  64. isEqual: true,
  65. });
  66. });
  67. });
  68. describe('parseMatchers', () => {
  69. it('should parse all operators', () => {
  70. expect(parseMatchers('foo=bar, bar=~ba.+, severity!=warning, email!~@grafana.com')).toEqual<Matcher[]>([
  71. { name: 'foo', value: 'bar', isRegex: false, isEqual: true },
  72. { name: 'bar', value: 'ba.+', isEqual: true, isRegex: true },
  73. { name: 'severity', value: 'warning', isRegex: false, isEqual: false },
  74. { name: 'email', value: '@grafana.com', isRegex: true, isEqual: false },
  75. ]);
  76. });
  77. it('should return nothing for invalid operator', () => {
  78. expect(parseMatchers('foo=!bar')).toEqual([]);
  79. });
  80. it('should parse matchers with or without quotes', () => {
  81. expect(parseMatchers('foo="bar",bar=bazz')).toEqual<Matcher[]>([
  82. { name: 'foo', value: 'bar', isRegex: false, isEqual: true },
  83. { name: 'bar', value: 'bazz', isEqual: true, isRegex: false },
  84. ]);
  85. });
  86. it('should parse matchers for key with special characters', () => {
  87. expect(parseMatchers('foo.bar-baz="bar",baz-bar.foo=bazz')).toEqual<Matcher[]>([
  88. { name: 'foo.bar-baz', value: 'bar', isRegex: false, isEqual: true },
  89. { name: 'baz-bar.foo', value: 'bazz', isEqual: true, isRegex: false },
  90. ]);
  91. });
  92. });
  93. describe('labelsMatchMatchers', () => {
  94. it('should return true for matching labels', () => {
  95. const labels: Labels = {
  96. foo: 'bar',
  97. bar: 'bazz',
  98. bazz: 'buzz',
  99. };
  100. const matchers = parseMatchers('foo=bar,bar=bazz');
  101. expect(labelsMatchMatchers(labels, matchers)).toBe(true);
  102. });
  103. it('should return false for no matching labels', () => {
  104. const labels: Labels = {
  105. foo: 'bar',
  106. bar: 'bazz',
  107. };
  108. const matchers = parseMatchers('foo=buzz');
  109. expect(labelsMatchMatchers(labels, matchers)).toBe(false);
  110. });
  111. it('should match with different operators', () => {
  112. const labels: Labels = {
  113. foo: 'bar',
  114. bar: 'bazz',
  115. email: 'admin@grafana.com',
  116. };
  117. const matchers = parseMatchers('foo!=bazz,bar=~ba.+');
  118. expect(labelsMatchMatchers(labels, matchers)).toBe(true);
  119. });
  120. });
  121. describe('removeMuteTimingFromRoute', () => {
  122. const route: Route = {
  123. receiver: 'gmail',
  124. object_matchers: [['foo', MatcherOperator.equal, 'bar']],
  125. mute_time_intervals: ['test1', 'test2'],
  126. routes: [
  127. {
  128. receiver: 'slack',
  129. object_matchers: [['env', MatcherOperator.equal, 'prod']],
  130. mute_time_intervals: ['test2'],
  131. },
  132. {
  133. receiver: 'pagerduty',
  134. object_matchers: [['env', MatcherOperator.equal, 'eu']],
  135. mute_time_intervals: ['test1'],
  136. },
  137. ],
  138. };
  139. it('should remove mute timings from routes', () => {
  140. expect(removeMuteTimingFromRoute('test1', route)).toEqual({
  141. mute_time_intervals: ['test2'],
  142. object_matchers: [['foo', '=', 'bar']],
  143. receiver: 'gmail',
  144. routes: [
  145. {
  146. mute_time_intervals: ['test2'],
  147. object_matchers: [['env', '=', 'prod']],
  148. receiver: 'slack',
  149. routes: undefined,
  150. },
  151. {
  152. mute_time_intervals: [],
  153. object_matchers: [['env', '=', 'eu']],
  154. receiver: 'pagerduty',
  155. routes: undefined,
  156. },
  157. ],
  158. });
  159. });
  160. });
  161. describe('matchersToString', () => {
  162. it('Should create a comma-separated list of labels and values wrapped into curly brackets', () => {
  163. const matchers: Matcher[] = [
  164. { name: 'severity', value: 'critical', isEqual: true, isRegex: false },
  165. { name: 'resource', value: 'cpu', isEqual: true, isRegex: true },
  166. { name: 'rule_uid', value: '2Otf8canzz', isEqual: false, isRegex: false },
  167. { name: 'cluster', value: 'prom', isEqual: false, isRegex: true },
  168. ];
  169. const matchersString = matchersToString(matchers);
  170. expect(matchersString).toBe('{severity="critical",resource=~"cpu",rule_uid!="2Otf8canzz",cluster!~"prom"}');
  171. });
  172. });
  173. });