Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update mimirtool grafana analyze to support more panel types #10669

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@

* [BUGFIX] Fix issue where `MIMIR_HTTP_PREFIX` environment variable was ignored and the value from `MIMIR_MIMIR_HTTP_PREFIX` was used instead. #10207
* [ENHANCEMENT] Unify mimirtool authentication options and add extra-headers support for commands that depend on MimirClient. #10178
* [ENHANCEMENT] `mimirtool grafana analyze` now supports custom panels. #10669
* [ENHANCEMENT] `mimirtool grafana analyze` now supports bar chart, pie chart, state timeline, status history,
histogram, candlestick, canvas, flame graph, geomap, node graph, trend, and XY chart panels. #10669

### Mimir Continuous Test

Expand Down
47 changes: 6 additions & 41 deletions pkg/mimirtool/analyze/grafana.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package analyze

import (
"encoding/json"
"fmt"
"slices"
"strings"
Expand Down Expand Up @@ -61,8 +60,8 @@ func ParseMetricsInBoard(mig *MetricsInGrafana, board minisdk.Board) {
// 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.SubPanels != nil {
for _, subPanel := range panel.SubPanels {
parseErrors = append(parseErrors, metricsFromPanel(subPanel, metrics)...)
}
}
Expand Down Expand Up @@ -157,48 +156,14 @@ func metricsFromTemplating(templating minisdk.Templating, metrics map[string]str
return parseErrors
}

// Workaround to support Grafana "timeseries" panel. This should
// be implemented in grafana/tools-sdk, and removed from here.
func getCustomPanelTargets(panel minisdk.Panel) *[]minisdk.Target {
if panel.CommonPanel.Type != "timeseries" {
return nil
}

// Heavy handed approach to re-marshal the panel and parse it again
// so that we can extract the 'targets' field in the right format.

bytes, err := json.Marshal(panel.CustomPanel)
if err != nil {
log.Debugln("msg", "panel re-marshalling error", "err", err)
return nil
}

type panelType struct {
Targets []minisdk.Target `json:"targets,omitempty"`
}

var parsedPanel panelType
err = json.Unmarshal(bytes, &parsedPanel)
if err != nil {
log.Debugln("msg", "panel parsing error", "err", err)
return nil
}

return &parsedPanel.Targets
}

func metricsFromPanel(panel minisdk.Panel, metrics map[string]struct{}) []error {
var parseErrors []error
if !panel.SupportsTargets() {
return []error{fmt.Errorf("unsupported panel type: %q", panel.Type)}
}

targets := panel.GetTargets()
if targets == nil {
targets = getCustomPanelTargets(panel)
if targets == nil {
parseErrors = append(parseErrors, fmt.Errorf("unsupported panel type: %q", panel.CommonPanel.Type))
return parseErrors
}
}

var parseErrors []error
for _, target := range *targets {
// Prometheus has this set.
if target.Expr == "" {
Expand Down
29 changes: 14 additions & 15 deletions pkg/mimirtool/commands/analyse_grafana_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,29 @@ import (
var dashboardMetrics = []string{
"apiserver_request:availability30d",
"apiserver_request_total",
"bar_chart_metric",
"candlestick_metric",
"canvas_metric",
"cluster_quantile:apiserver_request_duration_seconds:histogram_quantile",
"code_resource:apiserver_request_total:rate5m",
"flame_graph_metric",
"geomap_metric",
"go_goroutines",
"histogram_metric",
"kube_pod_info",
"my_lovely_metric",
"node_graph_metric",
"pie_chart_metric",
"polystat_panel_metric",
"process_cpu_seconds_total",
"process_resident_memory_bytes",
"state_timeline_metric",
"status_history_metric",
"trend_metric",
"workqueue_adds_total",
"workqueue_depth",
"workqueue_queue_duration_seconds_bucket",
"xy_chart_metric",
}

var expectedParseErrors = []string{
Expand Down Expand Up @@ -71,18 +85,3 @@ func BenchmarkParseMetricsInBoard(b *testing.B) {
analyze.ParseMetricsInBoard(output, board)
}
}

func TestParseMetricsInBoardWithTimeseriesPanel(t *testing.T) {
var board minisdk.Board
output := &analyze.MetricsInGrafana{}
output.OverallMetrics = make(map[string]struct{})

buf, err := loadFile("testdata/timeseries.json")
require.NoError(t, err)

err = json.Unmarshal(buf, &board)
require.NoError(t, err)

analyze.ParseMetricsInBoard(output, board)
assert.Equal(t, []string{"my_lovely_metric"}, output.Dashboards[0].Metrics)
}
Loading
Loading