mirror of https://github.com/grafana/grafana
Dashboards: Add Dashboard Schema validation (1) (#103662)
parent
920c7b1de5
commit
95f04c79cd
@ -0,0 +1,759 @@ |
||||
// This file is managed by Grafana - DO NOT EDIT MANUALLY |
||||
// Source: kinds/dashboard/dashboard_kind.cue |
||||
// To sync changes, run: make gen-cue |
||||
|
||||
package kind |
||||
|
||||
import ( |
||||
"strings" |
||||
t "time" |
||||
) |
||||
|
||||
name: "Dashboard" |
||||
maturity: "experimental" |
||||
description: "A Grafana dashboard." |
||||
|
||||
crd: dummySchema: true |
||||
|
||||
lineage: schemas: [{ |
||||
version: [0, 0] |
||||
schema: { |
||||
spec: { |
||||
// Unique numeric identifier for the dashboard. |
||||
// `id` is internal to a specific Grafana instance. `uid` should be used to identify a dashboard across Grafana instances. |
||||
id?: int64 | null // TODO eliminate this null option |
||||
|
||||
// Unique dashboard identifier that can be generated by anyone. string (8-40) |
||||
uid?: string |
||||
|
||||
// Title of dashboard. |
||||
title?: string |
||||
|
||||
// Description of dashboard. |
||||
description?: string |
||||
|
||||
// This property should only be used in dashboards defined by plugins. It is a quick check |
||||
// to see if the version has changed since the last time. |
||||
revision?: int64 |
||||
|
||||
// ID of a dashboard imported from the https://grafana.com/grafana/dashboards/ portal |
||||
gnetId?: string |
||||
|
||||
// Tags associated with dashboard. |
||||
tags?: [...string] |
||||
|
||||
// Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc". |
||||
timezone?: string | *"browser" |
||||
|
||||
// Whether a dashboard is editable or not. |
||||
editable?: bool | *true |
||||
|
||||
// Configuration of dashboard cursor sync behavior. |
||||
// Accepted values are 0 (sync turned off), 1 (shared crosshair), 2 (shared crosshair and tooltip). |
||||
graphTooltip?: #DashboardCursorSync |
||||
|
||||
// Time range for dashboard. |
||||
// Accepted values are relative time strings like {from: 'now-6h', to: 'now'} or absolute time strings like {from: '2020-07-10T08:00:00.000Z', to: '2020-07-10T14:00:00.000Z'}. |
||||
time?: { |
||||
from: string | *"now-6h" |
||||
to: string | *"now" |
||||
} |
||||
|
||||
// Configuration of the time picker shown at the top of a dashboard. |
||||
timepicker?: #TimePickerConfig |
||||
|
||||
// The month that the fiscal year starts on. 0 = January, 11 = December |
||||
fiscalYearStartMonth?: uint8 & <12 | *0 |
||||
|
||||
// When set to true, the dashboard will redraw panels at an interval matching the pixel width. |
||||
// This will keep data "moving left" regardless of the query refresh rate. This setting helps |
||||
// avoid dashboards presenting stale live data |
||||
liveNow?: bool |
||||
|
||||
// Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday". |
||||
weekStart?: string |
||||
|
||||
// Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". |
||||
refresh?: string |
||||
|
||||
// Version of the JSON schema, incremented each time a Grafana update brings |
||||
// changes to said schema. |
||||
schemaVersion: uint16 | *41 |
||||
|
||||
// Version of the dashboard, incremented each time the dashboard is updated. |
||||
version?: uint32 |
||||
|
||||
// List of dashboard panels |
||||
panels?: [...(#Panel | #RowPanel)] |
||||
|
||||
// Configured template variables |
||||
templating?: { |
||||
// List of configured template variables with their saved values along with some other metadata |
||||
list?: [...#VariableModel] |
||||
} |
||||
|
||||
// Contains the list of annotations that are associated with the dashboard. |
||||
// Annotations are used to overlay event markers and overlay event tags on graphs. |
||||
// Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API. |
||||
// See https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/annotate-visualizations/ |
||||
annotations?: #AnnotationContainer |
||||
|
||||
// Links with references to other dashboards or external websites. |
||||
links?: [...#DashboardLink] |
||||
|
||||
// Snapshot options. They are present only if the dashboard is a snapshot. |
||||
snapshot?: #Snapshot @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// When set to true, the dashboard will load all panels in the dashboard when it's loaded. |
||||
preload?: bool |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
/////////////////////////////////////// |
||||
// Definitions (referenced above) are declared below |
||||
|
||||
// TODO: this should be a regular DataQuery that depends on the selected dashboard |
||||
// these match the properties of the "grafana" datasouce that is default in most dashboards |
||||
#AnnotationTarget: { |
||||
// Only required/valid for the grafana datasource... |
||||
// but code+tests is already depending on it so hard to change |
||||
limit: int64 |
||||
// Only required/valid for the grafana datasource... |
||||
// but code+tests is already depending on it so hard to change |
||||
matchAny: bool |
||||
// Only required/valid for the grafana datasource... |
||||
// but code+tests is already depending on it so hard to change |
||||
tags: [...string] |
||||
// Only required/valid for the grafana datasource... |
||||
// but code+tests is already depending on it so hard to change |
||||
type: string |
||||
... // datasource will stick their raw DataQuery here |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
#AnnotationPanelFilter: { |
||||
// Should the specified panels be included or excluded |
||||
exclude?: bool | *false |
||||
|
||||
// Panel IDs that should be included or excluded |
||||
ids: [...uint8] |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Contains the list of annotations that are associated with the dashboard. |
||||
// Annotations are used to overlay event markers and overlay event tags on graphs. |
||||
// Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API. |
||||
// See https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/annotate-visualizations/ |
||||
#AnnotationContainer: { |
||||
// List of annotations |
||||
list?: [...#AnnotationQuery] |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
// TODO docs |
||||
// FROM: AnnotationQuery in grafana-data/src/types/annotations.ts |
||||
#AnnotationQuery: { |
||||
// Name of annotation. |
||||
name: string |
||||
|
||||
// Datasource where the annotations data is |
||||
datasource: #DataSourceRef |
||||
|
||||
// When enabled the annotation query is issued with every dashboard refresh |
||||
enable: bool | *true |
||||
|
||||
// Annotation queries can be toggled on or off at the top of the dashboard. |
||||
// When hide is true, the toggle is not shown in the dashboard. |
||||
hide?: bool | *false |
||||
|
||||
// Color to use for the annotation event markers |
||||
iconColor: string |
||||
|
||||
// Filters to apply when fetching annotations |
||||
filter?: #AnnotationPanelFilter |
||||
|
||||
// TODO.. this should just be a normal query target |
||||
target?: #AnnotationTarget |
||||
|
||||
// TODO -- this should not exist here, it is based on the --grafana-- datasource |
||||
type?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Set to 1 for the standard annotation query all dashboards have by default. |
||||
builtIn?: number | *0 |
||||
|
||||
// unless datasources have migrated to the target+mapping, |
||||
// they just spread their query into the base object :( |
||||
... |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// A variable is a placeholder for a value. You can use variables in metric queries and in panel titles. |
||||
#VariableModel: { |
||||
// Type of variable |
||||
type: #VariableType |
||||
// Name of variable |
||||
name: string |
||||
// Optional display name |
||||
label?: string |
||||
// Visibility configuration for the variable |
||||
hide?: #VariableHide |
||||
// Whether the variable value should be managed by URL query params or not |
||||
skipUrlSync?: bool | *false |
||||
// Description of variable. It can be defined but `null`. |
||||
description?: string |
||||
// Query used to fetch values for a variable |
||||
query?: string | {...} |
||||
// Data source used to fetch values for a variable. It can be defined but `null`. |
||||
datasource?: #DataSourceRef |
||||
// Shows current selected variable text/value on the dashboard |
||||
current?: #VariableOption |
||||
// Whether multiple values can be selected or not from variable value list |
||||
multi?: bool | *false |
||||
// Allow custom values to be entered in the variable |
||||
allowCustomValue?: bool | *true |
||||
// Options that can be selected for a variable. |
||||
options?: [...#VariableOption] |
||||
// Options to config when to refresh a variable |
||||
refresh?: #VariableRefresh |
||||
// Options sort order |
||||
sort?: #VariableSort |
||||
// Whether all value option is available or not |
||||
includeAll?: bool | *false |
||||
// Custom all value |
||||
allValue?: string |
||||
// Optional field, if you want to extract part of a series name or metric node segment. |
||||
// Named capture groups can be used to separate the display text and value. |
||||
regex?: string |
||||
... |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Option to be selected in a variable. |
||||
#VariableOption: { |
||||
// Whether the option is selected or not |
||||
selected?: bool |
||||
// Text to be displayed for the option |
||||
text: string | [...string] |
||||
// Value of the option |
||||
value: string | [...string] |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Options to config when to refresh a variable |
||||
// `0`: Never refresh the variable |
||||
// `1`: Queries the data source every time the dashboard loads. |
||||
// `2`: Queries the data source when the dashboard time range changes. |
||||
#VariableRefresh: 0 | 1 | 2 @cuetsy(kind="enum",memberNames="never|onDashboardLoad|onTimeRangeChanged") |
||||
|
||||
// Determine if the variable shows on dashboard |
||||
// Accepted values are 0 (show label and value), 1 (show value only), 2 (show nothing). |
||||
#VariableHide: 0 | 1 | 2 @cuetsy(kind="enum",memberNames="dontHide|hideLabel|hideVariable") @grafana(TSVeneer="type") |
||||
|
||||
// Sort variable options |
||||
// Accepted values are: |
||||
// `0`: No sorting |
||||
// `1`: Alphabetical ASC |
||||
// `2`: Alphabetical DESC |
||||
// `3`: Numerical ASC |
||||
// `4`: Numerical DESC |
||||
// `5`: Alphabetical Case Insensitive ASC |
||||
// `6`: Alphabetical Case Insensitive DESC |
||||
// `7`: Natural ASC |
||||
// `8`: Natural DESC |
||||
#VariableSort: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 @cuetsy(kind="enum",memberNames="disabled|alphabeticalAsc|alphabeticalDesc|numericalAsc|numericalDesc|alphabeticalCaseInsensitiveAsc|alphabeticalCaseInsensitiveDesc|naturalAsc|naturalDesc") |
||||
|
||||
// Ref to a DataSource instance |
||||
#DataSourceRef: { |
||||
// The plugin type-id |
||||
type?: string |
||||
|
||||
// Specific datasource instance |
||||
uid?: string |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
// Links with references to other dashboards or external resources |
||||
#DashboardLink: { |
||||
// Title to display with the link |
||||
title: string |
||||
// Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) |
||||
type: #DashboardLinkType |
||||
// Icon name to be displayed with the link |
||||
icon: string |
||||
// Tooltip to display when the user hovers their mouse over it |
||||
tooltip: string |
||||
// Link URL. Only required/valid if the type is link |
||||
url?: string |
||||
// List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards |
||||
tags: [...string] |
||||
// If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards |
||||
asDropdown: bool | *false |
||||
// If true, the link will be opened in a new tab |
||||
targetBlank: bool | *false |
||||
// If true, includes current template variables values in the link as query params |
||||
includeVars: bool | *false |
||||
// If true, includes current time range in the link as query params |
||||
keepTime: bool | *false |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) |
||||
#DashboardLinkType: "link" | "dashboards" @cuetsy(kind="type") |
||||
|
||||
// Dashboard variable type |
||||
// `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on. |
||||
// `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only). |
||||
// `constant`: Define a hidden constant. |
||||
// `datasource`: Quickly change the data source for an entire dashboard. |
||||
// `interval`: Interval variables represent time spans. |
||||
// `textbox`: Display a free text input field with an optional default value. |
||||
// `custom`: Define the variable options manually using a comma-separated list. |
||||
// `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables |
||||
#VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" | |
||||
"system" | "snapshot" @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. |
||||
// Continuous color interpolates a color using the percentage of a value relative to min and max. |
||||
// Accepted values are: |
||||
// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold |
||||
// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations |
||||
// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations |
||||
// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode |
||||
// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode |
||||
// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode |
||||
// `continuous-YlRd`: Continuous Yellow-Red palette mode |
||||
// `continuous-BlPu`: Continuous Blue-Purple palette mode |
||||
// `continuous-YlBl`: Continuous Yellow-Blue palette mode |
||||
// `continuous-blues`: Continuous Blue palette mode |
||||
// `continuous-reds`: Continuous Red palette mode |
||||
// `continuous-greens`: Continuous Green palette mode |
||||
// `continuous-purples`: Continuous Purple palette mode |
||||
// `shades`: Shades of a single color. Specify a single color, useful in an override rule. |
||||
// `fixed`: Fixed color mode. Specify a single color, useful in an override rule. |
||||
#FieldColorModeId: "thresholds" | "palette-classic" | "palette-classic-by-name" | "continuous-GrYlRd" | "continuous-RdYlGr" | "continuous-BlYlRd" | "continuous-YlRd" | "continuous-BlPu" | "continuous-YlBl" | "continuous-blues" | "continuous-reds" | "continuous-greens" | "continuous-purples" | "fixed" | "shades" @cuetsy(kind="enum",memberNames="Thresholds|PaletteClassic|PaletteClassicByName|ContinuousGrYlRd|ContinuousRdYlGr|ContinuousBlYlRd|ContinuousYlRd|ContinuousBlPu|ContinuousYlBl|ContinuousBlues|ContinuousReds|ContinuousGreens|ContinuousPurples|Fixed|Shades") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. |
||||
#FieldColorSeriesByMode: "min" | "max" | "last" @cuetsy(kind="type") |
||||
|
||||
// Map a field to a color. |
||||
#FieldColor: { |
||||
// The main color scheme mode. |
||||
mode: #FieldColorModeId |
||||
// The fixed color value for fixed or shades color modes. |
||||
fixedColor?: string |
||||
// Some visualizations need to know how to assign a series color from by value color schemes. |
||||
seriesBy?: #FieldColorSeriesByMode |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Position and dimensions of a panel in the grid |
||||
#GridPos: { |
||||
// Panel height. The height is the number of rows from the top edge of the panel. |
||||
h: uint32 & >0 | *9 |
||||
// Panel width. The width is the number of columns from the left edge of the panel. |
||||
w: uint32 & >0 & <=24 | *12 |
||||
// Panel x. The x coordinate is the number of columns from the left edge of the grid |
||||
x: uint32 & >=0 & <24 | *0 |
||||
// Panel y. The y coordinate is the number of rows from the top edge of the grid |
||||
y: uint32 & >=0 | *0 |
||||
// Whether the panel is fixed within the grid. If true, the panel will not be affected by other panels' interactions |
||||
static?: bool |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// User-defined value for a metric that triggers visual changes in a panel when this value is met or exceeded |
||||
// They are used to conditionally style and color visualizations based on query results , and can be applied to most visualizations. |
||||
#Threshold: { |
||||
// Value represents a specified metric for the threshold, which triggers a visual change in the dashboard when this value is met or exceeded. |
||||
// Nulls currently appear here when serializing -Infinity to JSON. |
||||
value: number | null @grafanamaturity(NeedsExpertReview) |
||||
// Color represents the color of the visual change that will occur in the dashboard when the threshold value is met or exceeded. |
||||
color: string @grafanamaturity(NeedsExpertReview) |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Thresholds can either be `absolute` (specific number) or `percentage` (relative to min or max, it will be values between 0 and 1). |
||||
#ThresholdsMode: "absolute" | "percentage" @cuetsy(kind="enum",memberNames="Absolute|Percentage") |
||||
|
||||
// Thresholds configuration for the panel |
||||
#ThresholdsConfig: { |
||||
// Thresholds mode. |
||||
mode: #ThresholdsMode |
||||
|
||||
// Must be sorted by 'value', first value is always -Infinity |
||||
steps: [...#Threshold] @grafanamaturity(NeedsExpertReview) |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Allow to transform the visual representation of specific data values in a visualization, irrespective of their original units |
||||
#ValueMapping: #ValueMap | #RangeMap | #RegexMap | #SpecialValueMap @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Supported value mapping types |
||||
// `value`: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. |
||||
// `range`: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. |
||||
// `regex`: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. |
||||
// `special`: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A. |
||||
#MappingType: "value" | "range" | "regex" | "special" @cuetsy(kind="enum",memberNames="ValueToText|RangeToText|RegexToText|SpecialValue") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Maps text values to a color or different display text and color. |
||||
// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. |
||||
#ValueMap: { |
||||
type: #MappingType & "value" |
||||
// Map with <value_to_match>: ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } } |
||||
options: [string]: #ValueMappingResult |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Maps numerical ranges to a display text and color. |
||||
// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. |
||||
#RangeMap: { |
||||
type: #MappingType & "range" |
||||
// Range to match against and the result to apply when the value is within the range |
||||
options: { |
||||
// Min value of the range. It can be null which means -Infinity |
||||
from: float64 | null |
||||
// Max value of the range. It can be null which means +Infinity |
||||
to: float64 | null |
||||
// Config to apply when the value is within the range |
||||
result: #ValueMappingResult |
||||
} |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Maps regular expressions to replacement text and a color. |
||||
// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. |
||||
#RegexMap: { |
||||
type: #MappingType & "regex" |
||||
// Regular expression to match against and the result to apply when the value matches the regex |
||||
options: { |
||||
// Regular expression to match against |
||||
pattern: string |
||||
// Config to apply when the value matches the regex |
||||
result: #ValueMappingResult |
||||
} |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. |
||||
// See SpecialValueMatch to see the list of special values. |
||||
// For example, you can configure a special value mapping so that null values appear as N/A. |
||||
#SpecialValueMap: { |
||||
type: #MappingType & "special" |
||||
options: { |
||||
// Special value to match against |
||||
match: #SpecialValueMatch |
||||
// Config to apply when the value matches the special value |
||||
result: #ValueMappingResult |
||||
} |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Special value types supported by the `SpecialValueMap` |
||||
#SpecialValueMatch: "true" | "false" | "null" | "nan" | "null+nan" | "empty" @cuetsy(kind="enum",memberNames="True|False|Null|NaN|NullAndNan|Empty") |
||||
|
||||
// Result used as replacement with text and color when the value matches |
||||
#ValueMappingResult: { |
||||
// Text to display when the value matches |
||||
text?: string |
||||
// Text to use when the value matches |
||||
color?: string |
||||
// Icon to display when the value matches. Only specific visualizations. |
||||
icon?: string |
||||
// Position in the mapping array. Only used internally. |
||||
index?: int32 |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Transformations allow to manipulate data returned by a query before the system applies a visualization. |
||||
// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, |
||||
// use the output of one transformation as the input to another transformation, etc. |
||||
#DataTransformerConfig: { |
||||
// Unique identifier of transformer |
||||
id: string |
||||
// Disabled transformations are skipped |
||||
disabled?: bool |
||||
// Optional frame matcher. When missing it will be applied to all results |
||||
filter?: #MatcherConfig |
||||
// Where to pull DataFrames from as input to transformation |
||||
topic?: "series" | "annotations" | "alertStates" // replaced with common.DataTopic |
||||
// Options to be passed to the transformer |
||||
// Valid options depend on the transformer id |
||||
options: _ |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
// Counterpart for TypeScript's TimeOption type. |
||||
#TimeOption: { |
||||
display: string |
||||
from: string |
||||
to: string |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
// Time picker configuration |
||||
// It defines the default config for the time picker and the refresh picker for the specific dashboard. |
||||
#TimePickerConfig: { |
||||
// Whether timepicker is visible or not. |
||||
hidden?: bool | *false |
||||
// Interval options available in the refresh picker dropdown. |
||||
refresh_intervals?: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] |
||||
// Quick ranges for time picker. |
||||
quick_ranges?: [...#TimeOption] |
||||
// Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. |
||||
nowDelay?: string |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
// 0 for no shared crosshair or tooltip (default). |
||||
// 1 for shared crosshair. |
||||
// 2 for shared crosshair AND shared tooltip. |
||||
#DashboardCursorSync: *0 | 1 | 2 @cuetsy(kind="enum",memberNames="Off|Crosshair|Tooltip") |
||||
|
||||
// Schema for panel targets is specified by datasource |
||||
// plugins. We use a placeholder definition, which the Go |
||||
// schema loader either left open/as-is with the Base |
||||
// variant of the Dashboard and Panel families, or filled |
||||
// with types derived from plugins in the Instance variant. |
||||
// When working directly from CUE, importers can extend this |
||||
// type directly to achieve the same effect. |
||||
#Target: {...} |
||||
|
||||
// A dashboard snapshot shares an interactive dashboard publicly. |
||||
// It is a read-only version of a dashboard, and is not editable. |
||||
// It is possible to create a snapshot of a snapshot. |
||||
// Grafana strips away all sensitive information from the dashboard. |
||||
// Sensitive information stripped: queries (metric, template,annotation) and panel links. |
||||
#Snapshot: { |
||||
// Time when the snapshot was created |
||||
created: string & t.Time |
||||
// Time when the snapshot expires, default is never to expire |
||||
expires: string @grafanamaturity(NeedsExpertReview) |
||||
// Is the snapshot saved in an external grafana instance |
||||
external: bool @grafanamaturity(NeedsExpertReview) |
||||
// external url, if snapshot was shared in external grafana instance |
||||
externalUrl: string @grafanamaturity(NeedsExpertReview) |
||||
// original url, url of the dashboard that was snapshotted |
||||
originalUrl: string @grafanamaturity(NeedsExpertReview) |
||||
// Unique identifier of the snapshot |
||||
id: uint32 @grafanamaturity(NeedsExpertReview) |
||||
// Optional, defined the unique key of the snapshot, required if external is true |
||||
key: string @grafanamaturity(NeedsExpertReview) |
||||
// Optional, name of the snapshot |
||||
name: string @grafanamaturity(NeedsExpertReview) |
||||
// org id of the snapshot |
||||
orgId: uint32 @grafanamaturity(NeedsExpertReview) |
||||
// last time when the snapshot was updated |
||||
updated: string & t.Time |
||||
// url of the snapshot, if snapshot was shared internally |
||||
url?: string @grafanamaturity(NeedsExpertReview) |
||||
// user id of the snapshot creator |
||||
userId: uint32 @grafanamaturity(NeedsExpertReview) |
||||
} @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Dashboard panels are the basic visualization building blocks. |
||||
#Panel: { |
||||
// The panel plugin type id. This is used to find the plugin to display the panel. |
||||
type: string & strings.MinRunes(1) |
||||
|
||||
// Unique identifier of the panel. Generated by Grafana when creating a new panel. It must be unique within a dashboard, but not globally. |
||||
id?: uint32 |
||||
|
||||
// The version of the plugin that is used for this panel. This is used to find the plugin to display the panel and to migrate old panel configs. |
||||
pluginVersion?: string |
||||
|
||||
// Depends on the panel plugin. See the plugin documentation for details. |
||||
targets?: [...#Target] |
||||
|
||||
// Panel title. |
||||
title?: string |
||||
|
||||
// Panel description. |
||||
description?: string |
||||
|
||||
// Whether to display the panel without a background. |
||||
transparent?: bool | *false |
||||
|
||||
// The datasource used in all targets. |
||||
datasource?: #DataSourceRef |
||||
|
||||
// Grid position. |
||||
gridPos?: #GridPos |
||||
|
||||
// Panel links. |
||||
links?: [...#DashboardLink] |
||||
|
||||
// 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" |
||||
|
||||
// Option for repeated panels that controls max items per row |
||||
// Only relevant for horizontally repeated panels |
||||
maxPerRow?: number |
||||
|
||||
// The maximum number of data points that the panel queries are retrieving. |
||||
maxDataPoints?: number |
||||
|
||||
// List of transformations that are applied to the panel data before rendering. |
||||
// When there are multiple transformations, Grafana applies them in the order they are listed. |
||||
// Each transformation creates a result set that then passes on to the next transformation in the processing pipeline. |
||||
transformations?: [...#DataTransformerConfig] |
||||
|
||||
// The min time interval setting defines a lower limit for the $__interval and $__interval_ms variables. |
||||
// This value must be formatted as a number followed by a valid time |
||||
// identifier like: "40s", "3d", etc. |
||||
// See: https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/#query-options |
||||
interval?: string |
||||
|
||||
// Overrides the relative time range for individual panels, |
||||
// which causes them to be different than what is selected in |
||||
// the dashboard time picker in the top-right corner of the dashboard. You can use this to show metrics from different |
||||
// time periods or days on the same dashboard. |
||||
// The value is formatted as time operation like: `now-5m` (Last 5 minutes), `now/d` (the day so far), |
||||
// `now-5d/d`(Last 5 days), `now/w` (This week so far), `now-2y/y` (Last 2 years). |
||||
// Note: Panel time overrides have no effect when the dashboard’s time range is absolute. |
||||
// See: https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/#query-options |
||||
timeFrom?: string |
||||
|
||||
// Overrides the time range for individual panels by shifting its start and end relative to the time picker. |
||||
// For example, you can shift the time range for the panel to be two hours earlier than the dashboard time picker setting `2h`. |
||||
// Note: Panel time overrides have no effect when the dashboard’s time range is absolute. |
||||
// See: https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/#query-options |
||||
timeShift?: string |
||||
|
||||
// Controls if the timeFrom or timeShift overrides are shown in the panel header |
||||
hideTimeOverride?: bool |
||||
|
||||
// Dynamically load the panel |
||||
libraryPanel?: #LibraryPanelRef |
||||
|
||||
// Sets panel queries cache timeout. |
||||
cacheTimeout?: string |
||||
|
||||
// Overrides the data source configured time-to-live for a query cache item in milliseconds |
||||
queryCachingTTL?: number |
||||
|
||||
// It depends on the panel plugin. They are specified by the Options field in panel plugin schemas. |
||||
options?: {...} @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Field options allow you to change how the data is displayed in your visualizations. |
||||
fieldConfig?: #FieldConfigSource |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. |
||||
// Each column within this structure is called a field. A field can represent a single time series or table column. |
||||
// Field options allow you to change how the data is displayed in your visualizations. |
||||
#FieldConfigSource: { |
||||
// Defaults are the options applied to all fields. |
||||
defaults: #FieldConfig |
||||
// Overrides are the options applied to specific fields overriding the defaults. |
||||
overrides: [...{ |
||||
matcher: #MatcherConfig |
||||
properties: [...#DynamicConfigValue] |
||||
}] @grafanamaturity(NeedsExpertReview) |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// A library panel is a reusable panel that you can use in any dashboard. |
||||
// When you make a change to a library panel, that change propagates to all instances of where the panel is used. |
||||
// Library panels streamline reuse of panels across multiple dashboards. |
||||
#LibraryPanelRef: { |
||||
// Library panel name |
||||
name: string |
||||
// Library panel uid |
||||
uid: string |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. |
||||
// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. |
||||
#MatcherConfig: { |
||||
// The matcher id. This is used to find the matcher implementation from registry. |
||||
id: string | *"" @grafanamaturity(NeedsExpertReview) |
||||
// The matcher options. This is specific to the matcher implementation. |
||||
options?: _ @grafanamaturity(NeedsExpertReview) |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
#DynamicConfigValue: { |
||||
id: string | *"" @grafanamaturity(NeedsExpertReview) |
||||
value?: _ @grafanamaturity(NeedsExpertReview) |
||||
} |
||||
|
||||
// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. |
||||
// Each column within this structure is called a field. A field can represent a single time series or table column. |
||||
// Field options allow you to change how the data is displayed in your visualizations. |
||||
#FieldConfig: { |
||||
// The display value for this field. This supports template variables blank is auto |
||||
displayName?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// This can be used by data sources that return and explicit naming structure for values and labels |
||||
// When this property is configured, this value is used rather than the default naming strategy. |
||||
displayNameFromDS?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Human readable field metadata |
||||
description?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// An explicit path to the field in the datasource. When the frame meta includes a path, |
||||
// This will default to `${frame.meta.path}/${field.name} |
||||
// |
||||
// When defined, this value can be used as an identifier within the datasource scope, and |
||||
// may be used to update the results |
||||
path?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// True if data source can write a value to the path. Auth/authz are supported separately |
||||
writeable?: bool @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// True if data source field supports ad-hoc filters |
||||
filterable?: bool @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Unit a field should use. The unit you select is applied to all fields except time. |
||||
// You can use the units ID availables in Grafana or a custom unit. |
||||
// Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts |
||||
// As custom unit, you can use the following formats: |
||||
// `suffix:<suffix>` for custom unit that should go after value. |
||||
// `prefix:<prefix>` for custom unit that should go before value. |
||||
// `time:<format>` For custom date time formats type for example `time:YYYY-MM-DD`. |
||||
// `si:<base scale><unit characters>` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character. |
||||
// `count:<unit>` for a custom count unit. |
||||
// `currency:<unit>` for custom a currency unit. |
||||
unit?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Specify the number of decimals Grafana includes in the rendered value. |
||||
// If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. |
||||
// For example 1.1234 will display as 1.12 and 100.456 will display as 100. |
||||
// To display all decimals, set the unit to `String`. |
||||
decimals?: number @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. |
||||
min?: number @grafanamaturity(NeedsExpertReview) |
||||
// The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. |
||||
max?: number @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Convert input values into a display string |
||||
mappings?: [...#ValueMapping] @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Map numeric values to states |
||||
thresholds?: #ThresholdsConfig @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Panel color configuration |
||||
color?: #FieldColor |
||||
|
||||
// The behavior when clicking on a result |
||||
links?: [...] @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Alternative to empty string |
||||
noValue?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// custom is specified by the FieldConfig field |
||||
// in panel plugin schemas. |
||||
custom?: {...} @grafanamaturity(NeedsExpertReview) |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Row panel |
||||
#RowPanel: { |
||||
// The panel type |
||||
type: "row" |
||||
|
||||
// Whether this row should be collapsed or not. |
||||
collapsed: bool | *false |
||||
|
||||
// Row title |
||||
title?: string |
||||
|
||||
// Name of default datasource for the row |
||||
datasource?: #DataSourceRef |
||||
|
||||
// Row grid position |
||||
gridPos?: #GridPos |
||||
|
||||
// Unique identifier of the panel. Generated by Grafana when creating a new panel. It must be unique within a dashboard, but not globally. |
||||
id: uint32 |
||||
|
||||
// List of panels in the row |
||||
panels: [...#Panel] |
||||
|
||||
// Name of template variable to repeat for. |
||||
repeat?: string |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
} |
||||
}, |
||||
] |
@ -0,0 +1,90 @@ |
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
_ "embed" |
||||
json "encoding/json" |
||||
fmt "fmt" |
||||
"strings" |
||||
"sync" |
||||
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" |
||||
"k8s.io/apimachinery/pkg/util/validation/field" |
||||
|
||||
"cuelang.org/go/cue" |
||||
"cuelang.org/go/cue/cuecontext" |
||||
"cuelang.org/go/cue/errors" |
||||
cuejson "cuelang.org/go/encoding/json" |
||||
) |
||||
|
||||
func ValidateDashboardSpec(obj *Dashboard, forceValidation bool) (field.ErrorList, field.ErrorList) { |
||||
var schemaVersionError field.ErrorList |
||||
schemaVersion := schemaversion.GetSchemaVersion(obj.Spec.Object) |
||||
if schemaVersion != schemaversion.LATEST_VERSION { |
||||
schemaVersionError = field.ErrorList{field.Invalid(field.NewPath("spec", "schemaVersion"), field.OmitValueType{}, fmt.Sprintf("Schema version %d is not supported - please upgrade to %d", schemaVersion, schemaversion.LATEST_VERSION))} |
||||
if !forceValidation { |
||||
return nil, schemaVersionError |
||||
} |
||||
} |
||||
|
||||
data, err := json.Marshal(obj.Spec.Object) |
||||
if err != nil { |
||||
return field.ErrorList{ |
||||
field.Invalid(field.NewPath("spec"), field.OmitValueType{}, err.Error()), |
||||
}, schemaVersionError |
||||
} |
||||
|
||||
if err := cuejson.Validate(data, getCueSchema()); err != nil { |
||||
errs := field.ErrorList{} |
||||
|
||||
for _, e := range errors.Errors(err) { |
||||
if |
||||
// We don't want to return confusing "empty disjunction" errors,
|
||||
// because the users don't necessarily understand what to do with them.
|
||||
// For empty disjunctions, CUE will also return more specific errors,
|
||||
// so we can safely ignore the generic ones.
|
||||
strings.Contains(e.Error(), "disjunction") || |
||||
// We don't want to return errors about unknown fields either.
|
||||
strings.Contains(e.Error(), "field not allowed") { |
||||
continue |
||||
} |
||||
|
||||
// We want to manually format the error message,
|
||||
// because e.Error() contains the full CUE path.
|
||||
format, args := e.Msg() |
||||
|
||||
errs = append(errs, field.Invalid( |
||||
field.NewPath(formatErrorPath(e.Path())), |
||||
field.OmitValueType{}, |
||||
fmt.Sprintf(format, args...), |
||||
)) |
||||
} |
||||
|
||||
return errs, schemaVersionError |
||||
} |
||||
|
||||
return nil, schemaVersionError |
||||
} |
||||
|
||||
func formatErrorPath(path []string) string { |
||||
// omitting the "lineage.schemas[0].schema.spec" prefix here.
|
||||
return strings.Join(path[4:], ".") |
||||
} |
||||
|
||||
var ( |
||||
compiledSchema cue.Value |
||||
getSchemaOnce sync.Once |
||||
) |
||||
|
||||
//go:embed dashboard_kind.cue
|
||||
var schemaSource string |
||||
|
||||
func getCueSchema() cue.Value { |
||||
getSchemaOnce.Do(func() { |
||||
cueCtx := cuecontext.New() |
||||
compiledSchema = cueCtx.CompileString(schemaSource).LookupPath( |
||||
cue.ParsePath("lineage.schemas[0].schema.spec"), |
||||
) |
||||
}) |
||||
|
||||
return compiledSchema |
||||
} |
@ -0,0 +1,759 @@ |
||||
// This file is managed by Grafana - DO NOT EDIT MANUALLY |
||||
// Source: kinds/dashboard/dashboard_kind.cue |
||||
// To sync changes, run: make gen-cue |
||||
|
||||
package kind |
||||
|
||||
import ( |
||||
"strings" |
||||
t "time" |
||||
) |
||||
|
||||
name: "Dashboard" |
||||
maturity: "experimental" |
||||
description: "A Grafana dashboard." |
||||
|
||||
crd: dummySchema: true |
||||
|
||||
lineage: schemas: [{ |
||||
version: [0, 0] |
||||
schema: { |
||||
spec: { |
||||
// Unique numeric identifier for the dashboard. |
||||
// `id` is internal to a specific Grafana instance. `uid` should be used to identify a dashboard across Grafana instances. |
||||
id?: int64 | null // TODO eliminate this null option |
||||
|
||||
// Unique dashboard identifier that can be generated by anyone. string (8-40) |
||||
uid?: string |
||||
|
||||
// Title of dashboard. |
||||
title?: string |
||||
|
||||
// Description of dashboard. |
||||
description?: string |
||||
|
||||
// This property should only be used in dashboards defined by plugins. It is a quick check |
||||
// to see if the version has changed since the last time. |
||||
revision?: int64 |
||||
|
||||
// ID of a dashboard imported from the https://grafana.com/grafana/dashboards/ portal |
||||
gnetId?: string |
||||
|
||||
// Tags associated with dashboard. |
||||
tags?: [...string] |
||||
|
||||
// Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc". |
||||
timezone?: string | *"browser" |
||||
|
||||
// Whether a dashboard is editable or not. |
||||
editable?: bool | *true |
||||
|
||||
// Configuration of dashboard cursor sync behavior. |
||||
// Accepted values are 0 (sync turned off), 1 (shared crosshair), 2 (shared crosshair and tooltip). |
||||
graphTooltip?: #DashboardCursorSync |
||||
|
||||
// Time range for dashboard. |
||||
// Accepted values are relative time strings like {from: 'now-6h', to: 'now'} or absolute time strings like {from: '2020-07-10T08:00:00.000Z', to: '2020-07-10T14:00:00.000Z'}. |
||||
time?: { |
||||
from: string | *"now-6h" |
||||
to: string | *"now" |
||||
} |
||||
|
||||
// Configuration of the time picker shown at the top of a dashboard. |
||||
timepicker?: #TimePickerConfig |
||||
|
||||
// The month that the fiscal year starts on. 0 = January, 11 = December |
||||
fiscalYearStartMonth?: uint8 & <12 | *0 |
||||
|
||||
// When set to true, the dashboard will redraw panels at an interval matching the pixel width. |
||||
// This will keep data "moving left" regardless of the query refresh rate. This setting helps |
||||
// avoid dashboards presenting stale live data |
||||
liveNow?: bool |
||||
|
||||
// Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday". |
||||
weekStart?: string |
||||
|
||||
// Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". |
||||
refresh?: string |
||||
|
||||
// Version of the JSON schema, incremented each time a Grafana update brings |
||||
// changes to said schema. |
||||
schemaVersion: uint16 | *41 |
||||
|
||||
// Version of the dashboard, incremented each time the dashboard is updated. |
||||
version?: uint32 |
||||
|
||||
// List of dashboard panels |
||||
panels?: [...(#Panel | #RowPanel)] |
||||
|
||||
// Configured template variables |
||||
templating?: { |
||||
// List of configured template variables with their saved values along with some other metadata |
||||
list?: [...#VariableModel] |
||||
} |
||||
|
||||
// Contains the list of annotations that are associated with the dashboard. |
||||
// Annotations are used to overlay event markers and overlay event tags on graphs. |
||||
// Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API. |
||||
// See https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/annotate-visualizations/ |
||||
annotations?: #AnnotationContainer |
||||
|
||||
// Links with references to other dashboards or external websites. |
||||
links?: [...#DashboardLink] |
||||
|
||||
// Snapshot options. They are present only if the dashboard is a snapshot. |
||||
snapshot?: #Snapshot @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// When set to true, the dashboard will load all panels in the dashboard when it's loaded. |
||||
preload?: bool |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
/////////////////////////////////////// |
||||
// Definitions (referenced above) are declared below |
||||
|
||||
// TODO: this should be a regular DataQuery that depends on the selected dashboard |
||||
// these match the properties of the "grafana" datasouce that is default in most dashboards |
||||
#AnnotationTarget: { |
||||
// Only required/valid for the grafana datasource... |
||||
// but code+tests is already depending on it so hard to change |
||||
limit: int64 |
||||
// Only required/valid for the grafana datasource... |
||||
// but code+tests is already depending on it so hard to change |
||||
matchAny: bool |
||||
// Only required/valid for the grafana datasource... |
||||
// but code+tests is already depending on it so hard to change |
||||
tags: [...string] |
||||
// Only required/valid for the grafana datasource... |
||||
// but code+tests is already depending on it so hard to change |
||||
type: string |
||||
... // datasource will stick their raw DataQuery here |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
#AnnotationPanelFilter: { |
||||
// Should the specified panels be included or excluded |
||||
exclude?: bool | *false |
||||
|
||||
// Panel IDs that should be included or excluded |
||||
ids: [...uint8] |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Contains the list of annotations that are associated with the dashboard. |
||||
// Annotations are used to overlay event markers and overlay event tags on graphs. |
||||
// Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API. |
||||
// See https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/annotate-visualizations/ |
||||
#AnnotationContainer: { |
||||
// List of annotations |
||||
list?: [...#AnnotationQuery] |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
// TODO docs |
||||
// FROM: AnnotationQuery in grafana-data/src/types/annotations.ts |
||||
#AnnotationQuery: { |
||||
// Name of annotation. |
||||
name: string |
||||
|
||||
// Datasource where the annotations data is |
||||
datasource: #DataSourceRef |
||||
|
||||
// When enabled the annotation query is issued with every dashboard refresh |
||||
enable: bool | *true |
||||
|
||||
// Annotation queries can be toggled on or off at the top of the dashboard. |
||||
// When hide is true, the toggle is not shown in the dashboard. |
||||
hide?: bool | *false |
||||
|
||||
// Color to use for the annotation event markers |
||||
iconColor: string |
||||
|
||||
// Filters to apply when fetching annotations |
||||
filter?: #AnnotationPanelFilter |
||||
|
||||
// TODO.. this should just be a normal query target |
||||
target?: #AnnotationTarget |
||||
|
||||
// TODO -- this should not exist here, it is based on the --grafana-- datasource |
||||
type?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Set to 1 for the standard annotation query all dashboards have by default. |
||||
builtIn?: number | *0 |
||||
|
||||
// unless datasources have migrated to the target+mapping, |
||||
// they just spread their query into the base object :( |
||||
... |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// A variable is a placeholder for a value. You can use variables in metric queries and in panel titles. |
||||
#VariableModel: { |
||||
// Type of variable |
||||
type: #VariableType |
||||
// Name of variable |
||||
name: string |
||||
// Optional display name |
||||
label?: string |
||||
// Visibility configuration for the variable |
||||
hide?: #VariableHide |
||||
// Whether the variable value should be managed by URL query params or not |
||||
skipUrlSync?: bool | *false |
||||
// Description of variable. It can be defined but `null`. |
||||
description?: string |
||||
// Query used to fetch values for a variable |
||||
query?: string | {...} |
||||
// Data source used to fetch values for a variable. It can be defined but `null`. |
||||
datasource?: #DataSourceRef |
||||
// Shows current selected variable text/value on the dashboard |
||||
current?: #VariableOption |
||||
// Whether multiple values can be selected or not from variable value list |
||||
multi?: bool | *false |
||||
// Allow custom values to be entered in the variable |
||||
allowCustomValue?: bool | *true |
||||
// Options that can be selected for a variable. |
||||
options?: [...#VariableOption] |
||||
// Options to config when to refresh a variable |
||||
refresh?: #VariableRefresh |
||||
// Options sort order |
||||
sort?: #VariableSort |
||||
// Whether all value option is available or not |
||||
includeAll?: bool | *false |
||||
// Custom all value |
||||
allValue?: string |
||||
// Optional field, if you want to extract part of a series name or metric node segment. |
||||
// Named capture groups can be used to separate the display text and value. |
||||
regex?: string |
||||
... |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Option to be selected in a variable. |
||||
#VariableOption: { |
||||
// Whether the option is selected or not |
||||
selected?: bool |
||||
// Text to be displayed for the option |
||||
text: string | [...string] |
||||
// Value of the option |
||||
value: string | [...string] |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Options to config when to refresh a variable |
||||
// `0`: Never refresh the variable |
||||
// `1`: Queries the data source every time the dashboard loads. |
||||
// `2`: Queries the data source when the dashboard time range changes. |
||||
#VariableRefresh: 0 | 1 | 2 @cuetsy(kind="enum",memberNames="never|onDashboardLoad|onTimeRangeChanged") |
||||
|
||||
// Determine if the variable shows on dashboard |
||||
// Accepted values are 0 (show label and value), 1 (show value only), 2 (show nothing). |
||||
#VariableHide: 0 | 1 | 2 @cuetsy(kind="enum",memberNames="dontHide|hideLabel|hideVariable") @grafana(TSVeneer="type") |
||||
|
||||
// Sort variable options |
||||
// Accepted values are: |
||||
// `0`: No sorting |
||||
// `1`: Alphabetical ASC |
||||
// `2`: Alphabetical DESC |
||||
// `3`: Numerical ASC |
||||
// `4`: Numerical DESC |
||||
// `5`: Alphabetical Case Insensitive ASC |
||||
// `6`: Alphabetical Case Insensitive DESC |
||||
// `7`: Natural ASC |
||||
// `8`: Natural DESC |
||||
#VariableSort: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 @cuetsy(kind="enum",memberNames="disabled|alphabeticalAsc|alphabeticalDesc|numericalAsc|numericalDesc|alphabeticalCaseInsensitiveAsc|alphabeticalCaseInsensitiveDesc|naturalAsc|naturalDesc") |
||||
|
||||
// Ref to a DataSource instance |
||||
#DataSourceRef: { |
||||
// The plugin type-id |
||||
type?: string |
||||
|
||||
// Specific datasource instance |
||||
uid?: string |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
// Links with references to other dashboards or external resources |
||||
#DashboardLink: { |
||||
// Title to display with the link |
||||
title: string |
||||
// Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) |
||||
type: #DashboardLinkType |
||||
// Icon name to be displayed with the link |
||||
icon: string |
||||
// Tooltip to display when the user hovers their mouse over it |
||||
tooltip: string |
||||
// Link URL. Only required/valid if the type is link |
||||
url?: string |
||||
// List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards |
||||
tags: [...string] |
||||
// If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards |
||||
asDropdown: bool | *false |
||||
// If true, the link will be opened in a new tab |
||||
targetBlank: bool | *false |
||||
// If true, includes current template variables values in the link as query params |
||||
includeVars: bool | *false |
||||
// If true, includes current time range in the link as query params |
||||
keepTime: bool | *false |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) |
||||
#DashboardLinkType: "link" | "dashboards" @cuetsy(kind="type") |
||||
|
||||
// Dashboard variable type |
||||
// `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on. |
||||
// `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only). |
||||
// `constant`: Define a hidden constant. |
||||
// `datasource`: Quickly change the data source for an entire dashboard. |
||||
// `interval`: Interval variables represent time spans. |
||||
// `textbox`: Display a free text input field with an optional default value. |
||||
// `custom`: Define the variable options manually using a comma-separated list. |
||||
// `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables |
||||
#VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" | |
||||
"system" | "snapshot" @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. |
||||
// Continuous color interpolates a color using the percentage of a value relative to min and max. |
||||
// Accepted values are: |
||||
// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold |
||||
// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations |
||||
// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations |
||||
// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode |
||||
// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode |
||||
// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode |
||||
// `continuous-YlRd`: Continuous Yellow-Red palette mode |
||||
// `continuous-BlPu`: Continuous Blue-Purple palette mode |
||||
// `continuous-YlBl`: Continuous Yellow-Blue palette mode |
||||
// `continuous-blues`: Continuous Blue palette mode |
||||
// `continuous-reds`: Continuous Red palette mode |
||||
// `continuous-greens`: Continuous Green palette mode |
||||
// `continuous-purples`: Continuous Purple palette mode |
||||
// `shades`: Shades of a single color. Specify a single color, useful in an override rule. |
||||
// `fixed`: Fixed color mode. Specify a single color, useful in an override rule. |
||||
#FieldColorModeId: "thresholds" | "palette-classic" | "palette-classic-by-name" | "continuous-GrYlRd" | "continuous-RdYlGr" | "continuous-BlYlRd" | "continuous-YlRd" | "continuous-BlPu" | "continuous-YlBl" | "continuous-blues" | "continuous-reds" | "continuous-greens" | "continuous-purples" | "fixed" | "shades" @cuetsy(kind="enum",memberNames="Thresholds|PaletteClassic|PaletteClassicByName|ContinuousGrYlRd|ContinuousRdYlGr|ContinuousBlYlRd|ContinuousYlRd|ContinuousBlPu|ContinuousYlBl|ContinuousBlues|ContinuousReds|ContinuousGreens|ContinuousPurples|Fixed|Shades") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. |
||||
#FieldColorSeriesByMode: "min" | "max" | "last" @cuetsy(kind="type") |
||||
|
||||
// Map a field to a color. |
||||
#FieldColor: { |
||||
// The main color scheme mode. |
||||
mode: #FieldColorModeId |
||||
// The fixed color value for fixed or shades color modes. |
||||
fixedColor?: string |
||||
// Some visualizations need to know how to assign a series color from by value color schemes. |
||||
seriesBy?: #FieldColorSeriesByMode |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Position and dimensions of a panel in the grid |
||||
#GridPos: { |
||||
// Panel height. The height is the number of rows from the top edge of the panel. |
||||
h: uint32 & >0 | *9 |
||||
// Panel width. The width is the number of columns from the left edge of the panel. |
||||
w: uint32 & >0 & <=24 | *12 |
||||
// Panel x. The x coordinate is the number of columns from the left edge of the grid |
||||
x: uint32 & >=0 & <24 | *0 |
||||
// Panel y. The y coordinate is the number of rows from the top edge of the grid |
||||
y: uint32 & >=0 | *0 |
||||
// Whether the panel is fixed within the grid. If true, the panel will not be affected by other panels' interactions |
||||
static?: bool |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// User-defined value for a metric that triggers visual changes in a panel when this value is met or exceeded |
||||
// They are used to conditionally style and color visualizations based on query results , and can be applied to most visualizations. |
||||
#Threshold: { |
||||
// Value represents a specified metric for the threshold, which triggers a visual change in the dashboard when this value is met or exceeded. |
||||
// Nulls currently appear here when serializing -Infinity to JSON. |
||||
value: number | null @grafanamaturity(NeedsExpertReview) |
||||
// Color represents the color of the visual change that will occur in the dashboard when the threshold value is met or exceeded. |
||||
color: string @grafanamaturity(NeedsExpertReview) |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Thresholds can either be `absolute` (specific number) or `percentage` (relative to min or max, it will be values between 0 and 1). |
||||
#ThresholdsMode: "absolute" | "percentage" @cuetsy(kind="enum",memberNames="Absolute|Percentage") |
||||
|
||||
// Thresholds configuration for the panel |
||||
#ThresholdsConfig: { |
||||
// Thresholds mode. |
||||
mode: #ThresholdsMode |
||||
|
||||
// Must be sorted by 'value', first value is always -Infinity |
||||
steps: [...#Threshold] @grafanamaturity(NeedsExpertReview) |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Allow to transform the visual representation of specific data values in a visualization, irrespective of their original units |
||||
#ValueMapping: #ValueMap | #RangeMap | #RegexMap | #SpecialValueMap @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Supported value mapping types |
||||
// `value`: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. |
||||
// `range`: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. |
||||
// `regex`: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. |
||||
// `special`: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A. |
||||
#MappingType: "value" | "range" | "regex" | "special" @cuetsy(kind="enum",memberNames="ValueToText|RangeToText|RegexToText|SpecialValue") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Maps text values to a color or different display text and color. |
||||
// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. |
||||
#ValueMap: { |
||||
type: #MappingType & "value" |
||||
// Map with <value_to_match>: ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } } |
||||
options: [string]: #ValueMappingResult |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Maps numerical ranges to a display text and color. |
||||
// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. |
||||
#RangeMap: { |
||||
type: #MappingType & "range" |
||||
// Range to match against and the result to apply when the value is within the range |
||||
options: { |
||||
// Min value of the range. It can be null which means -Infinity |
||||
from: float64 | null |
||||
// Max value of the range. It can be null which means +Infinity |
||||
to: float64 | null |
||||
// Config to apply when the value is within the range |
||||
result: #ValueMappingResult |
||||
} |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Maps regular expressions to replacement text and a color. |
||||
// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. |
||||
#RegexMap: { |
||||
type: #MappingType & "regex" |
||||
// Regular expression to match against and the result to apply when the value matches the regex |
||||
options: { |
||||
// Regular expression to match against |
||||
pattern: string |
||||
// Config to apply when the value matches the regex |
||||
result: #ValueMappingResult |
||||
} |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. |
||||
// See SpecialValueMatch to see the list of special values. |
||||
// For example, you can configure a special value mapping so that null values appear as N/A. |
||||
#SpecialValueMap: { |
||||
type: #MappingType & "special" |
||||
options: { |
||||
// Special value to match against |
||||
match: #SpecialValueMatch |
||||
// Config to apply when the value matches the special value |
||||
result: #ValueMappingResult |
||||
} |
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Special value types supported by the `SpecialValueMap` |
||||
#SpecialValueMatch: "true" | "false" | "null" | "nan" | "null+nan" | "empty" @cuetsy(kind="enum",memberNames="True|False|Null|NaN|NullAndNan|Empty") |
||||
|
||||
// Result used as replacement with text and color when the value matches |
||||
#ValueMappingResult: { |
||||
// Text to display when the value matches |
||||
text?: string |
||||
// Text to use when the value matches |
||||
color?: string |
||||
// Icon to display when the value matches. Only specific visualizations. |
||||
icon?: string |
||||
// Position in the mapping array. Only used internally. |
||||
index?: int32 |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Transformations allow to manipulate data returned by a query before the system applies a visualization. |
||||
// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, |
||||
// use the output of one transformation as the input to another transformation, etc. |
||||
#DataTransformerConfig: { |
||||
// Unique identifier of transformer |
||||
id: string |
||||
// Disabled transformations are skipped |
||||
disabled?: bool |
||||
// Optional frame matcher. When missing it will be applied to all results |
||||
filter?: #MatcherConfig |
||||
// Where to pull DataFrames from as input to transformation |
||||
topic?: "series" | "annotations" | "alertStates" // replaced with common.DataTopic |
||||
// Options to be passed to the transformer |
||||
// Valid options depend on the transformer id |
||||
options: _ |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
// Counterpart for TypeScript's TimeOption type. |
||||
#TimeOption: { |
||||
display: string |
||||
from: string |
||||
to: string |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
// Time picker configuration |
||||
// It defines the default config for the time picker and the refresh picker for the specific dashboard. |
||||
#TimePickerConfig: { |
||||
// Whether timepicker is visible or not. |
||||
hidden?: bool | *false |
||||
// Interval options available in the refresh picker dropdown. |
||||
refresh_intervals?: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] |
||||
// Quick ranges for time picker. |
||||
quick_ranges?: [...#TimeOption] |
||||
// Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. |
||||
nowDelay?: string |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
// 0 for no shared crosshair or tooltip (default). |
||||
// 1 for shared crosshair. |
||||
// 2 for shared crosshair AND shared tooltip. |
||||
#DashboardCursorSync: *0 | 1 | 2 @cuetsy(kind="enum",memberNames="Off|Crosshair|Tooltip") |
||||
|
||||
// Schema for panel targets is specified by datasource |
||||
// plugins. We use a placeholder definition, which the Go |
||||
// schema loader either left open/as-is with the Base |
||||
// variant of the Dashboard and Panel families, or filled |
||||
// with types derived from plugins in the Instance variant. |
||||
// When working directly from CUE, importers can extend this |
||||
// type directly to achieve the same effect. |
||||
#Target: {...} |
||||
|
||||
// A dashboard snapshot shares an interactive dashboard publicly. |
||||
// It is a read-only version of a dashboard, and is not editable. |
||||
// It is possible to create a snapshot of a snapshot. |
||||
// Grafana strips away all sensitive information from the dashboard. |
||||
// Sensitive information stripped: queries (metric, template,annotation) and panel links. |
||||
#Snapshot: { |
||||
// Time when the snapshot was created |
||||
created: string & t.Time |
||||
// Time when the snapshot expires, default is never to expire |
||||
expires: string @grafanamaturity(NeedsExpertReview) |
||||
// Is the snapshot saved in an external grafana instance |
||||
external: bool @grafanamaturity(NeedsExpertReview) |
||||
// external url, if snapshot was shared in external grafana instance |
||||
externalUrl: string @grafanamaturity(NeedsExpertReview) |
||||
// original url, url of the dashboard that was snapshotted |
||||
originalUrl: string @grafanamaturity(NeedsExpertReview) |
||||
// Unique identifier of the snapshot |
||||
id: uint32 @grafanamaturity(NeedsExpertReview) |
||||
// Optional, defined the unique key of the snapshot, required if external is true |
||||
key: string @grafanamaturity(NeedsExpertReview) |
||||
// Optional, name of the snapshot |
||||
name: string @grafanamaturity(NeedsExpertReview) |
||||
// org id of the snapshot |
||||
orgId: uint32 @grafanamaturity(NeedsExpertReview) |
||||
// last time when the snapshot was updated |
||||
updated: string & t.Time |
||||
// url of the snapshot, if snapshot was shared internally |
||||
url?: string @grafanamaturity(NeedsExpertReview) |
||||
// user id of the snapshot creator |
||||
userId: uint32 @grafanamaturity(NeedsExpertReview) |
||||
} @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Dashboard panels are the basic visualization building blocks. |
||||
#Panel: { |
||||
// The panel plugin type id. This is used to find the plugin to display the panel. |
||||
type: string & strings.MinRunes(1) |
||||
|
||||
// Unique identifier of the panel. Generated by Grafana when creating a new panel. It must be unique within a dashboard, but not globally. |
||||
id?: uint32 |
||||
|
||||
// The version of the plugin that is used for this panel. This is used to find the plugin to display the panel and to migrate old panel configs. |
||||
pluginVersion?: string |
||||
|
||||
// Depends on the panel plugin. See the plugin documentation for details. |
||||
targets?: [...#Target] |
||||
|
||||
// Panel title. |
||||
title?: string |
||||
|
||||
// Panel description. |
||||
description?: string |
||||
|
||||
// Whether to display the panel without a background. |
||||
transparent?: bool | *false |
||||
|
||||
// The datasource used in all targets. |
||||
datasource?: #DataSourceRef |
||||
|
||||
// Grid position. |
||||
gridPos?: #GridPos |
||||
|
||||
// Panel links. |
||||
links?: [...#DashboardLink] |
||||
|
||||
// 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" |
||||
|
||||
// Option for repeated panels that controls max items per row |
||||
// Only relevant for horizontally repeated panels |
||||
maxPerRow?: number |
||||
|
||||
// The maximum number of data points that the panel queries are retrieving. |
||||
maxDataPoints?: number |
||||
|
||||
// List of transformations that are applied to the panel data before rendering. |
||||
// When there are multiple transformations, Grafana applies them in the order they are listed. |
||||
// Each transformation creates a result set that then passes on to the next transformation in the processing pipeline. |
||||
transformations?: [...#DataTransformerConfig] |
||||
|
||||
// The min time interval setting defines a lower limit for the $__interval and $__interval_ms variables. |
||||
// This value must be formatted as a number followed by a valid time |
||||
// identifier like: "40s", "3d", etc. |
||||
// See: https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/#query-options |
||||
interval?: string |
||||
|
||||
// Overrides the relative time range for individual panels, |
||||
// which causes them to be different than what is selected in |
||||
// the dashboard time picker in the top-right corner of the dashboard. You can use this to show metrics from different |
||||
// time periods or days on the same dashboard. |
||||
// The value is formatted as time operation like: `now-5m` (Last 5 minutes), `now/d` (the day so far), |
||||
// `now-5d/d`(Last 5 days), `now/w` (This week so far), `now-2y/y` (Last 2 years). |
||||
// Note: Panel time overrides have no effect when the dashboard’s time range is absolute. |
||||
// See: https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/#query-options |
||||
timeFrom?: string |
||||
|
||||
// Overrides the time range for individual panels by shifting its start and end relative to the time picker. |
||||
// For example, you can shift the time range for the panel to be two hours earlier than the dashboard time picker setting `2h`. |
||||
// Note: Panel time overrides have no effect when the dashboard’s time range is absolute. |
||||
// See: https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/#query-options |
||||
timeShift?: string |
||||
|
||||
// Controls if the timeFrom or timeShift overrides are shown in the panel header |
||||
hideTimeOverride?: bool |
||||
|
||||
// Dynamically load the panel |
||||
libraryPanel?: #LibraryPanelRef |
||||
|
||||
// Sets panel queries cache timeout. |
||||
cacheTimeout?: string |
||||
|
||||
// Overrides the data source configured time-to-live for a query cache item in milliseconds |
||||
queryCachingTTL?: number |
||||
|
||||
// It depends on the panel plugin. They are specified by the Options field in panel plugin schemas. |
||||
options?: {...} @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Field options allow you to change how the data is displayed in your visualizations. |
||||
fieldConfig?: #FieldConfigSource |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. |
||||
// Each column within this structure is called a field. A field can represent a single time series or table column. |
||||
// Field options allow you to change how the data is displayed in your visualizations. |
||||
#FieldConfigSource: { |
||||
// Defaults are the options applied to all fields. |
||||
defaults: #FieldConfig |
||||
// Overrides are the options applied to specific fields overriding the defaults. |
||||
overrides: [...{ |
||||
matcher: #MatcherConfig |
||||
properties: [...#DynamicConfigValue] |
||||
}] @grafanamaturity(NeedsExpertReview) |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// A library panel is a reusable panel that you can use in any dashboard. |
||||
// When you make a change to a library panel, that change propagates to all instances of where the panel is used. |
||||
// Library panels streamline reuse of panels across multiple dashboards. |
||||
#LibraryPanelRef: { |
||||
// Library panel name |
||||
name: string |
||||
// Library panel uid |
||||
uid: string |
||||
} @cuetsy(kind="interface") |
||||
|
||||
// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. |
||||
// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. |
||||
#MatcherConfig: { |
||||
// The matcher id. This is used to find the matcher implementation from registry. |
||||
id: string | *"" @grafanamaturity(NeedsExpertReview) |
||||
// The matcher options. This is specific to the matcher implementation. |
||||
options?: _ @grafanamaturity(NeedsExpertReview) |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
|
||||
#DynamicConfigValue: { |
||||
id: string | *"" @grafanamaturity(NeedsExpertReview) |
||||
value?: _ @grafanamaturity(NeedsExpertReview) |
||||
} |
||||
|
||||
// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. |
||||
// Each column within this structure is called a field. A field can represent a single time series or table column. |
||||
// Field options allow you to change how the data is displayed in your visualizations. |
||||
#FieldConfig: { |
||||
// The display value for this field. This supports template variables blank is auto |
||||
displayName?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// This can be used by data sources that return and explicit naming structure for values and labels |
||||
// When this property is configured, this value is used rather than the default naming strategy. |
||||
displayNameFromDS?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Human readable field metadata |
||||
description?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// An explicit path to the field in the datasource. When the frame meta includes a path, |
||||
// This will default to `${frame.meta.path}/${field.name} |
||||
// |
||||
// When defined, this value can be used as an identifier within the datasource scope, and |
||||
// may be used to update the results |
||||
path?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// True if data source can write a value to the path. Auth/authz are supported separately |
||||
writeable?: bool @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// True if data source field supports ad-hoc filters |
||||
filterable?: bool @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Unit a field should use. The unit you select is applied to all fields except time. |
||||
// You can use the units ID availables in Grafana or a custom unit. |
||||
// Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts |
||||
// As custom unit, you can use the following formats: |
||||
// `suffix:<suffix>` for custom unit that should go after value. |
||||
// `prefix:<prefix>` for custom unit that should go before value. |
||||
// `time:<format>` For custom date time formats type for example `time:YYYY-MM-DD`. |
||||
// `si:<base scale><unit characters>` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character. |
||||
// `count:<unit>` for a custom count unit. |
||||
// `currency:<unit>` for custom a currency unit. |
||||
unit?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Specify the number of decimals Grafana includes in the rendered value. |
||||
// If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. |
||||
// For example 1.1234 will display as 1.12 and 100.456 will display as 100. |
||||
// To display all decimals, set the unit to `String`. |
||||
decimals?: number @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. |
||||
min?: number @grafanamaturity(NeedsExpertReview) |
||||
// The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. |
||||
max?: number @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Convert input values into a display string |
||||
mappings?: [...#ValueMapping] @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Map numeric values to states |
||||
thresholds?: #ThresholdsConfig @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Panel color configuration |
||||
color?: #FieldColor |
||||
|
||||
// The behavior when clicking on a result |
||||
links?: [...] @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Alternative to empty string |
||||
noValue?: string @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// custom is specified by the FieldConfig field |
||||
// in panel plugin schemas. |
||||
custom?: {...} @grafanamaturity(NeedsExpertReview) |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) |
||||
|
||||
// Row panel |
||||
#RowPanel: { |
||||
// The panel type |
||||
type: "row" |
||||
|
||||
// Whether this row should be collapsed or not. |
||||
collapsed: bool | *false |
||||
|
||||
// Row title |
||||
title?: string |
||||
|
||||
// Name of default datasource for the row |
||||
datasource?: #DataSourceRef |
||||
|
||||
// Row grid position |
||||
gridPos?: #GridPos |
||||
|
||||
// Unique identifier of the panel. Generated by Grafana when creating a new panel. It must be unique within a dashboard, but not globally. |
||||
id: uint32 |
||||
|
||||
// List of panels in the row |
||||
panels: [...#Panel] |
||||
|
||||
// Name of template variable to repeat for. |
||||
repeat?: string |
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") |
||||
} |
||||
}, |
||||
] |
@ -0,0 +1,90 @@ |
||||
package v1alpha1 |
||||
|
||||
import ( |
||||
_ "embed" |
||||
json "encoding/json" |
||||
fmt "fmt" |
||||
"strings" |
||||
"sync" |
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field" |
||||
|
||||
"cuelang.org/go/cue" |
||||
"cuelang.org/go/cue/cuecontext" |
||||
"cuelang.org/go/cue/errors" |
||||
cuejson "cuelang.org/go/encoding/json" |
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" |
||||
) |
||||
|
||||
func ValidateDashboardSpec(obj *Dashboard, forceValidation bool) (field.ErrorList, field.ErrorList) { |
||||
var schemaVersionError field.ErrorList |
||||
schemaVersion := schemaversion.GetSchemaVersion(obj.Spec.Object) |
||||
if schemaVersion != schemaversion.LATEST_VERSION { |
||||
schemaVersionError = field.ErrorList{field.Invalid(field.NewPath("spec", "schemaVersion"), field.OmitValueType{}, fmt.Sprintf("Schema version %d is not supported - please upgrade to %d", schemaVersion, schemaversion.LATEST_VERSION))} |
||||
if !forceValidation { |
||||
return nil, schemaVersionError |
||||
} |
||||
} |
||||
|
||||
data, err := json.Marshal(obj.Spec.Object) |
||||
if err != nil { |
||||
return field.ErrorList{ |
||||
field.Invalid(field.NewPath("spec"), field.OmitValueType{}, err.Error()), |
||||
}, schemaVersionError |
||||
} |
||||
|
||||
if err := cuejson.Validate(data, getCueSchema()); err != nil { |
||||
errs := field.ErrorList{} |
||||
|
||||
for _, e := range errors.Errors(err) { |
||||
if |
||||
// We don't want to return confusing "empty disjunction" errors,
|
||||
// because the users don't necessarily understand what to do with them.
|
||||
// For empty disjunctions, CUE will also return more specific errors,
|
||||
// so we can safely ignore the generic ones.
|
||||
strings.Contains(e.Error(), "disjunction") || |
||||
// We don't want to return errors about unknown fields either.
|
||||
strings.Contains(e.Error(), "field not allowed") { |
||||
continue |
||||
} |
||||
|
||||
// We want to manually format the error message,
|
||||
// because e.Error() contains the full CUE path.
|
||||
format, args := e.Msg() |
||||
|
||||
errs = append(errs, field.Invalid( |
||||
field.NewPath(formatErrorPath(e.Path())), |
||||
field.OmitValueType{}, |
||||
fmt.Sprintf(format, args...), |
||||
)) |
||||
} |
||||
|
||||
return errs, schemaVersionError |
||||
} |
||||
|
||||
return nil, schemaVersionError |
||||
} |
||||
|
||||
func formatErrorPath(path []string) string { |
||||
// omitting the "lineage.schemas[0].schema.spec" prefix here.
|
||||
return strings.Join(path[4:], ".") |
||||
} |
||||
|
||||
var ( |
||||
compiledSchema cue.Value |
||||
getSchemaOnce sync.Once |
||||
) |
||||
|
||||
//go:embed dashboard_kind.cue
|
||||
var schemaSource string |
||||
|
||||
func getCueSchema() cue.Value { |
||||
getSchemaOnce.Do(func() { |
||||
cueCtx := cuecontext.New() |
||||
compiledSchema = cueCtx.CompileString(schemaSource).LookupPath( |
||||
cue.ParsePath("lineage.schemas[0].schema.spec"), |
||||
) |
||||
}) |
||||
|
||||
return compiledSchema |
||||
} |
@ -0,0 +1,965 @@ |
||||
// This file is managed by grafana-app-sdk - DO NOT EDIT MANUALLY |
||||
// Source: apps/dashboard/kinds/v2alpha1/dashboard_spec.cue |
||||
// To sync changes, run: make generate in apps/dashboard |
||||
|
||||
package v2alpha1 |
||||
|
||||
DashboardSpec: { |
||||
// Title of dashboard. |
||||
annotations: [...AnnotationQueryKind] |
||||
|
||||
// Configuration of dashboard cursor sync behavior. |
||||
// "Off" for no shared crosshair or tooltip (default). |
||||
// "Crosshair" for shared crosshair. |
||||
// "Tooltip" for shared crosshair AND shared tooltip. |
||||
cursorSync: DashboardCursorSync |
||||
|
||||
// Description of dashboard. |
||||
description?: string |
||||
|
||||
// Whether a dashboard is editable or not. |
||||
editable?: bool | *true |
||||
|
||||
elements: [ElementReference.name]: Element |
||||
|
||||
layout: GridLayoutKind | RowsLayoutKind | AutoGridLayoutKind | TabsLayoutKind |
||||
|
||||
// Links with references to other dashboards or external websites. |
||||
links: [...DashboardLink] |
||||
|
||||
// When set to true, the dashboard will redraw panels at an interval matching the pixel width. |
||||
// This will keep data "moving left" regardless of the query refresh rate. This setting helps |
||||
// avoid dashboards presenting stale live data. |
||||
liveNow?: bool |
||||
|
||||
// When set to true, the dashboard will load all panels in the dashboard when it's loaded. |
||||
preload: bool |
||||
|
||||
// Plugins only. The version of the dashboard installed together with the plugin. |
||||
// This is used to determine if the dashboard should be updated when the plugin is updated. |
||||
revision?: uint16 |
||||
|
||||
// Tags associated with dashboard. |
||||
tags: [...string] |
||||
|
||||
timeSettings: TimeSettingsSpec |
||||
|
||||
// Title of dashboard. |
||||
title: string |
||||
|
||||
// Configured template variables. |
||||
variables: [...VariableKind] |
||||
} |
||||
|
||||
// Supported dashboard elements |
||||
Element: PanelKind | LibraryPanelKind // |* more element types in the future |
||||
|
||||
LibraryPanelKind: { |
||||
kind: "LibraryPanel" |
||||
spec: LibraryPanelKindSpec |
||||
} |
||||
|
||||
LibraryPanelKindSpec: { |
||||
// Panel ID for the library panel in the dashboard |
||||
id: number |
||||
// Title for the library panel in the dashboard |
||||
title: string |
||||
|
||||
libraryPanel: LibraryPanelRef |
||||
} |
||||
|
||||
// A library panel is a reusable panel that you can use in any dashboard. |
||||
// When you make a change to a library panel, that change propagates to all instances of where the panel is used. |
||||
// Library panels streamline reuse of panels across multiple dashboards. |
||||
LibraryPanelRef: { |
||||
// Library panel name |
||||
name: string |
||||
// Library panel uid |
||||
uid: string |
||||
} |
||||
|
||||
AnnotationPanelFilter: { |
||||
// Should the specified panels be included or excluded |
||||
exclude?: bool | *false |
||||
|
||||
// Panel IDs that should be included or excluded |
||||
ids: [...uint8] |
||||
} |
||||
|
||||
// "Off" for no shared crosshair or tooltip (default). |
||||
// "Crosshair" for shared crosshair. |
||||
// "Tooltip" for shared crosshair AND shared tooltip. |
||||
DashboardCursorSync: "Off" | "Crosshair" | "Tooltip" |
||||
|
||||
// Links with references to other dashboards or external resources |
||||
DashboardLink: { |
||||
// Title to display with the link |
||||
title: string |
||||
// Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) |
||||
// FIXME: The type is generated as `type: DashboardLinkType | dashboardLinkType.Link;` but it should be `type: DashboardLinkType` |
||||
type: DashboardLinkType |
||||
// Icon name to be displayed with the link |
||||
icon: string |
||||
// Tooltip to display when the user hovers their mouse over it |
||||
tooltip: string |
||||
// Link URL. Only required/valid if the type is link |
||||
url?: string |
||||
// List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards |
||||
tags: [...string] |
||||
// If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards |
||||
asDropdown: bool | *false |
||||
// If true, the link will be opened in a new tab |
||||
targetBlank: bool | *false |
||||
// If true, includes current template variables values in the link as query params |
||||
includeVars: bool | *false |
||||
// If true, includes current time range in the link as query params |
||||
keepTime: bool | *false |
||||
} |
||||
|
||||
DataSourceRef: { |
||||
// The plugin type-id |
||||
type?: string |
||||
|
||||
// Specific datasource instance |
||||
uid?: string |
||||
} |
||||
|
||||
// A topic is attached to DataFrame metadata in query results. |
||||
// This specifies where the data should be used. |
||||
DataTopic: "series" | "annotations" | "alertStates" @cog(kind="enum",memberNames="Series|Annotations|AlertStates") |
||||
|
||||
// Transformations allow to manipulate data returned by a query before the system applies a visualization. |
||||
// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, |
||||
// use the output of one transformation as the input to another transformation, etc. |
||||
DataTransformerConfig: { |
||||
// Unique identifier of transformer |
||||
id: string |
||||
// Disabled transformations are skipped |
||||
disabled?: bool |
||||
// Optional frame matcher. When missing it will be applied to all results |
||||
filter?: MatcherConfig |
||||
// Where to pull DataFrames from as input to transformation |
||||
topic?: DataTopic |
||||
// Options to be passed to the transformer |
||||
// Valid options depend on the transformer id |
||||
options: _ |
||||
} |
||||
|
||||
DataLink: { |
||||
title: string |
||||
url: string |
||||
targetBlank?: bool |
||||
} |
||||
|
||||
// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. |
||||
// Each column within this structure is called a field. A field can represent a single time series or table column. |
||||
// Field options allow you to change how the data is displayed in your visualizations. |
||||
FieldConfigSource: { |
||||
// Defaults are the options applied to all fields. |
||||
defaults: FieldConfig |
||||
// Overrides are the options applied to specific fields overriding the defaults. |
||||
overrides: [...{ |
||||
matcher: MatcherConfig |
||||
properties: [...DynamicConfigValue] |
||||
}] |
||||
} |
||||
|
||||
// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. |
||||
// Each column within this structure is called a field. A field can represent a single time series or table column. |
||||
// Field options allow you to change how the data is displayed in your visualizations. |
||||
FieldConfig: { |
||||
// The display value for this field. This supports template variables blank is auto |
||||
displayName?: string |
||||
|
||||
// This can be used by data sources that return and explicit naming structure for values and labels |
||||
// When this property is configured, this value is used rather than the default naming strategy. |
||||
displayNameFromDS?: string |
||||
|
||||
// Human readable field metadata |
||||
description?: string |
||||
|
||||
// An explicit path to the field in the datasource. When the frame meta includes a path, |
||||
// This will default to `${frame.meta.path}/${field.name} |
||||
// |
||||
// When defined, this value can be used as an identifier within the datasource scope, and |
||||
// may be used to update the results |
||||
path?: string |
||||
|
||||
// True if data source can write a value to the path. Auth/authz are supported separately |
||||
writeable?: bool |
||||
|
||||
// True if data source field supports ad-hoc filters |
||||
filterable?: bool |
||||
|
||||
// Unit a field should use. The unit you select is applied to all fields except time. |
||||
// You can use the units ID availables in Grafana or a custom unit. |
||||
// Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts |
||||
// As custom unit, you can use the following formats: |
||||
// `suffix:<suffix>` for custom unit that should go after value. |
||||
// `prefix:<prefix>` for custom unit that should go before value. |
||||
// `time:<format>` For custom date time formats type for example `time:YYYY-MM-DD`. |
||||
// `si:<base scale><unit characters>` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character. |
||||
// `count:<unit>` for a custom count unit. |
||||
// `currency:<unit>` for custom a currency unit. |
||||
unit?: string |
||||
|
||||
// Specify the number of decimals Grafana includes in the rendered value. |
||||
// If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. |
||||
// For example 1.1234 will display as 1.12 and 100.456 will display as 100. |
||||
// To display all decimals, set the unit to `String`. |
||||
decimals?: number |
||||
|
||||
// The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. |
||||
min?: number |
||||
// The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. |
||||
max?: number |
||||
|
||||
// Convert input values into a display string |
||||
mappings?: [...ValueMapping] |
||||
|
||||
// Map numeric values to states |
||||
thresholds?: ThresholdsConfig |
||||
|
||||
// Panel color configuration |
||||
color?: FieldColor |
||||
|
||||
// The behavior when clicking on a result |
||||
links?: [...] |
||||
|
||||
// Alternative to empty string |
||||
noValue?: string |
||||
|
||||
// custom is specified by the FieldConfig field |
||||
// in panel plugin schemas. |
||||
custom?: {...} |
||||
} |
||||
|
||||
DynamicConfigValue: { |
||||
id: string | *"" |
||||
value?: _ |
||||
} |
||||
|
||||
// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. |
||||
// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. |
||||
MatcherConfig: { |
||||
// The matcher id. This is used to find the matcher implementation from registry. |
||||
id: string | *"" |
||||
// The matcher options. This is specific to the matcher implementation. |
||||
options?: _ |
||||
} |
||||
|
||||
Threshold: { |
||||
value: number |
||||
color: string |
||||
} |
||||
|
||||
ThresholdsMode: "absolute" | "percentage" |
||||
|
||||
ThresholdsConfig: { |
||||
mode: ThresholdsMode |
||||
steps: [...Threshold] |
||||
} |
||||
|
||||
ValueMapping: ValueMap | RangeMap | RegexMap | SpecialValueMap |
||||
|
||||
// Supported value mapping types |
||||
// `value`: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. |
||||
// `range`: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. |
||||
// `regex`: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. |
||||
// `special`: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A. |
||||
MappingType: "value" | "range" | "regex" | "special" |
||||
|
||||
// Maps text values to a color or different display text and color. |
||||
// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. |
||||
ValueMap: { |
||||
type: MappingType & "value" |
||||
// Map with <value_to_match>: ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } } |
||||
options: [string]: ValueMappingResult |
||||
} |
||||
|
||||
// Maps numerical ranges to a display text and color. |
||||
// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. |
||||
RangeMap: { |
||||
type: MappingType & "range" |
||||
// Range to match against and the result to apply when the value is within the range |
||||
options: { |
||||
// Min value of the range. It can be null which means -Infinity |
||||
from: float64 | null |
||||
// Max value of the range. It can be null which means +Infinity |
||||
to: float64 | null |
||||
// Config to apply when the value is within the range |
||||
result: ValueMappingResult |
||||
} |
||||
} |
||||
|
||||
// Maps regular expressions to replacement text and a color. |
||||
// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. |
||||
RegexMap: { |
||||
type: MappingType & "regex" |
||||
// Regular expression to match against and the result to apply when the value matches the regex |
||||
options: { |
||||
// Regular expression to match against |
||||
pattern: string |
||||
// Config to apply when the value matches the regex |
||||
result: ValueMappingResult |
||||
} |
||||
} |
||||
|
||||
// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. |
||||
// See SpecialValueMatch to see the list of special values. |
||||
// For example, you can configure a special value mapping so that null values appear as N/A. |
||||
SpecialValueMap: { |
||||
type: MappingType & "special" |
||||
options: { |
||||
// Special value to match against |
||||
match: SpecialValueMatch |
||||
// Config to apply when the value matches the special value |
||||
result: ValueMappingResult |
||||
} |
||||
} |
||||
|
||||
// Special value types supported by the `SpecialValueMap` |
||||
SpecialValueMatch: "true" | "false" | "null" | "nan" | "null+nan" | "empty" @cog(kind="enum",memberNames="True|False|Null|NaN|NullAndNaN|Empty") |
||||
|
||||
// Result used as replacement with text and color when the value matches |
||||
ValueMappingResult: { |
||||
// Text to display when the value matches |
||||
text?: string |
||||
// Text to use when the value matches |
||||
color?: string |
||||
// Icon to display when the value matches. Only specific visualizations. |
||||
icon?: string |
||||
// Position in the mapping array. Only used internally. |
||||
index?: int32 |
||||
} |
||||
|
||||
// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. |
||||
// Continuous color interpolates a color using the percentage of a value relative to min and max. |
||||
// Accepted values are: |
||||
// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold |
||||
// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations |
||||
// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations |
||||
// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode |
||||
// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode |
||||
// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode |
||||
// `continuous-YlRd`: Continuous Yellow-Red palette mode |
||||
// `continuous-BlPu`: Continuous Blue-Purple palette mode |
||||
// `continuous-YlBl`: Continuous Yellow-Blue palette mode |
||||
// `continuous-blues`: Continuous Blue palette mode |
||||
// `continuous-reds`: Continuous Red palette mode |
||||
// `continuous-greens`: Continuous Green palette mode |
||||
// `continuous-purples`: Continuous Purple palette mode |
||||
// `shades`: Shades of a single color. Specify a single color, useful in an override rule. |
||||
// `fixed`: Fixed color mode. Specify a single color, useful in an override rule. |
||||
FieldColorModeId: "thresholds" | "palette-classic" | "palette-classic-by-name" | "continuous-GrYlRd" | "continuous-RdYlGr" | "continuous-BlYlRd" | "continuous-YlRd" | "continuous-BlPu" | "continuous-YlBl" | "continuous-blues" | "continuous-reds" | "continuous-greens" | "continuous-purples" | "fixed" | "shades" |
||||
|
||||
// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. |
||||
FieldColorSeriesByMode: "min" | "max" | "last" |
||||
|
||||
// Map a field to a color. |
||||
FieldColor: { |
||||
// The main color scheme mode. |
||||
mode: FieldColorModeId |
||||
// The fixed color value for fixed or shades color modes. |
||||
fixedColor?: string |
||||
// Some visualizations need to know how to assign a series color from by value color schemes. |
||||
seriesBy?: FieldColorSeriesByMode |
||||
} |
||||
|
||||
// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) |
||||
DashboardLinkType: "link" | "dashboards" |
||||
|
||||
// --- Common types --- |
||||
Kind: { |
||||
kind: string |
||||
spec: _ |
||||
metadata?: _ |
||||
} |
||||
|
||||
// --- Kinds --- |
||||
VizConfigSpec: { |
||||
pluginVersion: string |
||||
options: [string]: _ |
||||
fieldConfig: FieldConfigSource |
||||
} |
||||
|
||||
VizConfigKind: { |
||||
// The kind of a VizConfigKind is the plugin ID |
||||
kind: string |
||||
spec: VizConfigSpec |
||||
} |
||||
|
||||
AnnotationQuerySpec: { |
||||
datasource?: DataSourceRef |
||||
query?: DataQueryKind |
||||
enable: bool |
||||
hide: bool |
||||
iconColor: string |
||||
name: string |
||||
builtIn?: bool | *false |
||||
filter?: AnnotationPanelFilter |
||||
options?: [string]: _ //Catch-all field for datasource-specific properties |
||||
} |
||||
|
||||
AnnotationQueryKind: { |
||||
kind: "AnnotationQuery" |
||||
spec: AnnotationQuerySpec |
||||
} |
||||
|
||||
QueryOptionsSpec: { |
||||
timeFrom?: string |
||||
maxDataPoints?: int |
||||
timeShift?: string |
||||
queryCachingTTL?: int |
||||
interval?: string |
||||
cacheTimeout?: string |
||||
hideTimeOverride?: bool |
||||
} |
||||
|
||||
DataQueryKind: { |
||||
// The kind of a DataQueryKind is the datasource type |
||||
kind: string |
||||
spec: [string]: _ |
||||
} |
||||
|
||||
PanelQuerySpec: { |
||||
query: DataQueryKind |
||||
datasource?: DataSourceRef |
||||
|
||||
refId: string |
||||
hidden: bool |
||||
} |
||||
|
||||
PanelQueryKind: { |
||||
kind: "PanelQuery" |
||||
spec: PanelQuerySpec |
||||
} |
||||
|
||||
TransformationKind: { |
||||
// The kind of a TransformationKind is the transformation ID |
||||
kind: string |
||||
spec: DataTransformerConfig |
||||
} |
||||
|
||||
QueryGroupSpec: { |
||||
queries: [...PanelQueryKind] |
||||
transformations: [...TransformationKind] |
||||
queryOptions: QueryOptionsSpec |
||||
} |
||||
|
||||
QueryGroupKind: { |
||||
kind: "QueryGroup" |
||||
spec: QueryGroupSpec |
||||
} |
||||
|
||||
TimeRangeOption: { |
||||
display: string | *"Last 6 hours" |
||||
from: string | *"now-6h" |
||||
to: string | *"now" |
||||
} |
||||
|
||||
// Time configuration |
||||
// It defines the default time config for the time picker, the refresh picker for the specific dashboard. |
||||
TimeSettingsSpec: { |
||||
// Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc". |
||||
timezone?: string | *"browser" |
||||
// Start time range for dashboard. |
||||
// Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". |
||||
from: string | *"now-6h" |
||||
// End time range for dashboard. |
||||
// Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". |
||||
to: string | *"now" |
||||
// Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". |
||||
autoRefresh: string // v1: refresh |
||||
// Interval options available in the refresh picker dropdown. |
||||
autoRefreshIntervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] // v1: timepicker.refresh_intervals |
||||
// Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard. |
||||
quickRanges?: [...TimeRangeOption] // v1: timepicker.quick_ranges , not exposed in the UI |
||||
// Whether timepicker is visible or not. |
||||
hideTimepicker: bool // v1: timepicker.hidden |
||||
// Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday". |
||||
weekStart?: "saturday" | "monday" | "sunday" |
||||
// The month that the fiscal year starts on. 0 = January, 11 = December |
||||
fiscalYearStartMonth: int |
||||
// Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. |
||||
nowDelay?: string // v1: timepicker.nowDelay |
||||
} |
||||
|
||||
RepeatMode: "variable" // other repeat modes will be added in the future: label, frame |
||||
|
||||
RepeatOptions: { |
||||
mode: RepeatMode |
||||
value: string |
||||
direction?: "h" | "v" |
||||
maxPerRow?: int |
||||
} |
||||
|
||||
RowRepeatOptions: { |
||||
mode: RepeatMode |
||||
value: string |
||||
} |
||||
|
||||
AutoGridRepeatOptions: { |
||||
mode: RepeatMode |
||||
value: string |
||||
} |
||||
|
||||
GridLayoutItemSpec: { |
||||
x: int |
||||
y: int |
||||
width: int |
||||
height: int |
||||
element: ElementReference // reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference |
||||
repeat?: RepeatOptions |
||||
} |
||||
|
||||
GridLayoutItemKind: { |
||||
kind: "GridLayoutItem" |
||||
spec: GridLayoutItemSpec |
||||
} |
||||
|
||||
GridLayoutRowKind: { |
||||
kind: "GridLayoutRow" |
||||
spec: GridLayoutRowSpec |
||||
} |
||||
|
||||
GridLayoutRowSpec: { |
||||
y: int |
||||
collapsed: bool |
||||
title: string |
||||
elements: [...GridLayoutItemKind] // Grid items in the row will have their Y value be relative to the rows Y value. This means a panel positioned at Y: 0 in a row with Y: 10 will be positioned at Y: 11 (row header has a heigh of 1) in the dashboard. |
||||
repeat?: RowRepeatOptions |
||||
} |
||||
|
||||
GridLayoutSpec: { |
||||
items: [...GridLayoutItemKind | GridLayoutRowKind] |
||||
} |
||||
|
||||
GridLayoutKind: { |
||||
kind: "GridLayout" |
||||
spec: GridLayoutSpec |
||||
} |
||||
|
||||
RowsLayoutKind: { |
||||
kind: "RowsLayout" |
||||
spec: RowsLayoutSpec |
||||
} |
||||
|
||||
RowsLayoutSpec: { |
||||
rows: [...RowsLayoutRowKind] |
||||
} |
||||
|
||||
RowsLayoutRowKind: { |
||||
kind: "RowsLayoutRow" |
||||
spec: RowsLayoutRowSpec |
||||
} |
||||
|
||||
RowsLayoutRowSpec: { |
||||
title?: string |
||||
collapse?: bool |
||||
hideHeader?: bool |
||||
fillScreen?: bool |
||||
conditionalRendering?: ConditionalRenderingGroupKind |
||||
repeat?: RowRepeatOptions |
||||
layout: GridLayoutKind | AutoGridLayoutKind | TabsLayoutKind | RowsLayoutKind |
||||
} |
||||
|
||||
AutoGridLayoutKind: { |
||||
kind: "AutoGridLayout" |
||||
spec: AutoGridLayoutSpec |
||||
} |
||||
|
||||
AutoGridLayoutSpec: { |
||||
maxColumnCount?: number | *3 |
||||
columnWidthMode: "narrow" | *"standard" | "wide" | "custom" |
||||
columnWidth?: number |
||||
rowHeightMode: "short" | *"standard" | "tall" | "custom" |
||||
rowHeight?: number |
||||
fillScreen?: bool | *false |
||||
items: [...AutoGridLayoutItemKind] |
||||
} |
||||
|
||||
AutoGridLayoutItemKind: { |
||||
kind: "AutoGridLayoutItem" |
||||
spec: AutoGridLayoutItemSpec |
||||
} |
||||
|
||||
AutoGridLayoutItemSpec: { |
||||
element: ElementReference |
||||
repeat?: AutoGridRepeatOptions |
||||
conditionalRendering?: ConditionalRenderingGroupKind |
||||
} |
||||
|
||||
TabsLayoutKind: { |
||||
kind: "TabsLayout" |
||||
spec: TabsLayoutSpec |
||||
} |
||||
|
||||
TabsLayoutSpec: { |
||||
tabs: [...TabsLayoutTabKind] |
||||
} |
||||
|
||||
TabsLayoutTabKind: { |
||||
kind: "TabsLayoutTab" |
||||
spec: TabsLayoutTabSpec |
||||
} |
||||
|
||||
TabsLayoutTabSpec: { |
||||
title?: string |
||||
layout: GridLayoutKind | RowsLayoutKind | AutoGridLayoutKind | TabsLayoutKind |
||||
conditionalRendering?: ConditionalRenderingGroupKind |
||||
} |
||||
|
||||
PanelSpec: { |
||||
id: number |
||||
title: string |
||||
description: string |
||||
links: [...DataLink] |
||||
data: QueryGroupKind |
||||
vizConfig: VizConfigKind |
||||
transparent?: bool |
||||
} |
||||
|
||||
PanelKind: { |
||||
kind: "Panel" |
||||
spec: PanelSpec |
||||
} |
||||
|
||||
ElementReference: { |
||||
kind: "ElementReference" |
||||
name: string |
||||
} |
||||
|
||||
// Start FIXME: variables - in CUE PR - this are things that should be added into the cue schema |
||||
// TODO: properties such as `hide`, `skipUrlSync`, `multi` are type boolean, and in the old schema they are conditional, |
||||
// should we make them conditional in the new schema as well? or should we make them required but default to false? |
||||
|
||||
// Variable types |
||||
VariableValue: VariableValueSingle | [...VariableValueSingle] |
||||
|
||||
VariableValueSingle: string | bool | number | CustomVariableValue |
||||
|
||||
// Custom formatter variable |
||||
CustomFormatterVariable: { |
||||
name: string |
||||
type: VariableType |
||||
multi: bool |
||||
includeAll: bool |
||||
} |
||||
|
||||
// Custom variable value |
||||
CustomVariableValue: { |
||||
// The format name or function used in the expression |
||||
formatter: *null | string | VariableCustomFormatterFn |
||||
} |
||||
|
||||
// Custom formatter function |
||||
VariableCustomFormatterFn: { |
||||
value: _ |
||||
legacyVariableModel: { |
||||
name: string |
||||
type: VariableType |
||||
multi: bool |
||||
includeAll: bool |
||||
} |
||||
legacyDefaultFormatter?: VariableCustomFormatterFn |
||||
} |
||||
|
||||
// Dashboard variable type |
||||
// `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on. |
||||
// `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only). |
||||
// `constant`: Define a hidden constant. |
||||
// `datasource`: Quickly change the data source for an entire dashboard. |
||||
// `interval`: Interval variables represent time spans. |
||||
// `textbox`: Display a free text input field with an optional default value. |
||||
// `custom`: Define the variable options manually using a comma-separated list. |
||||
// `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables |
||||
VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" | |
||||
"system" | "snapshot" |
||||
|
||||
VariableKind: QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind |
||||
|
||||
// Sort variable options |
||||
// Accepted values are: |
||||
// `disabled`: No sorting |
||||
// `alphabeticalAsc`: Alphabetical ASC |
||||
// `alphabeticalDesc`: Alphabetical DESC |
||||
// `numericalAsc`: Numerical ASC |
||||
// `numericalDesc`: Numerical DESC |
||||
// `alphabeticalCaseInsensitiveAsc`: Alphabetical Case Insensitive ASC |
||||
// `alphabeticalCaseInsensitiveDesc`: Alphabetical Case Insensitive DESC |
||||
// `naturalAsc`: Natural ASC |
||||
// `naturalDesc`: Natural DESC |
||||
// VariableSort enum with default value |
||||
VariableSort: "disabled" | "alphabeticalAsc" | "alphabeticalDesc" | "numericalAsc" | "numericalDesc" | "alphabeticalCaseInsensitiveAsc" | "alphabeticalCaseInsensitiveDesc" | "naturalAsc" | "naturalDesc" |
||||
|
||||
// Options to config when to refresh a variable |
||||
// `never`: Never refresh the variable |
||||
// `onDashboardLoad`: Queries the data source every time the dashboard loads. |
||||
// `onTimeRangeChanged`: Queries the data source when the dashboard time range changes. |
||||
VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged" |
||||
|
||||
// Determine if the variable shows on dashboard |
||||
// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing). |
||||
VariableHide: *"dontHide" | "hideLabel" | "hideVariable" |
||||
|
||||
// FIXME: should we introduce this? --- Variable value option |
||||
VariableValueOption: { |
||||
label: string |
||||
value: VariableValueSingle |
||||
group?: string |
||||
} |
||||
|
||||
// Variable option specification |
||||
VariableOption: { |
||||
// Whether the option is selected or not |
||||
selected?: bool |
||||
// Text to be displayed for the option |
||||
text: string | [...string] |
||||
// Value of the option |
||||
value: string | [...string] |
||||
} |
||||
|
||||
// Query variable specification |
||||
QueryVariableSpec: { |
||||
name: string | *"" |
||||
current: VariableOption | *{ |
||||
text: "" |
||||
value: "" |
||||
} |
||||
label?: string |
||||
hide: VariableHide |
||||
refresh: VariableRefresh |
||||
skipUrlSync: bool | *false |
||||
description?: string |
||||
datasource?: DataSourceRef |
||||
query: DataQueryKind |
||||
regex: string | *"" |
||||
sort: VariableSort |
||||
definition?: string |
||||
options: [...VariableOption] | *[] |
||||
multi: bool | *false |
||||
includeAll: bool | *false |
||||
allValue?: string |
||||
placeholder?: string |
||||
} |
||||
|
||||
// Query variable kind |
||||
QueryVariableKind: { |
||||
kind: "QueryVariable" |
||||
spec: QueryVariableSpec |
||||
} |
||||
|
||||
// Text variable specification |
||||
TextVariableSpec: { |
||||
name: string | *"" |
||||
current: VariableOption | *{ |
||||
text: "" |
||||
value: "" |
||||
} |
||||
query: string | *"" |
||||
label?: string |
||||
hide: VariableHide |
||||
skipUrlSync: bool | *false |
||||
description?: string |
||||
} |
||||
|
||||
// Text variable kind |
||||
TextVariableKind: { |
||||
kind: "TextVariable" |
||||
spec: TextVariableSpec |
||||
} |
||||
|
||||
// Constant variable specification |
||||
ConstantVariableSpec: { |
||||
name: string | *"" |
||||
query: string | *"" |
||||
current: VariableOption | *{ |
||||
text: "" |
||||
value: "" |
||||
} |
||||
label?: string |
||||
hide: VariableHide |
||||
skipUrlSync: bool | *false |
||||
description?: string |
||||
} |
||||
|
||||
// Constant variable kind |
||||
ConstantVariableKind: { |
||||
kind: "ConstantVariable" |
||||
spec: ConstantVariableSpec |
||||
} |
||||
|
||||
// Datasource variable specification |
||||
DatasourceVariableSpec: { |
||||
name: string | *"" |
||||
pluginId: string | *"" |
||||
refresh: VariableRefresh |
||||
regex: string | *"" |
||||
current: VariableOption | *{ |
||||
text: "" |
||||
value: "" |
||||
} |
||||
options: [...VariableOption] | *[] |
||||
multi: bool | *false |
||||
includeAll: bool | *false |
||||
allValue?: string |
||||
label?: string |
||||
hide: VariableHide |
||||
skipUrlSync: bool | *false |
||||
description?: string |
||||
} |
||||
|
||||
// Datasource variable kind |
||||
DatasourceVariableKind: { |
||||
kind: "DatasourceVariable" |
||||
spec: DatasourceVariableSpec |
||||
} |
||||
|
||||
// Interval variable specification |
||||
IntervalVariableSpec: { |
||||
name: string | *"" |
||||
query: string | *"" |
||||
current: VariableOption | *{ |
||||
text: "" |
||||
value: "" |
||||
} |
||||
options: [...VariableOption] | *[] |
||||
auto: bool | *false |
||||
auto_min: string | *"" |
||||
auto_count: int | *0 |
||||
refresh: VariableRefresh |
||||
label?: string |
||||
hide: VariableHide |
||||
skipUrlSync: bool | *false |
||||
description?: string |
||||
} |
||||
|
||||
// Interval variable kind |
||||
IntervalVariableKind: { |
||||
kind: "IntervalVariable" |
||||
spec: IntervalVariableSpec |
||||
} |
||||
|
||||
// Custom variable specification |
||||
CustomVariableSpec: { |
||||
name: string | *"" |
||||
query: string | *"" |
||||
current: VariableOption |
||||
options: [...VariableOption] | *[] |
||||
multi: bool | *false |
||||
includeAll: bool | *false |
||||
allValue?: string |
||||
label?: string |
||||
hide: VariableHide |
||||
skipUrlSync: bool | *false |
||||
description?: string |
||||
} |
||||
|
||||
// Custom variable kind |
||||
CustomVariableKind: { |
||||
kind: "CustomVariable" |
||||
spec: CustomVariableSpec |
||||
} |
||||
|
||||
// GroupBy variable specification |
||||
GroupByVariableSpec: { |
||||
name: string | *"" |
||||
datasource?: DataSourceRef |
||||
current: VariableOption | *{ |
||||
text: "" |
||||
value: "" |
||||
} |
||||
options: [...VariableOption] | *[] |
||||
multi: bool | *false |
||||
label?: string |
||||
hide: VariableHide |
||||
skipUrlSync: bool | *false |
||||
description?: string |
||||
} |
||||
|
||||
// Group variable kind |
||||
GroupByVariableKind: { |
||||
kind: "GroupByVariable" |
||||
spec: GroupByVariableSpec |
||||
} |
||||
|
||||
// Adhoc variable specification |
||||
AdhocVariableSpec: { |
||||
name: string | *"" |
||||
datasource?: DataSourceRef |
||||
baseFilters: [...AdHocFilterWithLabels] | *[] |
||||
filters: [...AdHocFilterWithLabels] | *[] |
||||
defaultKeys: [...MetricFindValue] | *[] |
||||
label?: string |
||||
hide: VariableHide |
||||
skipUrlSync: bool | *false |
||||
description?: string |
||||
} |
||||
|
||||
// Define the MetricFindValue type |
||||
MetricFindValue: { |
||||
text: string |
||||
value?: string | number |
||||
group?: string |
||||
expandable?: bool |
||||
} |
||||
|
||||
// Define the AdHocFilterWithLabels type |
||||
AdHocFilterWithLabels: { |
||||
key: string |
||||
operator: string |
||||
value: string |
||||
values?: [...string] |
||||
keyLabel?: string |
||||
valueLabels?: [...string] |
||||
forceEdit?: bool |
||||
// @deprecated |
||||
condition?: string |
||||
} |
||||
|
||||
// Adhoc variable kind |
||||
AdhocVariableKind: { |
||||
kind: "AdhocVariable" |
||||
spec: AdhocVariableSpec |
||||
} |
||||
|
||||
ConditionalRenderingGroupKind: { |
||||
kind: "ConditionalRenderingGroup" |
||||
spec: ConditionalRenderingGroupSpec |
||||
} |
||||
|
||||
ConditionalRenderingGroupSpec: { |
||||
visibility: "show" | "hide" |
||||
condition: "and" | "or" |
||||
items: [...ConditionalRenderingVariableKind | ConditionalRenderingDataKind | ConditionalRenderingTimeRangeSizeKind] |
||||
} |
||||
|
||||
ConditionalRenderingVariableKind: { |
||||
kind: "ConditionalRenderingVariable" |
||||
spec: ConditionalRenderingVariableSpec |
||||
} |
||||
|
||||
ConditionalRenderingVariableSpec: { |
||||
variable: string |
||||
operator: "equals" | "notEquals" |
||||
value: string |
||||
} |
||||
|
||||
ConditionalRenderingDataKind: { |
||||
kind: "ConditionalRenderingData" |
||||
spec: ConditionalRenderingDataSpec |
||||
} |
||||
|
||||
ConditionalRenderingDataSpec: { |
||||
value: bool |
||||
} |
||||
|
||||
ConditionalRenderingTimeRangeSizeKind: { |
||||
kind: "ConditionalRenderingTimeRangeSize" |
||||
spec: ConditionalRenderingTimeRangeSizeSpec |
||||
} |
||||
|
||||
ConditionalRenderingTimeRangeSizeSpec: { |
||||
value: string |
||||
} |
@ -0,0 +1,84 @@ |
||||
package v2alpha1 |
||||
|
||||
import ( |
||||
_ "embed" |
||||
json "encoding/json" |
||||
fmt "fmt" |
||||
"strings" |
||||
"sync" |
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field" |
||||
|
||||
"cuelang.org/go/cue" |
||||
"cuelang.org/go/cue/cuecontext" |
||||
"cuelang.org/go/cue/errors" |
||||
cuejson "cuelang.org/go/encoding/json" |
||||
) |
||||
|
||||
func ValidateDashboardSpec(obj *Dashboard) field.ErrorList { |
||||
data, err := json.Marshal(obj.Spec) |
||||
if err != nil { |
||||
return field.ErrorList{ |
||||
field.Invalid(field.NewPath("spec"), field.OmitValueType{}, err.Error()), |
||||
} |
||||
} |
||||
|
||||
if err := cuejson.Validate(data, getCueSchema()); err != nil { |
||||
errs := field.ErrorList{} |
||||
|
||||
for _, e := range errors.Errors(err) { |
||||
if |
||||
// We don't want to return confusing "empty disjunction" errors,
|
||||
// because the users don't necessarily understand what to do with them.
|
||||
// For empty disjunctions, CUE will also return more specific errors,
|
||||
// so we can safely ignore the generic ones.
|
||||
strings.Contains(e.Error(), "disjunction") || |
||||
// We don't want to return errors about unknown fields either.
|
||||
strings.Contains(e.Error(), "field not allowed") { |
||||
continue |
||||
} |
||||
|
||||
if strings.Contains(e.Error(), "mismatched types null and list") { |
||||
// Go populates empty slices as nil, which the cue validator does not like
|
||||
continue |
||||
} |
||||
|
||||
// We want to manually format the error message,
|
||||
// because e.Error() contains the full CUE path.
|
||||
format, args := e.Msg() |
||||
|
||||
errs = append(errs, field.Invalid( |
||||
field.NewPath(formatErrorPath(e.Path())), |
||||
field.OmitValueType{}, |
||||
fmt.Sprintf(format, args...), |
||||
)) |
||||
} |
||||
|
||||
return errs |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func formatErrorPath(path []string) string { |
||||
return strings.Join(path, ".") |
||||
} |
||||
|
||||
var ( |
||||
compiledSchema cue.Value |
||||
getSchemaOnce sync.Once |
||||
) |
||||
|
||||
//go:embed dashboard_spec.cue
|
||||
var schemaSource string |
||||
|
||||
func getCueSchema() cue.Value { |
||||
getSchemaOnce.Do(func() { |
||||
cueCtx := cuecontext.New() |
||||
compiledSchema = cueCtx.CompileString(schemaSource).LookupPath( |
||||
cue.ParsePath("DashboardSpec"), |
||||
) |
||||
}) |
||||
|
||||
return compiledSchema |
||||
} |
@ -0,0 +1,78 @@ |
||||
package dashboard |
||||
|
||||
import ( |
||||
"context" |
||||
_ "embed" |
||||
"errors" |
||||
"fmt" |
||||
|
||||
"k8s.io/apimachinery/pkg/runtime" |
||||
"k8s.io/apimachinery/pkg/util/validation/field" |
||||
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" |
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1" |
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1" |
||||
"github.com/grafana/grafana/pkg/apimachinery/utils" |
||||
"github.com/grafana/grafana/pkg/services/featuremgmt" |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
) |
||||
|
||||
// ValidateDashboardSpec validates the dashboard spec and throws a detailed error if there are validation errors.
|
||||
func (b *DashboardsAPIBuilder) ValidateDashboardSpec(ctx context.Context, obj runtime.Object, fieldValidationMode string) (field.ErrorList, error) { |
||||
// This will be removed with the other PR
|
||||
return nil, nil |
||||
|
||||
// Unreachable code is intentional until the code above is removed
|
||||
//nolint:govet
|
||||
accessor, err := utils.MetaAccessor(obj) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("error getting meta accessor: %w", err) |
||||
} |
||||
|
||||
errorOnSchemaMismatches := false |
||||
mode := fieldValidationMode |
||||
if mode != metav1.FieldValidationIgnore { |
||||
switch obj.(type) { |
||||
case *v0alpha1.Dashboard: |
||||
errorOnSchemaMismatches = false // Never error for v0
|
||||
case *v1alpha1.Dashboard: |
||||
errorOnSchemaMismatches = !b.features.IsEnabled(ctx, featuremgmt.FlagDashboardDisableSchemaValidationV1) |
||||
case *v2alpha1.Dashboard: |
||||
errorOnSchemaMismatches = !b.features.IsEnabled(ctx, featuremgmt.FlagDashboardDisableSchemaValidationV2) |
||||
default: |
||||
return nil, fmt.Errorf("Invalid dashboard type: %T", obj) |
||||
} |
||||
} |
||||
if mode == metav1.FieldValidationWarn { |
||||
return nil, errors.New("FieldValidationWarn is not supported") |
||||
} |
||||
|
||||
alwaysLogSchemaValidationErrors := b.features.IsEnabled(ctx, featuremgmt.FlagDashboardSchemaValidationLogging) |
||||
|
||||
var errors field.ErrorList |
||||
var schemaVersionError field.ErrorList |
||||
if errorOnSchemaMismatches || alwaysLogSchemaValidationErrors { |
||||
switch v := obj.(type) { |
||||
case *v0alpha1.Dashboard: |
||||
errors, schemaVersionError = v0alpha1.ValidateDashboardSpec(v, alwaysLogSchemaValidationErrors) |
||||
case *v1alpha1.Dashboard: |
||||
errors, schemaVersionError = v1alpha1.ValidateDashboardSpec(v, alwaysLogSchemaValidationErrors) |
||||
case *v2alpha1.Dashboard: |
||||
errors = v2alpha1.ValidateDashboardSpec(v) |
||||
} |
||||
} |
||||
|
||||
if alwaysLogSchemaValidationErrors && len(errors) > 0 { |
||||
b.log.Info("Schema validation errors during dashboard validation", "group_version", obj.GetObjectKind().GroupVersionKind().GroupVersion().String(), "name", accessor.GetName(), "errors", errors.ToAggregate().Error(), "schema_version_mismatch", schemaVersionError != nil) |
||||
} |
||||
|
||||
if errorOnSchemaMismatches { |
||||
if schemaVersionError != nil { |
||||
return schemaVersionError, nil |
||||
} |
||||
if len(errors) > 0 { |
||||
return errors, nil |
||||
} |
||||
} |
||||
return nil, nil |
||||
} |
|
Loading…
Reference in new issue