Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Add ES healthchecks to /healthcheck/ endpoint (#1047)
Browse files Browse the repository at this point in the history
* Add api_client fixture to conftest

* Add ES check to healthcheck endpoint

* Fix timeout format
  • Loading branch information
sarayourfriend authored Jan 4, 2023
1 parent afcd7b8 commit 316815e
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 16 deletions.
28 changes: 26 additions & 2 deletions api/catalog/api/views/health_views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
from django.conf import settings
from rest_framework import status
from rest_framework.exceptions import APIException
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView


class ElasticsearchHealthcheckException(APIException):
status_code = status.HTTP_503_SERVICE_UNAVAILABLE


class HealthCheck(APIView):
"""
Returns a "200 OK" response if the server is running normally.
Returns a "200 OK" response if the server is running normally. Returns 503
otherwise.
This endpoint is used in production to ensure that the server should receive
traffic. If no response is provided, the server is deregistered from the
Expand All @@ -13,5 +22,20 @@ class HealthCheck(APIView):

swagger_schema = None

def get(self, request, format=None):
def _check_es(self) -> Response | None:
"""
Checks Elasticsearch cluster health. Raises an exception if ES is not healthy.
"""
es_health = settings.ES.cluster.health(timeout="5s")

if es_health["timed_out"]:
raise ElasticsearchHealthcheckException("es_timed_out")

if (status := es_health["status"]) != "green":
raise ElasticsearchHealthcheckException(f"es_status_{status}")

def get(self, request: Request):
if "check_es" in request.query_params:
self._check_es()

return Response({"status": "200 OK"}, status=200)
8 changes: 8 additions & 0 deletions api/test/unit/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from rest_framework.test import APIClient

import pytest


@pytest.fixture
def api_client():
return APIClient()
51 changes: 51 additions & 0 deletions api/test/unit/views/health_views_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import pook
import pytest


def mock_health_response(status="green", timed_out=False):
return (
pook.get(pook.regex(r"_cluster\/health"))
.times(1)
.reply(200)
.json(
{
"status": status if not timed_out else None,
"timed_out": timed_out,
}
)
)


def test_health_check_plain(api_client):
res = api_client.get("/healthcheck/")
assert res.status_code == 200


def test_health_check_es_timed_out(api_client):
mock_health_response(timed_out=True)
pook.on()
res = api_client.get("/healthcheck/", data={"check_es": True})
pook.off()

assert res.status_code == 503
assert res.json()["detail"] == "es_timed_out"


@pytest.mark.parametrize("status", ("yellow", "red"))
def test_health_check_es_status_bad(status, api_client):
mock_health_response(status=status)
pook.on()
res = api_client.get("/healthcheck/", data={"check_es": True})
pook.off()

assert res.status_code == 503
assert res.json()["detail"] == f"es_status_{status}"


def test_health_check_es_all_good(api_client):
mock_health_response(status="green")
pook.on()
res = api_client.get("/healthcheck/", data={"check_es": True})
pook.off()

assert res.status_code == 200
7 changes: 0 additions & 7 deletions api/test/unit/views/image_views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
from pathlib import Path
from test.factory.models.image import ImageFactory

from rest_framework.test import APIClient

import pytest
from requests import Request, Response

Expand All @@ -17,11 +15,6 @@
_MOCK_IMAGE_INFO = json.loads((_MOCK_IMAGE_PATH / "sample-image-info.json").read_text())


@pytest.fixture
def api_client():
return APIClient()


@dataclass
class RequestsFixture:
requests: list[Request]
Expand Down
7 changes: 0 additions & 7 deletions api/test/unit/views/media_views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
from unittest import mock
from unittest.mock import MagicMock, patch

from rest_framework.test import APIClient

import pytest
import pytest_django.asserts
import requests as requests_lib
Expand All @@ -23,11 +21,6 @@
_MOCK_IMAGE_INFO = json.loads((_MOCK_IMAGE_PATH / "sample-image-info.json").read_text())


@pytest.fixture
def api_client():
return APIClient()


@dataclass
class SentRequest:
request: PreparedRequest
Expand Down

0 comments on commit 316815e

Please sign in to comment.