@intentius/chant-lexicon-prometheus 0.98.0 → 0.99.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/dist/import/embedded.d.ts +14 -0
  3. package/dist/import/embedded.d.ts.map +1 -0
  4. package/dist/import/generator.d.ts +15 -0
  5. package/dist/import/generator.d.ts.map +1 -1
  6. package/dist/import/parser.d.ts.map +1 -1
  7. package/dist/integrations.d.ts +531 -0
  8. package/dist/integrations.d.ts.map +1 -0
  9. package/dist/integrity.json +4 -4
  10. package/dist/lint/rules/literal-credential.d.ts.map +1 -1
  11. package/dist/manifest.json +1 -1
  12. package/dist/model.d.ts +58 -123
  13. package/dist/model.d.ts.map +1 -1
  14. package/dist/plugin.d.ts.map +1 -1
  15. package/dist/rules/literal-credential.ts +41 -10
  16. package/dist/skills/chant-prometheus-alertmanager.md +1 -1
  17. package/package.json +2 -2
  18. package/src/import/embedded.test.ts +56 -0
  19. package/src/import/embedded.ts +44 -0
  20. package/src/import/generated-types.e2e.test.ts +3 -1
  21. package/src/import/generator.test.ts +21 -4
  22. package/src/import/generator.ts +32 -24
  23. package/src/import/parser.test.ts +13 -0
  24. package/src/import/parser.ts +18 -1
  25. package/src/import/roundtrip.test.ts +46 -9
  26. package/src/import/testdata/alertmanager-full.yml +3 -3
  27. package/src/import/testdata/alertmanager-integrations.yml +215 -0
  28. package/src/import/testdata/fixtures.ts +8 -0
  29. package/src/import/testdata/upstream/alertmanager-conf-good.yml +120 -0
  30. package/src/import/testdata/upstream/alertmanager-mattermost-default-webhook-url-file.yml +16 -0
  31. package/src/import/testdata/upstream/alertmanager-opsgenie-default-apikey-file.yml +27 -0
  32. package/src/import/testdata/upstream/alertmanager-rocketchat-default-token-file.yml +25 -0
  33. package/src/import/testdata/upstream/alertmanager-sns-topic-arn.yml +17 -0
  34. package/src/import/testdata/upstream/alertmanager-telegram-default-bot-token-file.yml +16 -0
  35. package/src/import/testdata/upstream/alertmanager-victorops-default-apikey-file.yml +23 -0
  36. package/src/import/testdata/upstream/alertmanager-wechat-default-api-secret-file.yml +18 -0
  37. package/src/integrations.ts +538 -0
  38. package/src/lint/rules/literal-credential.ts +41 -10
  39. package/src/lint/rules/rules.test.ts +31 -1
  40. package/src/model.ts +136 -110
  41. package/src/plugin.ts +5 -0
  42. package/src/skills/chant-prometheus-alertmanager.md +1 -1
@@ -31,6 +31,8 @@ import type {
31
31
  RuleGroupConfig,
32
32
  TimeIntervalConfig,
33
33
  } from "../model";
34
+ import { ALERTMANAGER_GLOBAL_FIELDS, RECEIVER_INTEGRATION_TYPES } from "../model";
35
+ import { PROMETHEUS_PIN } from "../pin";
34
36
  import {
35
37
  ALERTMANAGER_RESOURCE_TYPE,
36
38
  RULE_FILE_RESOURCE_TYPE,
@@ -270,7 +272,24 @@ const LEXICON_NAMES = [
270
272
 
271
273
  /** Generate the TypeScript declaring one rule file. */
272
274
  export function generateRuleFileFiles(file: RuleFileConfig): GeneratedFile[] {
275
+ return generateRuleFile(file).files;
276
+ }
277
+
278
+ /** A rule group's declaration: the module and variable, and `rules` when it is an `Slo`'s group. */
279
+ export interface GroupDeclaration {
280
+ readonly path: string;
281
+ readonly name: string;
282
+ readonly member?: "rules";
283
+ }
284
+
285
+ /**
286
+ * Generate the TypeScript declaring one rule file, and say where each of its
287
+ * groups is declared, in the file's order (#2962: a PrometheusRule's
288
+ * `spec.groups` refers to them).
289
+ */
290
+ export function generateRuleFile(file: RuleFileConfig): { files: GeneratedFile[]; groups: GroupDeclaration[] } {
273
291
  const names = new Names(LEXICON_NAMES);
292
+ const declared = new Map<RuleGroupConfig, GroupDeclaration>();
274
293
  const slos: Array<{ group: RuleGroupConfig; props: NonNullable<ReturnType<typeof recognizeSlo>> }> = [];
275
294
  const groups: RuleGroupConfig[] = [];
276
295
  for (const g of file.groups) {
@@ -308,56 +327,45 @@ export function generateRuleFileFiles(file: RuleFileConfig): GeneratedFile[] {
308
327
  lines.push(`${ctor}${objectOf(entries, 0, ctor.length)});`);
309
328
  mod.block(lines);
310
329
  mod.exports.push(v);
330
+ declared.set(g, { path: mod.path, name: v });
311
331
  }
312
332
  }
313
333
 
314
334
  for (const [mod, items] of chunk(slos, "slos", "Service level objectives, each built by Slo() to its rule group")) {
315
335
  modules.push(mod);
316
336
  mod.use("Slo");
317
- for (const { props } of items) {
337
+ for (const { group, props } of items) {
318
338
  const v = sloVars[slos.findIndex((s) => s.props === props)];
319
339
  const head = `const ${v} = Slo(`;
320
340
  mod.block([`${head}${tsLiteral(props, 0, head.length)});`]);
321
341
  mod.exports.push(v);
342
+ declared.set(group, { path: mod.path, name: v, member: "rules" });
322
343
  }
323
344
  }
324
345
 
325
- return modules.map((m) => ({ path: m.path, content: m.render() }));
346
+ return {
347
+ files: modules.map((m) => ({ path: m.path, content: m.render() })),
348
+ groups: file.groups.map((g) => declared.get(g)!),
349
+ };
326
350
  }
327
351
 
328
352
  // ── alertmanager.yml ─────────────────────────────────────────────────
329
353
 
330
354
  /** Receiver fields the lexicon types, with the type each list is declared as. */
331
355
  const RECEIVER_FIELD_TYPES: Record<string, string> = {
332
- webhook_configs: "WebhookConfig[]",
333
- email_configs: "EmailConfig[]",
334
- slack_configs: "SlackConfig[]",
335
- pagerduty_configs: "PagerDutyConfig[]",
356
+ ...Object.fromEntries(Object.entries(RECEIVER_INTEGRATION_TYPES).map(([k, t]) => [k, `${t}[]`])),
336
357
  labels: "LabelSet",
337
358
  };
338
359
 
339
360
  /** `global:` fields the lexicon types. */
340
- const GLOBAL_FIELDS = new Set([
341
- "resolve_timeout",
342
- "smtp_from",
343
- "smtp_smarthost",
344
- "smtp_hello",
345
- "smtp_auth_username",
346
- "smtp_auth_password",
347
- "smtp_auth_password_file",
348
- "smtp_auth_secret",
349
- "smtp_auth_secret_file",
350
- "smtp_auth_identity",
351
- "smtp_require_tls",
352
- "slack_api_url",
353
- "slack_api_url_file",
354
- "pagerduty_url",
355
- "http_config",
356
- ]);
361
+ const GLOBAL_FIELDS = new Set<string>(ALERTMANAGER_GLOBAL_FIELDS);
362
+
363
+ const AM_VERSION = PROMETHEUS_PIN.alertmanager.version;
357
364
 
358
365
  /** A comment naming the fields carried as data, wrapped at the line limit. */
359
366
  function untypedComment(what: string, keys: string[]): string[] {
360
- const text = `${what} ${keys.join(", ")} ${keys.length === 1 ? "is" : "are"} not typed by this lexicon, so ${keys.length === 1 ? "it is" : "they are"} carried as data.`;
367
+ const one = keys.length === 1;
368
+ const text = `${what} ${keys.join(", ")} ${one ? "is not a field" : "are not fields"} Alertmanager ${AM_VERSION} defines, so ${one ? "it is" : "they are"} carried as data, untyped.`;
361
369
  const lines: string[] = [];
362
370
  let cur = "//";
363
371
  for (const w of text.split(" ")) {
@@ -96,6 +96,19 @@ describe("parsePrometheusYaml", () => {
96
96
  opsgenie_configs: [{ api_key: "literal" }],
97
97
  },
98
98
  ]);
99
+ expect(parsed.warnings).toEqual([]);
100
+ });
101
+
102
+ test("a receiver or global key Alertmanager doesn't define is carried and named in a warning", () => {
103
+ const parsed = parsePrometheusYaml(
104
+ lines("global: { resolve_timeout: 5m, pigeon_loft: roof }", "receivers:", " - name: chat", " pigeon_configs: [{ loft: roof }]"),
105
+ );
106
+ if (parsed.kind !== "alertmanager") throw new Error("expected alertmanager.yml");
107
+ expect(parsed.config.receivers).toEqual([{ name: "chat", pigeon_configs: [{ loft: "roof" }] }]);
108
+ expect(parsed.warnings).toEqual([
109
+ "global: pigeon_loft is not a global field in Alertmanager v0.34.1; carried as data, untyped",
110
+ 'receiver "chat": pigeon_configs is not a receiver field in Alertmanager v0.34.1; carried as data, untyped',
111
+ ]);
99
112
  });
100
113
 
101
114
  test("dates and times stay strings, as the Go loaders read them", () => {
@@ -21,6 +21,8 @@
21
21
  import * as jsYaml from "js-yaml";
22
22
  import type { TemplateIR, TemplateParser } from "@intentius/chant/import/parser";
23
23
  import {
24
+ ALERTMANAGER_GLOBAL_FIELDS,
25
+ RECEIVER_INTEGRATIONS,
24
26
  looksLikeAlertmanagerConfig,
25
27
  looksLikeRuleFile,
26
28
  type AlertmanagerConfig,
@@ -32,6 +34,7 @@ import {
32
34
  type RuleGroupConfig,
33
35
  type TimeIntervalConfig,
34
36
  } from "../model";
37
+ import { PROMETHEUS_PIN } from "../pin";
35
38
 
36
39
  /** The IR resource type for a whole rule file. */
37
40
  export const RULE_FILE_RESOURCE_TYPE = "Prometheus::RuleFile";
@@ -264,12 +267,22 @@ function parseTimeIntervals(raw: unknown, at: string, warnings: string[]): TimeI
264
267
  return out;
265
268
  }
266
269
 
270
+ const AM_VERSION = PROMETHEUS_PIN.alertmanager.version;
271
+ const RECEIVER_KEYS = new Set<string>(["name", "labels", ...RECEIVER_INTEGRATIONS]);
272
+ const GLOBAL_KEYS = new Set<string>(ALERTMANAGER_GLOBAL_FIELDS);
273
+
267
274
  function parseAlertmanager(doc: Record<string, unknown>, warnings: string[]): AlertmanagerConfig {
268
275
  for (const k of Object.keys(doc)) {
269
276
  if (!AM_SECTIONS.includes(k)) warnings.push(`top-level section "${k}" is not one chant declares; it is not carried`);
270
277
  }
271
278
  const config: AlertmanagerConfig = {};
272
- if (isPlainObject(doc.global)) config.global = doc.global as AlertmanagerConfig["global"];
279
+ if (isPlainObject(doc.global)) {
280
+ config.global = doc.global as AlertmanagerConfig["global"];
281
+ const unknown = Object.keys(doc.global).filter((k) => !GLOBAL_KEYS.has(k));
282
+ if (unknown.length > 0) {
283
+ warnings.push(`global: ${unknown.join(", ")} ${unknown.length === 1 ? "is not a global field" : "are not global fields"} in Alertmanager ${AM_VERSION}; carried as data, untyped`);
284
+ }
285
+ }
273
286
  else if (doc.global !== undefined && doc.global !== null) warnings.push("global is not a mapping; it is not carried");
274
287
  const templates = stringList(doc.templates, "templates", warnings);
275
288
  if (templates) config.templates = templates;
@@ -288,6 +301,10 @@ function parseAlertmanager(doc: Record<string, unknown>, warnings: string[]): Al
288
301
  warnings.push(`receivers[${i}] has no name; it is not carried`);
289
302
  return;
290
303
  }
304
+ const unknown = Object.keys(r).filter((k) => !RECEIVER_KEYS.has(k));
305
+ if (unknown.length > 0) {
306
+ warnings.push(`receiver "${String(r.name)}": ${unknown.join(", ")} ${unknown.length === 1 ? "is not a receiver field" : "are not receiver fields"} in Alertmanager ${AM_VERSION}; carried as data, untyped`);
307
+ }
291
308
  receivers.push({ ...r, name: String(r.name) } as ReceiverConfig);
292
309
  });
293
310
  config.receivers = receivers;
@@ -24,7 +24,7 @@ import { build } from "@intentius/chant/build";
24
24
  import { lintCommand } from "@intentius/chant/cli/commands/lint";
25
25
  import { prometheusSerializer } from "../serializer";
26
26
  import { amtoolCheckConfig, hasTool, promtoolCheckRules } from "../tools";
27
- import type { AlertmanagerConfig, RouteConfig, RuleFileConfig } from "../model";
27
+ import { RECEIVER_INTEGRATION_TYPES, type AlertmanagerConfig, type RouteConfig, type RuleFileConfig } from "../model";
28
28
  import { PrometheusParser } from "./parser";
29
29
  import { PrometheusGenerator } from "./generator";
30
30
  import { builtFiles, exampleOutputs, pkgDir, read, sloOutputs, UPSTREAM, type BuiltFile } from "./testdata/fixtures";
@@ -256,10 +256,11 @@ describe("YAML -> TypeScript -> YAML", () => {
256
256
  expect(out.source).toContain('url_file: "/etc/alertmanager/secrets/default-webhook-url"');
257
257
  expect(out.source).toContain("description: '{{ template \"pagerduty.default.description\" . }}'");
258
258
  expect(out.source).toContain('summary: "{{ .GroupLabels.alertname }} in {{ .GroupLabels.cluster }}"');
259
- // Integrations and global fields the lexicon does not type are carried as data, spread in.
260
- expect(out.source).toContain("// Receiver \"oncall\": opsgenie_configs is not typed by this lexicon");
261
- expect(out.source).toContain("...oncallUntyped,");
262
- expect(out.source).toContain("...globalUntyped,");
259
+ // Every integration and global field is typed.
260
+ expect(out.source).toContain("const oncallOpsgenie: OpsGenieConfig[] = [");
261
+ expect(out.source).toContain("const dbChatMsteams: MSTeamsConfig[] = [");
262
+ expect(out.source).toContain('opsgenie_api_key_file: "/etc/alertmanager/secrets/opsgenie-key"');
263
+ expect(out.source).not.toContain("Untyped");
263
264
  // Deprecated spellings are rewritten, and each rewrite is named.
264
265
  expect(out.source).toContain(`matchers: ['severity="page"', 'service=~"^(api|web)$"']`);
265
266
  expect(out.source).toContain(`source_matchers: ['severity="page"']`);
@@ -270,20 +271,55 @@ describe("YAML -> TypeScript -> YAML", () => {
270
271
  expect(out.warnings.join("\n")).toContain("top-level mute_time_intervals (weekends)");
271
272
  });
272
273
 
274
+ /**
275
+ * What each upstream sample is expected to raise: literal credentials
276
+ * (Alertmanager's own examples write placeholder secrets inline), which
277
+ * PROM001 reports, and deprecated `match` spellings, which import warns
278
+ * about. A sample not listed raises nothing.
279
+ */
280
+ const UPSTREAM_EXPECT: Record<string, { prom001?: number; warnings?: number }> = {
281
+ "alertmanager-simple.yml": { prom001: 4 },
282
+ // smtp_auth_password, slack_api_url, three routing_keys, the VictorOps and Opsgenie api_keys, Pushover's token and user_key.
283
+ "alertmanager-conf-good.yml": { prom001: 9, warnings: 9 },
284
+ "alertmanager-opsgenie-default-apikey-file.yml": { warnings: 1 },
285
+ "alertmanager-rocketchat-default-token-file.yml": { warnings: 1 },
286
+ "alertmanager-victorops-default-apikey-file.yml": { warnings: 1 },
287
+ "alertmanager-wechat-default-api-secret-file.yml": { prom001: 1 },
288
+ };
289
+
290
+ test("an alertmanager.yml using every receiver integration", async () => {
291
+ const out = await expectRoundTrip(read("alertmanager-integrations.yml"));
292
+ expect(out.warnings).toEqual([]);
293
+ expect(out.source).not.toContain("Untyped");
294
+ for (const type of Object.values(RECEIVER_INTEGRATION_TYPES)) expect(out.source).toMatch(new RegExp(`: ${type}\\[\\] = `));
295
+ // Fields the common four integrations gained, and structured values, keep their types.
296
+ expect(out.source).toContain('message_text: "{{ .CommonLabels.alertname }}"');
297
+ expect(out.source).toContain('threading: { enabled: true, thread_by_date: "daily" }');
298
+ expect(out.source).toContain("chat_id: -1001234567890");
299
+ expect(out.source).toContain('description: { template: \'{{ template "jira.default.description" . }}\', enable_update: false }');
300
+ expect(out.source).toContain('http_headers: {');
301
+ });
302
+
273
303
  for (const file of UPSTREAM) {
274
304
  test(`upstream sample: ${file}`, async () => {
275
305
  const yaml = read("upstream", file);
276
- // The Alertmanager example writes placeholder secrets inline, which PROM001 reports.
277
- const out = await expectRoundTrip(yaml, file === "alertmanager-simple.yml" ? { lintRules: ["PROM001"] } : {});
278
- expect(out.warnings).toEqual([]);
306
+ const expected = UPSTREAM_EXPECT[file] ?? {};
307
+ const out = await expectRoundTrip(yaml, expected.prom001 ? { lintRules: ["PROM001"] } : {});
308
+ expect(out.lint.output.match(/PROM001/g) ?? []).toHaveLength(expected.prom001 ?? 0);
309
+ expect(out.warnings).toHaveLength(expected.warnings ?? 0);
310
+ for (const w of out.warnings) expect(w).toMatch(/match(_re)? are written as/);
311
+ // Every integration and global field is typed: nothing is carried as untyped data.
312
+ expect(out.source).not.toContain("Untyped");
279
313
  if (file === "alertmanager-simple.yml") {
280
314
  expect(out.source).toContain('service_key: "<team-X-key>"');
281
315
  expect(out.source).toContain("const tracing: AlertmanagerTracingConfig = {");
282
- expect(out.lint.output.match(/PROM001/g)).toHaveLength(4);
283
316
  }
284
317
  if (file === "alertmanager-route-labels.yml") {
285
318
  expect(out.source).toContain('reason: "database {{ .GroupLabels.database }}"');
286
319
  }
320
+ if (file === "alertmanager-sns-topic-arn.yml") {
321
+ expect(out.source).toContain("const snsApiNotificationsSns: SNSConfig[] = [");
322
+ }
287
323
  });
288
324
  }
289
325
  });
@@ -300,6 +336,7 @@ describe("upstream tools accept the re-emitted files", () => {
300
336
  const amFixtures = async (): Promise<BuiltFile[]> => [
301
337
  ...(await exampleOutputs()).filter((o) => o.name.endsWith("alertmanager.yml")),
302
338
  handWritten("alertmanager-full.yml"),
339
+ handWritten("alertmanager-integrations.yml"),
303
340
  ...UPSTREAM.filter((f) => f.startsWith("alertmanager-")).map(upstream),
304
341
  ];
305
342
 
@@ -1,7 +1,7 @@
1
1
  # Deprecated spellings (match, match_re, source_match, the top-level
2
- # mute_time_intervals), integrations this lexicon does not type (opsgenie,
3
- # msteams), global fields outside the typed set, *_file credentials, Go
4
- # templates, a YAML anchor with a merge key, and one literal credential.
2
+ # mute_time_intervals), integrations beside the common four (opsgenie,
3
+ # msteams) and their global fields, *_file credentials, Go templates, a YAML
4
+ # anchor with a merge key, and one literal credential.
5
5
  global:
6
6
  resolve_timeout: 5m
7
7
  smtp_smarthost: smtp.example.com:587
@@ -0,0 +1,215 @@
1
+ # Every receiver integration Alertmanager v0.34.1 defines, each with the
2
+ # fields its struct lists beyond the destination, every credential read from
3
+ # a *_file, and every global field that has a default for one of them.
4
+ global:
5
+ resolve_timeout: 5m
6
+ http_config:
7
+ follow_redirects: true
8
+ enable_http2: false
9
+ http_headers:
10
+ X-Tenant:
11
+ values: [platform]
12
+ X-Api-Key:
13
+ files: [/etc/alertmanager/secrets/api-key-header]
14
+ jira_api_url: https://jira.example.com/rest/api/2/
15
+ smtp_from: alertmanager@example.com
16
+ smtp_smarthost: smtp.example.com:465
17
+ smtp_auth_username: alertmanager
18
+ smtp_auth_password_file: /etc/alertmanager/secrets/smtp-password
19
+ smtp_require_tls: false
20
+ smtp_force_implicit_tls: true
21
+ smtp_tls_config:
22
+ ca_file: /etc/alertmanager/tls/ca.pem
23
+ min_version: TLS12
24
+ slack_app_token_file: /etc/alertmanager/secrets/slack-app-token
25
+ slack_app_url: https://slack.com/api/chat.postMessage
26
+ opsgenie_api_url: https://api.eu.opsgenie.com/
27
+ opsgenie_api_key_file: /etc/alertmanager/secrets/opsgenie-key
28
+ wechat_api_url: https://qyapi.weixin.qq.com/cgi-bin/
29
+ wechat_api_secret_file: /etc/alertmanager/secrets/wechat-secret
30
+ wechat_api_corp_id: corp-1
31
+ victorops_api_url: https://alert.victorops.com/integrations/generic/20131114/alert/
32
+ victorops_api_key_file: /etc/alertmanager/secrets/victorops-key
33
+ telegram_api_url: https://api.telegram.org
34
+ telegram_bot_token_file: /etc/alertmanager/secrets/telegram-token
35
+ webex_api_url: https://webexapis.com/v1/messages
36
+ rocketchat_api_url: https://open.rocket.chat
37
+ rocketchat_token_file: /etc/alertmanager/secrets/rocketchat-token
38
+ rocketchat_token_id_file: /etc/alertmanager/secrets/rocketchat-token-id
39
+ mattermost_webhook_url_file: /etc/alertmanager/secrets/mattermost-url
40
+ route:
41
+ receiver: chat
42
+ group_by: [alertname]
43
+ routes:
44
+ - matchers: ['team="ops"']
45
+ receiver: ops
46
+ - matchers: ['team="tickets"']
47
+ receiver: tickets
48
+ - matchers: ['team="mobile"']
49
+ receiver: mobile
50
+ receivers:
51
+ - name: chat
52
+ slack_configs:
53
+ - channel: '#alerts'
54
+ message_text: '{{ .CommonLabels.alertname }}'
55
+ callback_id: alerts
56
+ image_url: https://example.com/graph.png
57
+ thumb_url: https://example.com/thumb.png
58
+ update_message: true
59
+ timeout: 10s
60
+ fields:
61
+ - title: Severity
62
+ value: '{{ .CommonLabels.severity }}'
63
+ short: true
64
+ actions:
65
+ - type: button
66
+ text: Runbook
67
+ url: '{{ .CommonAnnotations.runbook_url }}'
68
+ style: primary
69
+ confirm:
70
+ text: Open the runbook?
71
+ ok_text: 'Yes'
72
+ dismiss_text: 'No'
73
+ discord_configs:
74
+ - webhook_url_file: /etc/alertmanager/secrets/discord-url
75
+ title: '{{ .CommonLabels.alertname }}'
76
+ message: '{{ .CommonAnnotations.summary }}'
77
+ content: '<@&123456>'
78
+ username: alertmanager
79
+ avatar_url: https://example.com/avatar.png
80
+ msteams_configs:
81
+ - webhook_url_file: /etc/alertmanager/secrets/teams-url
82
+ title: '{{ .CommonLabels.alertname }}'
83
+ summary: '{{ .CommonAnnotations.summary }}'
84
+ text: '{{ .CommonAnnotations.description }}'
85
+ msteamsv2_configs:
86
+ - webhook_url_file: /etc/alertmanager/secrets/teams-workflow-url
87
+ title: '{{ .CommonLabels.alertname }}'
88
+ text: '{{ .CommonAnnotations.description }}'
89
+ mattermost_configs:
90
+ - channel: alerts
91
+ username: alertmanager
92
+ text: '{{ .CommonAnnotations.summary }}'
93
+ icon_emoji: ':rotating_light:'
94
+ attachments:
95
+ - title: '{{ .CommonLabels.alertname }}'
96
+ color: danger
97
+ fields:
98
+ - title: Severity
99
+ value: '{{ .CommonLabels.severity }}'
100
+ short: true
101
+ props:
102
+ card: '{{ .CommonAnnotations.description }}'
103
+ priority:
104
+ priority: urgent
105
+ requested_ack: true
106
+ webex_configs:
107
+ - room_id: room-1
108
+ message: '{{ .CommonAnnotations.summary }}'
109
+ http_config:
110
+ authorization:
111
+ credentials_file: /etc/alertmanager/secrets/webex-token
112
+ rocketchat_configs:
113
+ - channel: '#alerts'
114
+ title: '{{ .CommonLabels.alertname }}'
115
+ emoji: ':fire:'
116
+ short_fields: true
117
+ fields:
118
+ - title: Severity
119
+ value: '{{ .CommonLabels.severity }}'
120
+ short: true
121
+ actions:
122
+ - type: button
123
+ text: Silence
124
+ url: https://alertmanager.example.com/#/silences/new
125
+ telegram_configs:
126
+ - chat_id: -1001234567890
127
+ message_thread_id: 7
128
+ parse_mode: HTML
129
+ disable_notifications: false
130
+ message: '{{ .CommonAnnotations.summary }}'
131
+ - name: ops
132
+ email_configs:
133
+ - to: ops@example.com
134
+ force_implicit_tls: true
135
+ threading:
136
+ enabled: true
137
+ thread_by_date: daily
138
+ opsgenie_configs:
139
+ - message: '{{ .CommonLabels.alertname }}'
140
+ priority: P2
141
+ tags: 'team,ops'
142
+ details:
143
+ cluster: '{{ .CommonLabels.cluster }}'
144
+ responders:
145
+ - name: ops
146
+ type: team
147
+ update_alerts: true
148
+ victorops_configs:
149
+ - routing_key: ops
150
+ message_type: CRITICAL
151
+ entity_display_name: '{{ .CommonLabels.alertname }}'
152
+ custom_fields:
153
+ cluster: '{{ .CommonLabels.cluster }}'
154
+ pagerduty_configs:
155
+ - routing_key_file: /etc/alertmanager/secrets/pagerduty-key
156
+ timeout: 20s
157
+ details:
158
+ firing: '{{ .Alerts.Firing | len }}'
159
+ incidentio_configs:
160
+ - url: https://api.incident.io/v2/alert_events/http/01ABC
161
+ alert_source_token_file: /etc/alertmanager/secrets/incidentio-token
162
+ max_alerts: 20
163
+ timeout: 15s
164
+ webhook_configs:
165
+ - url_file: /etc/alertmanager/secrets/webhook-url
166
+ timeout: 5s
167
+ http_config:
168
+ oauth2:
169
+ client_id: alertmanager
170
+ client_secret_file: /etc/alertmanager/secrets/oauth-secret
171
+ token_url: https://auth.example.com/token
172
+ scopes: [alerts]
173
+ - name: tickets
174
+ jira_configs:
175
+ - project: OPS
176
+ issue_type: Bug
177
+ summary: '{{ template "jira.default.summary" . }}'
178
+ description:
179
+ template: '{{ template "jira.default.description" . }}'
180
+ enable_update: false
181
+ labels: [alertmanager]
182
+ priority: High
183
+ resolve_transition: Done
184
+ reopen_transition: Reopen
185
+ wont_fix_resolution: "Won't Fix"
186
+ reopen_duration: 1h
187
+ fields:
188
+ customfield_10001: ops
189
+ http_config:
190
+ basic_auth:
191
+ username: alertmanager
192
+ password_file: /etc/alertmanager/secrets/jira-password
193
+ sns_configs:
194
+ - topic_arn: arn:aws:sns:us-east-1:123456789012:alerts
195
+ subject: '{{ .CommonLabels.alertname }}'
196
+ sigv4:
197
+ region: us-east-1
198
+ role_arn: arn:aws:iam::123456789012:role/alertmanager
199
+ attributes:
200
+ team: ops
201
+ - name: mobile
202
+ pushover_configs:
203
+ - user_key_file: /etc/alertmanager/secrets/pushover-user
204
+ token_file: /etc/alertmanager/secrets/pushover-token
205
+ priority: '{{ if eq .Status "firing" }}2{{ else }}0{{ end }}'
206
+ retry: 1m
207
+ expire: 1h
208
+ ttl: 24h
209
+ sound: siren
210
+ html: true
211
+ wechat_configs:
212
+ - to_user: '@all'
213
+ agent_id: '1000002'
214
+ message_type: markdown
215
+ message: '{{ .CommonAnnotations.summary }}'
@@ -111,4 +111,12 @@ export const UPSTREAM = [
111
111
  "prometheus-alerting-templates.yml",
112
112
  "alertmanager-simple.yml",
113
113
  "alertmanager-route-labels.yml",
114
+ "alertmanager-conf-good.yml",
115
+ "alertmanager-mattermost-default-webhook-url-file.yml",
116
+ "alertmanager-opsgenie-default-apikey-file.yml",
117
+ "alertmanager-rocketchat-default-token-file.yml",
118
+ "alertmanager-sns-topic-arn.yml",
119
+ "alertmanager-telegram-default-bot-token-file.yml",
120
+ "alertmanager-victorops-default-apikey-file.yml",
121
+ "alertmanager-wechat-default-api-secret-file.yml",
114
122
  ];
@@ -0,0 +1,120 @@
1
+ # Vendored from github.com/prometheus/alertmanager v0.34.1, config/testdata/conf.good.yml
2
+ # (https://github.com/prometheus/alertmanager/blob/v0.34.1/config/testdata/conf.good.yml). Apache-2.0.
3
+ # Unchanged below this header.
4
+ global:
5
+ # The smarthost and SMTP sender used for mail notifications.
6
+ smtp_smarthost: 'localhost:25'
7
+ smtp_from: 'alertmanager@example.org'
8
+ smtp_auth_username: 'alertmanager'
9
+ smtp_auth_password: "multiline\nmysecret"
10
+ smtp_hello: "host.example.org"
11
+ slack_api_url: "http://mysecret.example.com/"
12
+ http_config:
13
+ proxy_url: 'http://127.0.0.1:1025'
14
+ # The directory from which notification templates are read.
15
+ templates:
16
+ - '/etc/alertmanager/template/*.tmpl'
17
+ # The root route on which each incoming alert enters.
18
+ route:
19
+ # The labels by which incoming alerts are grouped together. For example,
20
+ # multiple alerts coming in for cluster=A and alertname=LatencyHigh would
21
+ # be batched into a single group.
22
+ group_by: ['alertname', 'cluster', 'service']
23
+ # When a new group of alerts is created by an incoming alert, wait at
24
+ # least 'group_wait' to send the initial notification.
25
+ # This way ensures that you get multiple alerts for the same group that start
26
+ # firing shortly after another are batched together on the first
27
+ # notification.
28
+ group_wait: 30s
29
+ # When the first notification was sent, wait 'group_interval' to send a batch
30
+ # of new alerts that started firing for that group.
31
+ group_interval: 5m
32
+ # If an alert has successfully been sent, wait 'repeat_interval' to
33
+ # resend them.
34
+ repeat_interval: 3h
35
+ # A default receiver
36
+ receiver: team-X-mails
37
+ # All the above attributes are inherited by all child routes and can
38
+ # overwritten on each.
39
+
40
+ # The child route trees.
41
+ routes:
42
+ # This routes performs a regular expression match on alert labels to
43
+ # catch alerts that are related to a list of services.
44
+ - match_re:
45
+ service: ^(foo1|foo2|baz)$
46
+ receiver: team-X-mails
47
+ # The service has a sub-route for critical alerts, any alerts
48
+ # that do not match, i.e. severity != critical, fall-back to the
49
+ # parent node and are sent to 'team-X-mails'
50
+ routes:
51
+ - match:
52
+ severity: critical
53
+ receiver: team-X-pager
54
+ - match:
55
+ service: files
56
+ receiver: team-Y-mails
57
+ routes:
58
+ - match:
59
+ severity: critical
60
+ receiver: team-Y-pager
61
+ # This route handles all alerts coming from a database service. If there's
62
+ # no team to handle it, it defaults to the DB team.
63
+ - match:
64
+ service: database
65
+ receiver: team-DB-pager
66
+ # Also group alerts by affected database.
67
+ group_by: [alertname, cluster, database]
68
+ routes:
69
+ - match:
70
+ owner2: team-X
71
+ receiver: team-X-pager
72
+ continue: true
73
+ - match:
74
+ owner: team-Y
75
+ receiver: team-Y-pager
76
+ # continue: true
77
+ # Inhibition rules allow to mute a set of alerts given that another alert is
78
+ # firing.
79
+ # We use this to mute any warning-level notifications if the same alert is
80
+ # already critical.
81
+ inhibit_rules:
82
+ - source_match:
83
+ severity: 'critical'
84
+ target_match:
85
+ severity: 'warning'
86
+ # Apply inhibition if the alertname is the same.
87
+ equal: ['alertname', 'cluster', 'service']
88
+ receivers:
89
+ - name: 'team-X-mails'
90
+ email_configs:
91
+ - to: 'team-X+alerts@example.org'
92
+ - name: 'team-X-pager'
93
+ email_configs:
94
+ - to: 'team-X+alerts-critical@example.org'
95
+ pagerduty_configs:
96
+ - routing_key: "mysecret"
97
+ - name: 'team-Y-mails'
98
+ email_configs:
99
+ - to: 'team-Y+alerts@example.org'
100
+ - name: 'team-Y-pager'
101
+ pagerduty_configs:
102
+ - routing_key: "mysecret"
103
+ - name: 'team-DB-pager'
104
+ pagerduty_configs:
105
+ - routing_key: "mysecret"
106
+ - name: victorOps-receiver
107
+ victorops_configs:
108
+ - api_key: mysecret
109
+ routing_key: Sample_route
110
+ - name: opsGenie-receiver
111
+ opsgenie_configs:
112
+ - api_key: mysecret
113
+ - name: pushover-receiver
114
+ pushover_configs:
115
+ - token: mysecret
116
+ user_key: key
117
+ - name: slack-receiver
118
+ slack_configs:
119
+ - channel: '#my-channel'
120
+ image_url: 'http://some.img.com/img.png'
@@ -0,0 +1,16 @@
1
+ # Vendored from github.com/prometheus/alertmanager v0.34.1, config/testdata/conf.mattermost-default-webhook-url-file.yml
2
+ # (https://github.com/prometheus/alertmanager/blob/v0.34.1/config/testdata/conf.mattermost-default-webhook-url-file.yml). Apache-2.0.
3
+ # Unchanged below this header.
4
+ global:
5
+ mattermost_webhook_url_file: /global_file
6
+ route:
7
+ receiver: team-X-mattermost
8
+
9
+ receivers:
10
+ - name: team-X-mattermost
11
+ mattermost_configs:
12
+ - channel: team-X
13
+ - name: team-Y-mattermost
14
+ mattermost_configs:
15
+ - channel: team-Y
16
+ webhook_url_file: /override_file
@@ -0,0 +1,27 @@
1
+ # Vendored from github.com/prometheus/alertmanager v0.34.1, config/testdata/conf.opsgenie-default-apikey-file.yml
2
+ # (https://github.com/prometheus/alertmanager/blob/v0.34.1/config/testdata/conf.opsgenie-default-apikey-file.yml). Apache-2.0.
3
+ # Unchanged below this header.
4
+ global:
5
+ opsgenie_api_key_file: '/global_file'
6
+ route:
7
+ group_by: ['alertname', 'cluster', 'service']
8
+ group_wait: 30s
9
+ group_interval: 5m
10
+ repeat_interval: 3h
11
+ receiver: escalation-Y-opsgenie
12
+ routes:
13
+ - match:
14
+ service: foo
15
+ receiver: team-X-opsgenie
16
+ receivers:
17
+ - name: 'team-X-opsgenie'
18
+ opsgenie_configs:
19
+ - responders:
20
+ - name: 'team-X'
21
+ type: 'team'
22
+ - name: 'escalation-Y-opsgenie'
23
+ opsgenie_configs:
24
+ - responders:
25
+ - name: 'escalation-Y'
26
+ type: 'escalation'
27
+ api_key_file: /override_file