Repository navigation
Expand file tree
/
Copy pathlambda_function.py
More file actions
110 lines (94 loc) · 3.24 KB
/
Copy pathlambda_function.py
File metadata and controls
110 lines (94 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# -- IMPORTS --
# -- Standard libraries --
import json
import os
import typing
from datetime import datetime, timezone
# -- 3rd party libraries --
import boto3
import urllib.request
import urllib.error
ENDPOINTS = [
{"name": "BRIDGE", "url": "https://bridge.isaric.org"},
{"name": "VERTEX", "url": "https://vertex.isaric.org"},
{"name": "LINK", "url": "https://link.isaric.org"},
]
S3_BUCKET = os.environ.get("S3_BUCKET")
S3_KEY = os.environ.get("S3_KEY")
def sanitise_error(msg: str) -> str:
"""Genericise any potentially sensitive internal details from error messages."""
m = msg.lower()
if "timeout" in m:
return "timeout"
if "ssl" in m or "certificate" in m:
return "ssl error"
if "connection" in m:
return "connection error"
if "http" in m:
return "http error"
return "unreachable"
def check_endpoint(name, url) -> dict[str, typing.Any]:
"""Check endpoint health by name and URL."""
try:
req = urllib.request.Request(url, method="GET")
with urllib.request.urlopen(req, timeout=10) as resp:
status_code = resp.status
ok = 200 <= status_code < 400
return {
"name": name,
"url": url,
"status": "up" if ok else "degraded",
"status_code": status_code,
"error": None,
}
except urllib.error.HTTPError as e:
return {
"name": name,
"url": url,
"status": "degraded",
"status_code": e.code,
"error": sanitise_error(str(e.reason)),
}
except Exception as e:
return {
"name": name,
"url": url,
"status": "down",
"status_code": None,
"error": sanitise_error(str(e)),
}
def lambda_handler(event, context) -> dict[str, typing.Any]:
"""The Lambda handler - checks and appends each endpoint's health status to a JSON file that it then uploads to S3."""
results = []
for ep in ENDPOINTS:
try:
result = check_endpoint(ep["name"], ep["url"])
except Exception:
# Broad catch — one failing check must never prevent the S3 write
result = {
"name": ep["name"],
"url": ep["url"],
"status": "down",
"status_code": None,
"error": "unreachable",
}
results.append(result)
for r in results:
print(json.dumps(r))
overall = "up"
if any(r["status"] == "down" for r in results): overall = "down"
elif any(r["status"] == "degraded" for r in results): overall = "degraded"
payload = {
"status": overall,
"checked_at": datetime.now(timezone.utc).isoformat(),
"services": results,
}
s3 = boto3.client("s3")
s3.put_object(
Bucket = S3_BUCKET,
Key = S3_KEY,
Body = json.dumps(payload, indent=2),
ContentType = "application/json",
CacheControl= "no-cache, no-store, must-revalidate",
)
return {"statusCode": 200, "body": json.dumps(payload)}