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

[grafana] Dashboards content validations improvements #300

Merged
merged 2 commits into from
Apr 15, 2024
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
59 changes: 51 additions & 8 deletions tools/validation/grafana_dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ func validateGrafanaDashboardFile(fileName string, fileContent []byte) *Messages

for _, panel := range dashboardPanels {
panelTitle := panel.Get("title").String()
panelType := panel.Get("type").String()
replaceWith, isDeprecated := evaluateDeprecatedPanelType(panelType)
if isDeprecated {
msgs.Add(
NewError(
fileName,
"deprecated panel type",
fmt.Sprintf("Panel %s is of deprecated type: '%s', consider using '%s'",
panelTitle, panelType, replaceWith),
),
)
}
intervals := evaluateDeprecatedIntervals(panel)
for _, interval := range intervals {
msgs.Add(
Expand All @@ -111,8 +123,18 @@ func validateGrafanaDashboardFile(fileName string, fileContent []byte) *Messages
),
)
}
datasourceUIDs := evaluateHardcodedDatasourceUIDs(panel)
for _, datasourceUID := range datasourceUIDs {
legacyDatasourceUIDs, hardcodedDatasourceUIDs := evaluateDeprecatedDatasourceUIDs(panel)
for _, datasourceUID := range legacyDatasourceUIDs {
msgs.Add(
NewError(
fileName,
"legacy datasource uid",
fmt.Sprintf("Panel %s contains legacy datasource uid: '%s', consider resaving dashboard using newer version of Grafana",
panelTitle, datasourceUID),
),
)
}
for _, datasourceUID := range hardcodedDatasourceUIDs {
msgs.Add(
NewError(
fileName,
Expand All @@ -126,19 +148,28 @@ func validateGrafanaDashboardFile(fileName string, fileContent []byte) *Messages
return msgs
}

func evaluateHardcodedDatasourceUIDs(panel gjson.Result) []string {
func evaluateDeprecatedDatasourceUIDs(panel gjson.Result) (legacyUIDs, hardcodedUIDs []string) {
targets := panel.Get("targets").Array()
datasourceUIDs := make([]string, 0)
legacyUIDs = make([]string, 0)
hardcodedUIDs = make([]string, 0)
for _, target := range targets {
datasource := target.Get("datasource")
if datasource.Exists() {
uid := datasource.Get("uid").String()
if !strings.HasPrefix(uid, "$") {
datasourceUIDs = append(datasourceUIDs, uid)
var uidStr string
uid := datasource.Get("uid")
if uid.Exists() {
uidStr = uid.String()
} else {
// some old dashboards (before Grafana 8.3) may implicitly contain uid as a string, not as parameter
uidStr = datasource.String()
legacyUIDs = append(legacyUIDs, uidStr)
}
if !strings.HasPrefix(uidStr, "$") {
hardcodedUIDs = append(hardcodedUIDs, uidStr)
}
}
}
return datasourceUIDs
return hardcodedUIDs, legacyUIDs
}

var (
Expand Down Expand Up @@ -169,3 +200,15 @@ func evaluateDeprecatedInterval(expression string) (string, bool) {
}
return "", false
}

var (
deprecatedPanelTypes = map[string]string{
"graph": "timeseries",
"flant-statusmap-panel": "state-timeline",
}
)

func evaluateDeprecatedPanelType(panelType string) (replaceWith string, isDeprecated bool) {
replaceWith, isDeprecated = deprecatedPanelTypes[panelType]
return replaceWith, isDeprecated
}
149 changes: 134 additions & 15 deletions tools/validation/grafana_dashboard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func TestValidateGrafanaDashboardFile(t *testing.T) {
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 77,
"id": 78,
"links": [],
"liveNow": false,
"panels": [
Expand Down Expand Up @@ -165,10 +165,7 @@ func TestValidateGrafanaDashboardFile(t *testing.T) {
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus_datasource_uid"
},
"datasource": "prometheus_datasource_uid",
"expr": "rate(metric_name[$__interval_rv])",
"refId": "A"
}
Expand All @@ -184,6 +181,44 @@ func TestValidateGrafanaDashboardFile(t *testing.T) {
"type": "timeseries"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"hiddenSeries": false,
"id": 7,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "8.5.13",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"datasource": {
Expand All @@ -194,9 +229,36 @@ func TestValidateGrafanaDashboardFile(t *testing.T) {
"refId": "A"
}
],
"thresholds": [],
"timeRegions": [],
"title": "Plugin Single Panel",
"type": "unknown_plugin",
"version": 1
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"version": 1,
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"logBase": 1,
"show": true
},
{
"format": "short",
"logBase": 1,
"show": true
}
],
"yaxis": {
"align": false
}
},
{
"gridPos": {
Expand Down Expand Up @@ -320,10 +382,7 @@ func TestValidateGrafanaDashboardFile(t *testing.T) {
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus_datasource_uid"
},
"datasource": "prometheus_datasource_uid",
"expr": "rate(metric_name[$__interval_sx3])",
"refId": "A"
}
Expand All @@ -339,6 +398,38 @@ func TestValidateGrafanaDashboardFile(t *testing.T) {
"type": "timeseries"
},
{
"cards": {
"cardHSpacing": 2,
"cardMinWidth": 5,
"cardVSpacing": 2
},
"color": {
"cardColor": "#b4ff00",
"colorScale": "sqrt",
"colorScheme": "interpolateGnYlRd",
"defaultColor": "#757575",
"exponent": 0.5,
"mode": "spectrum",
"thresholds": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 12,
"y": 9
},
"hideBranding": false,
"highlightCards": true,
"id": 8,
"legend": {
"show": true
},
"nullPointMode": "as empty",
"pageSize": 15,
"seriesFilterIndex": -1,
"statusmap": {
"ConfigVersion": "v1"
},
"targets": [
{
"datasource": {
Expand All @@ -350,8 +441,32 @@ func TestValidateGrafanaDashboardFile(t *testing.T) {
}
],
"title": "Plugin Panel Inside Row",
"type": "unknown_plugin",
"version": 1
"tooltip": {
"extraInfo": "",
"freezeOnClick": true,
"items": [],
"show": true,
"showExtraInfo": false,
"showItems": false
},
"type": "flant-statusmap-panel",
"useMax": true,
"usingPagination": false,
"version": 1,
"xAxis": {
"show": true
},
"yAxis": {
"maxWidth": -1,
"minWidth": -1,
"show": true
},
"yAxisSort": "metrics",
"yLabel": {
"delimiter": "",
"labelTemplate": "",
"usingSplitLabel": false
}
}
],
"schemaVersion": 36,
Expand All @@ -374,14 +489,18 @@ func TestValidateGrafanaDashboardFile(t *testing.T) {
expected := &Messages{[]Message{
NewError("dashboard.json", "deprecated interval", "Panel Single Panel contains deprecated interval: 'interval_rv', consider using '$__rate_interval'"),
NewError("dashboard.json", "legacy alert rule", "Panel Single Panel contains legacy alert rule: 'Alert Rule Inside Single Panel', consider using external alertmanager"),
NewError("dashboard.json", "legacy datasource uid", "Panel Single Panel contains legacy datasource uid: 'prometheus_datasource_uid', consider resaving dashboard using newer version of Grafana"),
NewError("dashboard.json", "hardcoded datasource uid", "Panel Single Panel contains hardcoded datasource uid: 'prometheus_datasource_uid', consider using grafana variable of type 'Datasource'"),
NewError("dashboard.json", "deprecated panel type", "Panel Plugin Single Panel is of deprecated type: 'graph', consider using 'timeseries'"),
NewError("dashboard.json", "deprecated interval", "Panel Plugin Single Panel contains deprecated interval: 'interval_rv', consider using '$__rate_interval'"),
NewError("dashboard.json", "hardcoded datasource uid", "Panel Plugin Single Panel contains hardcoded datasource uid: 'prometheus_datasource_uid', consider using grafana variable of type 'Datasource'"),
NewError("dashboard.json", "legacy datasource uid", "Panel Plugin Single Panel contains legacy datasource uid: 'prometheus_datasource_uid', consider resaving dashboard using newer version of Grafana"),
NewError("dashboard.json", "deprecated interval", "Panel Panel Inside Row contains deprecated interval: 'interval_sx3', consider using '$__rate_interval'"),
NewError("dashboard.json", "legacy alert rule", "Panel Panel Inside Row contains legacy alert rule: 'Panel Inside Row Alert Rule', consider using external alertmanager"),
NewError("dashboard.json", "legacy datasource uid", "Panel Panel Inside Row contains legacy datasource uid: 'prometheus_datasource_uid', consider resaving dashboard using newer version of Grafana"),
NewError("dashboard.json", "hardcoded datasource uid", "Panel Panel Inside Row contains hardcoded datasource uid: 'prometheus_datasource_uid', consider using grafana variable of type 'Datasource'"),
NewError("dashboard.json", "deprecated panel type", "Panel Plugin Panel Inside Row is of deprecated type: 'flant-statusmap-panel', consider using 'state-timeline'"),
NewError("dashboard.json", "deprecated interval", "Panel Plugin Panel Inside Row contains deprecated interval: 'interval_sx4', consider using '$__rate_interval'"),
NewError("dashboard.json", "hardcoded datasource uid", "Panel Plugin Panel Inside Row contains hardcoded datasource uid: 'prometheus_datasource_uid', consider using grafana variable of type 'Datasource'"),
NewError("dashboard.json", "legacy datasource uid", "Panel Plugin Panel Inside Row contains legacy datasource uid: 'prometheus_datasource_uid', consider resaving dashboard using newer version of Grafana"),
}}

actual := validateGrafanaDashboardFile("dashboard.json", []byte(in))
Expand Down
Loading