From 0d15ab7cf2a036fcbea7e5533768f02ca007cbee Mon Sep 17 00:00:00 2001 From: Patrick Date: Tue, 10 Sep 2024 17:17:48 -0600 Subject: [PATCH 1/3] Refactored checks.yaml to allow for groups --- checks.yaml | 122 +++++++++++++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 58 deletions(-) diff --git a/checks.yaml b/checks.yaml index ce0f868..4db4ea0 100644 --- a/checks.yaml +++ b/checks.yaml @@ -1,71 +1,77 @@ -- name: GitHub Home - type: http - host: https://github.com - expected_code: 200 +- title: 'Group 1' + checks: + - name: GitHub Home + type: http + host: https://github.com + expected_code: 200 -- name: GitHub API - type: http - host: https://api.github.com - expected_code: 200 + - name: GitHub API + type: http + host: https://api.github.com + expected_code: 200 -- name: Wikipedia Home - type: http - host: https://www.wikipedia.org - expected_code: 200 + - name: Wikipedia Home + type: http + host: https://www.wikipedia.org + expected_code: 200 -- name: Wikipedia API - type: http - host: https://en.wikipedia.org/w/api.php - expected_code: 200 + - name: Wikipedia API + type: http + host: https://en.wikipedia.org/w/api.php + expected_code: 200 -- name: Hacker News - type: http - host: https://news.ycombinator.com - expected_code: 200 + - name: Hacker News + type: http + host: https://news.ycombinator.com + expected_code: 200 -- name: Google Home - type: http - host: https://www.google.com - expected_code: 200 + - name: Google Home + type: http + host: https://www.google.com + expected_code: 200 -- name: Cloudflare DNS Checker - type: ping - host: 1.1.1.1 +- title: 'Group 2' + checks: + - name: Cloudflare DNS Checker + type: ping + host: 1.1.1.1 -- name: Google Public DNS - type: ping - host: 8.8.8.8 + - name: Google Public DNS + type: ping + host: 8.8.8.8 -- name: Dummy Postgres Database - type: port - host: ec2-54-173-89-248.compute-1.amazonaws.com - port: 5432 + - name: Dummy Postgres Database + type: port + host: ec2-54-173-89-248.compute-1.amazonaws.com + port: 5432 -- name: Dummy MySQL Database - type: port - host: db.example.com - port: 3306 + - name: Dummy MySQL Database + type: port + host: db.example.com + port: 3306 -- name: Amazon Web Services - type: http - host: https://aws.amazon.com - expected_code: 200 +- title: 'Group 3' + checks: + - name: Amazon Web Services + type: http + host: https://aws.amazon.com + expected_code: 200 -- name: AWS S3 API - type: http - host: https://s3.amazonaws.com - expected_code: 200 + - name: AWS S3 API + type: http + host: https://s3.amazonaws.com + expected_code: 200 -- name: Twitter - type: http - host: https://twitter.com - expected_code: 200 + - name: Twitter + type: http + host: https://twitter.com + expected_code: 200 -- name: Facebook Home - type: http - host: https://www.facebook.com - expected_code: 200 - -- name: Localhost - type: ping - host: localhost + - name: Facebook Home + type: http + host: https://www.facebook.com + expected_code: 200 + + - name: Localhost + type: ping + host: localhost From bff65ab61ce7c508ec82c8def941004f075d0f6b Mon Sep 17 00:00:00 2001 From: Patrick Date: Tue, 10 Sep 2024 19:18:54 -0600 Subject: [PATCH 2/3] Adjusted index.html.theme to support groupings --- index.html.theme | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.html.theme b/index.html.theme index 6df6869..0a236b1 100644 --- a/index.html.theme +++ b/index.html.theme @@ -83,6 +83,8 @@

TinyStatus

Current Status

+ {% for group, checks in groups.items() %} +

{{group}} Status

{% for check in checks %}
@@ -93,6 +95,7 @@
{% endfor %}
+ {% endfor %}

Incident History

{{ incidents | safe }} From 2b657066a67d49c6b20fa342de7dc4a258631283 Mon Sep 17 00:00:00 2001 From: Patrick Date: Tue, 10 Sep 2024 19:44:18 -0600 Subject: [PATCH 3/3] Adjusted tinystatus.py to support groupings in checks.yaml --- tinystatus.py | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/tinystatus.py b/tinystatus.py index 69f4876..a2279e0 100644 --- a/tinystatus.py +++ b/tinystatus.py @@ -25,6 +25,7 @@ STATUS_HISTORY_FILE = os.getenv('STATUS_HISTORY_FILE', 'history.json') HTML_OUTPUT_DIRECTORY = os.getenv('HTML_OUTPUT_DIRECTORY', os.getcwd()) + # Service check functions async def check_http(url, expected_code): async with aiohttp.ClientSession() as session: @@ -34,6 +35,7 @@ async def check_http(url, expected_code): except: return False + async def check_ping(host): try: result = subprocess.run(['ping', '-c', '1', '-W', '2', host], capture_output=True, text=True) @@ -41,6 +43,7 @@ async def check_ping(host): except: return False + async def check_port(host, port): try: _, writer = await asyncio.open_connection(host, port) @@ -50,6 +53,7 @@ async def check_port(host, port): except: return False + async def run_checks(checks): background_tasks = {} async with asyncio.TaskGroup() as tg: @@ -70,6 +74,7 @@ async def run_checks(checks): return results + # History management def load_history(): if os.path.exists(STATUS_HISTORY_FILE): @@ -77,28 +82,34 @@ def load_history(): return json.load(f) return {} + def save_history(history): with open(STATUS_HISTORY_FILE, 'w') as f: json.dump(history, f, indent=2) + def update_history(results): + # TODO: Configure groups in history page history = load_history() current_time = datetime.now().isoformat() - for check in results: - if check['name'] not in history: - history[check['name']] = [] - history[check['name']].append({'timestamp': current_time, 'status': check['status']}) - history[check['name']] = history[check['name']][-MAX_HISTORY_ENTRIES:] + for group in results.keys(): + for check in results[group]: + if check['name'] not in history: + history[check['name']] = [] + history[check['name']].append({'timestamp': current_time, 'status': check['status']}) + history[check['name']] = history[check['name']][-MAX_HISTORY_ENTRIES:] save_history(history) + # Main monitoring loop async def monitor_services(): os.makedirs(HTML_OUTPUT_DIRECTORY, exist_ok=True) - + while True: + down_services = [] try: with open(CHECKS_FILE, 'r') as f: - checks = yaml.safe_load(f) + groups = yaml.safe_load(f) with open(INCIDENTS_FILE, 'r') as f: incidents = markdown.markdown(f.read()) @@ -109,11 +120,13 @@ async def monitor_services(): with open(HISTORY_TEMPLATE_FILE, 'r') as f: history_template = Template(f.read()) - results = await run_checks(checks) + results = {} + for group in groups: + results[group['title']] = await run_checks(group['checks']) update_history(results) - html = template.render(checks=results, incidents=incidents, last_updated=datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + html = template.render(groups=results, incidents=incidents, last_updated=datetime.now().strftime("%Y-%m-%d %H:%M:%S")) with open(os.path.join(HTML_OUTPUT_DIRECTORY, 'index.html'), 'w') as f: f.write(html) @@ -122,18 +135,24 @@ async def monitor_services(): f.write(history_html) logging.info(f"Status page and history updated at {datetime.now()}") - down_services = [check['name'] for check in results if not check['status']] - if down_services: - logging.warning(f"Services currently down: {', '.join(down_services)}") + + for group in results: + group_down = [check['name'] for check in results[group] if not check['status']] + down_services += group_down except Exception as e: logging.error(f"An error occurred: {str(e)}") - if MONITOR_CONTINOUSLY == False: + # down_services = [group[check]['name'] for check in group in results if not check['status']] + if down_services: + logging.warning(f"Services currently down: {', '.join(down_services)}") + + if not MONITOR_CONTINOUSLY: return await asyncio.sleep(CHECK_INTERVAL) + # Main function def main(): with open(CHECKS_FILE, 'r') as f: