Introduce "scuemata" system for CUE-based specification of Grafana objects (#32527)
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
All artifact JSON contained in these subdirectories should be valid. Invalid
|
||||
JSON is handled elsewhere, as it must be coupled with expected error messages
|
||||
for testing purposes.
|
||||
@@ -0,0 +1,153 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_GDEV-TESTDATA",
|
||||
"label": "gdev-testdata",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "testdata",
|
||||
"pluginName": "TestData DB"
|
||||
}
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "7.5.0-pre"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "table",
|
||||
"name": "Table",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
"id": "testdata",
|
||||
"name": "TestData DB",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"rawQuery": "wtf",
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"id": 42,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"align": "right",
|
||||
"filterable": false
|
||||
},
|
||||
"decimals": 3,
|
||||
"mappings": [],
|
||||
"unit": "watt"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Max"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.displayMode",
|
||||
"value": "lcd-gauge"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "A"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"showHeader": true,
|
||||
"sortBy": []
|
||||
},
|
||||
"pluginVersion": "7.5.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"alias": "",
|
||||
"csvWave": {
|
||||
"timeStep": 60,
|
||||
"valuesCSV": "0,0,2,2,1,1"
|
||||
},
|
||||
"lines": 10,
|
||||
"points": [],
|
||||
"pulseWave": {
|
||||
"offCount": 3,
|
||||
"offValue": 1,
|
||||
"onCount": 3,
|
||||
"onValue": 2,
|
||||
"timeStep": 60
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk_table",
|
||||
"stream": {
|
||||
"bands": 1,
|
||||
"noise": 2.2,
|
||||
"speed": 250,
|
||||
"spread": 3.5,
|
||||
"type": "signal"
|
||||
},
|
||||
"stringInput": ""
|
||||
}
|
||||
],
|
||||
"title": "Panel Title",
|
||||
"type": "table",
|
||||
"panelSchema": {
|
||||
"maj": 0,
|
||||
"min": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"schemaVersion": 27,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timezone": "browser",
|
||||
"title": "with table",
|
||||
"uid": "emal8gQMz",
|
||||
"version": 2
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"align": "right",
|
||||
"filterable": false
|
||||
},
|
||||
"decimals": 3,
|
||||
"mappings": [],
|
||||
"unit": "watt"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Max"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.displayMode",
|
||||
"value": "lcd-gauge"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "A"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"options": {
|
||||
"showHeader": true,
|
||||
"sortBy": []
|
||||
},
|
||||
"pluginVersion": "7.5.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"alias": "",
|
||||
"csvWave": {
|
||||
"timeStep": 60,
|
||||
"valuesCSV": "0,0,2,2,1,1"
|
||||
},
|
||||
"lines": 10,
|
||||
"points": [],
|
||||
"pulseWave": {
|
||||
"offCount": 3,
|
||||
"offValue": 1,
|
||||
"onCount": 3,
|
||||
"onValue": 2,
|
||||
"timeStep": 60
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk_table",
|
||||
"stream": {
|
||||
"bands": 1,
|
||||
"noise": 2.2,
|
||||
"speed": 250,
|
||||
"spread": 3.5,
|
||||
"type": "signal"
|
||||
},
|
||||
"stringInput": ""
|
||||
}
|
||||
],
|
||||
"title": "Panel Title",
|
||||
"type": "table",
|
||||
"panelSchema": {
|
||||
"maj": 0,
|
||||
"min": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package grafanaschema
|
||||
|
||||
import (
|
||||
ui "github.com/grafana/grafana/cue/ui:grafanaschema"
|
||||
)
|
||||
|
||||
// TODO should we remove Family, and make lineages and migrations top-level values?
|
||||
// It's easy to do, and arguably increases clarity of this crucial file by
|
||||
// reducing one layer of nesting. But it sorta requires understanding that CUE
|
||||
// also thinks of an entire file (aka, an "instance") as a struct in order for
|
||||
// it to make sense that the file itself is schematized by #PanelFamily. What's
|
||||
// the best DX here?
|
||||
|
||||
// "Family" must be an instance of the #PanelFamily type, defined in
|
||||
// cue/scuemata/panel-plugin.cue. This ensures some key invariants:
|
||||
//
|
||||
// - lineages is an array of arrays. Outer array is major version, inner is minor.
|
||||
// (This IS NOT semver, though.)
|
||||
// - Within a single seq, each successive schema is backwards compatible with
|
||||
// the prior schema. (See, it's not semver. No special rules for v0.)
|
||||
// - For each seq/major version after the first, there exists a migration
|
||||
// that allows us to transform a resource compliant with the old version of
|
||||
// the schema into one compliant with the new one.
|
||||
//
|
||||
// That's right, we've schematized our schema declarations. Not all above
|
||||
// invariants are enforced right now, but they must be before launch.
|
||||
//
|
||||
// Grafana won't need to rely on multiple versions of schema until after this
|
||||
// system is released with Grafana 8. But it needs to be in place at the moment
|
||||
// Grafana 8 is released - especially for plugins, which have their own release
|
||||
// cycle, and could need to make breaking changes very shortly after v8's release.
|
||||
Family: {
|
||||
lineages: [
|
||||
[
|
||||
{ // v0.0. The actual schema is the contents of this struct.
|
||||
PanelOptions: {
|
||||
frameIndex: number | *0
|
||||
showHeader: bool | *true
|
||||
sortBy?: [...ui.TableSortByFieldState]
|
||||
}
|
||||
PanelFieldConfig: {
|
||||
width?: int
|
||||
align?: *null | string
|
||||
displayMode?: string | *"auto" // TODO? TableCellDisplayMode
|
||||
filterable?: bool
|
||||
}
|
||||
},
|
||||
{ // v0.1
|
||||
lineages[0][0]
|
||||
PanelOptions: foo: string | *"foo"
|
||||
}
|
||||
],
|
||||
[
|
||||
{ // v1.0 - breaking changes vs. v0.1 in this struct.
|
||||
PanelOptions: {
|
||||
frameIndex: number | *0
|
||||
includeHeader: bool | *true
|
||||
sortBy?: [...ui.TableSortByFieldState]
|
||||
}
|
||||
PanelFieldConfig: {
|
||||
width?: int
|
||||
align?: string
|
||||
displayMode?: string
|
||||
}
|
||||
}
|
||||
],
|
||||
]
|
||||
migrations: [
|
||||
{ // maps from v0.1 to v1.0
|
||||
// TODO it's not good that the user has to specify these. Should be
|
||||
// implicit, since we don't want to allow any actual choice here.
|
||||
// But NOT having it also means CUE can't actually tell if the
|
||||
// _rel definition makes any sense at all. UGHHH. Would it be
|
||||
// better to put these directly on the lineages?
|
||||
from: lineages[0][1]
|
||||
to: lineages[1][0]
|
||||
rel: {
|
||||
PanelOptions: {
|
||||
frameIndex: from.PanelOptions.frameIndex
|
||||
includeHeader: from.PanelOptions.showHeader
|
||||
if from.PanelOptions.sortBy != _|_ {
|
||||
sortBy: from.PanelOptions.sortBy | *null
|
||||
}
|
||||
}
|
||||
PanelFieldConfig: from.PanelFieldConfig
|
||||
}
|
||||
result: rel & to
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "panel",
|
||||
"name": "Sample plugin with lineage",
|
||||
"id": "with-lineage",
|
||||
|
||||
"info": {
|
||||
"description": "Show how complex history may work"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user