Search: Make the existing dashboard parsing code public (#95649)

This commit is contained in:
Ryan McKinley
2024-11-06 07:35:15 +03:00
committed by GitHub
parent ed52515ae3
commit 7f560c13e4
7 changed files with 248 additions and 35 deletions

View File

@@ -112,12 +112,15 @@ func newDatasourceVariableLookup(dsLookup DatasourceLookup) *datasourceVariableL
} }
} }
// nolint:gocyclo
// ReadDashboard will take a byte stream and return dashboard info // ReadDashboard will take a byte stream and return dashboard info
func readDashboard(stream io.Reader, lookup DatasourceLookup) (*dashboardInfo, error) { func ReadDashboard(stream io.Reader, lookup DatasourceLookup) (*DashboardSummaryInfo, error) {
dash := &dashboardInfo{}
iter := jsoniter.Parse(jsoniter.ConfigDefault, stream, 1024) iter := jsoniter.Parse(jsoniter.ConfigDefault, stream, 1024)
return readDashboardIter(iter, lookup)
}
// nolint:gocyclo
func readDashboardIter(iter *jsoniter.Iterator, lookup DatasourceLookup) (*DashboardSummaryInfo, error) {
dash := &DashboardSummaryInfo{}
datasourceVariablesLookup := newDatasourceVariableLookup(lookup) datasourceVariablesLookup := newDatasourceVariableLookup(lookup)
@@ -129,6 +132,14 @@ func readDashboard(stream io.Reader, lookup DatasourceLookup) (*dashboardInfo, e
} }
switch l1Field { switch l1Field {
// k8s metadata wrappers (skip)
case "metadata", "kind", "apiVersion":
_ = iter.Read()
// recursively read the spec as dashboard json
case "spec":
return readDashboardIter(iter, lookup)
case "id": case "id":
dash.ID = iter.ReadInt64() dash.ID = iter.ReadInt64()
@@ -268,7 +279,12 @@ func readDashboard(stream io.Reader, lookup DatasourceLookup) (*dashboardInfo, e
filterOutSpecialDatasources(dash) filterOutSpecialDatasources(dash)
targets := newTargetInfo(lookup) targets := newTargetInfo(lookup)
for _, panel := range dash.Panels { for idx, panel := range dash.Panels {
if panel.Type == "row" {
dash.Panels[idx].Datasource = nil
continue
}
targets.addPanel(panel) targets.addPanel(panel)
} }
dash.Datasource = targets.GetDatasourceInfo() dash.Datasource = targets.GetDatasourceInfo()
@@ -276,13 +292,13 @@ func readDashboard(stream io.Reader, lookup DatasourceLookup) (*dashboardInfo, e
return dash, iter.Error return dash, iter.Error
} }
func panelRequiresDatasource(panel panelInfo) bool { func panelRequiresDatasource(panel PanelSummaryInfo) bool {
return panel.Type != "row" return panel.Type != "row"
} }
func fillDefaultDatasources(dash *dashboardInfo, lookup DatasourceLookup) { func fillDefaultDatasources(dash *DashboardSummaryInfo, lookup DatasourceLookup) {
for i, panel := range dash.Panels { for i, panel := range dash.Panels {
if len(panel.Datasource) != 0 || !panelRequiresDatasource(panel) { if len(panel.Datasource) != 0 || !panelRequiresDatasource(PanelSummaryInfo{}) {
continue continue
} }
@@ -293,7 +309,7 @@ func fillDefaultDatasources(dash *dashboardInfo, lookup DatasourceLookup) {
} }
} }
func filterOutSpecialDatasources(dash *dashboardInfo) { func filterOutSpecialDatasources(dash *DashboardSummaryInfo) {
for i, panel := range dash.Panels { for i, panel := range dash.Panels {
var dsRefs []DataSourceRef var dsRefs []DataSourceRef
@@ -315,7 +331,7 @@ func filterOutSpecialDatasources(dash *dashboardInfo) {
} }
} }
func replaceDatasourceVariables(dash *dashboardInfo, datasourceVariablesLookup *datasourceVariableLookup) { func replaceDatasourceVariables(dash *DashboardSummaryInfo, datasourceVariablesLookup *datasourceVariableLookup) {
for i, panel := range dash.Panels { for i, panel := range dash.Panels {
var dsVariableRefs []DataSourceRef var dsVariableRefs []DataSourceRef
var dsRefs []DataSourceRef var dsRefs []DataSourceRef
@@ -362,8 +378,8 @@ func findDatasourceRefsForVariables(dsVariableRefs []DataSourceRef, datasourceVa
} }
// will always return strings for now // will always return strings for now
func readpanelInfo(iter *jsoniter.Iterator, lookup DatasourceLookup) panelInfo { func readpanelInfo(iter *jsoniter.Iterator, lookup DatasourceLookup) PanelSummaryInfo {
panel := panelInfo{} panel := PanelSummaryInfo{}
targets := newTargetInfo(lookup) targets := newTargetInfo(lookup)

View File

@@ -71,6 +71,7 @@ func TestReadDashboard(t *testing.T) {
"special-datasource-types", "special-datasource-types",
"panels-without-datasources", "panels-without-datasources",
"panel-with-library-panel-field", "panel-with-library-panel-field",
"k8s-wrapper",
} }
devdash := "../../../../../devenv/dev-dashboards/" devdash := "../../../../../devenv/dev-dashboards/"
@@ -89,7 +90,7 @@ func TestReadDashboard(t *testing.T) {
} }
require.NoError(t, err) require.NoError(t, err)
dash, err := readDashboard(f, dsLookupForTests()) dash, err := ReadDashboard(f, dsLookupForTests())
sortDatasources(dash) sortDatasources(dash)
require.NoError(t, err) require.NoError(t, err)
@@ -114,7 +115,7 @@ func TestReadDashboard(t *testing.T) {
} }
// assure consistent ordering of datasources to prevent random failures of `assert.JSONEq` // assure consistent ordering of datasources to prevent random failures of `assert.JSONEq`
func sortDatasources(dash *dashboardInfo) { func sortDatasources(dash *DashboardSummaryInfo) {
sort.Slice(dash.Datasource, func(i, j int) bool { sort.Slice(dash.Datasource, func(i, j int) bool {
return strings.Compare(dash.Datasource[i].UID, dash.Datasource[j].UID) > 0 return strings.Compare(dash.Datasource[i].UID, dash.Datasource[j].UID) > 0
}) })

View File

@@ -40,7 +40,7 @@ func NewStaticDashboardSummaryBuilder(lookup DatasourceLookup, sanitize bool) en
Fields: make(map[string]string), Fields: make(map[string]string),
} }
stream := bytes.NewBuffer(body) stream := bytes.NewBuffer(body)
dash, err := readDashboard(stream, lookup) dash, err := ReadDashboard(stream, lookup)
if err != nil { if err != nil {
summary.Error = &entity.EntityErrorInfo{ summary.Error = &entity.EntityErrorInfo{
Message: err.Error(), Message: err.Error(),
@@ -74,7 +74,7 @@ func NewStaticDashboardSummaryBuilder(lookup DatasourceLookup, sanitize bool) en
} }
// panelSummary take panel info and returns entity summaries for the given panel and all its collapsed panels. // panelSummary take panel info and returns entity summaries for the given panel and all its collapsed panels.
func panelSummary(panel panelInfo, uid string, dashboardRefs ReferenceAccumulator) []*entity.EntitySummary { func panelSummary(panel PanelSummaryInfo, uid string, dashboardRefs ReferenceAccumulator) []*entity.EntitySummary {
panels := []*entity.EntitySummary{} panels := []*entity.EntitySummary{}
panelRefs := NewReferenceAccumulator() panelRefs := NewReferenceAccumulator()

View File

@@ -82,7 +82,7 @@ func (s *targetInfo) addTarget(iter *jsoniter.Iterator) {
} }
} }
func (s *targetInfo) addPanel(panel panelInfo) { func (s *targetInfo) addPanel(panel PanelSummaryInfo) {
for idx, v := range panel.Datasource { for idx, v := range panel.Datasource {
if v.UID != "" { if v.UID != "" {
s.uids[v.UID] = &panel.Datasource[idx] s.uids[v.UID] = &panel.Datasource[idx]

View File

@@ -0,0 +1,74 @@
{
"id": 141,
"title": "pppp",
"tags": null,
"datasource": [
{
"uid": "default.uid",
"type": "default.type"
}
],
"panels": [
{
"id": 1,
"title": "green pie",
"libraryPanel": "a7975b7a-fb53-4ab7-951d-15810953b54f",
"datasource": [
{
"uid": "default.uid",
"type": "default.type"
}
]
},
{
"id": 2,
"title": "green pie",
"libraryPanel": "e1d5f519-dabd-47c6-9ad7-83d181ce1cee",
"datasource": [
{
"uid": "default.uid",
"type": "default.type"
}
]
},
{
"id": 7,
"title": "",
"type": "barchart",
"datasource": [
{
"uid": "default.uid",
"type": "default.type"
}
]
},
{
"id": 8,
"title": "",
"type": "graph",
"datasource": [
{
"uid": "default.uid",
"type": "default.type"
}
]
},
{
"id": 3,
"title": "collapsed row",
"type": "row",
"collapsed": [
{
"id": 42,
"title": "blue pie",
"libraryPanel": "l3d2s634-fdgf-75u4-3fg3-67j966ii7jur"
}
]
}
],
"schemaVersion": 38,
"linkCount": 0,
"timeFrom": "now-6h",
"timeTo": "now",
"timezone": ""
}

View File

@@ -0,0 +1,122 @@
{
"kind": "Dashboard",
"apiVersion": "dashboard.grafana.app/v0alpha1",
"metadata": {
"name": "adfbg6f",
"namespace": "default",
"uid": "b396894e-56bf-4a01-837b-64157912ca00",
"creationTimestamp": "2024-10-30T18:30:54Z",
"annotations": {
"grafana.app/createdBy": "user:be2g71ke8yoe8b",
"grafana.app/originHash": "Grafana v9.2.0 (NA)",
"grafana.app/originName": "UI",
"grafana.app/originPath": "/dashboard/new"
}
},
"spec": {
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 141,
"links": [],
"liveNow": false,
"panels": [
{
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"libraryPanel": {
"name": "green pie",
"uid": "a7975b7a-fb53-4ab7-951d-15810953b54f"
},
"title": "green pie"
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"libraryPanel": {
"name": "red pie",
"uid": "e1d5f519-dabd-47c6-9ad7-83d181ce1cee"
},
"title": "green pie"
},
{
"id": 7,
"type": "barchart"
},
{
"id": 8,
"type": "graph"
},
{
"collapsed": true,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 9
},
"id": 3,
"panels": [
{
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 42,
"libraryPanel": {
"name": "blue pie",
"uid": "l3d2s634-fdgf-75u4-3fg3-67j966ii7jur"
},
"title": "blue pie"
}
],
"title": "collapsed row",
"type": "row"
}
],
"refresh": "",
"schemaVersion": 38,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "pppp",
"uid": "adfbg6f",
"version": 3,
"weekStart": ""
}
}

View File

@@ -1,6 +1,6 @@
package dashboard package dashboard
type panelInfo struct { type PanelSummaryInfo struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
@@ -10,23 +10,23 @@ type panelInfo struct {
Datasource []DataSourceRef `json:"datasource,omitempty"` // UIDs Datasource []DataSourceRef `json:"datasource,omitempty"` // UIDs
Transformer []string `json:"transformer,omitempty"` // ids of the transformation steps Transformer []string `json:"transformer,omitempty"` // ids of the transformation steps
// Rows define panels as sub objects // Rows define panels as sub objects
Collapsed []panelInfo `json:"collapsed,omitempty"` Collapsed []PanelSummaryInfo `json:"collapsed,omitempty"`
} }
type dashboardInfo struct { type DashboardSummaryInfo struct {
UID string `json:"uid,omitempty"` UID string `json:"uid,omitempty"`
ID int64 `json:"id,omitempty"` // internal ID ID int64 `json:"id,omitempty"` // internal ID
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
TemplateVars []string `json:"templateVars,omitempty"` // the keys used TemplateVars []string `json:"templateVars,omitempty"` // the keys used
Datasource []DataSourceRef `json:"datasource,omitempty"` // UIDs Datasource []DataSourceRef `json:"datasource,omitempty"` // UIDs
Panels []panelInfo `json:"panels"` // nesed documents Panels []PanelSummaryInfo `json:"panels"` // nesed documents
SchemaVersion int64 `json:"schemaVersion"` SchemaVersion int64 `json:"schemaVersion"`
LinkCount int64 `json:"linkCount"` LinkCount int64 `json:"linkCount"`
TimeFrom string `json:"timeFrom"` TimeFrom string `json:"timeFrom"`
TimeTo string `json:"timeTo"` TimeTo string `json:"timeTo"`
TimeZone string `json:"timezone"` TimeZone string `json:"timezone"`
Refresh string `json:"refresh,omitempty"` Refresh string `json:"refresh,omitempty"`
ReadOnly bool `json:"readOnly,omitempty"` // editable = false ReadOnly bool `json:"readOnly,omitempty"` // editable = false
} }