Skip to content

Commit 59837c4

Browse files
committed
Add GitHub Actions health check workflow
Introduces a new GitHub Actions workflow to periodically check service health and version information for specified endpoints. The workflow includes: - Scheduled runs every 5 minutes - Checks version information - Monitors service status - Raises an error if any services are down
1 parent 176f50a commit 59837c4

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed

.github/workflows/health_check.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Health Check
2+
3+
on:
4+
push:
5+
paths:
6+
- './scripts/health_check.py'
7+
- './.github/workflows/health_check.yml'
8+
workflow_dispatch:
9+
schedule:
10+
# Every 5 minutes
11+
- cron: '*/5 * * * *'
12+
13+
jobs:
14+
health-check:
15+
runs-on: ubuntu-latest
16+
strategy:
17+
matrix:
18+
environment: ['dev', 'stage', 'prod']
19+
fail-fast: false
20+
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- name: Set up Python
25+
uses: actions/setup-python@v5
26+
with:
27+
python-version: '3.11'
28+
cache: 'pip'
29+
30+
- name: Install dependencies
31+
run: |
32+
python -m pip install --upgrade pip
33+
pip install requests
34+
35+
- name: Run Health Checks
36+
shell: bash
37+
run: |
38+
set -ue
39+
40+
environment="${{ matrix.environment }}"
41+
output_file="out.json"
42+
./scripts/health_check.py --env $environment --verbose --output $output_file
43+
44+
version=$(cat $output_file | jq -r '.version')
45+
monitors=$(cat $output_file | jq -r '.monitors')
46+
47+
echo "Version: $version"
48+
echo "Monitors: $monitors"
49+
50+
if [ "$version" = "null" ] || [ "$monitors" = "null" ]; then
51+
echo "Environment $environment is not reachable"
52+
exit 1
53+
fi
54+
55+
message=""
56+
57+
data=$(echo $monitors | jq -r 'to_entries[] | select(.value.state == false) | .key')
58+
for monitor in $data; do
59+
message="$message\n- $monitor: $(echo $monitors | jq -r ".[\"$monitor\"].status")"
60+
done
61+
62+
echo "Environment: $environment"
63+
echo "$message"
64+
65+
66+
67+

scripts/health_check.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import json
5+
from enum import Enum
6+
7+
import requests
8+
9+
10+
ENV_ENUM = Enum(
11+
'ENV',
12+
[
13+
('dev', 'https://addons-dev.allizom.org'),
14+
('stage', 'https://addons.allizom.org'),
15+
('prod', 'https://addons.mozilla.org'),
16+
],
17+
)
18+
19+
20+
class Fetcher:
21+
def __init__(self, env: ENV_ENUM, verbose: bool = False):
22+
self.environment = ENV_ENUM[env]
23+
self.verbose = verbose
24+
25+
def _fetch(self, path: str) -> dict[str, str] | None:
26+
url = f'{self.environment.value}/{path}'
27+
if self.verbose:
28+
print(f'Requesting {url} for {self.environment.name}')
29+
30+
try:
31+
response = requests.get(url)
32+
response.raise_for_status()
33+
try:
34+
data = response.json()
35+
except json.JSONDecodeError as e:
36+
if self.verbose:
37+
print(f'Error decoding JSON for {url}: {e}')
38+
39+
except requests.exceptions.HTTPError as e:
40+
if self.verbose:
41+
print(f'Error fetching {url}: {e}')
42+
43+
if self.verbose and data is not None:
44+
print(json.dumps(data, indent=2))
45+
46+
return data
47+
48+
def version(self):
49+
return self._fetch('__version__')
50+
51+
def monitors(self):
52+
return self._fetch('services/monitor.json')
53+
54+
55+
def main(env: ENV_ENUM, verbose: bool = False, output: str | None = None):
56+
fetcher = Fetcher(env, verbose)
57+
58+
version_data = fetcher.version()
59+
monitors_data = fetcher.monitors()
60+
61+
if output:
62+
with open(output, 'w') as f:
63+
json.dump({'version': version_data, 'monitors': monitors_data}, f, indent=2)
64+
elif monitors_data is not None:
65+
if any(monitor['state'] is False for monitor in monitors_data.values()):
66+
raise ValueError(f'Some monitors are failing {monitors_data}')
67+
68+
69+
if __name__ == '__main__':
70+
args = argparse.ArgumentParser()
71+
args.add_argument(
72+
'--env', type=str, choices=list(ENV_ENUM.__members__.keys()), required=True
73+
)
74+
args.add_argument('--verbose', action='store_true')
75+
args.add_argument('--output', type=str, required=False)
76+
args = args.parse_args()
77+
78+
main(args.env, args.verbose, args.output)

0 commit comments

Comments
 (0)