Announcing grafana-dashboards.nix
Few logs ago, I introduced dashboards I borrowed online.
I wrote they were looking like that:
services.grafana.provision.dashboards.settings.providers =
let
fetchDashboard = { name, hash, id, version }:
pkgs.fetchurl {
inherit name hash;
url = "https://grafana.com/api/dashboards/toString id/revisions/toString version/download";
recursiveHash = true;
postFetch = ''
mv "$out" temp
mkdir -p "$out"
mv temp "$out/name.json";
'';
};
dashboard = name: fetchArgs: { inherit name; options.path = fetchDashboard fetchArgs; };
in
[
(dashboard "Node Exporter Full"
{
name = "node-exporter-full";
hash = "sha256-ZiIsNaxPE5skpDykcugveAa3S8sCjR9bA9hbzyz7kvY=";
id = 1860;
version = 32;
})
(dashboard "Node Exporter"
{
name = "node-exporter";
hash = "sha256-2xgE0m3SUFiux501uCVb4aH3zGfapW/SmfxRsFC/514=";
id = 13978;
version = 2;
})
(dashboard "Docker"
{
name = "docker";
hash = "sha256-RdTcQxdBvbgKGrlhQB1obLrrJENcuRgZ92ZVj06Oiww=";
id = 10619;
version = 1;
})
(dashboard "Loki Stack"
{
name = "loki";
hash = "sha256-9kM8MrXuL0TD2Z1Uhs4Lp0mNAazLnVfZZk5argdSoRU=";
id = 14055;
version = 5;
})
(dashboard "AlertManager"
{
name = "alertmanager";
hash = "sha256-Yvw0DGQJpqBYNzE4ES/x7ZAYF7iJ4SUNBKB+sJRuGBw=";
id = 9578;
version = 4;
})
(dashboard "Restic"
{
name = "restic";
hash = "sha256-XDYT2VAJQ97rRO3kIysH7X980YCFhyEwr2rOMmHqljg=";
id = 17554;
version = 1;
})
];
But it was not the truth, actually, some of them were not working out-of-the box.
I had to download them, edit them (adding templating inputs, etc.), add the giant blob of json to git.
It not acceptable, not only it's kind of dirty, I completely loose track of the semantic of the changes, making upgrades painful.
That's one of the reason I have just create grafana-dashboards.nix, which aims to expression dashboards transformations, such as:
Filling a template variable:
let
raw =
lib.fetchDashboard {
name = "node-exporter-full";
hash = "sha256-ZiIsNaxPE5skpDykcugveAa3S8sCjR9bA9hbzyz7kvY=";
id = 1860;
version = 32;
};
in
lib.saveDashboard {
name = "saved-node-explorer-full";
path =
lib.changePath {
name = "final-dashboard-node-explorer-full";
path = raw;
transformations = lib.fillTemplating "job" "nodes";
};
};
Or adding a template variable
let
raw =
lib.fetchDashboard {
name = "restic-exporter";
hash = "sha256-HuN1YSR51mf6F56BDLYUOD1LaP5F2kae897y+uL9mwk=";
id = 17554;
version = 1;
};
in
lib.saveDashboard {
name = "saved-restic-exporter";
path =
lib.changePath {
name = "final-dashboard-restic-exporter";
path = raw;
transformations = lib.prependTemplatings [lib.templatingJob];
};
};
Which changes my dashboards definition to:
services.grafana.provision.dashboards.settings.providers =
let
fetchDashboard = { name, hash, id, version, dashboardTitle, transformations }@args:
with grafanaDashboardsLib;
saveDashboard
{
inherit name;
path = changePath {
name = "transformed-dashboard-name";
path = grafanaDashboardsLib.fetchDashboard { inherit name id version hash; };
transformations = original:
let
final = if args.transformations == null then (x: x) else args.transformations;
regular = x:
builtins.foldl' (acc: f: f acc) x [
(fillTemplating "DS_PROMETHEUS" prometheusGrafanaDataSourceUid)
(setTitle dashboardTitle)
];
in
final (regular original);
};
};
dashboard = dashboardTitle: fetchArgs: {
name = dashboardTitle;
options.path = fetchDashboard ({ inherit dashboardTitle; transformations = null; } // fetchArgs);
};
in
[
(dashboard "Host usage"
{
# https://grafana.com/grafana/dashboards/1860-node-exporter-full/
name = "node-exporter-full";
hash = "sha256-j7+JnG88/eU2xYahjz6J2mWE4w5dRGwvRGrpqp9hPjA=";
id = 1860;
version = 32;
transformations = x:
(grafanaDashboardsLib.fillTemplating "job" "nodes"
(grafanaDashboardsLib.fillTemplating "node" "127.0.0.1:toString config.services.prometheus.exporters.node.port" x));
})
(dashboard "Docker containers"
{
# https://grafana.com/grafana/dashboards/10619-docker-host-container-overview/
name = "docker";
hash = "sha256-FQThxcxrGtTzycsx2IsCq0ifXdXXR5//nuu+GbjAQro=";
id = 10619;
version = 1;
transformations = x:
grafanaDashboardsLib.fillTemplating "port" (toString config.services.cadvisor.port)
(grafanaDashboardsLib.fillTemplating "node" "127.0.0.1"
(grafanaDashboardsLib.fillTemplating "job" "advisor" x));
})
(dashboard "AlertManager"
{
name = "alertmanager";
hash = "sha256-jOINQJbKhaBSntco+3XP9n7l1XDYQy5iZ+TbKPfseCM=";
id = 9578;
version = 4;
transformations = x:
# grafanaDashboardsLib.fillTemplating "instance" "127.0.0.1:${toString config.services.prometheus.alertmanager.port}"
(grafanaDashboardsLib.fillTemplating "datasource" prometheusGrafanaDataSourceUid x);
})
(dashboard "Restic"
{
# https://github.com/ngosang/restic-exporter
# https://grafana.com/grafana/dashboards/17554-restic-exporter/
name = "restic";
hash = "sha256-HuN1YSR51mf6F56BDLYUOD1LaP5F2kae897y+uL9mwk=";
id = 17554;
version = 1;
})
];
In my case, I hard-code some parameters as I have a static, single-node infrastructure.