@intentius/chant-lexicon-grafana 0.106.1 → 0.106.3
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/dist/import/operator.d.ts +6 -1
- package/dist/import/operator.d.ts.map +1 -1
- package/dist/integrity.json +2 -2
- package/dist/k8s.d.ts +23 -3
- package/dist/k8s.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/package.json +2 -2
- package/src/import/operator.test.ts +23 -1
- package/src/import/operator.ts +16 -1
- package/src/k8s.test.ts +34 -0
- package/src/k8s.ts +56 -4
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* `chant import` (#3538).
|
|
4
4
|
*
|
|
5
5
|
* `GrafanaOperatorResources` (#3156) writes `GrafanaAlertRuleGroup`,
|
|
6
|
-
* `GrafanaContactPoint`, `GrafanaNotificationPolicy
|
|
6
|
+
* `GrafanaContactPoint`, `GrafanaNotificationPolicy` (with, for
|
|
7
|
+
* `policyRoutes`, a `GrafanaNotificationPolicyRoute` per child route),
|
|
8
|
+
* `GrafanaMuteTiming` and
|
|
7
9
|
* `GrafanaNotificationTemplate` from grafana alerting declarations. The k8s
|
|
8
10
|
* parser offers the one field of each that holds the alerting content
|
|
9
11
|
* (`spec.rules`, `spec.receivers`, `spec.route`, `spec.time_intervals`,
|
|
@@ -26,6 +28,9 @@
|
|
|
26
28
|
* name is not part of a declaration, so a contact point that reads a
|
|
27
29
|
* Secret is written with a small module holding
|
|
28
30
|
* `operatorReceivers(contactPoint, "<secret>")`;
|
|
31
|
+
* - a `GrafanaNotificationPolicyRoute`: its whole `spec` is the route, read
|
|
32
|
+
* as the one child of a policy that has its receiver, and written back by
|
|
33
|
+
* `operatorRouteSpec`;
|
|
29
34
|
* - the policy tree: `object_matchers` go back to Alertmanager `matchers`
|
|
30
35
|
* strings where each can be written as one.
|
|
31
36
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../../src/import/operator.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../../src/import/operator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAmB,uBAAuB,EAAkB,MAAM,kCAAkC,CAAC;AAKjH,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAmBpC,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAwBhD;AAYD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAoBpH;AAWD,oIAAoI;AACpI,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,CAWlD;AAiDD,eAAO,MAAM,gBAAgB,EAAE,uBA2C9B,CAAC"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "f95101f26cc36262685fe99c9eccc904ffca12e4e4e798d9fb0e35023060f041",
|
|
5
5
|
"meta.json": "330a1598c3b35ae62267e886a3416a494b0ebe9f1d2e2379253787d0a0bcb32e",
|
|
6
6
|
"types/index.d.ts": "ad385264290f2486f7a54be205684de8415a25b74eee5b809ca625ae19fb08da",
|
|
7
7
|
"rules/grafana-ast.ts": "51f4fac423f7a5c6d6f577074e95b4a70d4ab0a05883a86e04970a50c63d92e8",
|
|
@@ -31,5 +31,5 @@
|
|
|
31
31
|
"skills/chant-grafana-alerting.md": "d9648dc14c848851bdccbe069dac54ad06115066f9466c90c4137d07dde6d4ad",
|
|
32
32
|
"skills/chant-grafana-operations.md": "4e665f80292ab32bde6d77940cc3f3923678c58e6db0b80613489050d2cb665b"
|
|
33
33
|
},
|
|
34
|
-
"composite": "
|
|
34
|
+
"composite": "d3f04256f6713df0d65a9e5470148e3e3c70a5a5bb7f28e766dafef40f8ccfa4"
|
|
35
35
|
}
|
package/dist/k8s.d.ts
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
*/
|
|
36
36
|
import { type CompositeInstance } from "@intentius/chant/composite";
|
|
37
37
|
import type { Declarable } from "@intentius/chant/declarable";
|
|
38
|
-
import { ConfigMap, GrafanaAlertRuleGroup, GrafanaContactPoint, GrafanaDashboard, GrafanaDatasource, GrafanaFolder, GrafanaLibraryPanel, GrafanaMuteTiming, GrafanaNotificationPolicy, GrafanaNotificationTemplate } from "@intentius/chant-lexicon-k8s/generated/index";
|
|
38
|
+
import { ConfigMap, GrafanaAlertRuleGroup, GrafanaContactPoint, GrafanaDashboard, GrafanaDatasource, GrafanaFolder, GrafanaLibraryPanel, GrafanaMuteTiming, GrafanaNotificationPolicy, GrafanaNotificationPolicyRoute, GrafanaNotificationTemplate } from "@intentius/chant-lexicon-k8s/generated/index";
|
|
39
39
|
import { type ProvisionedAlertRule, type ProvisionedContactPoint } from "./alerting-build.js";
|
|
40
40
|
import type { AlertRuleGroupEntity, ContactPointEntity, MuteTimingEntity, NotificationPolicyEntity, NotificationTemplateEntity } from "./alerting.js";
|
|
41
41
|
import { type ProvisionedDatasource } from "./build.js";
|
|
@@ -163,8 +163,18 @@ export interface GrafanaOperatorResourcesProps {
|
|
|
163
163
|
* contact point has such a reference.
|
|
164
164
|
*/
|
|
165
165
|
secretName?: string;
|
|
166
|
+
/**
|
|
167
|
+
* Write each direct child route of a notification policy as a
|
|
168
|
+
* `GrafanaNotificationPolicyRoute` of its own, which the policy merges in
|
|
169
|
+
* through `spec.route.routeSelector` (the operator allows `routes` or
|
|
170
|
+
* `routeSelector`, not both). The routes carry the label
|
|
171
|
+
* `grafana.chant.dev/policy: <policy resource name>`, which the selector
|
|
172
|
+
* matches. A route's own nested routes stay inline in its `spec.routes`.
|
|
173
|
+
* Defaults to false: the whole tree inline in `spec.route`.
|
|
174
|
+
*/
|
|
175
|
+
policyRoutes?: boolean;
|
|
166
176
|
}
|
|
167
|
-
type OperatorEntity = (InstanceType<typeof GrafanaDashboard> | InstanceType<typeof GrafanaDatasource> | InstanceType<typeof GrafanaFolder> | InstanceType<typeof GrafanaLibraryPanel> | InstanceType<typeof GrafanaAlertRuleGroup> | InstanceType<typeof GrafanaContactPoint> | InstanceType<typeof GrafanaNotificationPolicy> | InstanceType<typeof GrafanaMuteTiming> | InstanceType<typeof GrafanaNotificationTemplate>) & Declarable;
|
|
177
|
+
type OperatorEntity = (InstanceType<typeof GrafanaDashboard> | InstanceType<typeof GrafanaDatasource> | InstanceType<typeof GrafanaFolder> | InstanceType<typeof GrafanaLibraryPanel> | InstanceType<typeof GrafanaAlertRuleGroup> | InstanceType<typeof GrafanaContactPoint> | InstanceType<typeof GrafanaNotificationPolicy> | InstanceType<typeof GrafanaNotificationPolicyRoute> | InstanceType<typeof GrafanaMuteTiming> | InstanceType<typeof GrafanaNotificationTemplate>) & Declarable;
|
|
168
178
|
export type GrafanaOperatorResourcesMembers = Record<string, OperatorEntity>;
|
|
169
179
|
export type GrafanaOperatorResourcesInstance = CompositeInstance<GrafanaOperatorResourcesMembers> & GrafanaOperatorResourcesMembers;
|
|
170
180
|
/**
|
|
@@ -178,6 +188,8 @@ export declare function operatorDatasource(ds: ProvisionedDatasource, secretName
|
|
|
178
188
|
datasource: Record<string, unknown>;
|
|
179
189
|
valuesFrom: Array<Record<string, unknown>>;
|
|
180
190
|
};
|
|
191
|
+
/** The label a `GrafanaNotificationPolicyRoute` carries for the policy whose `routeSelector` merges it in. */
|
|
192
|
+
export declare const POLICY_ROUTE_LABEL = "grafana.chant.dev/policy";
|
|
181
193
|
/**
|
|
182
194
|
* One contact point receiver's settings for `spec.receivers[].settings`, and
|
|
183
195
|
* the `valuesFrom` entries for the secrets in it. A setting that is wholly
|
|
@@ -216,6 +228,12 @@ export declare function operatorRules(group: AlertRuleGroupEntity): Array<Record
|
|
|
216
228
|
export declare function operatorReceivers(cp: ContactPointEntity, secretName?: string): Array<Record<string, unknown>>;
|
|
217
229
|
/** `GrafanaNotificationPolicy.spec.route` for a policy tree. */
|
|
218
230
|
export declare function operatorPolicy(policy: NotificationPolicyEntity): Record<string, unknown>;
|
|
231
|
+
/**
|
|
232
|
+
* `GrafanaNotificationPolicyRoute.spec` for a policy declaration holding the
|
|
233
|
+
* route as its one child: the child's fields, with `receiver` always set
|
|
234
|
+
* (the CRD requires it, so a child that inherits it gets the policy's).
|
|
235
|
+
*/
|
|
236
|
+
export declare function operatorRouteSpec(policy: NotificationPolicyEntity): Record<string, unknown>;
|
|
219
237
|
/** `GrafanaMuteTiming.spec.time_intervals` for a mute timing. */
|
|
220
238
|
export declare function operatorTimeIntervals(timing: MuteTimingEntity): Array<Record<string, unknown>>;
|
|
221
239
|
/** `GrafanaNotificationTemplate.spec.template` for a template. */
|
|
@@ -241,7 +259,9 @@ export declare function operatorTemplate(template: NotificationTemplateEntity):
|
|
|
241
259
|
* a `GrafanaContactPoint` per contact point, `<name>-contact-point-<name>`,
|
|
242
260
|
* whose `${NAME}` secrets go to each receiver's `valuesFrom`; a
|
|
243
261
|
* `GrafanaNotificationPolicy`, `<name>-notification-policy`, with its
|
|
244
|
-
* nested routes inline
|
|
262
|
+
* nested routes inline (or, with `policyRoutes`, one
|
|
263
|
+
* `GrafanaNotificationPolicyRoute` per direct child route, selected by
|
|
264
|
+
* label); a `GrafanaMuteTiming` per mute timing and a
|
|
245
265
|
* `GrafanaNotificationTemplate` per template.
|
|
246
266
|
*
|
|
247
267
|
* Every resource carries `instanceSelector`. Export the result from a k8s
|
package/dist/k8s.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"k8s.d.ts","sourceRoot":"","sources":["../src/k8s.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"k8s.d.ts","sourceRoot":"","sources":["../src/k8s.ts"],"names":[],"mappings":"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"}
|
package/dist/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-grafana",
|
|
3
|
-
"version": "0.106.
|
|
3
|
+
"version": "0.106.3",
|
|
4
4
|
"description": "Grafana lexicon for chant — typed dashboards, panels, queries and datasources, serialized to dashboard JSON and Grafana provisioning files",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"zod": "^4.3.6"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
|
-
"@intentius/chant": "^0.106.
|
|
86
|
+
"@intentius/chant": "^0.106.3",
|
|
87
87
|
"typescript": "^5.9.3",
|
|
88
88
|
"zod": "^4.3.6"
|
|
89
89
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from "vitest";
|
|
2
2
|
import type { EmbeddedContent } from "@intentius/chant/import/embedded";
|
|
3
3
|
import { grafanaPlugin } from "../plugin";
|
|
4
|
-
import { operatorContactPoint, operatorPolicyRoute, operatorRule } from "../k8s";
|
|
4
|
+
import { operatorContactPoint, operatorPolicyRoute, operatorRouteSpec, operatorRule } from "../k8s";
|
|
5
5
|
import { operatorImporter, provisionedReceivers, provisionedRoute, provisionedRule } from "./operator";
|
|
6
6
|
|
|
7
7
|
const site = (hostType: string, select: string, spec: Record<string, unknown>): EmbeddedContent => ({
|
|
@@ -25,6 +25,7 @@ describe("Grafana Operator alerting embedded in a k8s manifest (#3538)", () => {
|
|
|
25
25
|
expect(operatorImporter.matches(site("K8s::Grafana::GrafanaAlertRuleGroup", "rules", { rules: [] }))).toBe(true);
|
|
26
26
|
expect(operatorImporter.matches(site("K8s::Grafana::GrafanaContactPoint", "receivers", { receivers: [] }))).toBe(true);
|
|
27
27
|
expect(operatorImporter.matches(site("K8s::Grafana::GrafanaNotificationPolicy", "route", { route: {} }))).toBe(true);
|
|
28
|
+
expect(operatorImporter.matches(site("K8s::Grafana::GrafanaNotificationPolicyRoute", "route", { route: { receiver: "r" } }))).toBe(true);
|
|
28
29
|
expect(operatorImporter.matches(site("K8s::Grafana::GrafanaMuteTiming", "time_intervals", { time_intervals: [] }))).toBe(true);
|
|
29
30
|
expect(operatorImporter.matches(site("K8s::Grafana::GrafanaNotificationTemplate", "template", { template: "x" }))).toBe(true);
|
|
30
31
|
expect(operatorImporter.matches(site("K8s::Grafana::GrafanaAlertRuleGroup", "receivers", {}))).toBe(false);
|
|
@@ -205,6 +206,27 @@ describe("Grafana Operator alerting embedded in a k8s manifest (#3538)", () => {
|
|
|
205
206
|
expect(template.files.map((f) => f.content).join("\n")).toContain("new NotificationTemplate(");
|
|
206
207
|
});
|
|
207
208
|
|
|
209
|
+
test("a policy route is read as the child of a policy and written back through operatorRouteSpec", () => {
|
|
210
|
+
const spec = { receiver: "tickets", object_matchers: [["severity", "=", "ticket"]], mute_time_intervals: ["weekends"], routes: [{ object_matchers: [["team", "=", "a"]] }] };
|
|
211
|
+
const out = operatorImporter.import(site("K8s::Grafana::GrafanaNotificationPolicyRoute", "route", { route: spec }));
|
|
212
|
+
expect(out.value.through).toEqual({ from: "@intentius/chant-lexicon-grafana/k8s", name: "operatorRouteSpec" });
|
|
213
|
+
const text = out.files.map((f) => f.content).join("\n");
|
|
214
|
+
expect(text).toContain("new NotificationPolicy(");
|
|
215
|
+
expect(text).toContain('severity="ticket"');
|
|
216
|
+
expect(text).toContain('team="a"');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("a policy route's routeSelector is left out with a warning, and a route with no receiver throws", () => {
|
|
220
|
+
const out = operatorImporter.import(site("K8s::Grafana::GrafanaNotificationPolicyRoute", "route", { route: { receiver: "r", routeSelector: { matchLabels: { a: "b" } } } }));
|
|
221
|
+
expect((out.warnings ?? []).join("\n")).toContain("routeSelector");
|
|
222
|
+
expect(() => operatorImporter.import(site("K8s::Grafana::GrafanaNotificationPolicyRoute", "route", { route: { matchers: [] } }))).toThrow(/spec.receiver/);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("a policy that selects routes by label throws so the host keeps it as written", () => {
|
|
226
|
+
const route = { receiver: "oncall", routeSelector: { matchLabels: { a: "b" } } };
|
|
227
|
+
expect(() => operatorImporter.import(site("K8s::Grafana::GrafanaNotificationPolicy", "route", { route }))).toThrow(/routeSelector/);
|
|
228
|
+
});
|
|
229
|
+
|
|
208
230
|
test("a policy with no receiver, and a group with no folder, throw so the host keeps them as written", () => {
|
|
209
231
|
expect(() => operatorImporter.import(site("K8s::Grafana::GrafanaNotificationPolicy", "route", { route: { group_by: ["a"] } }))).toThrow();
|
|
210
232
|
expect(() => operatorImporter.import(site("K8s::Grafana::GrafanaAlertRuleGroup", "rules", { name: "g", rules: [] }))).toThrow(/names no folder/);
|
package/src/import/operator.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* `chant import` (#3538).
|
|
4
4
|
*
|
|
5
5
|
* `GrafanaOperatorResources` (#3156) writes `GrafanaAlertRuleGroup`,
|
|
6
|
-
* `GrafanaContactPoint`, `GrafanaNotificationPolicy
|
|
6
|
+
* `GrafanaContactPoint`, `GrafanaNotificationPolicy` (with, for
|
|
7
|
+
* `policyRoutes`, a `GrafanaNotificationPolicyRoute` per child route),
|
|
8
|
+
* `GrafanaMuteTiming` and
|
|
7
9
|
* `GrafanaNotificationTemplate` from grafana alerting declarations. The k8s
|
|
8
10
|
* parser offers the one field of each that holds the alerting content
|
|
9
11
|
* (`spec.rules`, `spec.receivers`, `spec.route`, `spec.time_intervals`,
|
|
@@ -26,6 +28,9 @@
|
|
|
26
28
|
* name is not part of a declaration, so a contact point that reads a
|
|
27
29
|
* Secret is written with a small module holding
|
|
28
30
|
* `operatorReceivers(contactPoint, "<secret>")`;
|
|
31
|
+
* - a `GrafanaNotificationPolicyRoute`: its whole `spec` is the route, read
|
|
32
|
+
* as the one child of a policy that has its receiver, and written back by
|
|
33
|
+
* `operatorRouteSpec`;
|
|
29
34
|
* - the policy tree: `object_matchers` go back to Alertmanager `matchers`
|
|
30
35
|
* strings where each can be written as one.
|
|
31
36
|
*
|
|
@@ -48,6 +53,7 @@ const KINDS: Record<string, { select: string; className: string; through: string
|
|
|
48
53
|
"K8s::Grafana::GrafanaAlertRuleGroup": { select: "rules", className: "AlertRuleGroup", through: "operatorRules" },
|
|
49
54
|
"K8s::Grafana::GrafanaContactPoint": { select: "receivers", className: "ContactPoint", through: "operatorReceivers" },
|
|
50
55
|
"K8s::Grafana::GrafanaNotificationPolicy": { select: "route", className: "NotificationPolicy", through: "operatorPolicy" },
|
|
56
|
+
"K8s::Grafana::GrafanaNotificationPolicyRoute": { select: "route", className: "NotificationPolicy", through: "operatorRouteSpec" },
|
|
51
57
|
"K8s::Grafana::GrafanaMuteTiming": { select: "time_intervals", className: "MuteTiming", through: "operatorTimeIntervals" },
|
|
52
58
|
"K8s::Grafana::GrafanaNotificationTemplate": { select: "template", className: "NotificationTemplate", through: "operatorTemplate" },
|
|
53
59
|
};
|
|
@@ -169,8 +175,17 @@ function provisioningFile(type: string, spec: Json, warnings: string[]): { file:
|
|
|
169
175
|
}
|
|
170
176
|
case "K8s::Grafana::GrafanaNotificationPolicy": {
|
|
171
177
|
if (!isObject(spec.route)) throw new Error(`${where} has no spec.route`);
|
|
178
|
+
// The routes it selects are their own resources, and a declaration cannot hold the selector: keep it as written.
|
|
179
|
+
if (spec.route.routeSelector !== undefined) throw new Error(`${where} merges routes in with spec.route.routeSelector, which a NotificationPolicy cannot hold`);
|
|
172
180
|
return { file: { apiVersion: 1, policies: [provisionedRoute(spec.route)] } };
|
|
173
181
|
}
|
|
182
|
+
case "K8s::Grafana::GrafanaNotificationPolicyRoute": {
|
|
183
|
+
// The k8s parser offers the whole spec as `route`. A route has no declaration of its own, so it is read as the one child of a policy with its receiver.
|
|
184
|
+
if (!isObject(spec.route) || typeof spec.route.receiver !== "string") throw new Error(`${where} has no spec.receiver`);
|
|
185
|
+
const { routeSelector: _selector, ...route } = spec.route;
|
|
186
|
+
if (_selector !== undefined) warnings.push("the route's routeSelector (routes merged in by label) is not part of a declaration, so it is left out; import the selected routes on their own");
|
|
187
|
+
return { file: { apiVersion: 1, policies: [{ receiver: route.receiver, routes: [provisionedRoute(route)] }] } };
|
|
188
|
+
}
|
|
174
189
|
case "K8s::Grafana::GrafanaMuteTiming": {
|
|
175
190
|
if (typeof name !== "string") throw new Error(`${where} has no spec.name`);
|
|
176
191
|
return { file: { apiVersion: 1, muteTimes: [{ name, time_intervals: spec.time_intervals }] } };
|
package/src/k8s.test.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
operatorDatasource,
|
|
19
19
|
operatorPolicyRoute,
|
|
20
20
|
operatorReceiverSettings,
|
|
21
|
+
operatorRouteSpec,
|
|
21
22
|
operatorRule,
|
|
22
23
|
} from "./k8s";
|
|
23
24
|
|
|
@@ -327,6 +328,12 @@ describe("GrafanaOperatorResources alerting (#3156)", () => {
|
|
|
327
328
|
]);
|
|
328
329
|
expect(() => operatorReceiverSettings("cp", { url: "https://x/${TOKEN}" }, "s")).toThrow(/inside the text/);
|
|
329
330
|
expect(() => operatorReceiverSettings("cp", { password: "$__file{/run/pw}" }, "s")).toThrow(/inside the text/);
|
|
331
|
+
// The alert template variables are not references, even when they are the whole setting.
|
|
332
|
+
expect(operatorReceiverSettings("cp", { message: "$labels", detail: "$value", more: "$values" }, undefined)).toEqual({
|
|
333
|
+
settings: { message: "$labels", detail: "$value", more: "$values" },
|
|
334
|
+
valuesFrom: [],
|
|
335
|
+
});
|
|
336
|
+
expect(operatorReceiverSettings("cp", { url: "$labelsToken" }, "s").valuesFrom).toEqual([{ targetPath: "url", valueFrom: { secretKeyRef: { name: "s", key: "labelsToken" } } }]);
|
|
330
337
|
// Go template text with a plain `$` is not a reference.
|
|
331
338
|
expect(operatorReceiverSettings("cp", { message: "{{ $labels.job }} is down" }, undefined)).toEqual({ settings: { message: "{{ $labels.job }} is down" }, valuesFrom: [] });
|
|
332
339
|
});
|
|
@@ -349,6 +356,33 @@ describe("GrafanaOperatorResources alerting (#3156)", () => {
|
|
|
349
356
|
expect(() => operatorPolicyRoute({ receiver: "x", routes: [{ matchers: ["nonsense"] }] })).toThrow(/cannot read the route matcher/);
|
|
350
357
|
});
|
|
351
358
|
|
|
359
|
+
test("policyRoutes writes each child route as a GrafanaNotificationPolicyRoute the policy selects by label", () => {
|
|
360
|
+
const docs = ops(alerting, { policyRoutes: true, labels: { team: "sre" } });
|
|
361
|
+
const root = docs.find((d) => d.kind === "GrafanaNotificationPolicy")!;
|
|
362
|
+
const label = { "grafana.chant.dev/policy": "grafana-notification-policy" };
|
|
363
|
+
expect(root.spec.route).toEqual({ receiver: "oncall", group_by: ["alertname"], routeSelector: { matchLabels: label } });
|
|
364
|
+
const routes = docs.filter((d) => d.kind === "GrafanaNotificationPolicyRoute");
|
|
365
|
+
expect(routes.map((d) => d.metadata.name)).toEqual(["grafana-notification-policy-route-1"]);
|
|
366
|
+
expect(routes[0].metadata.labels).toEqual({ team: "sre", ...label });
|
|
367
|
+
expect(routes[0].spec).toEqual({
|
|
368
|
+
receiver: "tickets",
|
|
369
|
+
object_matchers: [["severity", "=", "ticket"]],
|
|
370
|
+
mute_time_intervals: ["weekends"],
|
|
371
|
+
routes: [{ object_matchers: [["team", "=", "a"]] }],
|
|
372
|
+
});
|
|
373
|
+
expect(routes[0].spec.instanceSelector).toBeUndefined();
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("a policy with no child routes stays one resource under policyRoutes", () => {
|
|
377
|
+
const docs = ops([oncall, new NotificationPolicy({ receiver: oncall })], { policyRoutes: true });
|
|
378
|
+
expect(docs.some((d) => d.kind === "GrafanaNotificationPolicyRoute")).toBe(false);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test("operatorRouteSpec gives a child the policy's receiver when it has none", () => {
|
|
382
|
+
expect(operatorRouteSpec(new NotificationPolicy({ receiver: "oncall", routes: [{ matchers: ["a=\"b\""] }] }))).toEqual({ receiver: "oncall", object_matchers: [["a", "=", "b"]] });
|
|
383
|
+
expect(() => operatorRouteSpec(new NotificationPolicy({ receiver: "oncall" }))).toThrow(/no child route/);
|
|
384
|
+
});
|
|
385
|
+
|
|
352
386
|
test("a build with no alerting writes none, and options reach the alerting resources", () => {
|
|
353
387
|
expect(ops([prometheus]).map((d) => d.kind)).toEqual(["GrafanaDatasource"]);
|
|
354
388
|
const docs = ops([weekends], { allowCrossNamespaceImport: true, resyncPeriod: "5m", namespace: "obs", name: "obs" });
|
package/src/k8s.ts
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
GrafanaLibraryPanel,
|
|
47
47
|
GrafanaMuteTiming,
|
|
48
48
|
GrafanaNotificationPolicy,
|
|
49
|
+
GrafanaNotificationPolicyRoute,
|
|
49
50
|
GrafanaNotificationTemplate,
|
|
50
51
|
} from "@intentius/chant-lexicon-k8s/generated/index";
|
|
51
52
|
import {
|
|
@@ -275,6 +276,16 @@ export interface GrafanaOperatorResourcesProps {
|
|
|
275
276
|
* contact point has such a reference.
|
|
276
277
|
*/
|
|
277
278
|
secretName?: string;
|
|
279
|
+
/**
|
|
280
|
+
* Write each direct child route of a notification policy as a
|
|
281
|
+
* `GrafanaNotificationPolicyRoute` of its own, which the policy merges in
|
|
282
|
+
* through `spec.route.routeSelector` (the operator allows `routes` or
|
|
283
|
+
* `routeSelector`, not both). The routes carry the label
|
|
284
|
+
* `grafana.chant.dev/policy: <policy resource name>`, which the selector
|
|
285
|
+
* matches. A route's own nested routes stay inline in its `spec.routes`.
|
|
286
|
+
* Defaults to false: the whole tree inline in `spec.route`.
|
|
287
|
+
*/
|
|
288
|
+
policyRoutes?: boolean;
|
|
278
289
|
}
|
|
279
290
|
|
|
280
291
|
type OperatorEntity = (
|
|
@@ -285,6 +296,7 @@ type OperatorEntity = (
|
|
|
285
296
|
| InstanceType<typeof GrafanaAlertRuleGroup>
|
|
286
297
|
| InstanceType<typeof GrafanaContactPoint>
|
|
287
298
|
| InstanceType<typeof GrafanaNotificationPolicy>
|
|
299
|
+
| InstanceType<typeof GrafanaNotificationPolicyRoute>
|
|
288
300
|
| InstanceType<typeof GrafanaMuteTiming>
|
|
289
301
|
| InstanceType<typeof GrafanaNotificationTemplate>
|
|
290
302
|
) &
|
|
@@ -350,8 +362,15 @@ export function operatorDatasource(
|
|
|
350
362
|
|
|
351
363
|
// ── Alerting (#3156) ──────────────────────────────────────────────────
|
|
352
364
|
|
|
353
|
-
/**
|
|
354
|
-
const
|
|
365
|
+
/** The label a `GrafanaNotificationPolicyRoute` carries for the policy whose `routeSelector` merges it in. */
|
|
366
|
+
export const POLICY_ROUTE_LABEL = "grafana.chant.dev/policy";
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* A setting that is wholly a reference to a variable: `${NAME}`, `$__env{NAME}` or `$NAME`.
|
|
370
|
+
* The bare `$labels`, `$value` and `$values` are alert template variables, not environment
|
|
371
|
+
* variables, so a setting of exactly that text is left as written.
|
|
372
|
+
*/
|
|
373
|
+
const WHOLE_VARIABLE = /^(?:\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$__env\{([A-Za-z_][A-Za-z0-9_]*)\}|\$(?!(?:labels|values?)$)([A-Za-z_][A-Za-z0-9_]*))$/;
|
|
355
374
|
/** A braced reference inside longer text, which `valuesFrom` cannot substitute into. */
|
|
356
375
|
const EMBEDDED_VARIABLE = /\$\{[A-Za-z_][A-Za-z0-9_]*\}|\$__env\{[^}]*\}|\$__(?:file|vault)\{[^}]*\}/;
|
|
357
376
|
|
|
@@ -502,6 +521,19 @@ export function operatorPolicy(policy: NotificationPolicyEntity): Record<string,
|
|
|
502
521
|
return operatorPolicyRoute(notificationPolicyJson(policy));
|
|
503
522
|
}
|
|
504
523
|
|
|
524
|
+
/**
|
|
525
|
+
* `GrafanaNotificationPolicyRoute.spec` for a policy declaration holding the
|
|
526
|
+
* route as its one child: the child's fields, with `receiver` always set
|
|
527
|
+
* (the CRD requires it, so a child that inherits it gets the policy's).
|
|
528
|
+
*/
|
|
529
|
+
export function operatorRouteSpec(policy: NotificationPolicyEntity): Record<string, unknown> {
|
|
530
|
+
const tree = operatorPolicy(policy);
|
|
531
|
+
const routes = tree.routes as Array<Record<string, unknown>> | undefined;
|
|
532
|
+
const child = routes?.[0];
|
|
533
|
+
if (!child) throw new Error("grafana: the policy has no child route to write as a GrafanaNotificationPolicyRoute");
|
|
534
|
+
return { ...child, receiver: child.receiver ?? tree.receiver };
|
|
535
|
+
}
|
|
536
|
+
|
|
505
537
|
/** `GrafanaMuteTiming.spec.time_intervals` for a mute timing. */
|
|
506
538
|
export function operatorTimeIntervals(timing: MuteTimingEntity): Array<Record<string, unknown>> {
|
|
507
539
|
return muteTimingJson(timing).time_intervals;
|
|
@@ -533,7 +565,9 @@ export function operatorTemplate(template: NotificationTemplateEntity): string {
|
|
|
533
565
|
* a `GrafanaContactPoint` per contact point, `<name>-contact-point-<name>`,
|
|
534
566
|
* whose `${NAME}` secrets go to each receiver's `valuesFrom`; a
|
|
535
567
|
* `GrafanaNotificationPolicy`, `<name>-notification-policy`, with its
|
|
536
|
-
* nested routes inline
|
|
568
|
+
* nested routes inline (or, with `policyRoutes`, one
|
|
569
|
+
* `GrafanaNotificationPolicyRoute` per direct child route, selected by
|
|
570
|
+
* label); a `GrafanaMuteTiming` per mute timing and a
|
|
537
571
|
* `GrafanaNotificationTemplate` per template.
|
|
538
572
|
*
|
|
539
573
|
* Every resource carries `instanceSelector`. Export the result from a k8s
|
|
@@ -650,9 +684,27 @@ export const GrafanaOperatorResources = Composite<GrafanaOperatorResourcesProps,
|
|
|
650
684
|
}
|
|
651
685
|
for (const policy of alerting.policies ?? []) {
|
|
652
686
|
const name = unique(`${prefix}-notification-policy`);
|
|
687
|
+
const tree = operatorPolicyRoute(policy);
|
|
688
|
+
const children = Array.isArray(tree.routes) ? (tree.routes as Array<Record<string, unknown>>) : [];
|
|
689
|
+
if (props.policyRoutes && children.length > 0) {
|
|
690
|
+
const { routes: _inline, ...root } = tree;
|
|
691
|
+
const selector = { [POLICY_ROUTE_LABEL]: name };
|
|
692
|
+
members[memberName(name, prefix)] = new GrafanaNotificationPolicy({
|
|
693
|
+
metadata: metadata(name),
|
|
694
|
+
spec: { ...common, route: { ...root, routeSelector: { matchLabels: selector } } },
|
|
695
|
+
}) as OperatorEntity;
|
|
696
|
+
children.forEach((child, i) => {
|
|
697
|
+
const routeName = unique(`${prefix}-notification-policy-route-${i + 1}`);
|
|
698
|
+
members[memberName(routeName, prefix)] = new GrafanaNotificationPolicyRoute({
|
|
699
|
+
metadata: { ...metadata(routeName), labels: { ...(props.labels ?? {}), ...selector } },
|
|
700
|
+
spec: { ...child, receiver: child.receiver ?? tree.receiver },
|
|
701
|
+
}) as OperatorEntity;
|
|
702
|
+
});
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
653
705
|
members[memberName(name, prefix)] = new GrafanaNotificationPolicy({
|
|
654
706
|
metadata: metadata(name),
|
|
655
|
-
spec: { ...common, route:
|
|
707
|
+
spec: { ...common, route: tree },
|
|
656
708
|
}) as OperatorEntity;
|
|
657
709
|
}
|
|
658
710
|
for (const t of alerting.muteTimes ?? []) {
|