Skip to content

Commit

Permalink
[grafana] Dashboards content validations improvements (#300)
Browse files Browse the repository at this point in the history
* Update grafana_dashboard.go

Signed-off-by: ghostinsoba <125242947+ghostinsoba@users.noreply.github.com>

* Update grafana_dashboard_test.go

Signed-off-by: ghostinsoba <125242947+ghostinsoba@users.noreply.github.com>

---------

Signed-off-by: ghostinsoba <125242947+ghostinsoba@users.noreply.github.com>
  • Loading branch information
ghostinsoba authored Apr 15, 2024
1 parent 2d5b7a9 commit fbfeeb1
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 23 deletions.
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

0 comments on commit fbfeeb1

Please sign in to comment.