monitoring: harden grafana user dedupe

This commit is contained in:
Brad Stein 2026-01-21 12:30:08 -03:00
parent ae1fd5b661
commit 5ae6b4b00c

View File

@ -2,7 +2,7 @@
apiVersion: batch/v1 apiVersion: batch/v1
kind: Job kind: Job
metadata: metadata:
name: grafana-user-dedupe-api-v5 name: grafana-user-dedupe-api-v6
namespace: monitoring namespace: monitoring
spec: spec:
backoffLimit: 1 backoffLimit: 1
@ -60,35 +60,66 @@ spec:
import json import json
import os import os
import urllib.parse import urllib.parse
import urllib.error
import urllib.request import urllib.request
grafana_url = os.environ["GRAFANA_URL"].rstrip("/") grafana_url = os.environ["GRAFANA_URL"].rstrip("/")
user = os.environ["GRAFANA_USER"] user = os.environ["GRAFANA_USER"]
password = os.environ["GRAFANA_PASSWORD"] password = os.environ["GRAFANA_PASSWORD"]
emails = [e.strip() for e in os.environ["GRAFANA_DEDUPE_EMAILS"].split(",") if e.strip()] lookups = [e.strip() for e in os.environ["GRAFANA_DEDUPE_EMAILS"].split(",") if e.strip()]
token = base64.b64encode(f"{user}:{password}".encode("utf-8")).decode("utf-8") token = base64.b64encode(f"{user}:{password}".encode("utf-8")).decode("utf-8")
headers = {"Authorization": f"Basic {token}"} headers = {"Authorization": f"Basic {token}"}
def request(method: str, url: str): def request(method: str, url: str):
req = urllib.request.Request(url, headers=headers, method=method) req = urllib.request.Request(url, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=10) as resp:
return resp.read()
for email in emails:
lookup_url = f"{grafana_url}/api/users/lookup?loginOrEmail={urllib.parse.quote(email)}"
try: try:
payload = json.loads(request("GET", lookup_url)) with urllib.request.urlopen(req, timeout=10) as resp:
except Exception: return resp.status, resp.read()
print(f"no grafana user found for {email}") except urllib.error.HTTPError as err:
body = err.read()
return err.code, body
for _ in range(60):
status, _ = request("GET", f"{grafana_url}/api/health")
if status == 200:
break
else:
raise SystemExit("Grafana API did not become ready in time")
for lookup in lookups:
search_url = f"{grafana_url}/api/users/search?query={urllib.parse.quote(lookup)}"
status, body = request("GET", search_url)
if status != 200:
print(f"search failed for {lookup}: status={status} body={body.decode('utf-8', errors='ignore')}")
continue continue
user_id = payload.get("id") payload = json.loads(body)
if not user_id: users = payload.get("users", [])
print(f"no grafana user found for {email}") matches = [
user
for user in users
if user.get("email", "").lower() == lookup.lower()
or user.get("login", "").lower() == lookup.lower()
]
if not matches:
print(f"no grafana user found for {lookup}")
continue continue
print(f"deleting grafana user {user_id} ({email})") for user in matches:
delete_url = f"{grafana_url}/api/admin/users/{user_id}" user_id = user.get("id")
request("DELETE", delete_url) if not user_id:
continue
print(f"deleting grafana user {user_id} ({user.get('email')})")
delete_url = f"{grafana_url}/api/admin/users/{user_id}"
del_status, del_body = request("DELETE", delete_url)
if del_status not in (200, 202, 204):
print(
"delete failed for",
user_id,
"status",
del_status,
"body",
del_body.decode("utf-8", errors="ignore"),
)
PY PY
echo "done" echo "done"
env: env: