feat: add /alertmanager endpoint as Alertmanager -> Apprise bridge
🚀 Docker Build and Push / build-and-push (push) Successful in 3m30s
🚀 Docker Build and Push / build-and-push (push) Successful in 3m30s
Reuses the same notification path as /crowdsec: formats the
Alertmanager webhook payload (firing/resolved, severity, instance,
summary/description) and forwards it to APPRISE_URL as {title, body}.
This commit is contained in:
1 parent
bbcbb17302
commit
842c8f71eb
2 files changed
+83
No files matched your search
@@ -51,6 +51,21 @@ headers:
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
## Configuration Alertmanager
|
||||
|
||||
Sert aussi de pont entre Alertmanager et Apprise (le `webhook_configs` d'Alertmanager
|
||||
n'a pas de templating de payload, il envoie son format natif tel quel).
|
||||
|
||||
Dans `alertmanager.yml` :
|
||||
|
||||
```yaml
|
||||
receivers:
|
||||
- name: 'apprise-notifications'
|
||||
webhook_configs:
|
||||
- url: 'http://crowdsec-notify:5000/alertmanager'
|
||||
send_resolved: true
|
||||
```
|
||||
|
||||
## Auteur
|
||||
|
||||
Maxime Killinger
|
||||
@@ -111,6 +111,74 @@ def handle_crowdsec():
|
||||
logger.exception(f"Error processing CrowdSec alert: {e}")
|
||||
return jsonify({"status": "error", "message": str(e)}), 500
|
||||
|
||||
@app.route('/alertmanager', methods=['POST'])
|
||||
def handle_alertmanager():
|
||||
try:
|
||||
data = request.json or {}
|
||||
alerts = data.get("alerts", [])
|
||||
if not alerts:
|
||||
logger.debug("Received empty Alertmanager payload")
|
||||
return jsonify({"status": "no data"}), 200
|
||||
|
||||
def format_alert(alert):
|
||||
labels = alert.get("labels", {})
|
||||
annotations = alert.get("annotations", {})
|
||||
name = labels.get("alertname", "alerte")
|
||||
severity = labels.get("severity", "")
|
||||
instance = labels.get("instance") or labels.get("container") or labels.get("job") or ""
|
||||
|
||||
head = name
|
||||
if severity:
|
||||
head += f" [{severity}]"
|
||||
if instance:
|
||||
head += f" — {instance}"
|
||||
|
||||
out = [head]
|
||||
summary = annotations.get("summary")
|
||||
description = annotations.get("description")
|
||||
if summary:
|
||||
out.append(f" {summary}")
|
||||
if description and description != summary:
|
||||
out.append(f" {description}")
|
||||
return "\n".join(out)
|
||||
|
||||
firing = [a for a in alerts if a.get("status") == "firing"]
|
||||
resolved = [a for a in alerts if a.get("status") == "resolved"]
|
||||
|
||||
lines = []
|
||||
if firing:
|
||||
lines.append(f"🔥 {len(firing)} alerte(s) active(s)")
|
||||
lines.extend(format_alert(a) for a in firing)
|
||||
if resolved:
|
||||
if lines:
|
||||
lines.append("")
|
||||
lines.append(f"✅ {len(resolved)} résolue(s)")
|
||||
lines.extend(format_alert(a) for a in resolved)
|
||||
|
||||
status = data.get("status", "firing")
|
||||
group_name = data.get("groupLabels", {}).get("alertname", "")
|
||||
title = ("🔥" if status == "firing" else "✅") + " Alertmanager"
|
||||
if group_name:
|
||||
title += f" — {group_name}"
|
||||
|
||||
body = "\n".join(lines).strip()
|
||||
|
||||
# Send to Apprise
|
||||
response = requests.post(APPRISE_URL, json={"title": title, "body": body}, timeout=10)
|
||||
|
||||
if response.ok:
|
||||
logger.info(f"Alertmanager notification sent: {len(firing)} firing, {len(resolved)} resolved")
|
||||
else:
|
||||
logger.error(f"Apprise error: {response.status_code} - {response.text}")
|
||||
return jsonify({"status": "error", "code": response.status_code}), 502
|
||||
|
||||
return jsonify({"status": "sent", "firing": len(firing), "resolved": len(resolved)}), 200
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Error processing Alertmanager alert: {e}")
|
||||
return jsonify({"status": "error", "message": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/health', methods=['GET'])
|
||||
def health():
|
||||
return jsonify({"status": "ok"}), 200
|
||||
|
||||
Reference in new issue
Block a user