alertmanager-gotify-bridge: initial commit with bridge and CI
Some checks failed
ci/woodpecker/tag/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/tag/woodpecker Pipeline failed
This commit is contained in:
24
.woodpecker.yaml
Normal file
24
.woodpecker.yaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
labels:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
when:
|
||||||
|
- event: tag
|
||||||
|
ref: "refs/tags/v*"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build-and-push
|
||||||
|
image: git.oleks.space/oleks/nix-ci:latest
|
||||||
|
environment:
|
||||||
|
REGISTRY_TOKEN:
|
||||||
|
from_secret: registry_token
|
||||||
|
commands:
|
||||||
|
- echo "$REGISTRY_TOKEN" | docker login git.oleks.space -u oleks --password-stdin
|
||||||
|
- docker buildx create --name amd64 --driver remote "tcp://buildkit-rootless-amd64.infra.svc.cluster.local:1234"
|
||||||
|
- TAG=$(echo "$CI_COMMIT_TAG" | sed 's/^v//')
|
||||||
|
- IMAGE="git.oleks.space/oleks/alertmanager-gotify-bridge"
|
||||||
|
- echo "Building $IMAGE:$TAG"
|
||||||
|
- docker buildx build --builder amd64 --platform linux/amd64 --tag "$IMAGE:$TAG" --tag "$IMAGE:latest" --push .
|
||||||
|
backend_options:
|
||||||
|
kubernetes:
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/hostname: howard2404
|
||||||
5
Dockerfile
Normal file
5
Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FROM python:3.12-alpine
|
||||||
|
COPY bridge.py /app/bridge.py
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 8080
|
||||||
|
CMD ["python", "bridge.py"]
|
||||||
86
bridge.py
Normal file
86
bridge.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Alertmanager webhook receiver that forwards alerts to Gotify."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||||
|
from urllib.request import Request, urlopen
|
||||||
|
from urllib.error import URLError
|
||||||
|
|
||||||
|
GOTIFY_URL = os.environ["GOTIFY_URL"]
|
||||||
|
DISASTER_TOKEN = os.environ["DISASTER_TOKEN"]
|
||||||
|
WARNING_TOKEN = os.environ["WARNING_TOKEN"]
|
||||||
|
DISASTER_PRIORITY = int(os.environ.get("DISASTER_PRIORITY", "8"))
|
||||||
|
WARNING_PRIORITY = int(os.environ.get("WARNING_PRIORITY", "4"))
|
||||||
|
|
||||||
|
SEVERITY_MAP = {
|
||||||
|
"critical": (DISASTER_TOKEN, DISASTER_PRIORITY),
|
||||||
|
"warning": (WARNING_TOKEN, WARNING_PRIORITY),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def send_gotify(token, title, message, priority):
|
||||||
|
data = json.dumps({
|
||||||
|
"title": title,
|
||||||
|
"message": message,
|
||||||
|
"priority": priority,
|
||||||
|
}).encode()
|
||||||
|
req = Request(
|
||||||
|
f"{GOTIFY_URL}/message?token={token}",
|
||||||
|
data=data,
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
urlopen(req)
|
||||||
|
except URLError as e:
|
||||||
|
print(f"ERROR sending to Gotify: {e}", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(BaseHTTPRequestHandler):
|
||||||
|
def do_POST(self):
|
||||||
|
try:
|
||||||
|
length = int(self.headers.get("Content-Length", 0))
|
||||||
|
body = json.loads(self.rfile.read(length))
|
||||||
|
except (json.JSONDecodeError, ValueError) as e:
|
||||||
|
self.send_response(400)
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(f"Bad request: {e}".encode())
|
||||||
|
return
|
||||||
|
|
||||||
|
for alert in body.get("alerts", []):
|
||||||
|
severity = alert.get("labels", {}).get("severity", "warning")
|
||||||
|
token, priority = SEVERITY_MAP.get(severity, SEVERITY_MAP["warning"])
|
||||||
|
status = alert.get("status", "firing")
|
||||||
|
alertname = alert.get("labels", {}).get("alertname", "Unknown")
|
||||||
|
summary = alert.get("annotations", {}).get("summary", "")
|
||||||
|
|
||||||
|
prefix = "RESOLVED" if status == "resolved" else "FIRING"
|
||||||
|
title = f"[{prefix}] {alertname}"
|
||||||
|
message = summary or f"{alertname} is {status}"
|
||||||
|
|
||||||
|
send_gotify(token, title, message, priority)
|
||||||
|
print(f"Forwarded: {title} -> severity={severity} priority={priority}")
|
||||||
|
|
||||||
|
self.send_response(200)
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(b"ok")
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
if self.path == "/health":
|
||||||
|
self.send_response(200)
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(b"ok")
|
||||||
|
return
|
||||||
|
self.send_response(404)
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
print(format % args)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
port = int(os.environ.get("PORT", "8080"))
|
||||||
|
server = HTTPServer(("0.0.0.0", port), Handler)
|
||||||
|
print(f"Bridge listening on :{port}")
|
||||||
|
server.serve_forever()
|
||||||
Reference in New Issue
Block a user