diff options
Diffstat (limited to 'modules/dashboards.nix')
| -rw-r--r-- | modules/dashboards.nix | 52 |
1 files changed, 42 insertions, 10 deletions
diff --git a/modules/dashboards.nix b/modules/dashboards.nix index 125e3e2..2fc24c8 100644 --- a/modules/dashboards.nix +++ b/modules/dashboards.nix @@ -2,7 +2,7 @@ # # Returns an attrset of { name = derivation; }, where each derivation is a single # dashboard JSON file. modules/server.nix drops these into /etc/grafana-dashboards. -{ lib, runCommand, fetchurl, jq, dsUid }: +{ lib, runCommand, fetchurl, jq, python3, dsUid }: let # Dashboards exported from grafana.com carry an `__inputs` block and reference their @@ -29,12 +29,23 @@ let | .id = null ''; + # One jq invocation, shared by both sources of dashboards below. + # + # Deliberately a single line with no trailing newline: callers append redirections + # (`< $src > "$out"`), and a multi-line string would put those on a line of their own + # as a separate command -- which truncates $out to zero bytes and sends jq's output to + # the build log, without failing the build. + jqFixup = lib.concatStringsSep " " [ + "jq" + "--arg prom ${lib.escapeShellArg dsUid.prometheus}" + "--arg loki ${lib.escapeShellArg dsUid.loki}" + (lib.escapeShellArg fixupProgram) + ]; + fixup = name: src: runCommand "grafana-dashboard-${name}.json" { nativeBuildInputs = [ jq ]; } '' - jq --arg prom ${lib.escapeShellArg dsUid.prometheus} \ - --arg loki ${lib.escapeShellArg dsUid.loki} \ - ${lib.escapeShellArg fixupProgram} < ${src} > "$out" + ${jqFixup} < ${src} > "$out" ''; fromGrafanaCom = { name, id, rev, hash }: @@ -42,6 +53,28 @@ let url = "https://grafana.com/api/dashboards/${toString id}/revisions/${toString rev}/download"; inherit hash; }); + + # The generators and the panel helpers they import. + generators = ../dashboards; + + # Our own dashboards are generated here rather than committed as JSON. The JSON is + # machine-written and the generator beside it is the only source of truth, so keeping + # both in git only creates a way for them to drift -- and a build that runs the + # generator cannot serve a stale dashboard. Note flakes copy only git-tracked files + # into the store, so committing the .json is the *only* way to also read it from disk; + # there is no "gitignore it and read it anyway" option. + fromGenerator = { name, generator }: + runCommand "grafana-dashboard-${name}.json" + { + nativeBuildInputs = [ jq python3 ]; + # panels.py sits next to the generators and is imported by both of them. + PYTHONPATH = generators; + # The store path is read-only; without this Python warns about __pycache__. + PYTHONDONTWRITEBYTECODE = 1; + } + '' + python3 ${generators}/${generator} | ${jqFixup} > "$out" + ''; in { # https://grafana.com/grafana/dashboards/1860 @@ -65,11 +98,10 @@ in # can never render against Prometheus, however it is provisioned. This is a # Prometheus-native replacement built on the metrics we actually export: node # exporter's zfs collector plus the ZFS textfile collector. - # Generated by dashboards/gen_zfs.py. - zfs-pools = fixup "zfs-pools" ../dashboards/zfs-pools.json; + zfs-pools = fromGenerator { name = "zfs-pools"; generator = "gen_zfs.py"; }; - # CrystalDiskInfo-style per-drive SMART view. Generated by dashboards/gen_smart.py. - # The Good/Caution/Bad verdict and the per-drive row titles both come from the - # recording rules in rules/smartctl.yml, so this one needs those loaded to be useful. - drive-health = fixup "drive-health" ../dashboards/smart.json; + # CrystalDiskInfo-style per-drive SMART view. The Good/Caution/Bad verdict and the + # per-drive row titles both come from the recording rules in rules/smartctl.yml, so + # this one needs those loaded to be useful. + drive-health = fromGenerator { name = "drive-health"; generator = "gen_smart.py"; }; } |
