#!/usr/bin/env python3 """ Healthcheck script for Puppeteer API Monitors the API endpoint and restarts the container if it fails """ import os import time import logging import requests import docker from urllib.parse import quote from datetime import datetime # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(), logging.FileHandler('/app/healthcheck.log') ] ) logger = logging.getLogger(__name__) class PuppeteerHealthcheck: def __init__(self): self.base_url = os.getenv('BASE_URL', 'https://puppeteer.workwithkora.com') self.test_url = os.getenv('TEST_URL', 'https://www.google.com') self.api_key = os.getenv('API_KEY', 'Q7Sd#hhFkyHy*T') self.target_container = os.getenv('TARGET_CONTAINER', 'puppeteer-api') self.check_interval = int(os.getenv('CHECK_INTERVAL', '60')) # Initialize Docker client try: self.docker_client = docker.from_env() logger.info("Docker client initialized successfully") except Exception as e: logger.error(f"Failed to initialize Docker client: {e}") raise logger.info(f"Healthcheck initialized with:") logger.info(f" Base URL: {self.base_url}") logger.info(f" Test URL: {self.test_url}") logger.info(f" Target Container: {self.target_container}") logger.info(f" Check Interval: {self.check_interval} seconds") def perform_health_check(self): """Perform the health check by making a request to the puppeteer API""" try: # Construct the URL with the test URL as a parameter encoded_test_url = quote(self.test_url, safe='') url = f"{self.base_url}/?url={encoded_test_url}" headers = { 'x-api-key': self.api_key, 'User-Agent': 'Puppeteer-Healthcheck/1.0' } logger.info(f"Performing health check: {url}") response = requests.get( url, headers=headers, timeout=30, allow_redirects=True ) if response.status_code == 200: logger.info("Health check passed - API is responding correctly") return True else: logger.warning(f"Health check failed - Status code: {response.status_code}") logger.warning(f"Response: {response.text[:200]}...") return False except requests.exceptions.RequestException as e: logger.error(f"Health check failed - Request error: {e}") return False except Exception as e: logger.error(f"Health check failed - Unexpected error: {e}") return False def restart_container(self): """Restart the target puppeteer-api container""" try: logger.info(f"Attempting to restart container: {self.target_container}") # Get the container container = self.docker_client.containers.get(self.target_container) # Restart the container container.restart(timeout=30) logger.info(f"Successfully restarted container: {self.target_container}") return True except docker.errors.NotFound: logger.error(f"Container not found: {self.target_container}") return False except docker.errors.APIError as e: logger.error(f"Docker API error while restarting container: {e}") return False except Exception as e: logger.error(f"Unexpected error while restarting container: {e}") return False def run(self): """Main loop for the healthcheck""" logger.info("Starting Puppeteer API healthcheck service") consecutive_failures = 0 max_consecutive_failures = 3 # Restart after 3 consecutive failures while True: try: # Perform health check if self.perform_health_check(): consecutive_failures = 0 logger.info("Health check passed - resetting failure counter") else: consecutive_failures += 1 logger.warning(f"Health check failed - consecutive failures: {consecutive_failures}") # Restart container if we've had too many consecutive failures if consecutive_failures >= max_consecutive_failures: logger.error(f"Health check failed {consecutive_failures} times consecutively - restarting container") if self.restart_container(): consecutive_failures = 0 logger.info("Container restarted successfully - resetting failure counter") else: logger.error("Failed to restart container") # Wait before next check logger.info(f"Waiting {self.check_interval} seconds before next health check...") time.sleep(self.check_interval) except KeyboardInterrupt: logger.info("Healthcheck service stopped by user") break except Exception as e: logger.error(f"Unexpected error in main loop: {e}") time.sleep(self.check_interval) def main(): """Main entry point""" try: healthcheck = PuppeteerHealthcheck() healthcheck.run() except Exception as e: logger.error(f"Failed to start healthcheck service: {e}") exit(1) if __name__ == "__main__": main()