Dashboards: Add Dashboard Schema validation (2) (#103844)

* Activate schema validation and align underlying systems

* update to save as v0 if not the right schema version

* Resolve merge conflicts

* Move RequireApiErrorStatus to tests package

* Add mutation tests

* Fix lint

* Only do min version check if dashboard is v1

* Fix lint and disable provisioning test

* Revert provisioning changes

* Revert more tests and add schema test

* Reran gen

* SQL Dashboard save

* Adjust APIVERSION

* Fixed mutation test

* Add logging on downgrade

---------

Co-authored-by: Marco de Abreu <18629099+marcoabreu@users.noreply.github.com>
Co-authored-by: Stephanie Hingtgen <stephanie.hingtgen@grafana.com>
This commit is contained in:
Marco de Abreu
2025-04-11 23:05:41 +02:00
committed by GitHub
co-authored by Marco de Abreu Stephanie Hingtgen
parent 07a225649d
commit c47ab101d1
21 changed files with 500 additions and 128 deletions
@@ -1,8 +1,7 @@
package v2alpha1
DashboardSpec: {
// Title of dashboard.
annotations: [...AnnotationQueryKind]
annotations: [...AnnotationQueryKind] | *[]
// Configuration of dashboard cursor sync behavior.
// "Off" for no shared crosshair or tooltip (default).
@@ -16,12 +15,12 @@ DashboardSpec: {
// Whether a dashboard is editable or not.
editable?: bool | *true
elements: [ElementReference.name]: Element
elements: [ElementReference.name]: Element | *{}
layout: GridLayoutKind | RowsLayoutKind | AutoGridLayoutKind | TabsLayoutKind
// Links with references to other dashboards or external websites.
links: [...DashboardLink]
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
@@ -29,14 +28,14 @@ DashboardSpec: {
liveNow?: bool
// When set to true, the dashboard will load all panels in the dashboard when it's loaded.
preload: bool
preload: bool | *false
// 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]
tags: [...string] | *[]
timeSettings: TimeSettingsSpec
@@ -44,7 +43,7 @@ DashboardSpec: {
title: string
// Configured template variables.
variables: [...VariableKind]
variables: [...VariableKind] | *[]
}
// Supported dashboard elements
@@ -85,7 +84,7 @@ AnnotationPanelFilter: {
// "Off" for no shared crosshair or tooltip (default).
// "Crosshair" for shared crosshair.
// "Tooltip" for shared crosshair AND shared tooltip.
DashboardCursorSync: "Off" | "Crosshair" | "Tooltip"
DashboardCursorSync: "Crosshair" | "Tooltip" | *"Off"
// Links with references to other dashboards or external resources
DashboardLink: {
@@ -101,7 +100,7 @@ DashboardLink: {
// 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]
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
@@ -466,17 +465,17 @@ TimeSettingsSpec: {
// 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
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
hideTimepicker: bool | *false // 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
fiscalYearStartMonth: int | *0
// 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
}
@@ -5,8 +5,7 @@
package v2alpha1
DashboardSpec: {
// Title of dashboard.
annotations: [...AnnotationQueryKind]
annotations: [...AnnotationQueryKind] | *[]
// Configuration of dashboard cursor sync behavior.
// "Off" for no shared crosshair or tooltip (default).
@@ -20,12 +19,12 @@ DashboardSpec: {
// Whether a dashboard is editable or not.
editable?: bool | *true
elements: [ElementReference.name]: Element
elements: [ElementReference.name]: Element | *{}
layout: GridLayoutKind | RowsLayoutKind | AutoGridLayoutKind | TabsLayoutKind
// Links with references to other dashboards or external websites.
links: [...DashboardLink]
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
@@ -33,14 +32,14 @@ DashboardSpec: {
liveNow?: bool
// When set to true, the dashboard will load all panels in the dashboard when it's loaded.
preload: bool
preload: bool | *false
// 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]
tags: [...string] | *[]
timeSettings: TimeSettingsSpec
@@ -48,7 +47,7 @@ DashboardSpec: {
title: string
// Configured template variables.
variables: [...VariableKind]
variables: [...VariableKind] | *[]
}
// Supported dashboard elements
@@ -89,7 +88,7 @@ AnnotationPanelFilter: {
// "Off" for no shared crosshair or tooltip (default).
// "Crosshair" for shared crosshair.
// "Tooltip" for shared crosshair AND shared tooltip.
DashboardCursorSync: "Off" | "Crosshair" | "Tooltip"
DashboardCursorSync: "Crosshair" | "Tooltip" | *"Off"
// Links with references to other dashboards or external resources
DashboardLink: {
@@ -105,7 +104,7 @@ DashboardLink: {
// 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]
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
@@ -470,17 +469,17 @@ TimeSettingsSpec: {
// 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
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
hideTimepicker: bool | *false // 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
fiscalYearStartMonth: int | *0
// 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
}
@@ -499,6 +498,11 @@ RowRepeatOptions: {
value: string
}
TabRepeatOptions: {
mode: RepeatMode
value: string
}
AutoGridRepeatOptions: {
mode: RepeatMode
value: string
@@ -527,8 +531,8 @@ 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
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: {
@@ -608,6 +612,7 @@ TabsLayoutTabSpec: {
title?: string
layout: GridLayoutKind | RowsLayoutKind | AutoGridLayoutKind | TabsLayoutKind
conditionalRendering?: ConditionalRenderingGroupKind
repeat?: TabRepeatOptions
}
PanelSpec: {
@@ -90,9 +90,9 @@ func NewDashboardAnnotationPanelFilter() *DashboardAnnotationPanelFilter {
type DashboardDashboardCursorSync string
const (
DashboardDashboardCursorSyncOff DashboardDashboardCursorSync = "Off"
DashboardDashboardCursorSyncCrosshair DashboardDashboardCursorSync = "Crosshair"
DashboardDashboardCursorSyncTooltip DashboardDashboardCursorSync = "Tooltip"
DashboardDashboardCursorSyncOff DashboardDashboardCursorSync = "Off"
)
// Supported dashboard elements
@@ -1174,7 +1174,10 @@ func NewDashboardTimeSettingsSpec() *DashboardTimeSettingsSpec {
Timezone: (func(input string) *string { return &input })("browser"),
From: "now-6h",
To: "now",
AutoRefresh: "",
AutoRefreshIntervals: []string{"5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"},
HideTimepicker: false,
FiscalYearStartMonth: 0,
}
}
@@ -1694,7 +1697,6 @@ func NewDashboardMetricFindValue() *DashboardMetricFindValue {
// +k8s:openapi-gen=true
type DashboardSpec struct {
// Title of dashboard.
Annotations []DashboardAnnotationQueryKind `json:"annotations"`
// Configuration of dashboard cursor sync behavior.
// "Off" for no shared crosshair or tooltip (default).
@@ -1730,8 +1732,10 @@ type DashboardSpec struct {
// NewDashboardSpec creates a new DashboardSpec object.
func NewDashboardSpec() *DashboardSpec {
return &DashboardSpec{
CursorSync: DashboardDashboardCursorSyncOff,
Editable: (func(input bool) *bool { return &input })(true),
Layout: *NewDashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind(),
Preload: false,
TimeSettings: *NewDashboardTimeSettingsSpec(),
}
}
@@ -3678,8 +3678,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardSpec(ref common.ReferenceCallba
Properties: map[string]spec.Schema{
"annotations": {
SchemaProps: spec.SchemaProps{
Description: "Title of dashboard.",
Type: []string{"array"},
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{