From b915ebce028f411f9de987e131af3f887b18e110 Mon Sep 17 00:00:00 2001 From: hjet Date: Tue, 22 Mar 2022 17:59:44 -0400 Subject: [PATCH 1/2] Initial prototype --- pkg/analyse/dashboard.go | 36 ++++++++++++++++++++++++++++ pkg/analyse/grafana.go | 18 +++++++------- pkg/commands/analyse_dashboards.go | 3 +-- pkg/commands/analyse_grafana.go | 6 +++-- pkg/commands/analyse_grafana_test.go | 5 ++-- 5 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 pkg/analyse/dashboard.go diff --git a/pkg/analyse/dashboard.go b/pkg/analyse/dashboard.go new file mode 100644 index 000000000..37c63aa05 --- /dev/null +++ b/pkg/analyse/dashboard.go @@ -0,0 +1,36 @@ +package analyse + +type ( + Board struct { + ID uint `json:"id,omitempty"` + UID string `json:"uid,omitempty"` + Slug string `json:"slug"` + Title string `json:"title"` + Panels []*Panel `json:"panels"` + Rows []*Row `json:"rows"` + Templating Templating `json:"templating"` + } + Templating struct { + List []TemplateVar `json:"list"` + } + TemplateVar struct { + Name string `json:"name"` + Query interface{} `json:"query"` + Type string `json:"type"` + } + Panel struct { + Targets []Target `json:"targets,omitempty"` + Title string `json:"title"` // general + Panels []Panel `json:"panels"` //row panel + Type string `json:"type"` + } + Target struct { + RefID string `json:"refId"` + Datasource string `json:"datasource,omitempty"` + // For Prometheus + Expr string `json:"expr,omitempty"` + } + Row struct { + Panels []Panel `json:"panels"` + } +) diff --git a/pkg/analyse/grafana.go b/pkg/analyse/grafana.go index 780189e25..57a957905 100644 --- a/pkg/analyse/grafana.go +++ b/pkg/analyse/grafana.go @@ -8,7 +8,6 @@ import ( log "github.com/sirupsen/logrus" - "github.com/grafana-tools/sdk" "github.com/pkg/errors" "github.com/prometheus/prometheus/promql/parser" ) @@ -27,15 +26,15 @@ type DashboardMetrics struct { ParseErrors []string `json:"parse_errors"` } -func ParseMetricsInBoard(mig *MetricsInGrafana, board sdk.Board) { +func ParseMetricsInBoard(mig *MetricsInGrafana, board Board) { var parseErrors []error metrics := make(map[string]struct{}) // Iterate through all the panels and collect metrics for _, panel := range board.Panels { parseErrors = append(parseErrors, metricsFromPanel(*panel, metrics)...) - if panel.RowPanel != nil { - for _, subPanel := range panel.RowPanel.Panels { + if panel.Panels != nil { + for _, subPanel := range panel.Panels { parseErrors = append(parseErrors, metricsFromPanel(subPanel, metrics)...) } } @@ -77,7 +76,7 @@ func ParseMetricsInBoard(mig *MetricsInGrafana, board sdk.Board) { } -func metricsFromTemplating(templating sdk.Templating, metrics map[string]struct{}) []error { +func metricsFromTemplating(templating Templating, metrics map[string]struct{}) []error { parseErrors := []error{} for _, templateVar := range templating.List { if templateVar.Type != "query" { @@ -114,16 +113,15 @@ func metricsFromTemplating(templating sdk.Templating, metrics map[string]struct{ return parseErrors } -func metricsFromPanel(panel sdk.Panel, metrics map[string]struct{}) []error { +func metricsFromPanel(panel Panel, metrics map[string]struct{}) []error { var parseErrors []error - targets := panel.GetTargets() - if targets == nil { - parseErrors = append(parseErrors, fmt.Errorf("unsupported panel type: %q", panel.CommonPanel.Type)) + if panel.Targets == nil { + parseErrors = append(parseErrors, fmt.Errorf("unsupported panel type: %q", panel.Type)) return parseErrors } - for _, target := range *targets { + for _, target := range panel.Targets { // Prometheus has this set. if target.Expr == "" { continue diff --git a/pkg/commands/analyse_dashboards.go b/pkg/commands/analyse_dashboards.go index d099ec717..a75585d36 100644 --- a/pkg/commands/analyse_dashboards.go +++ b/pkg/commands/analyse_dashboards.go @@ -5,7 +5,6 @@ import ( "fmt" "os" - "github.com/grafana-tools/sdk" "gopkg.in/alecthomas/kingpin.v2" "github.com/grafana/cortex-tools/pkg/analyse" @@ -21,7 +20,7 @@ func (cmd *DashboardAnalyseCommand) run(k *kingpin.ParseContext) error { output.OverallMetrics = make(map[string]struct{}) for _, file := range cmd.DashFilesList { - var board sdk.Board + var board analyse.Board buf, err := loadFile(file) if err != nil { return err diff --git a/pkg/commands/analyse_grafana.go b/pkg/commands/analyse_grafana.go index aff46068d..18c9fc585 100644 --- a/pkg/commands/analyse_grafana.go +++ b/pkg/commands/analyse_grafana.go @@ -41,12 +41,14 @@ func (cmd *GrafanaAnalyseCommand) run(k *kingpin.ParseContext) error { } for _, link := range boardLinks { - board, _, err := c.GetDashboardByUID(ctx, link.UID) + //board, _, err := c.GetDashboardByUID(ctx, link.UID) + _, _, err := c.GetDashboardByUID(ctx, link.UID) if err != nil { fmt.Fprintf(os.Stderr, "%s for %s %s\n", err, link.UID, link.Title) continue } - analyse.ParseMetricsInBoard(output, board) + // TODO: convert into new board type + //analyse.ParseMetricsInBoard(output, board) } err = writeOut(output, cmd.outputFile) diff --git a/pkg/commands/analyse_grafana_test.go b/pkg/commands/analyse_grafana_test.go index 4a82ffc23..4cff3a14e 100644 --- a/pkg/commands/analyse_grafana_test.go +++ b/pkg/commands/analyse_grafana_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "testing" - "github.com/grafana-tools/sdk" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -25,7 +24,7 @@ var dashboardMetrics = []string{ } func TestParseMetricsInBoard(t *testing.T) { - var board sdk.Board + var board analyse.Board output := &analyse.MetricsInGrafana{} output.OverallMetrics = make(map[string]struct{}) @@ -40,7 +39,7 @@ func TestParseMetricsInBoard(t *testing.T) { } func TestParseMetricsInBoardWithTimeseriesPanel(t *testing.T) { - var board sdk.Board + var board analyse.Board output := &analyse.MetricsInGrafana{} output.OverallMetrics = make(map[string]struct{}) From 9f92d0137053e5d886f4ef02813af856300a2391 Mon Sep 17 00:00:00 2001 From: hjet Date: Wed, 23 Mar 2022 14:11:49 -0400 Subject: [PATCH 2/2] Unmarshal raw boards using new types --- pkg/analyse/dashboard.go | 4 ++-- pkg/analyse/grafana.go | 8 +++++--- pkg/commands/analyse_grafana.go | 23 ++++++++++++++++------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/pkg/analyse/dashboard.go b/pkg/analyse/dashboard.go index 37c63aa05..9d39fb47a 100644 --- a/pkg/analyse/dashboard.go +++ b/pkg/analyse/dashboard.go @@ -20,8 +20,8 @@ type ( } Panel struct { Targets []Target `json:"targets,omitempty"` - Title string `json:"title"` // general - Panels []Panel `json:"panels"` //row panel + Title string `json:"title"` + Panels []Panel `json:"panels"` // row panel type Type string `json:"type"` } Target struct { diff --git a/pkg/analyse/grafana.go b/pkg/analyse/grafana.go index 57a957905..08d586389 100644 --- a/pkg/analyse/grafana.go +++ b/pkg/analyse/grafana.go @@ -33,6 +33,7 @@ func ParseMetricsInBoard(mig *MetricsInGrafana, board Board) { // Iterate through all the panels and collect metrics for _, panel := range board.Panels { parseErrors = append(parseErrors, metricsFromPanel(*panel, metrics)...) + // row panel if panel.Panels != nil { for _, subPanel := range panel.Panels { parseErrors = append(parseErrors, metricsFromPanel(subPanel, metrics)...) @@ -99,8 +100,8 @@ func metricsFromTemplating(templating Templating, metrics map[string]struct{}) [ } err := parseQuery(query, metrics) if err != nil { - parseErrors = append(parseErrors, errors.Wrapf(err, "query=%v", query)) - log.Debugln("msg", "promql parse error", "err", err, "query", query) + parseErrors = append(parseErrors, errors.Wrapf(err, "error parsing templating query: %v", query)) + log.Debugln("msg", "promql parse error: templating query", "err", err, "query", query) continue } } else { @@ -113,11 +114,12 @@ func metricsFromTemplating(templating Templating, metrics map[string]struct{}) [ return parseErrors } +// for now, only look at panels with a prometheus datasource/targets func metricsFromPanel(panel Panel, metrics map[string]struct{}) []error { var parseErrors []error if panel.Targets == nil { - parseErrors = append(parseErrors, fmt.Errorf("unsupported panel type: %q", panel.Type)) + parseErrors = append(parseErrors, fmt.Errorf("unsupported panel type (no targets in panel): %q", panel.Type)) return parseErrors } diff --git a/pkg/commands/analyse_grafana.go b/pkg/commands/analyse_grafana.go index 18c9fc585..a55a2dd07 100644 --- a/pkg/commands/analyse_grafana.go +++ b/pkg/commands/analyse_grafana.go @@ -24,7 +24,13 @@ type GrafanaAnalyseCommand struct { } func (cmd *GrafanaAnalyseCommand) run(k *kingpin.ParseContext) error { - output := &analyse.MetricsInGrafana{} + var ( + boardLinks []sdk.FoundBoard + rawBoard []byte + board analyse.Board + err error + output *analyse.MetricsInGrafana + ) output.OverallMetrics = make(map[string]struct{}) ctx, cancel := context.WithTimeout(context.Background(), cmd.readTimeout) @@ -35,20 +41,23 @@ func (cmd *GrafanaAnalyseCommand) run(k *kingpin.ParseContext) error { return err } - boardLinks, err := c.SearchDashboards(ctx, "", false) + boardLinks, err = c.SearchDashboards(ctx, "", false) if err != nil { return err } for _, link := range boardLinks { - //board, _, err := c.GetDashboardByUID(ctx, link.UID) - _, _, err := c.GetDashboardByUID(ctx, link.UID) + rawBoard, _, err = c.GetRawDashboardBySlug(ctx, link.URI) if err != nil { - fmt.Fprintf(os.Stderr, "%s for %s %s\n", err, link.UID, link.Title) + fmt.Fprintf(os.Stderr, "%s for %s\n", err, link.URI) continue } - // TODO: convert into new board type - //analyse.ParseMetricsInBoard(output, board) + + if err = json.Unmarshal(rawBoard, &board); err != nil { + fmt.Fprintf(os.Stderr, "%s for %s\n", err, link.URI) + continue + } + analyse.ParseMetricsInBoard(output, board) } err = writeOut(output, cmd.outputFile)