export-dashboards.sh (2791B)
1 #!/usr/bin/env bash 2 # Export Grafana dashboards to grafana/provisioning/dashboards/ so UI edits 3 # can be committed to git. 4 # 5 # Grafana loads dashboards from those JSON files on startup, but edits made in 6 # the UI are saved to Grafana's internal DB — not back to the files. Run this 7 # after editing a dashboard, then `git add` the result. 8 # 9 # Usage: 10 # ./scripts/export-dashboards.sh # export every dashboard 11 # ./scripts/export-dashboards.sh ad79bw7 # export one, by uid 12 13 set -euo pipefail 14 15 GRAFANA_URL="${GRAFANA_URL:-http://localhost:3000}" 16 GRAFANA_AUTH="${GRAFANA_AUTH:-admin:admin}" 17 18 # Resolve the output dir relative to this script, so it works from any cwd. 19 REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" 20 OUT_DIR="$REPO_ROOT/grafana/provisioning/dashboards" 21 mkdir -p "$OUT_DIR" 22 23 # Collect the uids to export: either the one passed in, or all dashboards. 24 if [ $# -gt 0 ]; then 25 uids=("$@") 26 else 27 mapfile -t uids < <( 28 curl -sf -u "$GRAFANA_AUTH" "$GRAFANA_URL/api/search?type=dash-db" \ 29 | python3 -c 'import sys,json; [print(d["uid"]) for d in json.load(sys.stdin)]' 30 ) 31 fi 32 33 if [ ${#uids[@]} -eq 0 ]; then 34 echo "No dashboards found in $GRAFANA_URL" >&2 35 exit 1 36 fi 37 38 for uid in "${uids[@]}"; do 39 curl -sf -u "$GRAFANA_AUTH" "$GRAFANA_URL/api/dashboards/uid/$uid" \ 40 | OUT_DIR="$OUT_DIR" python3 -c ' 41 import json, os, re, sys 42 43 payload = json.load(sys.stdin) 44 dash = payload["dashboard"] 45 46 # `id` is local to a Grafana instance and must not be provisioned; `uid` and 47 # `title` are kept so this file updates the existing dashboard in place 48 # instead of creating a duplicate. 49 dash.pop("id", None) 50 51 out_dir = os.environ["OUT_DIR"] 52 53 # Reuse the file that already holds this uid, so re-exporting (or renaming a 54 # dashboard) updates it in place. Two files sharing a uid would make Grafana 55 # provisioning fight over which one wins. 56 path = None 57 for name in sorted(os.listdir(out_dir)): 58 if not name.endswith(".json"): 59 continue 60 candidate = os.path.join(out_dir, name) 61 try: 62 with open(candidate) as fh: 63 if json.load(fh).get("uid") == dash["uid"]: 64 path = candidate 65 break 66 except (json.JSONDecodeError, OSError): 67 continue 68 69 # No existing file: name it from the title, lowercased, non-alphanumerics dashed. 70 if path is None: 71 slug = re.sub(r"[^a-z0-9]+", "-", dash["title"].lower()).strip("-") or dash["uid"] 72 path = os.path.join(out_dir, slug + ".json") 73 74 with open(path, "w") as fh: 75 json.dump(dash, fh, indent=2) 76 fh.write("\n") 77 78 title = dash["title"] 79 print(f" {title} -> grafana/provisioning/dashboards/{os.path.basename(path)}") 80 ' 81 done 82 83 echo 84 echo "Done. Review and commit:" 85 echo " git add grafana/provisioning/dashboards/ && git commit -m 'update dashboards'"