@intentius/chant-lexicon-grafana 0.99.0 → 0.100.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 +12 -6
- package/dist/annotations.d.ts +61 -0
- package/dist/annotations.d.ts.map +1 -0
- package/dist/api/apply.d.ts +8 -4
- package/dist/api/apply.d.ts.map +1 -1
- package/dist/api/fake-grafana.d.ts +2 -0
- package/dist/api/fake-grafana.d.ts.map +1 -1
- package/dist/api/folders.d.ts +53 -17
- package/dist/api/folders.d.ts.map +1 -1
- package/dist/build.d.ts +26 -5
- package/dist/build.d.ts.map +1 -1
- package/dist/catalog.d.ts +1 -1
- package/dist/catalog.d.ts.map +1 -1
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/generate.d.ts +7 -1
- package/dist/codegen/generate.d.ts.map +1 -1
- package/dist/composites/shared.d.ts +4 -3
- package/dist/composites/shared.d.ts.map +1 -1
- package/dist/dashboard.d.ts +42 -14
- package/dist/dashboard.d.ts.map +1 -1
- package/dist/datasource-refs.d.ts +9 -5
- package/dist/datasource-refs.d.ts.map +1 -1
- package/dist/datasource-settings.d.ts +834 -0
- package/dist/datasource-settings.d.ts.map +1 -0
- package/dist/datasource.d.ts +53 -11
- package/dist/datasource.d.ts.map +1 -1
- package/dist/deep-observe-hooks.d.ts +1 -1
- package/dist/deep-observe-hooks.d.ts.map +1 -1
- package/dist/deep-observe.d.ts +9 -4
- package/dist/deep-observe.d.ts.map +1 -1
- package/dist/describe-resources.d.ts +1 -0
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/detect.d.ts +4 -2
- package/dist/detect.d.ts.map +1 -1
- package/dist/folder.d.ts +68 -0
- package/dist/folder.d.ts.map +1 -0
- package/dist/import/generator.d.ts.map +1 -1
- package/dist/import/mappings.d.ts +12 -6
- package/dist/import/mappings.d.ts.map +1 -1
- package/dist/import/model.d.ts +7 -0
- package/dist/import/model.d.ts.map +1 -1
- package/dist/import/normalize.d.ts.map +1 -1
- package/dist/import/parser.d.ts +16 -1
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/import/testdata/fixtures.d.ts +6 -0
- package/dist/import/testdata/fixtures.d.ts.map +1 -1
- package/dist/import/v2.d.ts +4 -3
- package/dist/import/v2.d.ts.map +1 -1
- package/dist/index.d.ts +11 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/init-templates.d.ts +27 -0
- package/dist/init-templates.d.ts.map +1 -0
- package/dist/integrity.json +11 -9
- package/dist/lint/audit-catalog.d.ts +1 -1
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/graf101.d.ts +1 -1
- package/dist/lint/post-synth/graf104.d.ts +2 -2
- package/dist/lint/post-synth/graf106.d.ts +2 -2
- package/dist/lint/post-synth/graf110.d.ts +8 -0
- package/dist/lint/post-synth/graf110.d.ts.map +1 -0
- package/dist/lint/post-synth/grafana-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/meta.json +70 -0
- package/dist/okf/index.md +18 -3
- package/dist/okf/rules/GRAF101.md +2 -2
- package/dist/okf/rules/GRAF104.md +2 -2
- package/dist/okf/rules/GRAF106.md +2 -2
- package/dist/okf/rules/GRAF110.md +11 -0
- package/dist/okf/types/AdhocVariable.md +9 -0
- package/dist/okf/types/AzureMonitorQuery.md +9 -0
- package/dist/okf/types/BigQueryQuery.md +9 -0
- package/dist/okf/types/CloudMonitoringQuery.md +9 -0
- package/dist/okf/types/CloudWatchQuery.md +9 -0
- package/dist/okf/types/Dashboard.md +1 -1
- package/dist/okf/types/Datasource.md +1 -1
- package/dist/okf/types/DatasourceProvisioning.md +9 -0
- package/dist/okf/types/ElasticsearchQuery.md +9 -0
- package/dist/okf/types/ExternalDatasource.md +1 -1
- package/dist/okf/types/Folder.md +9 -0
- package/dist/okf/types/GroupByVariable.md +9 -0
- package/dist/okf/types/MSSQLQuery.md +9 -0
- package/dist/okf/types/MySQLQuery.md +9 -0
- package/dist/okf/types/PostgresQuery.md +9 -0
- package/dist/okf/types/PyroscopeQuery.md +9 -0
- package/dist/okf/types/SwitchVariable.md +9 -0
- package/dist/op/activities/grafana-apply.d.ts +11 -0
- package/dist/op/activities/grafana-apply.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +1 -1
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/ownership.d.ts +11 -0
- package/dist/ownership.d.ts.map +1 -1
- package/dist/pin.d.ts +1 -1
- package/dist/pin.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/promql-check.d.ts.map +1 -1
- package/dist/query-models.d.ts +95 -0
- package/dist/query-models.d.ts.map +1 -0
- package/dist/query.d.ts +107 -10
- package/dist/query.d.ts.map +1 -1
- package/dist/rules/graf101.ts +2 -2
- package/dist/rules/graf104.ts +3 -3
- package/dist/rules/graf106.ts +3 -3
- package/dist/rules/graf110.ts +17 -0
- package/dist/rules/grafana-helpers.ts +13 -3
- package/dist/schema/azuremonitor.gen.d.ts +354 -0
- package/dist/schema/azuremonitor.gen.d.ts.map +1 -0
- package/dist/schema/bigquery.gen.d.ts +93 -0
- package/dist/schema/bigquery.gen.d.ts.map +1 -0
- package/dist/schema/cloudwatch.gen.d.ts +394 -0
- package/dist/schema/cloudwatch.gen.d.ts.map +1 -0
- package/dist/schema/elasticsearch.gen.d.ts +444 -0
- package/dist/schema/elasticsearch.gen.d.ts.map +1 -0
- package/dist/schema/googlecloudmonitoring.gen.d.ts +312 -0
- package/dist/schema/googlecloudmonitoring.gen.d.ts.map +1 -0
- package/dist/schema/grafanapyroscope.gen.d.ts +60 -0
- package/dist/schema/grafanapyroscope.gen.d.ts.map +1 -0
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/table.gen.d.ts +13 -0
- package/dist/schema/table.gen.d.ts.map +1 -1
- package/dist/schema-validate.d.ts +9 -0
- package/dist/schema-validate.d.ts.map +1 -1
- package/dist/skill-defs.d.ts.map +1 -1
- package/dist/skills/chant-grafana-operations.md +108 -0
- package/dist/skills/chant-grafana.md +5 -4
- package/dist/spec/schemas.d.ts +9 -7
- package/dist/spec/schemas.d.ts.map +1 -1
- package/dist/spec/schemas.gen.d.ts +3 -0
- package/dist/spec/schemas.gen.d.ts.map +1 -0
- package/dist/validate-alerting.d.ts.map +1 -1
- package/dist/validate-output.d.ts +15 -2
- package/dist/validate-output.d.ts.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validation.d.ts +14 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/variables.d.ts +102 -5
- package/dist/variables.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/annotations.ts +84 -0
- package/src/api/apply.test.ts +19 -1
- package/src/api/apply.ts +18 -8
- package/src/api/fake-grafana.ts +6 -2
- package/src/api/folders.ts +106 -32
- package/src/build.test.ts +142 -6
- package/src/build.ts +126 -24
- package/src/catalog.ts +20 -2
- package/src/codegen/docs.ts +13 -6
- package/src/codegen/generate.ts +27 -1
- package/src/composites/shared.ts +3 -2
- package/src/dashboard.ts +59 -14
- package/src/datasource-refs.ts +30 -6
- package/src/datasource-settings.test.ts +106 -0
- package/src/datasource-settings.ts +941 -0
- package/src/datasource.ts +71 -11
- package/src/deep-observe-hooks.ts +16 -1
- package/src/deep-observe.test.ts +47 -1
- package/src/deep-observe.ts +36 -13
- package/src/describe-resources.test.ts +16 -0
- package/src/describe-resources.ts +36 -1
- package/src/detect.ts +3 -3
- package/src/folder.ts +118 -0
- package/src/generated/lexicon-grafana.json +70 -0
- package/src/import/generated-types.e2e.test.ts +15 -3
- package/src/import/generator.ts +2 -1
- package/src/import/mappings.ts +98 -26
- package/src/import/model.ts +7 -0
- package/src/import/normalize.ts +32 -3
- package/src/import/parser.test.ts +97 -21
- package/src/import/parser.ts +169 -18
- package/src/import/roundtrip.test.ts +104 -9
- package/src/import/testdata/fixtures.ts +8 -0
- package/src/import/v2.ts +4 -3
- package/src/index.ts +122 -13
- package/src/init-templates.test.ts +74 -0
- package/src/init-templates.ts +349 -0
- package/src/lint/audit-catalog.ts +9 -1
- package/src/lint/post-synth/graf101.ts +2 -2
- package/src/lint/post-synth/graf104.ts +3 -3
- package/src/lint/post-synth/graf106.ts +3 -3
- package/src/lint/post-synth/graf110.ts +17 -0
- package/src/lint/post-synth/grafana-helpers.ts +13 -3
- package/src/lint/post-synth/index.ts +2 -0
- package/src/lint/post-synth/post-synth.test.ts +99 -2
- package/src/load-cost.test.ts +35 -0
- package/src/op/activities/grafana-apply.e2e.test.ts +14 -5
- package/src/op/activities/grafana-apply.test.ts +16 -2
- package/src/op/activities/grafana-apply.ts +29 -3
- package/src/op/activities/index.ts +1 -1
- package/src/ownership.ts +19 -0
- package/src/panels.test.ts +3 -1
- package/src/pin.ts +12 -0
- package/src/plugin.test.ts +9 -2
- package/src/plugin.ts +9 -0
- package/src/promql-check.ts +5 -1
- package/src/query-models.ts +94 -0
- package/src/query.test.ts +166 -0
- package/src/query.ts +177 -12
- package/src/schema/azuremonitor.gen.ts +381 -0
- package/src/schema/bigquery.gen.ts +110 -0
- package/src/schema/cloudwatch.gen.ts +423 -0
- package/src/schema/elasticsearch.gen.ts +508 -0
- package/src/schema/googlecloudmonitoring.gen.ts +330 -0
- package/src/schema/grafanapyroscope.gen.ts +66 -0
- package/src/schema/index.ts +6 -0
- package/src/schema/table.gen.ts +14 -0
- package/src/schema-validate.ts +58 -7
- package/src/skill-defs.ts +17 -0
- package/src/skills/chant-grafana-operations.md +108 -0
- package/src/skills/chant-grafana.md +5 -4
- package/src/spec/overlay/table.overlay.json +19 -0
- package/src/spec/schemas/azuremonitor.jsonschema.json +679 -0
- package/src/spec/schemas/bigquery.jsonschema.json +290 -0
- package/src/spec/schemas/cloudwatch.jsonschema.json +721 -0
- package/src/spec/schemas/elasticsearch.jsonschema.json +1695 -0
- package/src/spec/schemas/googlecloudmonitoring.jsonschema.json +488 -0
- package/src/spec/schemas/grafanapyroscope.jsonschema.json +93 -0
- package/src/spec/schemas.gen.ts +40 -0
- package/src/spec/schemas.ts +9 -7
- package/src/validate-alerting.ts +4 -2
- package/src/validate-output.ts +96 -4
- package/src/validate.ts +14 -0
- package/src/validation.ts +27 -0
- package/src/variables.ts +113 -6
package/src/index.ts
CHANGED
|
@@ -14,6 +14,12 @@ export {
|
|
|
14
14
|
isDatasourceEntity,
|
|
15
15
|
isExternalDatasource,
|
|
16
16
|
isDatasourceDeclaration,
|
|
17
|
+
DatasourceProvisioning,
|
|
18
|
+
DATASOURCE_PROVISIONING_TYPE,
|
|
19
|
+
isDatasourceProvisioningEntity,
|
|
20
|
+
type DatasourceProvisioningProps,
|
|
21
|
+
type DatasourceProvisioningEntity,
|
|
22
|
+
type DeletedDatasource,
|
|
17
23
|
type ExternalDatasourceProps,
|
|
18
24
|
type ExternalDatasourceEntity,
|
|
19
25
|
type ExternalDatasourceConstructor,
|
|
@@ -25,6 +31,64 @@ export {
|
|
|
25
31
|
type KnownDatasourceType,
|
|
26
32
|
} from "./datasource";
|
|
27
33
|
|
|
34
|
+
// Typed datasource settings (jsonData, secureJsonData keys) per plugin
|
|
35
|
+
export {
|
|
36
|
+
JSONDATA_LINKS,
|
|
37
|
+
TRACING_DATASOURCE_TYPES,
|
|
38
|
+
TRACE_TO_LOGS_DATASOURCE_TYPES,
|
|
39
|
+
TRACE_TO_METRICS_DATASOURCE_TYPES,
|
|
40
|
+
type JsonDataLink,
|
|
41
|
+
type LinkedDatasource,
|
|
42
|
+
type TracingDatasourceType,
|
|
43
|
+
type TraceToLogsDatasourceType,
|
|
44
|
+
type TraceToMetricsDatasourceType,
|
|
45
|
+
type TypedDatasourceType,
|
|
46
|
+
type DatasourceJsonData,
|
|
47
|
+
type DatasourceSecureJsonData,
|
|
48
|
+
type DatasourceJsonDataTypes,
|
|
49
|
+
type DatasourceSecureJsonKeys,
|
|
50
|
+
type CommonJsonData,
|
|
51
|
+
type HttpJsonData,
|
|
52
|
+
type HttpSecureJsonKey,
|
|
53
|
+
type PrometheusJsonData,
|
|
54
|
+
type ExemplarTraceIdDestination,
|
|
55
|
+
type LokiJsonData,
|
|
56
|
+
type DerivedFieldConfig,
|
|
57
|
+
type TempoJsonData,
|
|
58
|
+
type TraceToLogsTag,
|
|
59
|
+
type TraceToLogsOptions,
|
|
60
|
+
type TraceToLogsOptionsV2,
|
|
61
|
+
type TraceToMetricsOptions,
|
|
62
|
+
type TraceToProfilesOptions,
|
|
63
|
+
type ElasticsearchJsonData,
|
|
64
|
+
type ElasticsearchDataLink,
|
|
65
|
+
type ElasticsearchSecureJsonKey,
|
|
66
|
+
type AwsAuthJsonData,
|
|
67
|
+
type AwsAuthSecureJsonKey,
|
|
68
|
+
type CloudWatchJsonData,
|
|
69
|
+
type CloudWatchLogGroup,
|
|
70
|
+
type CloudWatchSecureJsonKey,
|
|
71
|
+
type AzureCloudName,
|
|
72
|
+
type AzureCredentials,
|
|
73
|
+
type AzureMonitorJsonData,
|
|
74
|
+
type AzureMonitorSecureJsonKey,
|
|
75
|
+
type GoogleAuthJsonData,
|
|
76
|
+
type GoogleAuthSecureJsonKey,
|
|
77
|
+
type CloudMonitoringJsonData,
|
|
78
|
+
type CloudMonitoringSecureJsonKey,
|
|
79
|
+
type BigQueryJsonData,
|
|
80
|
+
type BigQuerySecureJsonKey,
|
|
81
|
+
type PyroscopeJsonData,
|
|
82
|
+
type PyroscopeSecureJsonKey,
|
|
83
|
+
type SqlJsonData,
|
|
84
|
+
type PostgresJsonData,
|
|
85
|
+
type PostgresSecureJsonKey,
|
|
86
|
+
type MySQLJsonData,
|
|
87
|
+
type MySQLSecureJsonKey,
|
|
88
|
+
type MSSQLJsonData,
|
|
89
|
+
type MSSQLSecureJsonKey,
|
|
90
|
+
} from "./datasource-settings";
|
|
91
|
+
|
|
28
92
|
// Dashboards and provisioning
|
|
29
93
|
export {
|
|
30
94
|
Dashboard,
|
|
@@ -39,8 +103,14 @@ export {
|
|
|
39
103
|
type DashboardLinkInput,
|
|
40
104
|
type DashboardProviderProps,
|
|
41
105
|
type DashboardProviderEntity,
|
|
106
|
+
type DashboardEntityProps,
|
|
107
|
+
type DashboardProviderEntityProps,
|
|
42
108
|
} from "./dashboard";
|
|
43
109
|
|
|
110
|
+
// Folders and annotations
|
|
111
|
+
export { Folder, FOLDER_TYPE, isFolderEntity, folderLevels, type FolderProps, type FolderEntity } from "./folder";
|
|
112
|
+
export { annotationJson, ANNOTATION_REF_ID, type AnnotationInput } from "./annotations";
|
|
113
|
+
|
|
44
114
|
// Grafana-managed alerting
|
|
45
115
|
export {
|
|
46
116
|
AlertRuleGroup,
|
|
@@ -179,6 +249,15 @@ export {
|
|
|
179
249
|
PromQuery,
|
|
180
250
|
TempoQuery,
|
|
181
251
|
LokiQuery,
|
|
252
|
+
ElasticsearchQuery,
|
|
253
|
+
CloudWatchQuery,
|
|
254
|
+
AzureMonitorQuery,
|
|
255
|
+
CloudMonitoringQuery,
|
|
256
|
+
BigQueryQuery,
|
|
257
|
+
PyroscopeQuery,
|
|
258
|
+
PostgresQuery,
|
|
259
|
+
MySQLQuery,
|
|
260
|
+
MSSQLQuery,
|
|
182
261
|
defineQuery,
|
|
183
262
|
registeredQueries,
|
|
184
263
|
queryDefinitionFor,
|
|
@@ -186,6 +265,8 @@ export {
|
|
|
186
265
|
QUERY_TYPE_PREFIX,
|
|
187
266
|
type DatasourceInput,
|
|
188
267
|
type QueryModel,
|
|
268
|
+
type Loosen,
|
|
269
|
+
type MergedModel,
|
|
189
270
|
type QueryProps,
|
|
190
271
|
type QueryDefinition,
|
|
191
272
|
type QueryEntity,
|
|
@@ -193,8 +274,30 @@ export {
|
|
|
193
274
|
type PromQueryEntity,
|
|
194
275
|
type TempoQueryEntity,
|
|
195
276
|
type LokiQueryEntity,
|
|
277
|
+
type ElasticsearchQueryEntity,
|
|
278
|
+
type CloudWatchQueryEntity,
|
|
279
|
+
type AzureMonitorQueryEntity,
|
|
280
|
+
type CloudMonitoringQueryEntity,
|
|
281
|
+
type BigQueryQueryEntity,
|
|
282
|
+
type PyroscopeQueryEntity,
|
|
283
|
+
type PostgresQueryEntity,
|
|
284
|
+
type MySQLQueryEntity,
|
|
285
|
+
type MSSQLQueryEntity,
|
|
196
286
|
} from "./query";
|
|
197
287
|
|
|
288
|
+
// Hand-written query models for datasources with no schema
|
|
289
|
+
export type {
|
|
290
|
+
SqlQueryModel,
|
|
291
|
+
SqlBuilderQuery,
|
|
292
|
+
SqlQueryFormat,
|
|
293
|
+
SqlExpressionType,
|
|
294
|
+
SqlProperty,
|
|
295
|
+
SqlFunctionExpression,
|
|
296
|
+
SqlFunctionParameterExpression,
|
|
297
|
+
SqlGroupByExpression,
|
|
298
|
+
SqlPropertyExpression,
|
|
299
|
+
} from "./query-models";
|
|
300
|
+
|
|
198
301
|
// Variables
|
|
199
302
|
export {
|
|
200
303
|
QueryVariable,
|
|
@@ -219,6 +322,17 @@ export {
|
|
|
219
322
|
type DatasourceVariableProps,
|
|
220
323
|
type ConstantVariableProps,
|
|
221
324
|
type TextboxVariableProps,
|
|
325
|
+
AdhocVariable,
|
|
326
|
+
GroupByVariable,
|
|
327
|
+
SwitchVariable,
|
|
328
|
+
MULTI_VALUE_KINDS,
|
|
329
|
+
type AdhocVariableProps,
|
|
330
|
+
type GroupByVariableProps,
|
|
331
|
+
type SwitchVariableProps,
|
|
332
|
+
type AdhocFilter,
|
|
333
|
+
type VariableKeyOption,
|
|
334
|
+
type VariableQueryObject,
|
|
335
|
+
type PrometheusVariableQuery,
|
|
222
336
|
} from "./variables";
|
|
223
337
|
|
|
224
338
|
// The generated schema types and their pin
|
|
@@ -251,7 +365,7 @@ export {
|
|
|
251
365
|
type ReducerId,
|
|
252
366
|
} from "./transformations";
|
|
253
367
|
|
|
254
|
-
// Plain-data API: render
|
|
368
|
+
// Plain-data API: render and detect without a build
|
|
255
369
|
export {
|
|
256
370
|
buildGrafana,
|
|
257
371
|
grafanaFiles,
|
|
@@ -267,6 +381,7 @@ export {
|
|
|
267
381
|
externalDatasourceRecord,
|
|
268
382
|
provisionedProvider,
|
|
269
383
|
datasourcesYaml,
|
|
384
|
+
datasourceFileSettings,
|
|
270
385
|
dashboardProvidersYaml,
|
|
271
386
|
GRID_COLUMNS,
|
|
272
387
|
DATASOURCES_FILE,
|
|
@@ -278,24 +393,19 @@ export {
|
|
|
278
393
|
type ProvisionedDatasource,
|
|
279
394
|
type ExternalDatasourceRecord,
|
|
280
395
|
type ProvisionedProvider,
|
|
396
|
+
type DatasourceFileSettings,
|
|
281
397
|
type DashboardJson,
|
|
282
398
|
type PanelJson,
|
|
283
399
|
type RowPanelJson,
|
|
284
400
|
type VariableModel,
|
|
285
401
|
type DataSourceRef,
|
|
286
402
|
} from "./build";
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
knownDatasourcesOf,
|
|
291
|
-
variableReferences,
|
|
292
|
-
type GrafanaIssue,
|
|
293
|
-
type GrafanaIssueCode,
|
|
294
|
-
type GrafanaArtifacts,
|
|
295
|
-
type DashboardDoc,
|
|
296
|
-
} from "./validate-output";
|
|
403
|
+
// The checks as plain functions (validateGrafanaOutput, validateDashboardSchema)
|
|
404
|
+
// are on the `@intentius/chant-lexicon-grafana/validation` subpath (#2958), so
|
|
405
|
+
// declaring dashboards does not load them.
|
|
297
406
|
export {
|
|
298
407
|
datasourceUses,
|
|
408
|
+
variableReferences,
|
|
299
409
|
resolveDatasourceRef,
|
|
300
410
|
knownDatasources,
|
|
301
411
|
type DatasourceUse,
|
|
@@ -303,7 +413,6 @@ export {
|
|
|
303
413
|
type KnownDatasource,
|
|
304
414
|
type DatasourceRefJson,
|
|
305
415
|
} from "./datasource-refs";
|
|
306
|
-
export { validateDashboardSchema, validateExpressionSchema, type SchemaProblem } from "./schema-validate";
|
|
307
416
|
export { looksLikeDashboard, looksLikeDatasourceProvisioning, looksLikeDashboardProvisioning, looksLikeAlertingProvisioning } from "./detect";
|
|
308
417
|
export { slugUid, isValidUid, UID_PATTERN, type DeepPartial, type PropsOf } from "./util";
|
|
309
418
|
|
|
@@ -357,7 +466,7 @@ export { bindGrafana, classifyGrafanaFailure, GrafanaBindingError } from "./api/
|
|
|
357
466
|
export { grafanaApply } from "./op/builders";
|
|
358
467
|
export { applyGrafana, planFromDashboards, GRAFANA_APPLY_KINDS } from "./api/apply";
|
|
359
468
|
export type { GrafanaApplyPlan, GrafanaApplyOutcome, DashboardPlan, BuiltDashboardInput } from "./api/apply";
|
|
360
|
-
export { folderUidFor, foldersForDashboards, type FolderPlan } from "./api/folders";
|
|
469
|
+
export { folderUidFor, foldersForDashboards, resolveFolders, liveFolderPath, type FolderPlan, type ResolvedFolders } from "./api/folders";
|
|
361
470
|
|
|
362
471
|
// The Grafana RBAC actions a service account needs to observe, apply and prune.
|
|
363
472
|
export { GrafanaActions, grafanaActionsFor, type GrafanaAccessLevel } from "./actions/index";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { build } from "@intentius/chant/build";
|
|
5
|
+
import type { Serializer } from "@intentius/chant/serializer";
|
|
6
|
+
import { lintCommand } from "@intentius/chant/cli/commands/lint";
|
|
7
|
+
import { runPostSynthChecks } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { k8sPlugin } from "@intentius/chant-lexicon-k8s";
|
|
9
|
+
import { prometheusPlugin } from "@intentius/chant-lexicon-prometheus";
|
|
10
|
+
import { grafanaPlugin } from "./plugin";
|
|
11
|
+
import { postSynthChecks } from "./lint/post-synth";
|
|
12
|
+
|
|
13
|
+
// Every template builds with the lexicons its chant.config.ts names, and its
|
|
14
|
+
// output passes every GRAF check, and the other lexicon's checks, with
|
|
15
|
+
// nothing to report.
|
|
16
|
+
const cases: Array<[string | undefined, Serializer[], string[]]> = [
|
|
17
|
+
[undefined, [grafanaPlugin.serializer], ["grafana"]],
|
|
18
|
+
["red", [grafanaPlugin.serializer], ["grafana"]],
|
|
19
|
+
["k8s-pods", [k8sPlugin.serializer, grafanaPlugin.serializer], ["k8s", "grafana"]],
|
|
20
|
+
["slo", [prometheusPlugin.serializer, grafanaPlugin.serializer], ["prometheus", "grafana"]],
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
describe("init templates", () => {
|
|
24
|
+
test.each(cases)("%s builds, lints clean and passes every check", async (name, serializers, outputs) => {
|
|
25
|
+
const set = grafanaPlugin.initTemplates!(name);
|
|
26
|
+
const dir = mkdtempSync(join(import.meta.dirname, "..", ".init-template-"));
|
|
27
|
+
try {
|
|
28
|
+
mkdirSync(join(dir, "src"));
|
|
29
|
+
for (const [file, text] of Object.entries(set.src)) writeFileSync(join(dir, "src", file), text);
|
|
30
|
+
for (const [file, text] of Object.entries(set.root ?? {})) writeFileSync(join(dir, file), text);
|
|
31
|
+
const result = await build(join(dir, "src"), serializers);
|
|
32
|
+
expect(result.errors).toEqual([]);
|
|
33
|
+
for (const lexicon of outputs) expect(result.outputs.get(lexicon), lexicon).toBeTruthy();
|
|
34
|
+
|
|
35
|
+
expect(runPostSynthChecks(postSynthChecks, result)).toEqual([]);
|
|
36
|
+
const other = name === "k8s-pods" ? k8sPlugin : name === "slo" ? prometheusPlugin : undefined;
|
|
37
|
+
if (other) {
|
|
38
|
+
expect(runPostSynthChecks(other.postSynthChecks!(), result)).toEqual([]);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const lint = await lintCommand({ path: join(dir, "src"), format: "stylish", fix: false });
|
|
42
|
+
expect(lint.errorCount + lint.warningCount, lint.output).toBe(0);
|
|
43
|
+
} finally {
|
|
44
|
+
rmSync(dir, { recursive: true, force: true });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("the RED template puts its dashboard in the pinned folder and counts served spans only", async () => {
|
|
49
|
+
const dir = mkdtempSync(join(import.meta.dirname, "..", ".init-template-"));
|
|
50
|
+
try {
|
|
51
|
+
mkdirSync(join(dir, "src"));
|
|
52
|
+
for (const [file, text] of Object.entries(grafanaPlugin.initTemplates!("red").src)) writeFileSync(join(dir, "src", file), text);
|
|
53
|
+
const result = await build(join(dir, "src"), [grafanaPlugin.serializer]);
|
|
54
|
+
const out = result.outputs.get("grafana") as { primary: string; files: Record<string, string> };
|
|
55
|
+
const index = JSON.parse(out.primary) as { dashboards: Array<{ uid: string; folderUid?: string }>; datasources: unknown[] };
|
|
56
|
+
expect(index.dashboards).toEqual([expect.objectContaining({ uid: "services-red", folderUid: "observability" })]);
|
|
57
|
+
// An ExternalDatasource is checked against, never provisioned.
|
|
58
|
+
expect(index.datasources).toEqual([]);
|
|
59
|
+
const dashboard = Object.entries(out.files).find(([path]) => path.endsWith("services-red.json"))![1];
|
|
60
|
+
expect(dashboard).toContain("SPAN_KIND_SERVER|SPAN_KIND_CONSUMER");
|
|
61
|
+
} finally {
|
|
62
|
+
rmSync(dir, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("the k8s-pods and slo templates build every lexicon their config names", () => {
|
|
67
|
+
for (const name of ["k8s-pods", "slo"]) {
|
|
68
|
+
const set = grafanaPlugin.initTemplates!(name);
|
|
69
|
+
expect(set.root?.["chant.config.ts"]).toContain('"grafana"');
|
|
70
|
+
expect(set.scripts?.build).toBe("chant build src");
|
|
71
|
+
}
|
|
72
|
+
expect(grafanaPlugin.initTemplates!("no-such-template")).toBe(grafanaPlugin.initTemplates!());
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant init --lexicon grafana` scaffolds.
|
|
3
|
+
*
|
|
4
|
+
* - default: a Prometheus datasource and a job overview dashboard with a
|
|
5
|
+
* typed variable, in a folder.
|
|
6
|
+
* - `red`: rate, errors and duration per service from the OpenTelemetry
|
|
7
|
+
* collector's span metrics, over a Prometheus Grafana already has
|
|
8
|
+
* (`ExternalDatasource`), in a pinned `Folder`.
|
|
9
|
+
* - `k8s-pods`: CPU, memory, restarts and pod phase per namespace, delivered
|
|
10
|
+
* as ConfigMaps the Grafana Helm chart's sidecar loads, from the `/k8s`
|
|
11
|
+
* subpath. Builds with the k8s lexicon.
|
|
12
|
+
* - `slo`: an SLO's recording rules, its dashboard and its burn-rate alerts
|
|
13
|
+
* as Grafana-managed rules. Builds with the prometheus lexicon.
|
|
14
|
+
*
|
|
15
|
+
* The otel, prometheus and k8s lexicons these import are dependencies of
|
|
16
|
+
* the grafana lexicon, so they are installed with it. The templates that
|
|
17
|
+
* build with a second lexicon ship their own `chant.config.ts` and a build
|
|
18
|
+
* script without `--lexicon`, so every lexicon's output and checks run.
|
|
19
|
+
*/
|
|
20
|
+
import type { InitTemplateSet } from "@intentius/chant/lexicon";
|
|
21
|
+
|
|
22
|
+
// ── default ────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
const DEFAULT_DATASOURCES = `/**
|
|
25
|
+
* The datasource the dashboards query, declared in the same build root so
|
|
26
|
+
* GRAF101 and GRAF102 check every panel's reference against it.
|
|
27
|
+
*/
|
|
28
|
+
import { Datasource } from "@intentius/chant-lexicon-grafana";
|
|
29
|
+
|
|
30
|
+
const prometheus = new Datasource({ name: "Prometheus", type: "prometheus", url: "http://prometheus:9090", isDefault: true });
|
|
31
|
+
|
|
32
|
+
export { prometheus };
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const DEFAULT_DASHBOARD = `/**
|
|
36
|
+
* One dashboard: which of a job's targets are up, and how long their
|
|
37
|
+
* scrapes take. Panels without a gridPos are laid out left to right.
|
|
38
|
+
*/
|
|
39
|
+
import { Dashboard, PromQuery, QueryVariable, StatPanel, TimeSeriesPanel } from "@intentius/chant-lexicon-grafana";
|
|
40
|
+
import { prometheus } from "./datasources";
|
|
41
|
+
|
|
42
|
+
const jobQuery = { query: "label_values(up, job)", qryType: 1 as const, label: "job", metric: "up" };
|
|
43
|
+
const job = new QueryVariable({ name: "job", label: "Job", datasource: prometheus, query: jobQuery, refresh: "onTimeRangeChange", sort: 1 });
|
|
44
|
+
|
|
45
|
+
const up = new PromQuery({ expr: 'sum(up{job="$job"})', instant: true });
|
|
46
|
+
const targetsUp = new StatPanel({ title: "Targets up", datasource: prometheus, targets: [up] });
|
|
47
|
+
|
|
48
|
+
const scrape = new PromQuery({ expr: 'avg by (instance) (scrape_duration_seconds{job="$job"})', legendFormat: "{{instance}}" });
|
|
49
|
+
const seconds = { defaults: { unit: "s" } };
|
|
50
|
+
const scrapeDuration = new TimeSeriesPanel({ title: "Scrape duration", datasource: prometheus, targets: [scrape], fieldConfig: seconds });
|
|
51
|
+
|
|
52
|
+
const lastHour = { from: "now-1h", to: "now" };
|
|
53
|
+
|
|
54
|
+
const overview = new Dashboard({
|
|
55
|
+
title: "Overview",
|
|
56
|
+
uid: "overview",
|
|
57
|
+
tags: ["chant"],
|
|
58
|
+
time: lastHour,
|
|
59
|
+
refresh: "30s",
|
|
60
|
+
variables: [job],
|
|
61
|
+
panels: [targetsUp, scrapeDuration],
|
|
62
|
+
folder: "Services",
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export { overview };
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
// ── red ────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
const RED_DATASOURCES = `/**
|
|
71
|
+
* The Prometheus the dashboard reads, one Grafana already has. It is checked
|
|
72
|
+
* against (GRAF101, GRAF102) and never provisioned: set \`uid\` to its uid,
|
|
73
|
+
* from Connections > Data sources in Grafana.
|
|
74
|
+
*/
|
|
75
|
+
import { ExternalDatasource } from "@intentius/chant-lexicon-grafana";
|
|
76
|
+
|
|
77
|
+
const prometheus = new ExternalDatasource({ type: "prometheus", uid: "prometheus", name: "Prometheus" });
|
|
78
|
+
|
|
79
|
+
export { prometheus };
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
const RED_DASHBOARD = `/**
|
|
83
|
+
* Rate, errors and duration per service, from the span metrics the
|
|
84
|
+
* OpenTelemetry collector's \`spanmetrics\` connector writes. The metric and
|
|
85
|
+
* label names come from the connector's settings, so change them here to
|
|
86
|
+
* match your collector (or pass the connector itself, if the collector is
|
|
87
|
+
* declared with the otel lexicon) and every query follows.
|
|
88
|
+
*/
|
|
89
|
+
import { Folder, RedDashboard, type SpanKind } from "@intentius/chant-lexicon-grafana";
|
|
90
|
+
import { spanMetricsNames } from "@intentius/chant-lexicon-otel";
|
|
91
|
+
import { prometheus } from "./datasources";
|
|
92
|
+
|
|
93
|
+
const milliseconds = { unit: "ms" };
|
|
94
|
+
const connector = { namespace: "traces.span.metrics", histogram: milliseconds };
|
|
95
|
+
const names = spanMetricsNames(connector);
|
|
96
|
+
|
|
97
|
+
// The spans that serve a request or consume a message, so a service's own
|
|
98
|
+
// outgoing calls don't count toward its rate. [] counts every kind.
|
|
99
|
+
const served: SpanKind[] = ["SPAN_KIND_SERVER", "SPAN_KIND_CONSUMER"];
|
|
100
|
+
|
|
101
|
+
// A Folder pins the uid, for links and alert rules that name it.
|
|
102
|
+
const observability = new Folder({ title: "Observability", uid: "observability" });
|
|
103
|
+
|
|
104
|
+
const services = RedDashboard({ spanMetrics: names, datasource: prometheus, spanKinds: served, folder: observability, uid: "services-red" });
|
|
105
|
+
|
|
106
|
+
export { observability, services };
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
// ── k8s-pods ───────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
const K8S_CONFIG = `import type { ChantConfig } from "@intentius/chant";
|
|
112
|
+
|
|
113
|
+
// The dashboard and the ConfigMaps that deliver it share one build root: the
|
|
114
|
+
// grafana output is checked by the GRAF checks, the ConfigMaps are applied
|
|
115
|
+
// with kubectl.
|
|
116
|
+
export default { lexicons: ["k8s", "grafana"] } satisfies ChantConfig;
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
const K8S_DATASOURCES = `/**
|
|
120
|
+
* The Prometheus that scrapes cAdvisor and kube-state-metrics, as Grafana
|
|
121
|
+
* reaches it inside the cluster.
|
|
122
|
+
*/
|
|
123
|
+
import { Datasource } from "@intentius/chant-lexicon-grafana";
|
|
124
|
+
|
|
125
|
+
const prometheus = new Datasource({
|
|
126
|
+
name: "Prometheus",
|
|
127
|
+
type: "prometheus",
|
|
128
|
+
uid: "prometheus",
|
|
129
|
+
url: "http://prometheus-server.monitoring.svc:80",
|
|
130
|
+
isDefault: true,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
export { prometheus };
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
const K8S_VARIABLES = `/**
|
|
137
|
+
* Which namespace, and which of its pods. Queries use them as $namespace
|
|
138
|
+
* and $pod; \`pod\` takes several values, or all of them.
|
|
139
|
+
*/
|
|
140
|
+
import { QueryVariable } from "@intentius/chant-lexicon-grafana";
|
|
141
|
+
import { prometheus } from "./datasources";
|
|
142
|
+
|
|
143
|
+
const namespace = new QueryVariable({
|
|
144
|
+
name: "namespace",
|
|
145
|
+
label: "Namespace",
|
|
146
|
+
datasource: prometheus,
|
|
147
|
+
query: "label_values(kube_pod_info, namespace)",
|
|
148
|
+
refresh: "onTimeRangeChange",
|
|
149
|
+
sort: 1,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const pod = new QueryVariable({
|
|
153
|
+
name: "pod",
|
|
154
|
+
label: "Pod",
|
|
155
|
+
datasource: prometheus,
|
|
156
|
+
query: 'label_values(kube_pod_info{namespace="$namespace"}, pod)',
|
|
157
|
+
refresh: "onTimeRangeChange",
|
|
158
|
+
sort: 1,
|
|
159
|
+
multi: true,
|
|
160
|
+
includeAll: true,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
export { namespace, pod };
|
|
164
|
+
`;
|
|
165
|
+
|
|
166
|
+
const K8S_QUERIES = `/**
|
|
167
|
+
* PromQL over cAdvisor (container_*) and kube-state-metrics (kube_*).
|
|
168
|
+
*/
|
|
169
|
+
import { PromQuery } from "@intentius/chant-lexicon-grafana";
|
|
170
|
+
|
|
171
|
+
const scope = 'namespace="$namespace", pod=~"$pod"';
|
|
172
|
+
|
|
173
|
+
const cpu = new PromQuery({
|
|
174
|
+
expr: \`sum by (pod) (rate(container_cpu_usage_seconds_total{\${scope}, container!=""}[$__rate_interval]))\`,
|
|
175
|
+
legendFormat: "{{pod}}",
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const memory = new PromQuery({
|
|
179
|
+
expr: \`sum by (pod) (container_memory_working_set_bytes{\${scope}, container!=""})\`,
|
|
180
|
+
legendFormat: "{{pod}}",
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const restarts = new PromQuery({
|
|
184
|
+
expr: \`sum by (pod) (increase(kube_pod_container_status_restarts_total{\${scope}}[1h]))\`,
|
|
185
|
+
legendFormat: "{{pod}}",
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const phase = new PromQuery({
|
|
189
|
+
expr: \`sum by (pod, phase) (kube_pod_status_phase{\${scope}}) > 0\`,
|
|
190
|
+
instant: true,
|
|
191
|
+
format: "table",
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
export { cpu, memory, restarts, phase };
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
const K8S_DASHBOARD = `/**
|
|
198
|
+
* Pods in a namespace: what they use, how often they restart, and which
|
|
199
|
+
* phase each is in.
|
|
200
|
+
*/
|
|
201
|
+
import { Dashboard, TablePanel, TimeSeriesPanel, transformation } from "@intentius/chant-lexicon-grafana";
|
|
202
|
+
import { prometheus } from "./datasources";
|
|
203
|
+
import { namespace, pod } from "./variables";
|
|
204
|
+
import { cpu, memory, restarts, phase } from "./queries";
|
|
205
|
+
|
|
206
|
+
const cores = { defaults: { unit: "suffix: cores" } };
|
|
207
|
+
const cpuPanel = new TimeSeriesPanel({ title: "CPU", datasource: prometheus, targets: [cpu], fieldConfig: cores });
|
|
208
|
+
|
|
209
|
+
const bytes = { defaults: { unit: "bytes" } };
|
|
210
|
+
const memoryPanel = new TimeSeriesPanel({ title: "Memory working set", datasource: prometheus, targets: [memory], fieldConfig: bytes });
|
|
211
|
+
|
|
212
|
+
const count = { defaults: { unit: "short", decimals: 0 } };
|
|
213
|
+
const restartPanel = new TimeSeriesPanel({ title: "Restarts in the last hour", datasource: prometheus, targets: [restarts], fieldConfig: count });
|
|
214
|
+
|
|
215
|
+
// The query returns one row per pod with its phase; drop the time and value columns.
|
|
216
|
+
const hidden = { Time: true, Value: true };
|
|
217
|
+
const renamed = { pod: "Pod", phase: "Phase" };
|
|
218
|
+
const phaseColumns = [transformation("organize", { excludeByName: hidden, renameByName: renamed })];
|
|
219
|
+
const phasePanel = new TablePanel({ title: "Pod phase", datasource: prometheus, targets: [phase], transformations: phaseColumns });
|
|
220
|
+
|
|
221
|
+
const pods = new Dashboard({
|
|
222
|
+
title: "Kubernetes pods",
|
|
223
|
+
uid: "k8s-pods",
|
|
224
|
+
tags: ["chant", "kubernetes"],
|
|
225
|
+
refresh: "30s",
|
|
226
|
+
variables: [namespace, pod],
|
|
227
|
+
panels: [cpuPanel, memoryPanel, restartPanel, phasePanel],
|
|
228
|
+
folder: "Kubernetes",
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
export { pods };
|
|
232
|
+
`;
|
|
233
|
+
|
|
234
|
+
const K8S_DELIVERY = `/**
|
|
235
|
+
* The dashboard and the datasource as ConfigMaps, labelled the way the
|
|
236
|
+
* Grafana Helm chart's sidecar (also in kube-prometheus-stack) finds them:
|
|
237
|
+
* \`grafana_dashboard: "1"\` and \`grafana_datasource: "1"\`, the folder in the
|
|
238
|
+
* \`k8s-sidecar-target-directory\` annotation. \`kubectl apply -f\` the k8s
|
|
239
|
+
* output into the namespace Grafana watches.
|
|
240
|
+
*/
|
|
241
|
+
import { GrafanaConfigMaps } from "@intentius/chant-lexicon-grafana/k8s";
|
|
242
|
+
import { prometheus } from "./datasources";
|
|
243
|
+
import { pods } from "./dashboard";
|
|
244
|
+
|
|
245
|
+
/** The namespace Grafana's sidecar watches for ConfigMaps. */
|
|
246
|
+
export const GRAFANA_NAMESPACE = "monitoring";
|
|
247
|
+
|
|
248
|
+
const delivered = [prometheus, pods];
|
|
249
|
+
const labels = { "app.kubernetes.io/part-of": "grafana" };
|
|
250
|
+
|
|
251
|
+
const grafanaConfigMaps = GrafanaConfigMaps({ entities: delivered, namespace: GRAFANA_NAMESPACE, labels });
|
|
252
|
+
|
|
253
|
+
export { grafanaConfigMaps };
|
|
254
|
+
`;
|
|
255
|
+
|
|
256
|
+
// ── slo ────────────────────────────────────────────────────────────────
|
|
257
|
+
|
|
258
|
+
const SLO_CONFIG = `import type { ChantConfig } from "@intentius/chant";
|
|
259
|
+
|
|
260
|
+
// The SLO's Prometheus rules, and the Grafana dashboard and alert rules that
|
|
261
|
+
// read what they record, share one build root: GRAF108 and GRAF112 check the
|
|
262
|
+
// queries against the datasource declared here.
|
|
263
|
+
export default { lexicons: ["prometheus", "grafana"] } satisfies ChantConfig;
|
|
264
|
+
`;
|
|
265
|
+
|
|
266
|
+
const SLO_DATASOURCES = `/**
|
|
267
|
+
* The Prometheus that evaluates the SLO's recording rules.
|
|
268
|
+
*/
|
|
269
|
+
import { Datasource } from "@intentius/chant-lexicon-grafana";
|
|
270
|
+
|
|
271
|
+
const prometheus = new Datasource({ name: "Prometheus", type: "prometheus", uid: "prometheus", url: "http://prometheus:9090", isDefault: true });
|
|
272
|
+
|
|
273
|
+
export { prometheus };
|
|
274
|
+
`;
|
|
275
|
+
|
|
276
|
+
const SLO_DECLARATION = `/**
|
|
277
|
+
* The SLO: 99.9% of checkout requests answer without a 5xx over 30 days.
|
|
278
|
+
* The prometheus lexicon builds its recording rules; the dashboard and the
|
|
279
|
+
* alert rules read the series they record, by the same names.
|
|
280
|
+
*/
|
|
281
|
+
import { Slo } from "@intentius/chant-lexicon-prometheus";
|
|
282
|
+
|
|
283
|
+
const sli = {
|
|
284
|
+
errors: 'sum(rate(http_requests_total{job="checkout",code=~"5.."}[{{window}}]))',
|
|
285
|
+
total: 'sum(rate(http_requests_total{job="checkout"}[{{window}}]))',
|
|
286
|
+
};
|
|
287
|
+
const team = { team: "payments" };
|
|
288
|
+
|
|
289
|
+
const checkout = Slo({
|
|
290
|
+
name: "checkout",
|
|
291
|
+
objective: 0.999,
|
|
292
|
+
window: "30d",
|
|
293
|
+
description: "Checkout requests answer without a 5xx.",
|
|
294
|
+
sli,
|
|
295
|
+
labels: team,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
export { checkout };
|
|
299
|
+
`;
|
|
300
|
+
|
|
301
|
+
const SLO_DASHBOARD = `/**
|
|
302
|
+
* The SLO's dashboard (SLI against the objective, error budget left, each
|
|
303
|
+
* burn-rate pair against the rate it fires at), and the same burn rates as
|
|
304
|
+
* Grafana-managed alert rules in the same folder.
|
|
305
|
+
*/
|
|
306
|
+
import { SloAlertRules, SloDashboard } from "@intentius/chant-lexicon-grafana";
|
|
307
|
+
import { prometheus } from "./datasources";
|
|
308
|
+
import { checkout } from "./slo";
|
|
309
|
+
|
|
310
|
+
const checkoutSlo = SloDashboard({ slo: checkout, datasource: prometheus, folder: "SLOs" });
|
|
311
|
+
|
|
312
|
+
const team = { team: "payments" };
|
|
313
|
+
const runbook = { runbook_url: "https://runbooks.example.com/checkout-slo" };
|
|
314
|
+
const checkoutBurn = SloAlertRules({ slo: checkout, datasource: prometheus, folder: "SLOs", labels: team, annotations: runbook });
|
|
315
|
+
|
|
316
|
+
export { checkoutSlo, checkoutBurn };
|
|
317
|
+
`;
|
|
318
|
+
|
|
319
|
+
// Build every lexicon the config names, not just grafana.
|
|
320
|
+
const ALL_LEXICONS = { build: "chant build src", dev: "chant build src --watch" };
|
|
321
|
+
|
|
322
|
+
export const DEFAULT_TEMPLATE: InitTemplateSet = {
|
|
323
|
+
src: { "datasources.ts": DEFAULT_DATASOURCES, "dashboard.ts": DEFAULT_DASHBOARD },
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
export const RED_TEMPLATE: InitTemplateSet = {
|
|
327
|
+
src: { "datasources.ts": RED_DATASOURCES, "red.ts": RED_DASHBOARD },
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
export const K8S_PODS_TEMPLATE: InitTemplateSet = {
|
|
331
|
+
src: {
|
|
332
|
+
"datasources.ts": K8S_DATASOURCES,
|
|
333
|
+
"variables.ts": K8S_VARIABLES,
|
|
334
|
+
"queries.ts": K8S_QUERIES,
|
|
335
|
+
"dashboard.ts": K8S_DASHBOARD,
|
|
336
|
+
"k8s.ts": K8S_DELIVERY,
|
|
337
|
+
},
|
|
338
|
+
root: { "chant.config.ts": K8S_CONFIG },
|
|
339
|
+
scripts: ALL_LEXICONS,
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export const SLO_TEMPLATE: InitTemplateSet = {
|
|
343
|
+
src: { "datasources.ts": SLO_DATASOURCES, "slo.ts": SLO_DECLARATION, "dashboards.ts": SLO_DASHBOARD },
|
|
344
|
+
root: { "chant.config.ts": SLO_CONFIG },
|
|
345
|
+
scripts: ALL_LEXICONS,
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
/** The template names `chant init --lexicon grafana --template <name>` takes, besides the default. */
|
|
349
|
+
export const TEMPLATE_NAMES = ["red", "k8s-pods", "slo"] as const;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The grafana lexicon's chant audit catalog, contributed via
|
|
3
3
|
* `grafanaPlugin.auditCatalog()` (#687, #1346).
|
|
4
4
|
*
|
|
5
|
-
* GRAF101-
|
|
5
|
+
* GRAF101-GRAF110, GRAF111-GRAF114 and GRAF115 read the emitted dashboard
|
|
6
6
|
* JSON and provisioning files (alerting included), so
|
|
7
7
|
* they fire on an audit of files chant didn't build too, and are
|
|
8
8
|
* `yamlBased`. GRAF001 and GRAF002 read TypeScript source, so they are
|
|
@@ -101,6 +101,14 @@ export const grafanaAuditCatalog: Record<string, RuleMeta> = {
|
|
|
101
101
|
"Give each dashboard provider its own path; set foldersFromFilesStructure: true (and no folder) on the provider when dashboards declare a folder; for a nested folder, run Grafana 13.1 or later or use a single-level folder.",
|
|
102
102
|
{ category: "correctness" },
|
|
103
103
|
),
|
|
104
|
+
GRAF110: auditRule(
|
|
105
|
+
"GRAF110",
|
|
106
|
+
"merge-worthy",
|
|
107
|
+
"guidance",
|
|
108
|
+
"Panel or row repeats over a variable that cannot give it more than one value",
|
|
109
|
+
"Repeat over a query, custom, datasource or group by variable, and set multi or includeAll on a query, custom or datasource one; Grafana shows a panel repeated over anything else once.",
|
|
110
|
+
{ category: "correctness" },
|
|
111
|
+
),
|
|
104
112
|
GRAF111: auditRule(
|
|
105
113
|
"GRAF111",
|
|
106
114
|
"merge-worthy",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* GRAF101: A panel, query or
|
|
2
|
+
* GRAF101: A panel, query, variable or annotation uses a datasource no Datasource or ExternalDatasource in the build declares, or one the build deletes
|
|
3
3
|
*
|
|
4
4
|
* Grafana shows "datasource not found" on the panel. Typed references cannot go wrong this way, so it mostly catches a { type, uid } ref or a hand-edited dashboard. A datasource variable whose plugin type no declared datasource has is an error too. A panel with queries and no datasource at all is a warning: Grafana sends them to its default datasource. This check joins dashboards against the datasources of the same build root (chant #1939): declare one that exists in Grafana but is provisioned elsewhere with ExternalDatasource, which the check counts and the build never provisions. A build that declares no datasource at all gets one warning per dashboard that references any, since nothing can be checked.
|
|
5
5
|
*/
|
|
@@ -9,7 +9,7 @@ import { grafanaDiagnostics } from "./grafana-helpers";
|
|
|
9
9
|
|
|
10
10
|
export const graf101: PostSynthCheck = {
|
|
11
11
|
id: "GRAF101",
|
|
12
|
-
description: "A panel, query or
|
|
12
|
+
description: "A panel, query, variable or annotation uses a datasource no Datasource or ExternalDatasource in the build declares, or one the build deletes",
|
|
13
13
|
|
|
14
14
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
15
|
return grafanaDiagnostics(ctx, "GRAF101");
|