Introduce "scuemata" system for CUE-based specification of Grafana objects (#32527)

This commit is contained in:
sam boyer
2021-04-08 01:11:11 -07:00
committed by GitHub
parent 7351645d63
commit bba4d9bd7f
87 changed files with 2446 additions and 1136 deletions
+3
View File
@@ -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.
+153
View File
@@ -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"
}
}