From 11ad5169f7106c9fc02c93ed518eb80aa6fe463d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9line=20MS?= Date: Mon, 18 Mar 2024 14:03:49 +0100 Subject: [PATCH] Patch: apply health_check commit without the commit --- config/settings/base.py | 2 ++ itou/www/middleware.py | 36 ++++++++++++++++++++++++++++++++++ tests/www/test_check_health.py | 21 ++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 tests/www/test_check_health.py diff --git a/config/settings/base.py b/config/settings/base.py index ddcf960bf94..596e2e7fa00 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -109,6 +109,8 @@ MIDDLEWARE = [ # Generate request Id "django_datadog_logger.middleware.request_id.RequestIdMiddleware", + # Itou health check for Clever Cloud, don’t require requests to match ALLOWED_HOSTS + "itou.www.middleware.public_health_check", # Django stack "django.middleware.gzip.GZipMiddleware", "django.middleware.security.SecurityMiddleware", diff --git a/itou/www/middleware.py b/itou/www/middleware.py index cbcc78712d8..897557c9733 100644 --- a/itou/www/middleware.py +++ b/itou/www/middleware.py @@ -1,3 +1,8 @@ +import sentry_sdk +from django.core.cache import cache +from django.core.files.storage import default_storage +from django.db import connection +from django.http.response import HttpResponse, HttpResponseServerError from django.utils.cache import add_never_cache_headers @@ -9,3 +14,34 @@ def middleware(request): return response return middleware + + +def public_health_check(get_response): + def middleware(request): + """ + Bypass ALLOWED_HOSTS checks + CleverCloud probes access this path through IP directly, we don’t want to serve + a 400 BadRequest because the request Host header is not in the ALLOWED_HOSTS. + """ + if request.path == "/check-health": + try: + with connection.cursor() as c: + c.execute("SELECT 'check-database-connection'") + cache.get("check-cache-connection") + default_storage.exists("check-s3-file-access.txt") + body = b"Healthy\n" + return HttpResponse( + body, + content_type="text/plain", + charset="utf-8", + # CommonMiddleware is later in the middleware chain, and it checks ALLOWED_HOSTS. + headers={ + "Content-Length": str(len(body)), + }, + ) + except Exception as e: + sentry_sdk.capture_exception(e) + return HttpResponseServerError() + return get_response(request) + + return middleware diff --git a/tests/www/test_check_health.py b/tests/www/test_check_health.py new file mode 100644 index 00000000000..322e10d904b --- /dev/null +++ b/tests/www/test_check_health.py @@ -0,0 +1,21 @@ +class TestCheckHealth: + def test_get(self, client): + response = client.get("/check-health") + assert response.status_code == 200 + assert response.charset == "utf-8" + assert response["Content-Type"] == "text/plain" + assert response["Content-Length"] == "8" + assert response.content.decode() == "Healthy\n" + + def test_get_as_clever(self, client): + response = client.get( + "/check-health", + # CleverCloud probes connect directly through the IP, their HOST is not in the ALLOWED_HOSTS. + HTTP_HOST="10.2.2.2", + ) + assert response.status_code == 200 + + def test_get_with_error(self, client, mocker): + mocker.patch("itou.www.middleware.connection.cursor", side_effect=Exception("Boom!")) + response = client.get("/check-health") + assert response.status_code == 500