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

fix: Test grafana dashboards in hubble deploy #650

Merged
merged 5 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -537,3 +537,4 @@ quick-deploy-hubble:
.PHONY: simplify-dashboards
simplify-dashboards:
cd deploy/legacy/grafana/dashboards && go test . -tags=dashboard,simplifydashboard -v && cd $(REPO_ROOT)
whatnick marked this conversation as resolved.
Show resolved Hide resolved
cd deploy/hubble/grafana/dashboards && go test . -tags=dashboard,simplifydashboard -v && cd $(REPO_ROOT)
14 changes: 7 additions & 7 deletions deploy/hubble/grafana/dashboards/clusters.json
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
{
"__inputs": [],
"__elements": {},
"__inputs": [],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"type": "grafana",
"version": "9.5.15"
},
{
"type": "datasource",
"id": "prometheus",
"name": "Prometheus",
"type": "datasource",
"version": "1.0.0"
},
{
"type": "panel",
"id": "stat",
"name": "Stat",
"type": "panel",
"version": ""
},
{
"type": "panel",
"id": "table",
"name": "Table",
"type": "panel",
"version": ""
},
{
"type": "panel",
"id": "text",
"name": "Text",
"type": "panel",
"version": ""
},
{
"type": "panel",
"id": "timeseries",
"name": "Time series",
"type": "panel",
"version": ""
}
],
Expand Down
10 changes: 5 additions & 5 deletions deploy/hubble/grafana/dashboards/dns.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
{
"__inputs": [],
"__elements": {},
"__inputs": [],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"type": "grafana",
"version": "9.5.15"
},
{
"type": "datasource",
"id": "prometheus",
"name": "Prometheus",
"type": "datasource",
"version": "1.0.0"
},
{
"type": "panel",
"id": "table",
"name": "Table",
"type": "panel",
"version": ""
},
{
"type": "panel",
"id": "timeseries",
"name": "Time series",
"type": "panel",
"version": ""
}
],
Expand Down
14 changes: 7 additions & 7 deletions deploy/hubble/grafana/dashboards/pod-flows-namespace.json
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
{
"__inputs": [],
"__elements": {},
"__inputs": [],
"__requires": [
{
"type": "panel",
"id": "bargauge",
"name": "Bar gauge",
"type": "panel",
"version": ""
},
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"type": "grafana",
"version": "9.5.15"
},
{
"type": "panel",
"id": "heatmap",
"name": "Heatmap",
"type": "panel",
"version": ""
},
{
"type": "datasource",
"id": "prometheus",
"name": "Prometheus",
"type": "datasource",
"version": "1.0.0"
},
{
"type": "panel",
"id": "stat",
"name": "Stat",
"type": "panel",
"version": ""
},
{
"type": "panel",
"id": "timeseries",
"name": "Time series",
"type": "panel",
"version": ""
}
],
Expand Down
12 changes: 6 additions & 6 deletions deploy/hubble/grafana/dashboards/pod-flows-workload.json
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
{
"__inputs": [],
"__elements": {},
"__inputs": [],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"type": "grafana",
"version": "9.5.15"
},
{
"type": "panel",
"id": "heatmap",
"name": "Heatmap",
"type": "panel",
"version": ""
},
{
"type": "datasource",
"id": "prometheus",
"name": "Prometheus",
"type": "datasource",
"version": "1.0.0"
},
{
"type": "panel",
"id": "stat",
"name": "Stat",
"type": "panel",
"version": ""
},
{
"type": "panel",
"id": "timeseries",
"name": "Time series",
"type": "panel",
"version": ""
}
],
Expand Down
whatnick marked this conversation as resolved.
Show resolved Hide resolved
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build dashboard && simplifydashboard

package dashboard

import (
"os"
"path/filepath"
"testing"
)

// TestOverwriteDashboards simplifies and overwrites Grafana dashboards in this folder.
func TestOverwriteDashboards(t *testing.T) {
// get all json's in this folder
files, err := os.ReadDir("./")
if err != nil {
t.Fatal(err)
}

for _, file := range files {
if filepath.Ext(file.Name()) == ".json" {
t.Logf("simplifying/overwriting dashboard: %s", file.Name())

sourcePath := file.Name()
_ = SimplifyGrafana(sourcePath, true)
}
}
}
126 changes: 126 additions & 0 deletions deploy/legacy/grafana/dashboards/simplify-grafana.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//go:build dashboard

package dashboard

import (
"bytes"
"encoding/json"
"log"
"os"
)

// SimplifyGrafana parses a json file representing a Grafana dashboard and overwrites it with a simplified version.
// It removes some unnecessary fields and changes some values so that the dashboard can be used by anyone.
func SimplifyGrafana(filename string, overwrite bool) map[string]interface{} {
dashboard := ParseDashboard(filename)

// remove unnecessary top-level fields
delete(dashboard, "weekStart")
delete(dashboard, "fiscalYearStartMonth")
delete(dashboard, "graphTooltip")
delete(dashboard, "annotations")

// set empty __inputs
if _, ok := dashboard["__inputs"]; ok {
dashboard["__inputs"] = []interface{}{}
}

// set current variables to be empty
if _, ok := dashboard["templating"]; ok {
templating := dashboard["templating"].(map[string]interface{})
if _, ok := templating["list"]; ok {
list := templating["list"].([]interface{})
for _, item := range list {
if item, ok := item.(map[string]interface{}); ok {
item["current"] = map[string]interface{}{}
}
}
}
}

// remove any pluginVersion
removeFieldAnywhere(dashboard, "pluginVersion")

// change all datasource.uid to "${datasource}"
replaceDatasource(dashboard)

if !overwrite {
return dashboard
}

// overwrite the file with the simplified version
simplifiedData, err := JSONMarhsalIndent(dashboard, "", " ")
if err != nil {
log.Fatal(err)
}

err = os.WriteFile(filename, simplifiedData, 0o644)
if err != nil {
log.Fatal(err)
}

return dashboard
}

func ParseDashboard(filename string) map[string]interface{} {
data, err := os.ReadFile(filename)
if err != nil {
log.Fatal(err)
}

var dashboard map[string]interface{}
err = json.Unmarshal(data, &dashboard)
if err != nil {
log.Fatal(err)
}

return dashboard
}

func removeFieldAnywhere(data map[string]interface{}, field string) {
if _, ok := data[field]; ok {
delete(data, field)
}

for _, value := range data {
if m, ok := value.(map[string]interface{}); ok {
removeFieldAnywhere(m, field)
} else if l, ok := value.([]interface{}); ok {
for _, item := range l {
if m, ok := item.(map[string]interface{}); ok {
removeFieldAnywhere(m, field)
}
}
}
}
}

func replaceDatasource(data map[string]interface{}) {
if datasource, ok := data["datasource"].(map[string]interface{}); ok {
if _, ok := datasource["uid"].(string); ok {
datasource["uid"] = "${datasource}"
}
}

for _, value := range data {
if m, ok := value.(map[string]interface{}); ok {
replaceDatasource(m)
} else if l, ok := value.([]interface{}); ok {
for _, item := range l {
if m, ok := item.(map[string]interface{}); ok {
replaceDatasource(m)
}
}
}
}
}

// JSONMarhsalIndent is json.MarshalIndent without HTML escaping (e.g. converting > to \u003e)
func JSONMarhsalIndent(t interface{}, prefix, indent string) ([]byte, error) {
buffer := &bytes.Buffer{}
encoder := json.NewEncoder(buffer)
encoder.SetEscapeHTML(false)
encoder.SetIndent(prefix, indent)
err := encoder.Encode(t)
return buffer.Bytes(), err
}
33 changes: 33 additions & 0 deletions deploy/legacy/grafana/dashboards/simplify-grafana_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//go:build dashboard && !simplifydashboard

package dashboard

import (
"os"
"path/filepath"
"reflect"
"testing"
)

// TestDashboardsAreSimplified ensures that all dashboards are simplified
func TestDashboardsAreSimplified(t *testing.T) {
// get all json's in this folder
files, err := os.ReadDir("./")
if err != nil {
t.Fatal(err)
}

for _, file := range files {
if filepath.Ext(file.Name()) == ".json" {
t.Logf("verifying that dashboard is simplified: %s", file.Name())

sourcePath := file.Name()
simplified := SimplifyGrafana(sourcePath, false)
original := ParseDashboard(sourcePath)

if !reflect.DeepEqual(simplified, original) {
t.Errorf("ERROR: dashboard has not been simplified. Please run: make simplify-dashboards")
}
}
}
}
Loading