@intentius/chant-lexicon-grafana 0.98.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 +24 -8
- package/dist/actions/grafana.d.ts +24 -0
- package/dist/actions/grafana.d.ts.map +1 -0
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/alerting-build.d.ts +128 -0
- package/dist/alerting-build.d.ts.map +1 -0
- package/dist/alerting.d.ts +273 -0
- package/dist/alerting.d.ts.map +1 -0
- package/dist/annotations.d.ts +61 -0
- package/dist/annotations.d.ts.map +1 -0
- package/dist/api/apply.d.ts +160 -0
- package/dist/api/apply.d.ts.map +1 -0
- package/dist/api/bind.d.ts +32 -0
- package/dist/api/bind.d.ts.map +1 -0
- package/dist/api/client.d.ts +76 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/converge.d.ts +33 -0
- package/dist/api/converge.d.ts.map +1 -0
- package/dist/api/dashboards.d.ts +102 -0
- package/dist/api/dashboards.d.ts.map +1 -0
- package/dist/api/datasources.d.ts +26 -0
- package/dist/api/datasources.d.ts.map +1 -0
- package/dist/api/fake-grafana-writes.d.ts +41 -0
- package/dist/api/fake-grafana-writes.d.ts.map +1 -0
- package/dist/api/fake-grafana.d.ts +63 -0
- package/dist/api/fake-grafana.d.ts.map +1 -0
- package/dist/api/folders.d.ts +146 -0
- package/dist/api/folders.d.ts.map +1 -0
- package/dist/api/library-panels.d.ts +52 -0
- package/dist/api/library-panels.d.ts.map +1 -0
- package/dist/build.d.ts +55 -6
- 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/codegen/schema-types.d.ts +3 -3
- package/dist/codegen/schema-types.d.ts.map +1 -1
- package/dist/composites/agent-dashboard.d.ts.map +1 -1
- package/dist/composites/catalog.d.ts.map +1 -1
- package/dist/composites/index.d.ts +4 -2
- package/dist/composites/index.d.ts.map +1 -1
- package/dist/composites/red-dashboard.d.ts +21 -2
- package/dist/composites/red-dashboard.d.ts.map +1 -1
- package/dist/composites/shared.d.ts +11 -3
- package/dist/composites/shared.d.ts.map +1 -1
- package/dist/composites/slo-alert-rules.d.ts +92 -0
- package/dist/composites/slo-alert-rules.d.ts.map +1 -0
- package/dist/config.d.ts +111 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/contact-point-secrets.d.ts +17 -0
- package/dist/contact-point-secrets.d.ts.map +1 -0
- package/dist/dashboard.d.ts +51 -9
- package/dist/dashboard.d.ts.map +1 -1
- package/dist/datasource-refs.d.ts +99 -0
- package/dist/datasource-refs.d.ts.map +1 -0
- package/dist/datasource-settings.d.ts +834 -0
- package/dist/datasource-settings.d.ts.map +1 -0
- package/dist/datasource.d.ts +82 -11
- package/dist/datasource.d.ts.map +1 -1
- package/dist/deep-observe-hooks.d.ts +64 -0
- package/dist/deep-observe-hooks.d.ts.map +1 -0
- package/dist/deep-observe.d.ts +47 -0
- package/dist/deep-observe.d.ts.map +1 -0
- package/dist/describe-resources.d.ts +50 -0
- package/dist/describe-resources.d.ts.map +1 -0
- package/dist/detect.d.ts +32 -3
- package/dist/detect.d.ts.map +1 -1
- package/dist/export-resources.d.ts +38 -0
- package/dist/export-resources.d.ts.map +1 -0
- package/dist/folder.d.ts +68 -0
- package/dist/folder.d.ts.map +1 -0
- package/dist/import/alerting.d.ts +40 -0
- package/dist/import/alerting.d.ts.map +1 -0
- package/dist/import/edits.d.ts +47 -0
- package/dist/import/edits.d.ts.map +1 -0
- package/dist/import/embedded.d.ts +14 -0
- package/dist/import/embedded.d.ts.map +1 -0
- package/dist/import/generator.d.ts +60 -0
- package/dist/import/generator.d.ts.map +1 -0
- package/dist/import/live-export.d.ts +108 -0
- package/dist/import/live-export.d.ts.map +1 -0
- package/dist/import/mappings.d.ts +89 -0
- package/dist/import/mappings.d.ts.map +1 -0
- package/dist/import/model.d.ts +105 -0
- package/dist/import/model.d.ts.map +1 -0
- package/dist/import/normalize-alerting.d.ts +26 -0
- package/dist/import/normalize-alerting.d.ts.map +1 -0
- package/dist/import/normalize.d.ts +74 -0
- package/dist/import/normalize.d.ts.map +1 -0
- package/dist/import/parser.d.ts +125 -0
- package/dist/import/parser.d.ts.map +1 -0
- package/dist/import/report.d.ts +26 -0
- package/dist/import/report.d.ts.map +1 -0
- package/dist/import/testdata/fixtures.d.ts +41 -0
- package/dist/import/testdata/fixtures.d.ts.map +1 -0
- package/dist/import/v2.d.ts +73 -0
- package/dist/import/v2.d.ts.map +1 -0
- package/dist/index.d.ts +31 -12
- 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 +22 -12
- package/dist/k8s.d.ts +107 -0
- package/dist/k8s.d.ts.map +1 -0
- package/dist/lint/audit-catalog.d.ts +2 -1
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/graf101.d.ts +2 -2
- package/dist/lint/post-synth/graf102.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/graf107.d.ts +1 -1
- package/dist/lint/post-synth/graf108.d.ts +8 -0
- package/dist/lint/post-synth/graf108.d.ts.map +1 -0
- package/dist/lint/post-synth/graf109.d.ts +8 -0
- package/dist/lint/post-synth/graf109.d.ts.map +1 -0
- 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/graf111.d.ts +8 -0
- package/dist/lint/post-synth/graf111.d.ts.map +1 -0
- package/dist/lint/post-synth/graf112.d.ts +8 -0
- package/dist/lint/post-synth/graf112.d.ts.map +1 -0
- package/dist/lint/post-synth/graf113.d.ts +8 -0
- package/dist/lint/post-synth/graf113.d.ts.map +1 -0
- package/dist/lint/post-synth/graf114.d.ts +8 -0
- package/dist/lint/post-synth/graf114.d.ts.map +1 -0
- package/dist/lint/post-synth/graf115.d.ts +8 -0
- package/dist/lint/post-synth/graf115.d.ts.map +1 -0
- package/dist/lint/post-synth/grafana-helpers.d.ts +7 -5
- 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/lint/rules/literal-secret.d.ts +7 -3
- package/dist/lint/rules/literal-secret.d.ts.map +1 -1
- package/dist/lsp/hover.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/members.d.ts +27 -0
- package/dist/members.d.ts.map +1 -0
- package/dist/meta.json +205 -0
- package/dist/okf/index.md +53 -4
- package/dist/okf/rules/GRAF002.md +3 -2
- package/dist/okf/rules/GRAF101.md +3 -2
- package/dist/okf/rules/GRAF102.md +1 -0
- package/dist/okf/rules/GRAF104.md +2 -2
- package/dist/okf/rules/GRAF106.md +2 -2
- package/dist/okf/rules/GRAF108.md +11 -0
- package/dist/okf/rules/GRAF109.md +15 -0
- package/dist/okf/rules/GRAF110.md +11 -0
- package/dist/okf/rules/GRAF111.md +11 -0
- package/dist/okf/rules/GRAF112.md +16 -0
- package/dist/okf/rules/GRAF113.md +16 -0
- package/dist/okf/rules/GRAF114.md +11 -0
- package/dist/okf/rules/GRAF115.md +11 -0
- package/dist/okf/types/AdhocVariable.md +9 -0
- package/dist/okf/types/AlertListPanel.md +9 -0
- package/dist/okf/types/AlertQuery.md +9 -0
- package/dist/okf/types/AlertRule.md +9 -0
- package/dist/okf/types/AlertRuleGroup.md +9 -0
- package/dist/okf/types/AzureMonitorQuery.md +9 -0
- package/dist/okf/types/BarChartPanel.md +9 -0
- package/dist/okf/types/BarGaugePanel.md +9 -0
- package/dist/okf/types/BigQueryQuery.md +9 -0
- package/dist/okf/types/CanvasPanel.md +9 -0
- package/dist/okf/types/ClassicConditionsExpression.md +9 -0
- package/dist/okf/types/CloudMonitoringQuery.md +9 -0
- package/dist/okf/types/CloudWatchQuery.md +9 -0
- package/dist/okf/types/ContactPoint.md +14 -0
- package/dist/okf/types/Dashboard.md +2 -1
- package/dist/okf/types/Datasource.md +3 -2
- package/dist/okf/types/DatasourceProvisioning.md +9 -0
- package/dist/okf/types/ElasticsearchQuery.md +9 -0
- package/dist/okf/types/ExternalDatasource.md +15 -0
- package/dist/okf/types/FlameGraphPanel.md +9 -0
- package/dist/okf/types/Folder.md +9 -0
- package/dist/okf/types/GeomapPanel.md +9 -0
- package/dist/okf/types/GroupByVariable.md +9 -0
- package/dist/okf/types/HistogramPanel.md +9 -0
- package/dist/okf/types/MSSQLQuery.md +9 -0
- package/dist/okf/types/MathExpression.md +9 -0
- package/dist/okf/types/MuteTiming.md +13 -0
- package/dist/okf/types/MySQLQuery.md +9 -0
- package/dist/okf/types/NodeGraphPanel.md +9 -0
- package/dist/okf/types/NotificationPolicy.md +9 -0
- package/dist/okf/types/NotificationTemplate.md +9 -0
- package/dist/okf/types/PieChartPanel.md +9 -0
- package/dist/okf/types/PostgresQuery.md +9 -0
- package/dist/okf/types/PyroscopeQuery.md +9 -0
- package/dist/okf/types/ReduceExpression.md +9 -0
- package/dist/okf/types/ResampleExpression.md +9 -0
- package/dist/okf/types/SqlExpression.md +9 -0
- package/dist/okf/types/StateTimelinePanel.md +9 -0
- package/dist/okf/types/StatusHistoryPanel.md +9 -0
- package/dist/okf/types/SwitchVariable.md +9 -0
- package/dist/okf/types/ThresholdExpression.md +9 -0
- package/dist/okf/types/TrendPanel.md +9 -0
- package/dist/okf/types/XYChartPanel.md +9 -0
- package/dist/op/activities/grafana-apply.d.ts +86 -0
- package/dist/op/activities/grafana-apply.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +9 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/op/builders.d.ts +24 -0
- package/dist/op/builders.d.ts.map +1 -0
- package/dist/ownership.d.ts +78 -0
- package/dist/ownership.d.ts.map +1 -0
- package/dist/panel-options.d.ts +65 -0
- package/dist/panel-options.d.ts.map +1 -0
- package/dist/panels.d.ts +46 -9
- package/dist/panels.d.ts.map +1 -1
- package/dist/pin.d.ts +29 -7
- package/dist/pin.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/promql-check.d.ts +51 -0
- package/dist/promql-check.d.ts.map +1 -0
- package/dist/query-models.d.ts +95 -0
- package/dist/query-models.d.ts.map +1 -0
- package/dist/query.d.ts +116 -18
- package/dist/query.d.ts.map +1 -1
- package/dist/rules/graf101.ts +3 -3
- package/dist/rules/graf102.ts +1 -1
- package/dist/rules/graf104.ts +3 -3
- package/dist/rules/graf106.ts +3 -3
- package/dist/rules/graf107.ts +1 -1
- package/dist/rules/graf108.ts +17 -0
- package/dist/rules/graf109.ts +17 -0
- package/dist/rules/graf110.ts +17 -0
- package/dist/rules/graf111.ts +17 -0
- package/dist/rules/graf112.ts +17 -0
- package/dist/rules/graf113.ts +17 -0
- package/dist/rules/graf114.ts +17 -0
- package/dist/rules/graf115.ts +17 -0
- package/dist/rules/grafana-helpers.ts +27 -10
- package/dist/rules/literal-secret.ts +60 -6
- package/dist/schema/azuremonitor.gen.d.ts +354 -0
- package/dist/schema/azuremonitor.gen.d.ts.map +1 -0
- package/dist/schema/barchart.gen.d.ts +198 -0
- package/dist/schema/barchart.gen.d.ts.map +1 -0
- package/dist/schema/bargauge.gen.d.ts +97 -0
- package/dist/schema/bargauge.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/canvas.gen.d.ts +152 -0
- package/dist/schema/canvas.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/dashboard.gen.d.ts +196 -21
- package/dist/schema/dashboard.gen.d.ts.map +1 -1
- package/dist/schema/elasticsearch.gen.d.ts +444 -0
- package/dist/schema/elasticsearch.gen.d.ts.map +1 -0
- package/dist/schema/expr.gen.d.ts +606 -0
- package/dist/schema/expr.gen.d.ts.map +1 -0
- package/dist/schema/gauge.gen.d.ts.map +1 -1
- package/dist/schema/geomap.gen.d.ts +100 -0
- package/dist/schema/geomap.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/heatmap.gen.d.ts.map +1 -1
- package/dist/schema/histogram.gen.d.ts +132 -0
- package/dist/schema/histogram.gen.d.ts.map +1 -0
- package/dist/schema/index.d.ts +18 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/logs.gen.d.ts +1 -0
- package/dist/schema/logs.gen.d.ts.map +1 -1
- package/dist/schema/loki.gen.d.ts.map +1 -1
- package/dist/schema/nodegraph.gen.d.ts +49 -0
- package/dist/schema/nodegraph.gen.d.ts.map +1 -0
- package/dist/schema/piechart.gen.d.ts +127 -0
- package/dist/schema/piechart.gen.d.ts.map +1 -0
- package/dist/schema/prometheus.gen.d.ts.map +1 -1
- package/dist/schema/stat.gen.d.ts.map +1 -1
- package/dist/schema/statetimeline.gen.d.ts +124 -0
- package/dist/schema/statetimeline.gen.d.ts.map +1 -0
- package/dist/schema/statushistory.gen.d.ts +114 -0
- package/dist/schema/statushistory.gen.d.ts.map +1 -0
- package/dist/schema/table.gen.d.ts +35 -3
- package/dist/schema/table.gen.d.ts.map +1 -1
- package/dist/schema/tempo.gen.d.ts.map +1 -1
- package/dist/schema/text.gen.d.ts.map +1 -1
- package/dist/schema/timeseries.gen.d.ts +5 -1
- package/dist/schema/timeseries.gen.d.ts.map +1 -1
- package/dist/schema/trend.gen.d.ts +180 -0
- package/dist/schema/trend.gen.d.ts.map +1 -0
- package/dist/schema/xychart.gen.d.ts +145 -0
- package/dist/schema/xychart.gen.d.ts.map +1 -0
- package/dist/schema-validate.d.ts +39 -4
- package/dist/schema-validate.d.ts.map +1 -1
- package/dist/serializer.d.ts +11 -3
- package/dist/serializer.d.ts.map +1 -1
- package/dist/skill-defs.d.ts.map +1 -1
- package/dist/skills/chant-grafana-alerting.md +49 -0
- package/dist/skills/chant-grafana-operations.md +108 -0
- package/dist/skills/chant-grafana-provisioning.md +3 -3
- package/dist/skills/chant-grafana.md +15 -8
- package/dist/spec/fetch-units-cli.d.ts +3 -0
- package/dist/spec/fetch-units-cli.d.ts.map +1 -0
- package/dist/spec/overlay.d.ts +43 -0
- package/dist/spec/overlay.d.ts.map +1 -0
- package/dist/spec/schemas.d.ts +21 -13
- 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/spec/units.d.ts +35 -0
- package/dist/spec/units.d.ts.map +1 -0
- package/dist/spec/units.gen.d.ts +2 -0
- package/dist/spec/units.gen.d.ts.map +1 -0
- package/dist/transformations.d.ts +459 -0
- package/dist/transformations.d.ts.map +1 -0
- package/dist/util.d.ts +6 -0
- package/dist/util.d.ts.map +1 -1
- package/dist/validate-alerting.d.ts +65 -0
- package/dist/validate-alerting.d.ts.map +1 -0
- package/dist/validate-output.d.ts +62 -9
- 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 +109 -7
- package/dist/variables.d.ts.map +1 -1
- package/package.json +12 -4
- package/src/actions/actions.test.ts +24 -0
- package/src/actions/grafana.ts +40 -0
- package/src/actions/index.ts +1 -0
- package/src/alerting-build.ts +376 -0
- package/src/alerting.e2e.test.ts +212 -0
- package/src/alerting.test.ts +272 -0
- package/src/alerting.ts +435 -0
- package/src/annotations.ts +84 -0
- package/src/api/apply.test.ts +256 -0
- package/src/api/apply.ts +394 -0
- package/src/api/bind.ts +58 -0
- package/src/api/client.ts +148 -0
- package/src/api/converge.ts +63 -0
- package/src/api/dashboards.ts +247 -0
- package/src/api/datasources.ts +33 -0
- package/src/api/fake-grafana-writes.ts +201 -0
- package/src/api/fake-grafana.ts +190 -0
- package/src/api/folders.ts +339 -0
- package/src/api/library-panels.ts +101 -0
- package/src/build.test.ts +351 -10
- package/src/build.ts +294 -55
- package/src/catalog.ts +104 -2
- package/src/codegen/docs.ts +50 -10
- package/src/codegen/generate.ts +27 -1
- package/src/codegen/schema-types.ts +16 -6
- package/src/composites/agent-dashboard.ts +6 -3
- package/src/composites/catalog.test.ts +2 -2
- package/src/composites/catalog.ts +82 -0
- package/src/composites/composites.test.ts +73 -9
- package/src/composites/index.ts +4 -2
- package/src/composites/queries.e2e.test.ts +374 -0
- package/src/composites/red-dashboard.ts +83 -12
- package/src/composites/shared.ts +14 -2
- package/src/composites/slo-alert-rules.ts +149 -0
- package/src/config.test.ts +61 -0
- package/src/config.ts +138 -0
- package/src/contact-point-secrets.ts +63 -0
- package/src/dashboard.ts +68 -9
- package/src/datasource-refs.test.ts +56 -0
- package/src/datasource-refs.ts +213 -0
- package/src/datasource-settings.test.ts +106 -0
- package/src/datasource-settings.ts +941 -0
- package/src/datasource.ts +123 -11
- package/src/deep-observe-hooks.ts +267 -0
- package/src/deep-observe.test.ts +311 -0
- package/src/deep-observe.ts +187 -0
- package/src/describe-resources.test.ts +271 -0
- package/src/describe-resources.ts +244 -0
- package/src/detect.ts +82 -5
- package/src/export-resources.test.ts +129 -0
- package/src/export-resources.ts +84 -0
- package/src/folder.ts +118 -0
- package/src/generated/lexicon-grafana.json +205 -0
- package/src/import/alerting-roundtrip.test.ts +161 -0
- package/src/import/alerting.ts +500 -0
- package/src/import/edits.ts +95 -0
- package/src/import/embedded.test.ts +45 -0
- package/src/import/embedded.ts +47 -0
- package/src/import/generated-types.e2e.test.ts +130 -0
- package/src/import/generator.test.ts +210 -0
- package/src/import/generator.ts +463 -0
- package/src/import/live-export.ts +264 -0
- package/src/import/mappings.ts +336 -0
- package/src/import/model.ts +117 -0
- package/src/import/normalize-alerting.ts +101 -0
- package/src/import/normalize.test.ts +117 -0
- package/src/import/normalize.ts +393 -0
- package/src/import/parser.test.ts +516 -0
- package/src/import/parser.ts +1387 -0
- package/src/import/report.ts +62 -0
- package/src/import/roundtrip.test.ts +563 -0
- package/src/import/testdata/fixtures.ts +137 -0
- package/src/import/v2.test.ts +292 -0
- package/src/import/v2.ts +804 -0
- package/src/import.e2e.test.ts +31 -84
- package/src/import.test.ts +9 -3
- package/src/index.ts +308 -14
- package/src/init-templates.test.ts +74 -0
- package/src/init-templates.ts +349 -0
- package/src/k8s.test.ts +98 -0
- package/src/k8s.ts +197 -0
- package/src/lint/audit-catalog.ts +68 -3
- package/src/lint/core-lint.test.ts +43 -0
- package/src/lint/post-synth/graf101.ts +3 -3
- package/src/lint/post-synth/graf102.ts +1 -1
- package/src/lint/post-synth/graf104.ts +3 -3
- package/src/lint/post-synth/graf106.ts +3 -3
- package/src/lint/post-synth/graf107.ts +1 -1
- package/src/lint/post-synth/graf108.ts +17 -0
- package/src/lint/post-synth/graf109.ts +17 -0
- package/src/lint/post-synth/graf110.ts +17 -0
- package/src/lint/post-synth/graf111.ts +17 -0
- package/src/lint/post-synth/graf112.ts +17 -0
- package/src/lint/post-synth/graf113.ts +17 -0
- package/src/lint/post-synth/graf114.ts +17 -0
- package/src/lint/post-synth/graf115.ts +17 -0
- package/src/lint/post-synth/grafana-helpers.ts +27 -10
- package/src/lint/post-synth/index.ts +16 -0
- package/src/lint/post-synth/post-synth.test.ts +333 -12
- package/src/lint/rules/literal-secret.ts +60 -6
- package/src/lint/rules/rules.test.ts +27 -0
- package/src/load-cost.test.ts +35 -0
- package/src/lsp/completions.test.ts +5 -0
- package/src/lsp/hover.test.ts +7 -0
- package/src/lsp/hover.ts +8 -1
- package/src/members.ts +68 -0
- package/src/observe.e2e.test.ts +255 -0
- package/src/op/activities/grafana-apply.e2e.test.ts +184 -0
- package/src/op/activities/grafana-apply.test.ts +243 -0
- package/src/op/activities/grafana-apply.ts +197 -0
- package/src/op/activities/index.ts +8 -0
- package/src/op/builders.test.ts +40 -0
- package/src/op/builders.ts +34 -0
- package/src/ownership.ts +122 -0
- package/src/panel-options.ts +66 -0
- package/src/panels.test.ts +53 -0
- package/src/panels.ts +148 -10
- package/src/pin.ts +69 -6
- package/src/plugin.test.ts +12 -2
- package/src/plugin.ts +75 -0
- package/src/promql-check.test.ts +108 -0
- package/src/promql-check.ts +193 -0
- package/src/query-models.ts +94 -0
- package/src/query.test.ts +166 -0
- package/src/query.ts +190 -20
- package/src/schema/azuremonitor.gen.ts +381 -0
- package/src/schema/barchart.gen.ts +221 -0
- package/src/schema/bargauge.gen.ts +111 -0
- package/src/schema/bigquery.gen.ts +110 -0
- package/src/schema/canvas.gen.ts +176 -0
- package/src/schema/cloudwatch.gen.ts +423 -0
- package/src/schema/dashboard.gen.ts +206 -22
- package/src/schema/elasticsearch.gen.ts +508 -0
- package/src/schema/expr.gen.ts +618 -0
- package/src/schema/gauge.gen.ts +2 -1
- package/src/schema/geomap.gen.ts +112 -0
- package/src/schema/googlecloudmonitoring.gen.ts +330 -0
- package/src/schema/grafanapyroscope.gen.ts +66 -0
- package/src/schema/heatmap.gen.ts +2 -1
- package/src/schema/histogram.gen.ts +151 -0
- package/src/schema/index.ts +18 -0
- package/src/schema/logs.gen.ts +3 -1
- package/src/schema/loki.gen.ts +2 -1
- package/src/schema/nodegraph.gen.ts +58 -0
- package/src/schema/piechart.gen.ts +146 -0
- package/src/schema/prometheus.gen.ts +2 -1
- package/src/schema/stat.gen.ts +2 -1
- package/src/schema/statetimeline.gen.ts +143 -0
- package/src/schema/statushistory.gen.ts +132 -0
- package/src/schema/table.gen.ts +41 -4
- package/src/schema/tempo.gen.ts +2 -1
- package/src/schema/text.gen.ts +2 -1
- package/src/schema/timeseries.gen.ts +7 -2
- package/src/schema/trend.gen.ts +208 -0
- package/src/schema/xychart.gen.ts +167 -0
- package/src/schema-overlay.test.ts +282 -0
- package/src/schema-validate.ts +199 -57
- package/src/schema.test.ts +4 -2
- package/src/serializer.test.ts +13 -1
- package/src/serializer.ts +11 -3
- package/src/skill-defs.ts +34 -0
- package/src/skills/chant-grafana-alerting.md +49 -0
- package/src/skills/chant-grafana-operations.md +108 -0
- package/src/skills/chant-grafana-provisioning.md +3 -3
- package/src/skills/chant-grafana.md +15 -8
- package/src/spec/fetch-cli.ts +3 -3
- package/src/spec/fetch-units-cli.ts +37 -0
- package/src/spec/overlay/canvas.overlay.json +142 -0
- package/src/spec/overlay/dashboard.overlay.json +378 -0
- package/src/spec/overlay/expr.overlay.json +14 -0
- package/src/spec/overlay/geomap.overlay.json +35 -0
- package/src/spec/overlay/logs.overlay.json +14 -0
- package/src/spec/overlay/nodegraph.overlay.json +31 -0
- package/src/spec/overlay/piechart.overlay.json +17 -0
- package/src/spec/overlay/table.overlay.json +103 -0
- package/src/spec/overlay/timeseries.overlay.json +21 -0
- package/src/spec/overlay/trend.overlay.json +17 -0
- package/src/spec/overlay/xychart.overlay.json +16 -0
- package/src/spec/overlay.ts +105 -0
- package/src/spec/schemas/azuremonitor.jsonschema.json +679 -0
- package/src/spec/schemas/barchart.jsonschema.json +419 -0
- package/src/spec/schemas/bargauge.jsonschema.json +224 -0
- package/src/spec/schemas/bigquery.jsonschema.json +290 -0
- package/src/spec/schemas/canvas.jsonschema.json +356 -0
- package/src/spec/schemas/cloudwatch.jsonschema.json +721 -0
- package/src/spec/schemas/dashboardv2.jsonschema.json +3387 -0
- package/src/spec/schemas/elasticsearch.jsonschema.json +1695 -0
- package/src/spec/schemas/expr.jsonschema.json +987 -0
- package/src/spec/schemas/geomap.jsonschema.json +220 -0
- package/src/spec/schemas/googlecloudmonitoring.jsonschema.json +488 -0
- package/src/spec/schemas/grafanapyroscope.jsonschema.json +93 -0
- package/src/spec/schemas/histogram.jsonschema.json +308 -0
- package/src/spec/schemas/nodegraph.jsonschema.json +77 -0
- package/src/spec/schemas/piechart.jsonschema.json +275 -0
- package/src/spec/schemas/statetimeline.jsonschema.json +324 -0
- package/src/spec/schemas/statushistory.jsonschema.json +292 -0
- package/src/spec/schemas/trend.jsonschema.json +449 -0
- package/src/spec/schemas/xychart.jsonschema.json +428 -0
- package/src/spec/schemas.gen.ts +40 -0
- package/src/spec/schemas.ts +30 -16
- package/src/spec/units.gen.ts +281 -0
- package/src/spec/units.ts +77 -0
- package/src/transformations.test.ts +65 -0
- package/src/transformations.ts +574 -0
- package/src/util.ts +8 -0
- package/src/validate-alerting.test.ts +255 -0
- package/src/validate-alerting.ts +430 -0
- package/src/validate-output.ts +427 -104
- package/src/validate.ts +28 -0
- package/src/validation.ts +27 -0
- package/src/variables.ts +120 -8
|
@@ -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;
|
package/src/k8s.test.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { expandComposite } from "@intentius/chant/composite";
|
|
3
|
+
import { k8sSerializer } from "@intentius/chant-lexicon-k8s";
|
|
4
|
+
import { loadAll } from "js-yaml";
|
|
5
|
+
import { Dashboard } from "./dashboard";
|
|
6
|
+
import { Datasource } from "./datasource";
|
|
7
|
+
import { StatPanel } from "./panels";
|
|
8
|
+
import { PromQuery } from "./query";
|
|
9
|
+
import { dashboardJson } from "./build";
|
|
10
|
+
import { GrafanaConfigMaps, grafanaConfigMapLayout, grafanaVolumes } from "./k8s";
|
|
11
|
+
|
|
12
|
+
const prometheus = new Datasource({ name: "Prometheus", type: "prometheus", url: "http://prometheus:9090" });
|
|
13
|
+
const stat = () => new StatPanel({ title: "Up", datasource: prometheus, targets: [new PromQuery({ expr: "sum(up)" })] });
|
|
14
|
+
const top = new Dashboard({ title: "Top", uid: "Top_Level", panels: [stat()] });
|
|
15
|
+
const red = new Dashboard({ title: "RED", uid: "red", folder: "Services", panels: [stat()] });
|
|
16
|
+
const slo = new Dashboard({ title: "SLO", uid: "slo", folder: "Services", panels: [stat()] });
|
|
17
|
+
const entities = [prometheus, top, red, slo];
|
|
18
|
+
|
|
19
|
+
type Manifest = { kind: string; metadata: { name: string; namespace?: string; labels?: Record<string, string>; annotations?: Record<string, string> }; data: Record<string, string> };
|
|
20
|
+
|
|
21
|
+
function manifests(instance: ReturnType<typeof GrafanaConfigMaps>): Manifest[] {
|
|
22
|
+
const out = k8sSerializer.serialize(expandComposite("grafana", instance));
|
|
23
|
+
return (loadAll(typeof out === "string" ? out : out.primary) as Manifest[]).filter(Boolean);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe("GrafanaConfigMaps", () => {
|
|
27
|
+
test("one ConfigMap per dashboard, labelled for the sidecar, holding dashboardJson(dashboard)", () => {
|
|
28
|
+
const docs = manifests(GrafanaConfigMaps({ entities, namespace: "obs", labels: { team: "sre" } }));
|
|
29
|
+
expect(docs.map((d) => d.metadata.name)).toEqual([
|
|
30
|
+
"grafana-dashboard-top-level",
|
|
31
|
+
"grafana-dashboard-red",
|
|
32
|
+
"grafana-dashboard-slo",
|
|
33
|
+
"grafana-datasources",
|
|
34
|
+
"grafana-dashboard-providers",
|
|
35
|
+
]);
|
|
36
|
+
const [topCm, redCm] = docs;
|
|
37
|
+
expect(topCm.metadata).toEqual({ name: "grafana-dashboard-top-level", namespace: "obs", labels: { team: "sre", grafana_dashboard: "1" } });
|
|
38
|
+
expect(topCm.data).toEqual({ "Top_Level.json": dashboardJson(top) });
|
|
39
|
+
expect(redCm.metadata.annotations).toEqual({ "k8s-sidecar-target-directory": "Services" });
|
|
40
|
+
expect(docs[3].metadata.labels).toEqual({ team: "sre", grafana_datasource: "1" });
|
|
41
|
+
expect(Object.keys(docs[3].data)).toEqual(["chant.yaml"]);
|
|
42
|
+
expect(docs[3].data["chant.yaml"]).toContain("url: http://prometheus:9090");
|
|
43
|
+
expect(docs[4].metadata.labels).toEqual({ team: "sre" });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("labels and the folder annotation can be changed or left off", () => {
|
|
47
|
+
const docs = manifests(GrafanaConfigMaps({ entities: [red], name: "Obs", dashboardLabel: ["dashboards", "obs"], folderAnnotation: false }));
|
|
48
|
+
expect(docs[0].metadata).toEqual({ name: "obs-dashboard-red", labels: { dashboards: "obs" } });
|
|
49
|
+
const bare = manifests(GrafanaConfigMaps({ entities: [prometheus], datasourceLabel: false }));
|
|
50
|
+
expect(bare.map((d) => [d.metadata.name, d.metadata.labels])).toEqual([["grafana-datasources", {}]]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("names stay unique and DNS-1123 when uids collide after lower-casing", () => {
|
|
54
|
+
const a = new Dashboard({ title: "A", uid: "Same" });
|
|
55
|
+
const b = new Dashboard({ title: "B", uid: "same" });
|
|
56
|
+
expect(grafanaConfigMapLayout({ entities: [a, b] }).dashboards.map((d) => d.configMap)).toEqual(["grafana-dashboard-same", "grafana-dashboard-same-2"]);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("grafanaVolumes", () => {
|
|
61
|
+
test("provisioning under /etc/grafana/provisioning, each dashboard folder a projected volume of its own", () => {
|
|
62
|
+
const { volumes, volumeMounts } = grafanaVolumes({ entities });
|
|
63
|
+
expect(volumeMounts).toEqual([
|
|
64
|
+
{ name: "grafana-provisioning", mountPath: "/etc/grafana/provisioning", readOnly: true },
|
|
65
|
+
{ name: "grafana-dashboards-0", mountPath: "/var/lib/grafana/dashboards", readOnly: true },
|
|
66
|
+
{ name: "grafana-dashboards-1", mountPath: "/var/lib/grafana/dashboards/Services", readOnly: true },
|
|
67
|
+
]);
|
|
68
|
+
expect(volumes).toEqual([
|
|
69
|
+
{
|
|
70
|
+
name: "grafana-provisioning",
|
|
71
|
+
projected: {
|
|
72
|
+
sources: [
|
|
73
|
+
{ configMap: { name: "grafana-datasources", items: [{ key: "chant.yaml", path: "datasources/chant.yaml" }] } },
|
|
74
|
+
{ configMap: { name: "grafana-dashboard-providers", items: [{ key: "chant.yaml", path: "dashboards/chant.yaml" }] } },
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{ name: "grafana-dashboards-0", projected: { sources: [{ configMap: { name: "grafana-dashboard-top-level", items: [{ key: "Top_Level.json", path: "Top_Level.json" }] } }] } },
|
|
79
|
+
{
|
|
80
|
+
name: "grafana-dashboards-1",
|
|
81
|
+
projected: {
|
|
82
|
+
sources: [
|
|
83
|
+
{ configMap: { name: "grafana-dashboard-red", items: [{ key: "red.json", path: "red.json" }] } },
|
|
84
|
+
{ configMap: { name: "grafana-dashboard-slo", items: [{ key: "slo.json", path: "slo.json" }] } },
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("a different dashboards path or volume name", () => {
|
|
92
|
+
const { volumeMounts } = grafanaVolumes({ entities: [red] }, { dashboardsPath: "/dash/", volumeName: "g" });
|
|
93
|
+
expect(volumeMounts.map((m) => [m.name, m.mountPath])).toEqual([
|
|
94
|
+
["g-provisioning", "/etc/grafana/provisioning"],
|
|
95
|
+
["g-dashboards-0", "/dash/Services"],
|
|
96
|
+
]);
|
|
97
|
+
});
|
|
98
|
+
});
|