From 7f560c13e49b02caf6fcb04353b3c262134898fa Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 6 Nov 2024 07:35:15 +0300 Subject: [PATCH] Search: Make the existing dashboard parsing code public (#95649) --- .../store/kind/dashboard/dashboard.go | 40 ++++-- .../store/kind/dashboard/dashboard_test.go | 5 +- pkg/services/store/kind/dashboard/summary.go | 4 +- pkg/services/store/kind/dashboard/targets.go | 2 +- .../dashboard/testdata/k8s-wrapper-info.json | 74 +++++++++++ .../kind/dashboard/testdata/k8s-wrapper.json | 122 ++++++++++++++++++ pkg/services/store/kind/dashboard/types.go | 36 +++--- 7 files changed, 248 insertions(+), 35 deletions(-) create mode 100644 pkg/services/store/kind/dashboard/testdata/k8s-wrapper-info.json create mode 100644 pkg/services/store/kind/dashboard/testdata/k8s-wrapper.json diff --git a/pkg/services/store/kind/dashboard/dashboard.go b/pkg/services/store/kind/dashboard/dashboard.go index 6c50b68bf42..76a62c506df 100644 --- a/pkg/services/store/kind/dashboard/dashboard.go +++ b/pkg/services/store/kind/dashboard/dashboard.go @@ -112,12 +112,15 @@ func newDatasourceVariableLookup(dsLookup DatasourceLookup) *datasourceVariableL } } -// nolint:gocyclo // ReadDashboard will take a byte stream and return dashboard info -func readDashboard(stream io.Reader, lookup DatasourceLookup) (*dashboardInfo, error) { - dash := &dashboardInfo{} - +func ReadDashboard(stream io.Reader, lookup DatasourceLookup) (*DashboardSummaryInfo, error) { 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) @@ -129,6 +132,14 @@ func readDashboard(stream io.Reader, lookup DatasourceLookup) (*dashboardInfo, e } 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": dash.ID = iter.ReadInt64() @@ -268,7 +279,12 @@ func readDashboard(stream io.Reader, lookup DatasourceLookup) (*dashboardInfo, e filterOutSpecialDatasources(dash) 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) } dash.Datasource = targets.GetDatasourceInfo() @@ -276,13 +292,13 @@ func readDashboard(stream io.Reader, lookup DatasourceLookup) (*dashboardInfo, e return dash, iter.Error } -func panelRequiresDatasource(panel panelInfo) bool { +func panelRequiresDatasource(panel PanelSummaryInfo) bool { return panel.Type != "row" } -func fillDefaultDatasources(dash *dashboardInfo, lookup DatasourceLookup) { +func fillDefaultDatasources(dash *DashboardSummaryInfo, lookup DatasourceLookup) { for i, panel := range dash.Panels { - if len(panel.Datasource) != 0 || !panelRequiresDatasource(panel) { + if len(panel.Datasource) != 0 || !panelRequiresDatasource(PanelSummaryInfo{}) { 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 { 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 { var dsVariableRefs []DataSourceRef var dsRefs []DataSourceRef @@ -362,8 +378,8 @@ func findDatasourceRefsForVariables(dsVariableRefs []DataSourceRef, datasourceVa } // will always return strings for now -func readpanelInfo(iter *jsoniter.Iterator, lookup DatasourceLookup) panelInfo { - panel := panelInfo{} +func readpanelInfo(iter *jsoniter.Iterator, lookup DatasourceLookup) PanelSummaryInfo { + panel := PanelSummaryInfo{} targets := newTargetInfo(lookup) diff --git a/pkg/services/store/kind/dashboard/dashboard_test.go b/pkg/services/store/kind/dashboard/dashboard_test.go index 5be34abbbca..02ebfff70bb 100644 --- a/pkg/services/store/kind/dashboard/dashboard_test.go +++ b/pkg/services/store/kind/dashboard/dashboard_test.go @@ -71,6 +71,7 @@ func TestReadDashboard(t *testing.T) { "special-datasource-types", "panels-without-datasources", "panel-with-library-panel-field", + "k8s-wrapper", } devdash := "../../../../../devenv/dev-dashboards/" @@ -89,7 +90,7 @@ func TestReadDashboard(t *testing.T) { } require.NoError(t, err) - dash, err := readDashboard(f, dsLookupForTests()) + dash, err := ReadDashboard(f, dsLookupForTests()) sortDatasources(dash) 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` -func sortDatasources(dash *dashboardInfo) { +func sortDatasources(dash *DashboardSummaryInfo) { sort.Slice(dash.Datasource, func(i, j int) bool { return strings.Compare(dash.Datasource[i].UID, dash.Datasource[j].UID) > 0 }) diff --git a/pkg/services/store/kind/dashboard/summary.go b/pkg/services/store/kind/dashboard/summary.go index 7987f1c6c1e..b682c2dd302 100644 --- a/pkg/services/store/kind/dashboard/summary.go +++ b/pkg/services/store/kind/dashboard/summary.go @@ -40,7 +40,7 @@ func NewStaticDashboardSummaryBuilder(lookup DatasourceLookup, sanitize bool) en Fields: make(map[string]string), } stream := bytes.NewBuffer(body) - dash, err := readDashboard(stream, lookup) + dash, err := ReadDashboard(stream, lookup) if err != nil { summary.Error = &entity.EntityErrorInfo{ 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. -func panelSummary(panel panelInfo, uid string, dashboardRefs ReferenceAccumulator) []*entity.EntitySummary { +func panelSummary(panel PanelSummaryInfo, uid string, dashboardRefs ReferenceAccumulator) []*entity.EntitySummary { panels := []*entity.EntitySummary{} panelRefs := NewReferenceAccumulator() diff --git a/pkg/services/store/kind/dashboard/targets.go b/pkg/services/store/kind/dashboard/targets.go index 3dcfb67790b..603fec0b0d6 100644 --- a/pkg/services/store/kind/dashboard/targets.go +++ b/pkg/services/store/kind/dashboard/targets.go @@ -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 { if v.UID != "" { s.uids[v.UID] = &panel.Datasource[idx] diff --git a/pkg/services/store/kind/dashboard/testdata/k8s-wrapper-info.json b/pkg/services/store/kind/dashboard/testdata/k8s-wrapper-info.json new file mode 100644 index 00000000000..6451c3d805b --- /dev/null +++ b/pkg/services/store/kind/dashboard/testdata/k8s-wrapper-info.json @@ -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": "" +} \ No newline at end of file diff --git a/pkg/services/store/kind/dashboard/testdata/k8s-wrapper.json b/pkg/services/store/kind/dashboard/testdata/k8s-wrapper.json new file mode 100644 index 00000000000..94b1395af1d --- /dev/null +++ b/pkg/services/store/kind/dashboard/testdata/k8s-wrapper.json @@ -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": "" + } +} \ No newline at end of file diff --git a/pkg/services/store/kind/dashboard/types.go b/pkg/services/store/kind/dashboard/types.go index 53beaf5981a..51aa00a79fd 100644 --- a/pkg/services/store/kind/dashboard/types.go +++ b/pkg/services/store/kind/dashboard/types.go @@ -1,6 +1,6 @@ package dashboard -type panelInfo struct { +type PanelSummaryInfo struct { ID int64 `json:"id"` Title string `json:"title"` Description string `json:"description,omitempty"` @@ -10,23 +10,23 @@ type panelInfo struct { Datasource []DataSourceRef `json:"datasource,omitempty"` // UIDs Transformer []string `json:"transformer,omitempty"` // ids of the transformation steps // Rows define panels as sub objects - Collapsed []panelInfo `json:"collapsed,omitempty"` + Collapsed []PanelSummaryInfo `json:"collapsed,omitempty"` } -type dashboardInfo struct { - UID string `json:"uid,omitempty"` - ID int64 `json:"id,omitempty"` // internal ID - Title string `json:"title"` - Description string `json:"description,omitempty"` - Tags []string `json:"tags"` - TemplateVars []string `json:"templateVars,omitempty"` // the keys used - Datasource []DataSourceRef `json:"datasource,omitempty"` // UIDs - Panels []panelInfo `json:"panels"` // nesed documents - SchemaVersion int64 `json:"schemaVersion"` - LinkCount int64 `json:"linkCount"` - TimeFrom string `json:"timeFrom"` - TimeTo string `json:"timeTo"` - TimeZone string `json:"timezone"` - Refresh string `json:"refresh,omitempty"` - ReadOnly bool `json:"readOnly,omitempty"` // editable = false +type DashboardSummaryInfo struct { + UID string `json:"uid,omitempty"` + ID int64 `json:"id,omitempty"` // internal ID + Title string `json:"title"` + Description string `json:"description,omitempty"` + Tags []string `json:"tags"` + TemplateVars []string `json:"templateVars,omitempty"` // the keys used + Datasource []DataSourceRef `json:"datasource,omitempty"` // UIDs + Panels []PanelSummaryInfo `json:"panels"` // nesed documents + SchemaVersion int64 `json:"schemaVersion"` + LinkCount int64 `json:"linkCount"` + TimeFrom string `json:"timeFrom"` + TimeTo string `json:"timeTo"` + TimeZone string `json:"timezone"` + Refresh string `json:"refresh,omitempty"` + ReadOnly bool `json:"readOnly,omitempty"` // editable = false }