generated from erangel1/generic-template
75 lines
2.0 KiB
Python
75 lines
2.0 KiB
Python
"""
|
|
Health check endpoint required by the docker-compose web service healthcheck:
|
|
test: ["CMD-SHELL", "curl -fs http://localhost:8000/api/health/ || exit 1"]
|
|
|
|
Does NOT require authentication — the docker daemon calls this, not users.
|
|
Returns HTTP 200 when all services are healthy, HTTP 503 on any failure.
|
|
"""
|
|
|
|
import logging
|
|
|
|
from django.core.cache import cache
|
|
from django.db import OperationalError, connection
|
|
from django.http import JsonResponse
|
|
from django.views import View
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
VERSION = "1.0.0"
|
|
|
|
|
|
class HealthCheckView(View):
|
|
"""
|
|
Synchronous health check for web, database, and Redis.
|
|
|
|
Response body:
|
|
{
|
|
"status": "ok" | "degraded",
|
|
"version": "1.0.0",
|
|
"services": {
|
|
"database": "ok" | "error",
|
|
"redis": "ok" | "error"
|
|
}
|
|
}
|
|
"""
|
|
|
|
def get(self, request) -> JsonResponse: # type: ignore[override]
|
|
db_status = _check_database()
|
|
redis_status = _check_redis()
|
|
|
|
all_ok = db_status == "ok" and redis_status == "ok"
|
|
http_status = 200 if all_ok else 503
|
|
|
|
return JsonResponse(
|
|
{
|
|
"status": "ok" if all_ok else "degraded",
|
|
"version": VERSION,
|
|
"services": {
|
|
"database": db_status,
|
|
"redis": redis_status,
|
|
},
|
|
},
|
|
status=http_status,
|
|
)
|
|
|
|
|
|
def _check_database() -> str:
|
|
try:
|
|
with connection.cursor() as cursor:
|
|
cursor.execute("SELECT 1")
|
|
return "ok"
|
|
except OperationalError:
|
|
logger.exception("Database health check failed")
|
|
return "error"
|
|
|
|
|
|
def _check_redis() -> str:
|
|
try:
|
|
cache.set("_health_check", "1", timeout=5)
|
|
if cache.get("_health_check") != "1":
|
|
raise RuntimeError("Redis round-trip failed")
|
|
return "ok"
|
|
except Exception:
|
|
logger.exception("Redis health check failed")
|
|
return "error"
|