Files
foxhunt/infra/k8s/monitoring/dashboards/import.sh
jgrusewski dd43316a66 feat(monitoring): add Scaleway infrastructure cockpit dashboard
- Pure-Python scraper replaces shell+Python hybrid (fixes pushgateway
  RemoteDisconnected crash caused by duplicate volume name labels)
- Scraper dynamically discovers all Scaleway resources via API:
  billing, instances, K8s pools, block volumes, IPs
- 40-panel cockpit dashboard with fully dynamic tables (auto-adapts
  when infra changes — no hardcoded instance/volume names)
- Volume table keyed by UUID (not truncated name) to prevent collisions
- Label sanitization for Prometheus text format safety
- Pushgateway push via PUT + Content-Type: text/plain; version=0.0.4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 03:09:40 +01:00

213 lines
5.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Import all Grafana dashboards via API (Postgres-backed, no ConfigMaps).
#
# Dashboards use ${DS_PROMETHEUS}, ${DS_LOKI}, ${DS_TEMPO} variables.
# This script auto-detects datasource UIDs, resolves variables, and
# imports via the Grafana API. Dashboards are stored in Postgres.
#
# Required environment variables:
# GRAFANA_PASS - Password for admin user
#
# Optional overrides:
# GRAFANA_URL - Grafana base URL (default: https://grafana.fxhnt.ai)
# GRAFANA_USER - Grafana user (default: admin)
# GRAFANA_PROM_UID - Override Prometheus datasource UID
# GRAFANA_LOKI_UID - Override Loki datasource UID
# GRAFANA_TEMPO_UID - Override Tempo datasource UID
#
# Usage:
# GRAFANA_PASS="xxx" ./import.sh
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
GRAFANA_USER="${GRAFANA_USER:-admin}"
GRAFANA_URL="${GRAFANA_URL:-https://grafana.fxhnt.ai}"
GRAFANA_URL="${GRAFANA_URL%/}"
if [[ -z "${GRAFANA_PASS:-}" ]]; then
echo "ERROR: GRAFANA_PASS is not set" >&2
exit 1
fi
# --- Datasource auto-detection ---
detect_datasource_uid() {
local ds_type="$1"
curl -sf -u "${GRAFANA_USER}:${GRAFANA_PASS}" "${GRAFANA_URL}/api/datasources" 2>/dev/null | \
python3 -c "
import sys, json
for ds in json.load(sys.stdin):
if ds.get('type') == '${ds_type}':
print(ds['uid'])
break
" 2>/dev/null || true
}
echo "Detecting datasource UIDs..."
PROM_UID="${GRAFANA_PROM_UID:-$(detect_datasource_uid prometheus)}"
LOKI_UID="${GRAFANA_LOKI_UID:-$(detect_datasource_uid loki)}"
TEMPO_UID="${GRAFANA_TEMPO_UID:-$(detect_datasource_uid tempo)}"
echo " Prometheus: ${PROM_UID:-NOT FOUND}"
echo " Loki: ${LOKI_UID:-NOT FOUND}"
echo " Tempo: ${TEMPO_UID:-NOT FOUND}"
if [[ -z "$PROM_UID" ]]; then
echo "ERROR: Could not detect Prometheus datasource. Set GRAFANA_PROM_UID." >&2
exit 1
fi
# --- Resolve datasource variables ---
resolve_dashboard() {
local file="$1"
python3 -c "
import sys, json
with open('${file}') as f:
d = json.load(f)
d.pop('__inputs', None)
d.pop('__requires', None)
raw = json.dumps(d)
raw = raw.replace('\${DS_PROMETHEUS}', '${PROM_UID}')
raw = raw.replace('\${DS_LOKI}', '${LOKI_UID}')
raw = raw.replace('\${DS_TEMPO}', '${TEMPO_UID}')
print(raw)
"
}
# --- Ensure folders exist ---
ensure_folder() {
local title="$1"
local uid
uid=$(curl -sf -u "${GRAFANA_USER}:${GRAFANA_PASS}" "${GRAFANA_URL}/api/folders" 2>/dev/null | \
python3 -c "
import sys, json
for f in json.load(sys.stdin):
if f['title'] == '${title}':
print(f['uid'])
break
" 2>/dev/null || true)
if [[ -z "$uid" ]]; then
uid=$(curl -sf -u "${GRAFANA_USER}:${GRAFANA_PASS}" \
-H "Content-Type: application/json" \
-d "{\"title\": \"${title}\"}" \
"${GRAFANA_URL}/api/folders" 2>/dev/null | \
python3 -c "import sys,json; print(json.load(sys.stdin)['uid'])" 2>/dev/null)
echo " Created folder: ${title} (${uid})"
fi
echo "$uid"
}
echo ""
echo "Ensuring folders..."
FOLDER_CI=$(ensure_folder "CI")
FOLDER_INFRA=$(ensure_folder "Infrastructure")
FOLDER_OPS=$(ensure_folder "Operations")
FOLDER_TRADING=$(ensure_folder "Trading")
FOLDER_TRAINING=$(ensure_folder "Training")
FOLDER_SERVICES=$(ensure_folder "Services")
FOLDER_PLATFORM=$(ensure_folder "Platform")
# --- Import a single dashboard to a folder ---
import_dashboard() {
local file="$1"
local folder_uid="$2"
local name
name="$(basename "$file" .json)"
local resolved
resolved=$(resolve_dashboard "$file")
local response
response=$(curl -s -w "\n%{http_code}" \
-u "${GRAFANA_USER}:${GRAFANA_PASS}" \
-H "Content-Type: application/json" \
-d "{\"dashboard\": ${resolved}, \"folderUid\": \"${folder_uid}\", \"overwrite\": true}" \
"${GRAFANA_URL}/api/dashboards/db")
local http_code
http_code=$(echo "$response" | tail -1)
if [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]]; then
echo " OK ${name}"
else
local body
body=$(echo "$response" | sed '$d')
echo " FAIL ${name} (${http_code}): ${body}" >&2
return 1
fi
}
# --- Dashboard → Folder mapping ---
declare -A DASHBOARD_FOLDERS=(
# Original dashboards
[foxhunt-cockpit]="${FOLDER_OPS}"
[foxhunt-trading]="${FOLDER_TRADING}"
[foxhunt-training]="${FOLDER_TRAINING}"
[foxhunt-infrastructure]="${FOLDER_INFRA}"
[foxhunt-observability]="${FOLDER_INFRA}"
[foxhunt-ci-logs]="${FOLDER_CI}"
[foxhunt-training-logs]="${FOLDER_TRAINING}"
[foxhunt-training-deep-dive]="${FOLDER_TRAINING}"
# Application services
[svc-api-gateway]="${FOLDER_SERVICES}"
[svc-trading-service]="${FOLDER_SERVICES}"
[svc-backtesting]="${FOLDER_SERVICES}"
[svc-data-acquisition]="${FOLDER_SERVICES}"
[svc-ml-training]="${FOLDER_SERVICES}"
[svc-trading-agent]="${FOLDER_SERVICES}"
[svc-web-gateway]="${FOLDER_SERVICES}"
# Platform services
[platform-prometheus]="${FOLDER_PLATFORM}"
[platform-grafana]="${FOLDER_PLATFORM}"
[platform-loki]="${FOLDER_PLATFORM}"
[platform-tempo]="${FOLDER_PLATFORM}"
[platform-redis]="${FOLDER_PLATFORM}"
[platform-postgres]="${FOLDER_PLATFORM}"
[platform-argo]="${FOLDER_PLATFORM}"
[platform-questdb]="${FOLDER_PLATFORM}"
[platform-costs]="${FOLDER_PLATFORM}"
[platform-scaleway]="${FOLDER_PLATFORM}"
)
# --- Deploy all dashboards ---
echo ""
echo "Importing dashboards..."
failed=0
total=0
for dashboard in "${SCRIPT_DIR}"/*.json; do
[[ -f "$dashboard" ]] || continue
name="$(basename "$dashboard" .json)"
folder_uid="${DASHBOARD_FOLDERS[$name]:-}"
if [[ -z "$folder_uid" ]]; then
echo " SKIP ${name} (no folder mapping)"
continue
fi
total=$((total + 1))
if ! import_dashboard "$dashboard" "$folder_uid"; then
failed=$((failed + 1))
fi
done
echo ""
if [[ $failed -eq 0 ]]; then
echo "All ${total} dashboards deployed successfully."
else
echo "ERROR: ${failed}/${total} dashboard(s) failed." >&2
exit 1
fi