Add some output

Resolves: #2, #3
This commit is contained in:
Scott Wallace 2020-10-15 09:08:23 +01:00
parent 0bdfa3502c
commit 2d5f17bacb
Signed by: scott
GPG key ID: AA742FDC5AFE2A72
2 changed files with 123 additions and 41 deletions

View file

@ -7,15 +7,12 @@ Module to act as a Prometheus Exporter for Docker containers with a
import argparse import argparse
import http.client import http.client
import json import json
import logging
import os import os
import sys import sys
from http.server import HTTPServer, SimpleHTTPRequestHandler from http.server import HTTPServer, SimpleHTTPRequestHandler
LISTEN_PORT = 8080 LISTEN_PORT = 8080
VERBOSE = int(os.environ.get('ALERTIFY_VERBOSE', 0))
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
class HTTPHandler(SimpleHTTPRequestHandler): class HTTPHandler(SimpleHTTPRequestHandler):
@ -31,7 +28,11 @@ class HTTPHandler(SimpleHTTPRequestHandler):
Method to handle GET requests Method to handle GET requests
""" """
if self.path == '/healthcheck': if self.path == '/healthcheck':
self._healthcheck() if not healthy():
print('ERROR: Check requirements')
self._respond(500, 'ERR')
self._respond(200, 'OK')
# Override built-in method # Override built-in method
# pylint: disable=invalid-name # pylint: disable=invalid-name
@ -42,56 +43,72 @@ class HTTPHandler(SimpleHTTPRequestHandler):
if self.path == '/alert': if self.path == '/alert':
self._alerts() self._alerts()
def _healthcheck(self, message=True): def _respond(self, status, message):
""" self.send_response(int(status) or 500)
Method to return 200 or 500 response and an optional message
"""
if not healthy():
self.send_response(500)
self.end_headers() self.end_headers()
if message: self.wfile.write(bytes(str(message).encode()))
self.wfile.write(b'ERR')
return False
self.send_response(200)
self.end_headers()
if message:
self.wfile.write(b'OK')
return True
def _alerts(self): def _alerts(self):
""" """
Method to handle the request for alerts Method to handle the request for alerts
""" """
if not self._healthcheck(message=False): if not healthy():
print('ERROR: Check requirements')
self._respond(500, 'Server not configured correctly')
return return
content_length = int(self.headers['Content-Length']) content_length = int(self.headers['Content-Length'])
rawdata = self.rfile.read(content_length) rawdata = self.rfile.read(content_length)
try:
alert = json.loads(rawdata.decode()) alert = json.loads(rawdata.decode())
except json.decoder.JSONDecodeError as error:
print(f'ERROR: Bad JSON: {error}')
self._respond(400, f'Bad JSON: {error}')
return
if VERBOSE:
print('Received from Alertmanager:')
print(json.dumps(alert, indent=2))
try:
if alert['status'] == 'resolved': if alert['status'] == 'resolved':
prefix = 'Resolved' prefix = 'Resolved'
else: else:
prefix = alert['commonLabels'].get('severity', 'default').capitalize() prefix = alert['commonLabels'].get(
'severity', 'default').capitalize()
gotify_msg = { gotify_msg = {
'title': '{}: {}'.format(
alert['receiver'],
alert['commonLabels'].get('instance', 'Unknown')
),
'message': '{}: {}'.format( 'message': '{}: {}'.format(
prefix, prefix,
alert['commonAnnotations'].get('description', '...') alert['commonAnnotations'].get('description', '...')
), ),
'priority': int(alert['commonLabels'].get('priority', 5)) 'priority': int(alert['commonLabels'].get('priority', 5))
} }
except KeyError as error:
print(f'ERROR: KeyError: {error}')
self._respond(400, f'Missing field: {error}')
return
(status, reason) = gotify_send( if VERBOSE:
print('Sending to Gotify:')
print(json.dumps(gotify_msg, indent=2))
response = 'Status: {status}, Reason: {reason}'.format(
**gotify_send(
os.environ['GOTIFY_SERVER'], os.environ['GOTIFY_SERVER'],
os.environ['GOTIFY_PORT'], os.environ['GOTIFY_PORT'],
os.environ['GOTIFY_KEY'], os.environ['GOTIFY_KEY'],
gotify_msg gotify_msg
) )
)
self.wfile.write(f'Status: {status}, Reason: {reason}'.encode()) if VERBOSE:
print(response)
self._respond(200, response)
def gotify_send(server, port, authkey, payload): def gotify_send(server, port, authkey, payload):
@ -108,7 +125,10 @@ def gotify_send(server, port, authkey, payload):
gotify.request('POST', '/message', json.dumps(payload), headers) gotify.request('POST', '/message', json.dumps(payload), headers)
response = gotify.getresponse() response = gotify.getresponse()
return (response.status, response.reason) return {
'status': response.status,
'reason': response.reason
}
def healthy(): def healthy():
@ -154,7 +174,11 @@ if __name__ == '__main__':
# Invert the sense of 'healthy' for Unix CLI usage # Invert the sense of 'healthy' for Unix CLI usage
return not healthy() return not healthy()
print(f'Starting web server on port {LISTEN_PORT}')
try:
HTTPServer(('', LISTEN_PORT), HTTPHandler).serve_forever() HTTPServer(('', LISTEN_PORT), HTTPHandler).serve_forever()
except KeyboardInterrupt:
print('Exiting')
return 0 return 0

58
test.sh Executable file
View file

@ -0,0 +1,58 @@
#!/usr/bin/env bash
SERVER=${1:-'localhost:8080'}
name=testAlert-$RANDOM
URL="http://${SERVER}/alert"
bold=$(tput bold)
normal=$(tput sgr0)
call_alertmanager() {
curl -v "${URL}" --header 'Content-type: application/json' --data @<(cat <<EOF
{
"version": "4",
"groupKey": "testGroup",
"truncatedAlerts": 0,
"status": "${STATUS}",
"receiver": "alertify",
"commonLabels": {
"alertname": "${name}",
"service": "testService",
"severity":"warning",
"instance": "server.example.net",
"namespace": "testNamespace",
"label_costcentre": "testCostCentre"
},
"commonAnnotations": {
"summary": "Testing latency is high!",
"description": "Testing latency is at ${1}"
},
"alerts": [
{
"status": "${STATUS}",
"generatorURL": "http://alertmanager.example.net/$name",
"startsAt": "${START}",
"endsAt": "${END}"
}
]
}
EOF
)
}
echo "${bold}Firing alert ${name} ${normal}"
STATUS='firing'
START=$(date --rfc-3339=seconds | sed 's/ /T/')
END="0001-01-01T00:00:00Z"
call_alertmanager 42
echo -e "\n"
echo "${bold}Press enter to resolve alert ${name} ${normal}"
read -r
echo "${bold}Sending resolved ${normal}"
STATUS='resolved'
END=$(date --rfc-3339=seconds | sed 's/ /T/')
call_alertmanager 0
echo -e "\n"