From a6eebc22e8e2e14d95ad088e3efe03b50a787633 Mon Sep 17 00:00:00 2001 From: Chris Trott <908409+trotttrotttrott@users.noreply.github.com> Date: Tue, 24 Nov 2020 01:54:34 -0600 Subject: [PATCH] Dashboard Schemas (#28793) * cue mod init github.com/grafana/grafana/dashboard-schemas * dashboard-schemas: Document exporting to OpenAPI Signed-off-by: Arve Knudsen Co-authored-by: Arve Knudsen --- dashboard-schemas/Dashboard.cue | 68 +++++++ dashboard-schemas/README.md | 71 +++++++ dashboard-schemas/cue.mod/module.cue | 1 + dashboard-schemas/panels/Gauge.cue | 74 +++++++ dashboard-schemas/panels/Graph.cue | 192 ++++++++++++++++++ dashboard-schemas/panels/Row.cue | 24 +++ dashboard-schemas/panels/gridPos.cue | 12 ++ dashboard-schemas/panels/link.cue | 14 ++ dashboard-schemas/panels/mapping.cue | 11 + dashboard-schemas/panels/override.cue | 12 ++ dashboard-schemas/panels/panel.cue | 24 +++ dashboard-schemas/panels/thresholds.cue | 11 + dashboard-schemas/targets/Prometheus.cue | 19 ++ .../transformations/CalculateField.cue | 39 ++++ .../transformations/Organize.cue | 16 ++ dashboard-schemas/variables/Custom.cue | 9 + dashboard-schemas/variables/Datasource.cue | 18 ++ dashboard-schemas/variables/Query.cue | 30 +++ dashboard-schemas/variables/variable.cue | 33 +++ 19 files changed, 678 insertions(+) create mode 100644 dashboard-schemas/Dashboard.cue create mode 100644 dashboard-schemas/README.md create mode 100644 dashboard-schemas/cue.mod/module.cue create mode 100644 dashboard-schemas/panels/Gauge.cue create mode 100644 dashboard-schemas/panels/Graph.cue create mode 100644 dashboard-schemas/panels/Row.cue create mode 100644 dashboard-schemas/panels/gridPos.cue create mode 100644 dashboard-schemas/panels/link.cue create mode 100644 dashboard-schemas/panels/mapping.cue create mode 100644 dashboard-schemas/panels/override.cue create mode 100644 dashboard-schemas/panels/panel.cue create mode 100644 dashboard-schemas/panels/thresholds.cue create mode 100644 dashboard-schemas/targets/Prometheus.cue create mode 100644 dashboard-schemas/transformations/CalculateField.cue create mode 100644 dashboard-schemas/transformations/Organize.cue create mode 100644 dashboard-schemas/variables/Custom.cue create mode 100644 dashboard-schemas/variables/Datasource.cue create mode 100644 dashboard-schemas/variables/Query.cue create mode 100644 dashboard-schemas/variables/variable.cue diff --git a/dashboard-schemas/Dashboard.cue b/dashboard-schemas/Dashboard.cue new file mode 100644 index 00000000000..97417057968 --- /dev/null +++ b/dashboard-schemas/Dashboard.cue @@ -0,0 +1,68 @@ +package main + +#Dashboard: { + // Unique numeric identifier for the dashboard. (generated by the db) + id: int + // Unique dashboard identifier that can be generated by anyone. string (8-40) + uid: string + // Title of dashboard. + title?: string + // Description of dashboard. + description?: string + // Tags associated with dashboard. + tags?: [...string] + // Theme of dashboard. + style: *"light" | "dark" + // Timezone of dashboard, + timezone?: *"browser" | "utc" + // Whether a dashboard is editable or not. + editable: bool | *true + // 0 for no shared crosshair or tooltip (default). + // 1 for shared crosshair. + // 2 for shared crosshair AND shared tooltip. + graphTooltip: int & >=0 & <=2 | *0 + // Time range for dashboard, e.g. last 6 hours, last 7 days, etc + time?: { + from: string | *"now-6h" + to: string | *"now" + } + // Timepicker metadata. + timepicker?: { + // Whether timepicker is collapsed or not. + collapse: bool | *false + // Whether timepicker is enabled or not. + enable: bool | *true + // Whether timepicker is visible or not. + hidden: bool | *false + // Selectable intervals for auto-refresh. + refresh_intervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] + } + // Templating. + templating?: list: [...{}] + // Annotations. + annotations?: list: [...{ + builtIn: int | *0 + // Datasource to use for annotation. + datasource: string + // Whether annotation is enabled. + enable?: bool | *true + // Whether to hide annotation. + hide?: bool | *false + // Annotation icon color. + iconColor?: string + // Name of annotation. + name?: string + // Query for annotation data. + rawQuery: string + showIn: int | *0 + }] | *[] + // Auto-refresh interval. + refresh: string + // Version of the JSON schema, incremented each time a Grafana update brings + // changes to said schema. + schemaVersion: int | *25 + // Version of the dashboard, incremented each time the dashboard is updated. + version: string + // Dashboard panels. + panels?: [...{}] +} diff --git a/dashboard-schemas/README.md b/dashboard-schemas/README.md new file mode 100644 index 00000000000..9df290d0e1a --- /dev/null +++ b/dashboard-schemas/README.md @@ -0,0 +1,71 @@ +# Dashboard Schemas + +Schema description documents for [Grafana Dashboard +JSON](https://grafana.com/docs/grafana/latest/reference/dashboard/) and core +panels. + +> **Note:** This directory is experimental. The schemas are not currently +> implemented or enforced in Grafana. + +Schemas are defined in [Cue](https://cuelang.org/). Cue was chosen because it +strongly facilitates our primary use cases - [schema +definition](https://cuelang.org/docs/usecases/datadef/), [data +validation](https://cuelang.org/docs/usecases/validation/), and [code +generation/extraction](https://cuelang.org/docs/usecases/generate/). + +## Schema Organization + +Each schema describes part of a dashboard. `Dashboard.cue` is the main dashboard +schema object. All other schemas describe nested objects within a dashboard. +They are grouped in the following directories: + +* `panels` - schemas for + [panels](https://grafana.com/docs/grafana/latest/panels/panels-overview/). +* `targets` - targets represent + [queries](https://grafana.com/docs/grafana/latest/panels/queries/). Each [data + source](https://grafana.com/docs/grafana/latest/datasources/) type has a + unique target schema. +* `variables` - schemas for + [variables](https://grafana.com/docs/grafana/latest/variables/variable-types/). +* `transformations` - schemas for + [transformations](https://grafana.com/docs/grafana/latest/panels/transformations/types-options/). + +The following somewhat conveys how they fit together when constructing a +dashboard: + +``` ++-----------+ +-----------+ +| Dashboard +------> Variables | ++---------+-+ +-----------+ + | +--------+ +---------+ + +----> Panels +----> Targets | + +------+-+ +---------+ + | +-----------------+ + +------> Transformations | + +-----------------+ +``` + +## Definitions + +All schemas are [Cue +definitions](https://cuelang.org/docs/references/spec/#definitions-and-hidden-fields). +Schemas intended to be exported must begin with a capital letter. For example, +[Gauge](./panels/Gauge.cue). Definitions beginning with a lowercase letter will +not be exported. These are reusable components for constructing the exported +definitions. For example, [`#panel`](./panels/panel.cue) is intended to +be a base schema for panels. `#Gauge` extends `#panel` with the following: + +``` +#Gauge: panel & { + ... +} +``` + +## Exporting OpenAPI + +[OpenAPI](https://swagger.io/specification/) schemas can be exported from these CUE sources. Use the `cue` +command as follows, to generate OpenAPI JSON to stdout: + +``` +cue export --out openapi -o - ./dashboard-schemas/... +``` diff --git a/dashboard-schemas/cue.mod/module.cue b/dashboard-schemas/cue.mod/module.cue new file mode 100644 index 00000000000..14eaf4ef3d8 --- /dev/null +++ b/dashboard-schemas/cue.mod/module.cue @@ -0,0 +1 @@ +module: "github.com/grafana/grafana/dashboard-schemas" diff --git a/dashboard-schemas/panels/Gauge.cue b/dashboard-schemas/panels/Gauge.cue new file mode 100644 index 00000000000..253920aa6c7 --- /dev/null +++ b/dashboard-schemas/panels/Gauge.cue @@ -0,0 +1,74 @@ +package panels + +// Gauge is a single value panel that can repeat a gauge for every series, +// column or row. +#Gauge: panel & { + // Field config. + fieldConfig: { + // Defaults. + defaults: { + // Custom. + custom: {} + // Unit. + unit: string + // Min. + min: int + // Max. + max: int + // Decimals. + decimals: int + // Change the field or series name. + displayName: string + // What to show when there is no value. + noValue: string + // Threshold config. + thresholds: thresholds + // Mappings. + mappings: [...mapping] + // Data Links. + links: [...dataLink] + } + // Overrides. + overrides: [...override] + } + // Options. + options: { + // Reduce options. + reduceOptions: { + // * `true` - Show a calculated value based on all rows. + // * `false` - Show a separate stat for every row. + values: bool | *false + // If values is false, sets max number of rows to + // display. + limit: int + // Reducer function/calculation. + calcs: [ + "allIsZero", + "allIsNull", + "changeCount", + "count", + "delta", + "diff", + "distinctCount", + "first", + "firstNotNull", + "lastNotNull", + "last", + "logmin", + "max", + "min", + "range", + "step", + "sum", + ] | *["mean"] + // Fields that should be included in the panel. + fields: string | *"" + } + // Render the threshold values around the gauge bar. + showThresholdLabels: bool | *false + // Render the thresholds as an outer bar. + showThresholdMarkers: bool | *true + } + // Panel type. + type: string | *"gauge" +} diff --git a/dashboard-schemas/panels/Graph.cue b/dashboard-schemas/panels/Graph.cue new file mode 100644 index 00000000000..456a090f5da --- /dev/null +++ b/dashboard-schemas/panels/Graph.cue @@ -0,0 +1,192 @@ +package panels + +#Graph: panel & { + // Display values as a bar chart. + bars: bool | *false + // Dashed line length. + dashLength: int | *10 + // Show line with dashes. + dashes: bool | *false + // Dashed line spacing when `dashes` is true. + spaceLength: int | *10 + // Controls how many decimals are displayed for legend values and graph hover + // tooltips. + decimals: int + // Field config. + fieldConfig: { + // Defaults. + defaults: custom: {} + // Overrides. + overrides: [...override] + } + // Amount of color fill for a series. Expects a value between 0 and 1. + fill: number & >=0 & <=1 | *1 + // Degree of gradient on the area fill. 0 is no gradient, 10 is a steep + // gradient. + fillGradient: int & >=0 & <=10 | *0 + // Hide the series. + hiddenSeries: bool | *false + // Lengend options. + legend: { + // Whether to display legend in table. + alignAsTable: bool | *false + // Average of all values returned from the metric query. + avg: bool | *false + // Last value returned from the metric query. + current: bool | *false + // Maximum of all values returned from the metric query. + max: bool | *false + // Minimum of all values returned from the metric query. + min: bool | *false + // Display legend to the right. + rightSide: bool | *false + // Show or hide the legend. + show: bool | *true + // Available when `rightSide` is true. The minimum width for the legend in + // pixels. + sideWidth?: int + // Sum of all values returned from the metric query. + total: bool | *false + // Values. + values: bool | *true + } + // Display values as a line graph. + lines: bool | *true + // The width of the line for a series. + linewidth: int | *1 + // How null values are displayed. + // * 'null' - If there is a gap in the series, meaning a null value, then the + // line in the graph will be broken and show the gap. + // * 'null as zero' - If there is a gap in the series, meaning a null value, + // then it will be displayed as a zero value in the graph panel. + // * 'connected' - If there is a gap in the series, meaning a null value or + // values, then the line will skip the gap and connect to the next non-null + // value. + nullPointMode: string | *"null" + // Options. + options: { + // Data links. + dataLinks: [...dataLink] + } + // Available when `stack` is true. Each series is drawn as a percentage of the + // total of all series. + percentage: bool | *false + // Controls how large the points are. + pointradius: int + // Display points for values. + points: bool | *true + // Renderer. + renderer: string | *"flot" + // Series overrides allow a series in a graph panel to be rendered + // differently from the others. You can customize display options on a + // per-series bases or by using regex rules. For example, one series can have + // a thicker line width to make it stand out or be moved to the right Y-axis. + seriesOverrides: [...{ + // Alias or regex matching the series you'd like to target. + alias?: string + bars?: bool + lines?: bool + fill?: int + fillGradient?: int + linewidth?: int + nullPointMode?: string + fillBelowTo?: string + steppedLine?: bool + dashes?: bool + hiddenSeries?: bool + dashLength?: int + spaceLength?: int + points?: bool + pointradius?: int + stack?: int + color?: string + yaxis?: int + zindex?: int + transform?: string + legend?: bool + hideTooltip?: bool + }] + // Each series is stacked on top of another. + stack: bool | *false + // Draws adjacent points as staircase. + steppedLine: bool | *false + // Threshold config. + thresholds: thresholds + // Time from. + timeFrom: string + // Time regions. + timeRegions: [...string] + // Time shift + timeShift: string + // Tooltip settings. + tooltip: { + // * true - The hover tooltip shows all series in the graph. Grafana + // highlights the series that you are hovering over in bold in the series + // list in the tooltip. + // * false - The hover tooltip shows only a single series, the one that you + // are hovering over on the graph. + shared: bool | *true + // * 0 (none) - The order of the series in the tooltip is determined by the + // sort order in your query. For example, they could be alphabetically + // sorted by series name. + // * 1 (increasing) - The series in the hover tooltip are sorted by value + // and in increasing order, with the lowest value at the top of the list. + // * 2 (decreasing) - The series in the hover tooltip are sorted by value + // and in decreasing order, with the highest value at the top of the list. + sort: int & >=0 & <=2 | *2 + // Value type. + value_type: string | *"individual" + } + // Panel type. + type: string | *"graph" + xaxis: { + // Buckets. + buckets: string + // The display mode completely changes the visualization of the graph + // panel. It’s like three panels in one. The main mode is the time series + // mode with time on the X-axis. The other two modes are a basic bar chart + // mode with series on the X-axis instead of time and a histogram mode. + // * 'time' - The X-axis represents time and that the data is grouped by + // time (for example, by hour, or by minute). + // * 'series' - The data is grouped by series and not by time. The Y-axis + // still represents the value. + // * 'histogram' - Converts the graph into a histogram. A histogram is a + // kind of bar chart that groups numbers into ranges, often called buckets + // or bins. Taller bars show that more data falls in that range. + mode: string | *"time" + // Name. + name: string + // Show or hide the axis. + show: bool | *true + // Values + values: [...number] + } + yaxes: [...{ + // Defines how many decimals are displayed for Y value. + decimals: int + // The display unit for the Y value. + format: string | *"short" + // The Y axis label. + label: string + // The scale to use for the Y value - linear, or logarithmic. + // * 1 - linear + // * 2 - log (base 2) + // * 10 - log (base 10) + // * 32 - log (base 32) + // * 1024 - log (base 1024) + logBase: int | *1 + // The maximum Y value. + max?: int + // The minimum Y value. + min?: int + // Show or hide the axis. + show: bool | *true + }] + yaxis: { + // Align left and right Y-axes by value. + align: bool | *false + // Available when align is true. Value to use for alignment of left and + // right Y-axes, starting from Y=0. + alignLevel: int | *0 + } +} diff --git a/dashboard-schemas/panels/Row.cue b/dashboard-schemas/panels/Row.cue new file mode 100644 index 00000000000..7c9d8bf498b --- /dev/null +++ b/dashboard-schemas/panels/Row.cue @@ -0,0 +1,24 @@ +package panels + +// A row is a logical divider within a dashboard. It is used +// to group panels together. +#Row: { + // Whether the row is collapsed or not. + collapsed: bool | *true + // Name of default data source. + datasource?: string + // Grid position. + gridPos?: gridPos + // Dashboard panels. + panels?: [...{}] + // Name of template variable to repeat for. + repeat?: string + // Whether to display the title. + showTitle: bool | *true + // Title. + title?: string + // Size of title. + titleSize: string | *"h6" + // Panel type. + type: string | *"row" +} diff --git a/dashboard-schemas/panels/gridPos.cue b/dashboard-schemas/panels/gridPos.cue new file mode 100644 index 00000000000..ebfd83feb56 --- /dev/null +++ b/dashboard-schemas/panels/gridPos.cue @@ -0,0 +1,12 @@ +package panels + +gridPos: { + // Panel height. + h: int & >0 | *9 + // Panel width. + w: int & >0 <= 24 | 12 + // Panel x position. + x: int & >0 < 24 + // Panel y position. + y: int & >0 +} diff --git a/dashboard-schemas/panels/link.cue b/dashboard-schemas/panels/link.cue new file mode 100644 index 00000000000..4680fe4a06e --- /dev/null +++ b/dashboard-schemas/panels/link.cue @@ -0,0 +1,14 @@ +package panels + +_link: { + // Link title. + title?: string + // Whether to open link in new browser tab. + targetBlank: bool | *true + // URL of link. + url: string +} + +panelLink: _link + +dataLink: _link diff --git a/dashboard-schemas/panels/mapping.cue b/dashboard-schemas/panels/mapping.cue new file mode 100644 index 00000000000..9dc19a1134b --- /dev/null +++ b/dashboard-schemas/panels/mapping.cue @@ -0,0 +1,11 @@ +package panels + +mapping: { + id: int + from: string + operator: string + to: string + text: string + type: int + value: string +} diff --git a/dashboard-schemas/panels/override.cue b/dashboard-schemas/panels/override.cue new file mode 100644 index 00000000000..bc31af8d75a --- /dev/null +++ b/dashboard-schemas/panels/override.cue @@ -0,0 +1,12 @@ +package panels + +override: { + matcher: { + id: string + options: string + } + properties: [...{ + id: string + value: int + }] +} diff --git a/dashboard-schemas/panels/panel.cue b/dashboard-schemas/panels/panel.cue new file mode 100644 index 00000000000..f2a4fa2cd91 --- /dev/null +++ b/dashboard-schemas/panels/panel.cue @@ -0,0 +1,24 @@ +package panels + +panel: { + // Panel title. + title?: string + // Description. + description?: string + // Whether to display the panel without a background. + transparent: bool | *false + // Name of default datasource. + datasource?: string + // Grid position. + gridPos?: gridPos + // Panel links. + links?: [...panelLink] + // Name of template variable to repeat for. + repeat?: string + // Direction to repeat in if 'repeat' is set. + // "h" for horizontal, "v" for vertical. + repeatDirection: *"h" | "v" + // Panel targets - datasource and query configurations to use as + // a basis for vizualization. + targets?: [...{}] +} diff --git a/dashboard-schemas/panels/thresholds.cue b/dashboard-schemas/panels/thresholds.cue new file mode 100644 index 00000000000..06635a5cc0e --- /dev/null +++ b/dashboard-schemas/panels/thresholds.cue @@ -0,0 +1,11 @@ +package panels + +thresholds: { + // Threshold mode. + mode: string | *"absolute" + // Threshold steps. + steps: [...{ + color: string + value: number + }] +} diff --git a/dashboard-schemas/targets/Prometheus.cue b/dashboard-schemas/targets/Prometheus.cue new file mode 100644 index 00000000000..93052139d5c --- /dev/null +++ b/dashboard-schemas/targets/Prometheus.cue @@ -0,0 +1,19 @@ +package targets + +#Prometheus: { + // Query expression. + expr: string + // Controls the name of the time series, using name or pattern. + legendFormat?: string + // Interval. + interval?: int | *1 + // Target reference ID. + refId: string + // Perform an “instant” query, to return only the latest value that + // Prometheus has scraped for the requested time series. + instant: bool | *false + // Resolution. + intervalFactor?: int + // Format. + format: *"time_series" | "table" | "heat_map" +} diff --git a/dashboard-schemas/transformations/CalculateField.cue b/dashboard-schemas/transformations/CalculateField.cue new file mode 100644 index 00000000000..79fcd457acd --- /dev/null +++ b/dashboard-schemas/transformations/CalculateField.cue @@ -0,0 +1,39 @@ +package transformations + +// Add field from calculation. +#CalculateField: { + // Transformation ID. + id: string | *"calculateField" + // Configuration options. + options: { + // The name of your new field. If you leave this blank, then the field will + // be named to match the calculation. + alias: string + // Binary options. + binary: { + // Field or number for left side of equation. + left: string + // Field or number for right side of equation. + right: string + // Operator. + operator: string | *"+" + // Calculation to use. + reducer: string | *"sum" + } + // 'reduceRow' - apply selected calculation on each row of selected fields + // independently. + // 'binary' - apply basic math operation(sum, multiply, etc) on values in a + // single row from two selected fields. + mode: *"reduceRow" | "binary" + // Reduce options. + reduce: { + // Calculation to use. + reducer: string + // Fields to include in calculation. + include: [...string] + } + // Hide all other fields and display only your calculated field in the + // visualization. + replaceFields: bool | *false + } +} diff --git a/dashboard-schemas/transformations/Organize.cue b/dashboard-schemas/transformations/Organize.cue new file mode 100644 index 00000000000..fb2387508fa --- /dev/null +++ b/dashboard-schemas/transformations/Organize.cue @@ -0,0 +1,16 @@ +package transformations + +// Reorder, hide, or rename fields/columns. +#Organize: { + // Transformation ID. + id: string | *"organize" + // Configuration options. + options: { + // Exclude fields by name. + excludeByName: {} + // Set field order by name. + indexByName: {} + // Rename a field by name. + renameByName: {} + } +} diff --git a/dashboard-schemas/variables/Custom.cue b/dashboard-schemas/variables/Custom.cue new file mode 100644 index 00000000000..b1405cb6bc8 --- /dev/null +++ b/dashboard-schemas/variables/Custom.cue @@ -0,0 +1,9 @@ +package variables + +// Custom variables are for values that do not change. +#Custom: variable & { + // Options as comma separated values. + query: string + // Variable type. + type: string | *"custom" +} diff --git a/dashboard-schemas/variables/Datasource.cue b/dashboard-schemas/variables/Datasource.cue new file mode 100644 index 00000000000..e34a747d10c --- /dev/null +++ b/dashboard-schemas/variables/Datasource.cue @@ -0,0 +1,18 @@ +package variables + +// Data source variables allow you to quickly change the data source for an +// entire dashboard. +#Datasource: variable & { + // Data source type. + query: string + // Query value. + queryValue: string | *"" + // Refresh. + refresh: int | *1 + // Regex filter for which data source instances to choose + // from in the variable value dropdown. Leave empty for + // all. + regex: string + // Variable type. + type: string | *"datasource" +} diff --git a/dashboard-schemas/variables/Query.cue b/dashboard-schemas/variables/Query.cue new file mode 100644 index 00000000000..f56c8235707 --- /dev/null +++ b/dashboard-schemas/variables/Query.cue @@ -0,0 +1,30 @@ +package variables + +// Query variables allow you to write a data source query that can return a +// list of metric names, tag values, or keys. +#Query: variable & { + // Data source to use. + datasource: string + // Definition. + definition?: string + // Query. + query: string + // Refresh. + refresh: int | *1 + // Regex. + regex?: string + // * 0 - Disabled. + // * 1 - Alphabetical (asc). + // * 2 - Alphabetical (desc). + // * 3 - Numerical (asc). + // * 4 - Numerical (desc). + // * 5 - Alphabetical (case-insensitive, asc). + // * 6 - Alphabetical (case-insensitive, desc). + sort: int & >=0 & <=6 | *0 + tagValuesQuery?: string + tags: [...string] | *[] + tagsQuery?: string + // Variable type. + type: "query" + useTags: bool | *false +} diff --git a/dashboard-schemas/variables/variable.cue b/dashboard-schemas/variables/variable.cue new file mode 100644 index 00000000000..a2ce1d6ab40 --- /dev/null +++ b/dashboard-schemas/variables/variable.cue @@ -0,0 +1,33 @@ +package variables + +variable: { + // Currently selected value. + current: { + selected: bool | *false + text: string | [...string] + value: string | [...string] + } + // Whether to hide the label and variable. + // * 0 - Show all. + // * 1 - Hide label. + // * 2 - Hide label and variable. + hide: int & >=0 & <=2 | *0 + // Enable include all option. + includeAll: bool | *false + // When includeAll is enabled, this sets its value. + allValue?: string + // Optional display name. + label?: string + // Allows mutltiple values to be selected at the same time. + multi: bool | *false + // Variable name. + name: string + // Options for variable. + options: [...{ + selected: bool + text: string + value: string + }] + // Skip URL sync. + skipUrlSync: bool | *false +}