@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.
- package/README.md +1 -1
- package/dist/import/embedded.d.ts +14 -0
- package/dist/import/embedded.d.ts.map +1 -0
- package/dist/import/generator.d.ts +15 -0
- package/dist/import/generator.d.ts.map +1 -1
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/integrations.d.ts +531 -0
- package/dist/integrations.d.ts.map +1 -0
- package/dist/integrity.json +4 -4
- package/dist/lint/rules/literal-credential.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/model.d.ts +58 -123
- package/dist/model.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/rules/literal-credential.ts +41 -10
- package/dist/skills/chant-prometheus-alertmanager.md +1 -1
- package/package.json +2 -2
- package/src/import/embedded.test.ts +56 -0
- package/src/import/embedded.ts +44 -0
- package/src/import/generated-types.e2e.test.ts +3 -1
- package/src/import/generator.test.ts +21 -4
- package/src/import/generator.ts +32 -24
- package/src/import/parser.test.ts +13 -0
- package/src/import/parser.ts +18 -1
- package/src/import/roundtrip.test.ts +46 -9
- package/src/import/testdata/alertmanager-full.yml +3 -3
- package/src/import/testdata/alertmanager-integrations.yml +215 -0
- package/src/import/testdata/fixtures.ts +8 -0
- package/src/import/testdata/upstream/alertmanager-conf-good.yml +120 -0
- package/src/import/testdata/upstream/alertmanager-mattermost-default-webhook-url-file.yml +16 -0
- package/src/import/testdata/upstream/alertmanager-opsgenie-default-apikey-file.yml +27 -0
- package/src/import/testdata/upstream/alertmanager-rocketchat-default-token-file.yml +25 -0
- package/src/import/testdata/upstream/alertmanager-sns-topic-arn.yml +17 -0
- package/src/import/testdata/upstream/alertmanager-telegram-default-bot-token-file.yml +16 -0
- package/src/import/testdata/upstream/alertmanager-victorops-default-apikey-file.yml +23 -0
- package/src/import/testdata/upstream/alertmanager-wechat-default-api-secret-file.yml +18 -0
- package/src/integrations.ts +538 -0
- package/src/lint/rules/literal-credential.ts +41 -10
- package/src/lint/rules/rules.test.ts +31 -1
- package/src/model.ts +136 -110
- package/src/plugin.ts +5 -0
- package/src/skills/chant-prometheus-alertmanager.md +1 -1
|
@@ -7,20 +7,47 @@ import { calleeName, constInitializers, CREDENTIAL_CLASS, literalText, position,
|
|
|
7
7
|
* reads it from a mounted file instead.
|
|
8
8
|
*/
|
|
9
9
|
export const SECRET_FIELDS = new Set([
|
|
10
|
-
"api_url", // slack_configs: the incoming-webhook URL is the credential
|
|
11
|
-
"
|
|
12
|
-
"
|
|
10
|
+
"api_url", // slack_configs only: the incoming-webhook URL is the credential (see SECRET_ONLY_IN)
|
|
11
|
+
"webhook_url", // discord, msteams, msteamsv2 and mattermost
|
|
12
|
+
"app_token",
|
|
13
|
+
"routing_key", // pagerduty (victorops' routing_key names a route, see SECRET_ONLY_IN)
|
|
13
14
|
"service_key",
|
|
15
|
+
"api_key",
|
|
16
|
+
"api_secret",
|
|
17
|
+
"bot_token",
|
|
18
|
+
"token",
|
|
19
|
+
"token_id",
|
|
20
|
+
"user_key",
|
|
21
|
+
"alert_source_token",
|
|
14
22
|
"auth_password",
|
|
15
23
|
"auth_secret",
|
|
16
24
|
"smtp_auth_password",
|
|
17
25
|
"smtp_auth_secret",
|
|
26
|
+
"slack_api_url",
|
|
27
|
+
"slack_app_token",
|
|
28
|
+
"opsgenie_api_key",
|
|
29
|
+
"wechat_api_secret",
|
|
30
|
+
"victorops_api_key",
|
|
31
|
+
"telegram_bot_token",
|
|
32
|
+
"rocketchat_token",
|
|
33
|
+
"rocketchat_token_id",
|
|
34
|
+
"mattermost_webhook_url",
|
|
18
35
|
"password",
|
|
19
36
|
"credentials",
|
|
20
37
|
"bearer_token",
|
|
21
38
|
"client_secret",
|
|
22
39
|
]);
|
|
23
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Fields that are a secret in one integration and plain elsewhere: `api_url`
|
|
43
|
+
* is Slack's webhook credential but only an endpoint for the others, and a
|
|
44
|
+
* VictorOps `routing_key` names a route.
|
|
45
|
+
*/
|
|
46
|
+
const SECRET_ONLY_IN: Record<string, string> = {
|
|
47
|
+
api_url: "slack_configs",
|
|
48
|
+
routing_key: "pagerduty_configs",
|
|
49
|
+
};
|
|
50
|
+
|
|
24
51
|
/**
|
|
25
52
|
* PROM001: a credential written as a literal in a `Receiver` or
|
|
26
53
|
* `AlertmanagerSettings`.
|
|
@@ -46,16 +73,20 @@ export const literalCredentialRule: LintRule = {
|
|
|
46
73
|
|
|
47
74
|
// A value is followed to the const it names, so a list lifted out of the
|
|
48
75
|
// constructor (`email_configs: heartbeatEmail`) is scanned where it is used.
|
|
49
|
-
|
|
76
|
+
// `integration` is the `*_configs` list a value sits in, when that is known.
|
|
77
|
+
const scanValue = (key: string | undefined, value: ts.Expression, integration?: string) => {
|
|
50
78
|
const init = resolveConst(value, consts);
|
|
79
|
+
const within = key?.endsWith("_configs") ? key : integration;
|
|
51
80
|
if (ts.isObjectLiteralExpression(init) || ts.isArrayLiteralExpression(init)) {
|
|
52
81
|
if (scanned.has(init)) return;
|
|
53
82
|
scanned.add(init);
|
|
54
|
-
if (ts.isObjectLiteralExpression(init)) scan(init);
|
|
55
|
-
else for (const el of init.elements) scanValue(undefined, el);
|
|
83
|
+
if (ts.isObjectLiteralExpression(init)) scan(init, within);
|
|
84
|
+
else for (const el of init.elements) scanValue(undefined, el, within);
|
|
56
85
|
return;
|
|
57
86
|
}
|
|
58
87
|
if (!key || !SECRET_FIELDS.has(key)) return;
|
|
88
|
+
const only = SECRET_ONLY_IN[key];
|
|
89
|
+
if (only && integration !== undefined && integration !== only) return;
|
|
59
90
|
const text = literalText(init);
|
|
60
91
|
if (text === undefined || text === "") return;
|
|
61
92
|
diagnostics.push({
|
|
@@ -67,11 +98,11 @@ export const literalCredentialRule: LintRule = {
|
|
|
67
98
|
});
|
|
68
99
|
};
|
|
69
100
|
|
|
70
|
-
const scan = (obj: ts.ObjectLiteralExpression) => {
|
|
101
|
+
const scan = (obj: ts.ObjectLiteralExpression, integration?: string) => {
|
|
71
102
|
for (const prop of obj.properties) {
|
|
72
|
-
if (ts.isPropertyAssignment(prop)) scanValue(propertyName(prop), prop.initializer);
|
|
73
|
-
else if (ts.isShorthandPropertyAssignment(prop)) scanValue(prop.name.text, prop.name);
|
|
74
|
-
else if (ts.isSpreadAssignment(prop)) scanValue(undefined, prop.expression);
|
|
103
|
+
if (ts.isPropertyAssignment(prop)) scanValue(propertyName(prop), prop.initializer, integration);
|
|
104
|
+
else if (ts.isShorthandPropertyAssignment(prop)) scanValue(prop.name.text, prop.name, integration);
|
|
105
|
+
else if (ts.isSpreadAssignment(prop)) scanValue(undefined, prop.expression, integration);
|
|
75
106
|
}
|
|
76
107
|
};
|
|
77
108
|
|
|
@@ -58,7 +58,37 @@ describe("PROM001 literal credential", () => {
|
|
|
58
58
|
const pager = new Receiver({ name: "pager", pagerduty_configs: safe });
|
|
59
59
|
`),
|
|
60
60
|
);
|
|
61
|
-
expect(diags.map((d) => d.message.split("`")[1])).toEqual(["auth_password", "service_key", "smtp_auth_password"]);
|
|
61
|
+
expect(diags.map((d) => d.message.split("`")[1])).toEqual(["auth_password", "api_key", "service_key", "smtp_auth_password"]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("covers every integration's credential, and api_url and routing_key only where they are one", () => {
|
|
65
|
+
const diags = literalCredentialRule.check(
|
|
66
|
+
ctx(`
|
|
67
|
+
const chatSlack: SlackConfig[] = [{ api_url: "https://hooks.slack.com/services/T/B/X", app_token: "xoxb-1" }];
|
|
68
|
+
new Receiver({
|
|
69
|
+
name: "chat",
|
|
70
|
+
slack_configs: chatSlack,
|
|
71
|
+
discord_configs: [{ webhook_url: "https://discord.com/api/webhooks/1/x" }],
|
|
72
|
+
telegram_configs: [{ bot_token: "123:abc", chat_id: 1 }],
|
|
73
|
+
webex_configs: [{ api_url: "https://webexapis.com/v1/messages", room_id: "r" }],
|
|
74
|
+
victorops_configs: [{ api_key: "k", routing_key: "ops" }],
|
|
75
|
+
pushover_configs: [{ user_key: "u", token: "t" }],
|
|
76
|
+
rocketchat_configs: [{ token_id: "i", token_file: "/etc/rc" }],
|
|
77
|
+
});
|
|
78
|
+
new AlertmanagerSettings({ global: { telegram_bot_token: "123:abc", webex_api_url: "https://webexapis.com/v1/messages" } });
|
|
79
|
+
`),
|
|
80
|
+
);
|
|
81
|
+
expect(diags.map((d) => d.message.split("`")[1])).toEqual([
|
|
82
|
+
"api_url",
|
|
83
|
+
"app_token",
|
|
84
|
+
"webhook_url",
|
|
85
|
+
"bot_token",
|
|
86
|
+
"api_key",
|
|
87
|
+
"user_key",
|
|
88
|
+
"token",
|
|
89
|
+
"token_id",
|
|
90
|
+
"telegram_bot_token",
|
|
91
|
+
]);
|
|
62
92
|
});
|
|
63
93
|
});
|
|
64
94
|
|
package/src/model.ts
CHANGED
|
@@ -13,6 +13,31 @@
|
|
|
13
13
|
* docs apply word for word.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import {
|
|
17
|
+
RECEIVER_INTEGRATION_TYPES,
|
|
18
|
+
type DiscordConfig,
|
|
19
|
+
type EmailConfig,
|
|
20
|
+
type HttpClientConfig,
|
|
21
|
+
type IncidentioConfig,
|
|
22
|
+
type JiraConfig,
|
|
23
|
+
type MattermostConfig,
|
|
24
|
+
type MSTeamsConfig,
|
|
25
|
+
type MSTeamsV2Config,
|
|
26
|
+
type OpsGenieConfig,
|
|
27
|
+
type PagerDutyConfig,
|
|
28
|
+
type PushoverConfig,
|
|
29
|
+
type ReceiverIntegration,
|
|
30
|
+
type RocketchatConfig,
|
|
31
|
+
type SlackConfig,
|
|
32
|
+
type SNSConfig,
|
|
33
|
+
type TelegramConfig,
|
|
34
|
+
type TlsConfig,
|
|
35
|
+
type VictorOpsConfig,
|
|
36
|
+
type WebexConfig,
|
|
37
|
+
type WebhookConfig,
|
|
38
|
+
type WechatConfig,
|
|
39
|
+
} from "./integrations";
|
|
40
|
+
|
|
16
41
|
/** Labels or annotations: string keys to string values. */
|
|
17
42
|
export type LabelSet = Record<string, string>;
|
|
18
43
|
|
|
@@ -78,114 +103,31 @@ export function ruleName(rule: RuleConfig): string {
|
|
|
78
103
|
|
|
79
104
|
// ── Alertmanager ────────────────────────────────────────────────────
|
|
80
105
|
|
|
81
|
-
|
|
82
|
-
export interface HttpClientConfig {
|
|
83
|
-
basic_auth?: { username?: string; username_file?: string; password?: string; password_file?: string };
|
|
84
|
-
authorization?: { type?: string; credentials?: string; credentials_file?: string };
|
|
85
|
-
bearer_token?: string;
|
|
86
|
-
bearer_token_file?: string;
|
|
87
|
-
oauth2?: Record<string, unknown>;
|
|
88
|
-
proxy_url?: string;
|
|
89
|
-
follow_redirects?: boolean;
|
|
90
|
-
enable_http2?: boolean;
|
|
91
|
-
tls_config?: {
|
|
92
|
-
ca_file?: string;
|
|
93
|
-
cert_file?: string;
|
|
94
|
-
key_file?: string;
|
|
95
|
-
server_name?: string;
|
|
96
|
-
insecure_skip_verify?: boolean;
|
|
97
|
-
min_version?: string;
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
interface NotifierBase {
|
|
102
|
-
/** Whether to notify about resolved alerts too. */
|
|
103
|
-
send_resolved?: boolean;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface WebhookConfig extends NotifierBase {
|
|
107
|
-
/** Where alerts are POSTed. Set this or `url_file`. */
|
|
108
|
-
url?: string;
|
|
109
|
-
url_file?: string;
|
|
110
|
-
http_config?: HttpClientConfig;
|
|
111
|
-
/** Most alerts per message; 0 sends them all. */
|
|
112
|
-
max_alerts?: number;
|
|
113
|
-
timeout?: string;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export interface EmailConfig extends NotifierBase {
|
|
117
|
-
to: string;
|
|
118
|
-
from?: string;
|
|
119
|
-
/** `host:port` of the SMTP server. Falls back to `global.smtp_smarthost`. */
|
|
120
|
-
smarthost?: string;
|
|
121
|
-
hello?: string;
|
|
122
|
-
auth_username?: string;
|
|
123
|
-
auth_password?: string;
|
|
124
|
-
auth_password_file?: string;
|
|
125
|
-
auth_secret?: string;
|
|
126
|
-
auth_secret_file?: string;
|
|
127
|
-
auth_identity?: string;
|
|
128
|
-
require_tls?: boolean;
|
|
129
|
-
tls_config?: HttpClientConfig["tls_config"];
|
|
130
|
-
html?: string;
|
|
131
|
-
text?: string;
|
|
132
|
-
headers?: Record<string, string>;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export interface SlackConfig extends NotifierBase {
|
|
136
|
-
/** The incoming-webhook URL. It is a credential: prefer `api_url_file`. */
|
|
137
|
-
api_url?: string;
|
|
138
|
-
api_url_file?: string;
|
|
139
|
-
channel?: string;
|
|
140
|
-
username?: string;
|
|
141
|
-
color?: string;
|
|
142
|
-
title?: string;
|
|
143
|
-
title_link?: string;
|
|
144
|
-
pretext?: string;
|
|
145
|
-
text?: string;
|
|
146
|
-
footer?: string;
|
|
147
|
-
fallback?: string;
|
|
148
|
-
icon_emoji?: string;
|
|
149
|
-
icon_url?: string;
|
|
150
|
-
link_names?: boolean;
|
|
151
|
-
short_fields?: boolean;
|
|
152
|
-
mrkdwn_in?: string[];
|
|
153
|
-
fields?: Array<{ title: string; value: string; short?: boolean }>;
|
|
154
|
-
actions?: Array<Record<string, unknown>>;
|
|
155
|
-
http_config?: HttpClientConfig;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export interface PagerDutyConfig extends NotifierBase {
|
|
159
|
-
/** Events API v2 integration key. A credential: prefer `routing_key_file`. */
|
|
160
|
-
routing_key?: string;
|
|
161
|
-
routing_key_file?: string;
|
|
162
|
-
/** Events API v1 integration key. A credential: prefer `service_key_file`. */
|
|
163
|
-
service_key?: string;
|
|
164
|
-
service_key_file?: string;
|
|
165
|
-
url?: string;
|
|
166
|
-
client?: string;
|
|
167
|
-
client_url?: string;
|
|
168
|
-
description?: string;
|
|
169
|
-
severity?: string;
|
|
170
|
-
class?: string;
|
|
171
|
-
component?: string;
|
|
172
|
-
group?: string;
|
|
173
|
-
source?: string;
|
|
174
|
-
details?: Record<string, string>;
|
|
175
|
-
links?: Array<{ href: string; text?: string }>;
|
|
176
|
-
images?: Array<{ src: string; alt?: string; href?: string }>;
|
|
177
|
-
http_config?: HttpClientConfig;
|
|
178
|
-
}
|
|
106
|
+
export * from "./integrations";
|
|
179
107
|
|
|
180
108
|
/** One entry under `receivers:`. */
|
|
181
109
|
export interface ReceiverConfig {
|
|
182
110
|
name: string;
|
|
183
111
|
/** Labels on the receiver, exposed to notification templates. */
|
|
184
112
|
labels?: LabelSet;
|
|
185
|
-
|
|
113
|
+
discord_configs?: DiscordConfig[];
|
|
186
114
|
email_configs?: EmailConfig[];
|
|
187
|
-
|
|
115
|
+
incidentio_configs?: IncidentioConfig[];
|
|
188
116
|
pagerduty_configs?: PagerDutyConfig[];
|
|
117
|
+
slack_configs?: SlackConfig[];
|
|
118
|
+
webhook_configs?: WebhookConfig[];
|
|
119
|
+
opsgenie_configs?: OpsGenieConfig[];
|
|
120
|
+
wechat_configs?: WechatConfig[];
|
|
121
|
+
pushover_configs?: PushoverConfig[];
|
|
122
|
+
victorops_configs?: VictorOpsConfig[];
|
|
123
|
+
sns_configs?: SNSConfig[];
|
|
124
|
+
telegram_configs?: TelegramConfig[];
|
|
125
|
+
webex_configs?: WebexConfig[];
|
|
126
|
+
msteams_configs?: MSTeamsConfig[];
|
|
127
|
+
msteamsv2_configs?: MSTeamsV2Config[];
|
|
128
|
+
jira_configs?: JiraConfig[];
|
|
129
|
+
rocketchat_configs?: RocketchatConfig[];
|
|
130
|
+
mattermost_configs?: MattermostConfig[];
|
|
189
131
|
}
|
|
190
132
|
|
|
191
133
|
/** A route in the routing tree. The top-level `route:` is the root. */
|
|
@@ -244,12 +186,18 @@ export interface TimeIntervalConfig {
|
|
|
244
186
|
time_intervals: TimePeriodConfig[];
|
|
245
187
|
}
|
|
246
188
|
|
|
247
|
-
/**
|
|
189
|
+
/**
|
|
190
|
+
* `global:` settings: defaults the receivers fall back to. Credentials with
|
|
191
|
+
* a `*_file` sibling are best set through it; PROM001 flags a literal.
|
|
192
|
+
*/
|
|
248
193
|
export interface AlertmanagerGlobalConfig {
|
|
194
|
+
/** How long an alert that stops being updated stays firing. */
|
|
249
195
|
resolve_timeout?: string;
|
|
196
|
+
http_config?: HttpClientConfig;
|
|
197
|
+
jira_api_url?: string;
|
|
250
198
|
smtp_from?: string;
|
|
251
|
-
smtp_smarthost?: string;
|
|
252
199
|
smtp_hello?: string;
|
|
200
|
+
smtp_smarthost?: string;
|
|
253
201
|
smtp_auth_username?: string;
|
|
254
202
|
smtp_auth_password?: string;
|
|
255
203
|
smtp_auth_password_file?: string;
|
|
@@ -257,12 +205,83 @@ export interface AlertmanagerGlobalConfig {
|
|
|
257
205
|
smtp_auth_secret_file?: string;
|
|
258
206
|
smtp_auth_identity?: string;
|
|
259
207
|
smtp_require_tls?: boolean;
|
|
208
|
+
smtp_tls_config?: TlsConfig;
|
|
209
|
+
smtp_force_implicit_tls?: boolean;
|
|
260
210
|
slack_api_url?: string;
|
|
261
211
|
slack_api_url_file?: string;
|
|
212
|
+
slack_app_token?: string;
|
|
213
|
+
slack_app_token_file?: string;
|
|
214
|
+
slack_app_url?: string;
|
|
262
215
|
pagerduty_url?: string;
|
|
263
|
-
|
|
216
|
+
opsgenie_api_url?: string;
|
|
217
|
+
opsgenie_api_key?: string;
|
|
218
|
+
opsgenie_api_key_file?: string;
|
|
219
|
+
wechat_api_url?: string;
|
|
220
|
+
wechat_api_secret?: string;
|
|
221
|
+
wechat_api_secret_file?: string;
|
|
222
|
+
wechat_api_corp_id?: string;
|
|
223
|
+
victorops_api_url?: string;
|
|
224
|
+
victorops_api_key?: string;
|
|
225
|
+
victorops_api_key_file?: string;
|
|
226
|
+
telegram_api_url?: string;
|
|
227
|
+
telegram_bot_token?: string;
|
|
228
|
+
telegram_bot_token_file?: string;
|
|
229
|
+
webex_api_url?: string;
|
|
230
|
+
rocketchat_api_url?: string;
|
|
231
|
+
rocketchat_token?: string;
|
|
232
|
+
rocketchat_token_file?: string;
|
|
233
|
+
rocketchat_token_id?: string;
|
|
234
|
+
rocketchat_token_id_file?: string;
|
|
235
|
+
mattermost_webhook_url?: string;
|
|
236
|
+
mattermost_webhook_url_file?: string;
|
|
264
237
|
}
|
|
265
238
|
|
|
239
|
+
/** Every `global:` field, as a list, for code that reads a parsed file. */
|
|
240
|
+
export const ALERTMANAGER_GLOBAL_FIELDS = Object.freeze(Object.keys({
|
|
241
|
+
resolve_timeout: true,
|
|
242
|
+
http_config: true,
|
|
243
|
+
jira_api_url: true,
|
|
244
|
+
smtp_from: true,
|
|
245
|
+
smtp_hello: true,
|
|
246
|
+
smtp_smarthost: true,
|
|
247
|
+
smtp_auth_username: true,
|
|
248
|
+
smtp_auth_password: true,
|
|
249
|
+
smtp_auth_password_file: true,
|
|
250
|
+
smtp_auth_secret: true,
|
|
251
|
+
smtp_auth_secret_file: true,
|
|
252
|
+
smtp_auth_identity: true,
|
|
253
|
+
smtp_require_tls: true,
|
|
254
|
+
smtp_tls_config: true,
|
|
255
|
+
smtp_force_implicit_tls: true,
|
|
256
|
+
slack_api_url: true,
|
|
257
|
+
slack_api_url_file: true,
|
|
258
|
+
slack_app_token: true,
|
|
259
|
+
slack_app_token_file: true,
|
|
260
|
+
slack_app_url: true,
|
|
261
|
+
pagerduty_url: true,
|
|
262
|
+
opsgenie_api_url: true,
|
|
263
|
+
opsgenie_api_key: true,
|
|
264
|
+
opsgenie_api_key_file: true,
|
|
265
|
+
wechat_api_url: true,
|
|
266
|
+
wechat_api_secret: true,
|
|
267
|
+
wechat_api_secret_file: true,
|
|
268
|
+
wechat_api_corp_id: true,
|
|
269
|
+
victorops_api_url: true,
|
|
270
|
+
victorops_api_key: true,
|
|
271
|
+
victorops_api_key_file: true,
|
|
272
|
+
telegram_api_url: true,
|
|
273
|
+
telegram_bot_token: true,
|
|
274
|
+
telegram_bot_token_file: true,
|
|
275
|
+
webex_api_url: true,
|
|
276
|
+
rocketchat_api_url: true,
|
|
277
|
+
rocketchat_token: true,
|
|
278
|
+
rocketchat_token_file: true,
|
|
279
|
+
rocketchat_token_id: true,
|
|
280
|
+
rocketchat_token_id_file: true,
|
|
281
|
+
mattermost_webhook_url: true,
|
|
282
|
+
mattermost_webhook_url_file: true,
|
|
283
|
+
} satisfies Record<keyof AlertmanagerGlobalConfig, true>)) as readonly (keyof AlertmanagerGlobalConfig)[];
|
|
284
|
+
|
|
266
285
|
/** `tracing:` settings: where Alertmanager sends its own traces. */
|
|
267
286
|
export interface AlertmanagerTracingConfig {
|
|
268
287
|
client_type?: "grpc" | "http";
|
|
@@ -272,7 +291,7 @@ export interface AlertmanagerTracingConfig {
|
|
|
272
291
|
headers?: Record<string, string>;
|
|
273
292
|
compression?: string;
|
|
274
293
|
timeout?: string;
|
|
275
|
-
tls_config?:
|
|
294
|
+
tls_config?: TlsConfig;
|
|
276
295
|
}
|
|
277
296
|
|
|
278
297
|
/** A whole `alertmanager.yml`, the file `amtool check-config` reads. */
|
|
@@ -286,17 +305,24 @@ export interface AlertmanagerConfig {
|
|
|
286
305
|
tracing?: AlertmanagerTracingConfig;
|
|
287
306
|
}
|
|
288
307
|
|
|
289
|
-
/** The
|
|
290
|
-
export const RECEIVER_INTEGRATIONS =
|
|
308
|
+
/** The receiver integrations, every one Alertmanager defines. */
|
|
309
|
+
export const RECEIVER_INTEGRATIONS = Object.keys(RECEIVER_INTEGRATION_TYPES) as readonly ReceiverIntegration[];
|
|
291
310
|
|
|
292
311
|
/** True when a parsed document has the shape of a Prometheus rule file. */
|
|
293
312
|
export function looksLikeRuleFile(value: unknown): value is RuleFileConfig {
|
|
294
313
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
295
|
-
const
|
|
314
|
+
const v = value as Record<string, unknown>;
|
|
315
|
+
const groups = v.groups;
|
|
296
316
|
if (!Array.isArray(groups)) return false;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
);
|
|
317
|
+
// Grafana's alerting provisioning files also hold `groups:` of named rule lists. Prometheus reads rule
|
|
318
|
+
// files strictly (unknown keys are errors), so `apiVersion`, a group `folder` or a rule's `data` means Grafana.
|
|
319
|
+
if ("apiVersion" in v) return false;
|
|
320
|
+
return groups.every((g) => {
|
|
321
|
+
if (typeof g !== "object" || g === null) return false;
|
|
322
|
+
const group = g as Record<string, unknown>;
|
|
323
|
+
if (typeof group.name !== "string" || !Array.isArray(group.rules) || "folder" in group) return false;
|
|
324
|
+
return !group.rules.some((r) => typeof r === "object" && r !== null && "data" in r);
|
|
325
|
+
});
|
|
300
326
|
}
|
|
301
327
|
|
|
302
328
|
/** True when a parsed document has the shape of an `alertmanager.yml`. */
|
package/src/plugin.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { hover } from "./lsp/hover";
|
|
|
11
11
|
import { detectTemplate } from "./detect";
|
|
12
12
|
import { PrometheusParser } from "./import/parser";
|
|
13
13
|
import { PrometheusGenerator } from "./import/generator";
|
|
14
|
+
import { ruleGroupsImporter } from "./import/embedded";
|
|
14
15
|
import { initTemplates } from "./init-templates";
|
|
15
16
|
import { prometheusSkills } from "./skill-defs";
|
|
16
17
|
import { CATALOG } from "./catalog";
|
|
@@ -120,6 +121,10 @@ export const prometheusPlugin: LexiconPlugin = {
|
|
|
120
121
|
return new PrometheusGenerator();
|
|
121
122
|
},
|
|
122
123
|
|
|
124
|
+
embeddedImporters() {
|
|
125
|
+
return [ruleGroupsImporter];
|
|
126
|
+
},
|
|
127
|
+
|
|
123
128
|
initTemplates(template?: string) {
|
|
124
129
|
return initTemplates(template);
|
|
125
130
|
},
|
|
@@ -49,7 +49,7 @@ export const pageMutesTicket = new InhibitRule({
|
|
|
49
49
|
|
|
50
50
|
## Starting from an existing alertmanager.yml
|
|
51
51
|
|
|
52
|
-
Import it rather than retyping it: `chant import alertmanager.yml --output src` (with `--lexicon prometheus` outside a chant project). The importer writes `receivers.ts`, `time-intervals.ts`, `routes.ts` (the root `Route`, children as `RouteProps` that reference receivers and time intervals by variable), `inhibit-rules.ts` and `settings.ts`. `*_file` paths and Go templates are kept as written; a literal credential is imported as found and PROM001 reports it, so offer to move it to the `*_file` field.
|
|
52
|
+
Import it rather than retyping it: `chant import alertmanager.yml --output src` (with `--lexicon prometheus` outside a chant project). The importer writes `receivers.ts`, `time-intervals.ts`, `routes.ts` (the root `Route`, children as `RouteProps` that reference receivers and time intervals by variable), `inhibit-rules.ts` and `settings.ts`. `*_file` paths and Go templates are kept as written; a literal credential is imported as found and PROM001 reports it, so offer to move it to the `*_file` field. Every integration comes back typed (`OpsGenieConfig[]`, `MSTeamsV2Config[]`, ...); a receiver or `global` key Alertmanager doesn't define is spread in from an untyped const, with a warning. `match`/`match_re` and the top-level `mute_time_intervals` come back in their current spelling (`matchers`, `time_intervals`), and `chant import` prints a warning for each.
|
|
53
53
|
|
|
54
54
|
## What the checks hold you to
|
|
55
55
|
|