Skip to content

Commit 6ff8622

Browse files
committed
Implement /__error__ endpoint
1 parent 7297801 commit 6ff8622

File tree

10 files changed

+82
-2
lines changed

10 files changed

+82
-2
lines changed

src/dockerflow/django/middleware.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class DockerflowMiddleware(MiddlewareMixin):
3232
(re.compile(r"/__version__/?$"), views.version),
3333
(re.compile(r"/__heartbeat__/?$"), views.heartbeat),
3434
(re.compile(r"/__lbheartbeat__/?$"), views.lbheartbeat),
35+
(re.compile(r"/__error__/?$"), views.error),
3536
]
3637

3738
def __init__(self, get_response=None, *args, **kwargs):

src/dockerflow/django/views.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,11 @@ def heartbeat(request):
7575
payload["checks"] = check_results.statuses
7676
payload["details"] = check_results.details
7777
return JsonResponse(payload, status=status_code)
78+
79+
80+
def error(request):
81+
"""
82+
A view that raises an exception, used to test error handling.
83+
"""
84+
logger.error("The __error__ endpoint was called")
85+
raise Exception("This is a test exception from the /__error__ endpoint.")

src/dockerflow/fastapi/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
from fastapi import APIRouter
22
from fastapi.routing import APIRoute
33

4-
from .views import heartbeat, lbheartbeat, version
4+
from .views import error, heartbeat, lbheartbeat, version
55

66
router = APIRouter(
77
tags=["Dockerflow"],
88
routes=[
99
APIRoute("/__lbheartbeat__", endpoint=lbheartbeat, methods=["GET", "HEAD"]),
1010
APIRoute("/__heartbeat__", endpoint=heartbeat, methods=["GET", "HEAD"]),
1111
APIRoute("/__version__", endpoint=version, methods=["GET"]),
12+
APIRoute("/__error__", endpoint=error, methods=["GET"]),
1213
],
1314
)
1415
"""This router adds the Dockerflow views."""

src/dockerflow/fastapi/views.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
import os
23

34
from fastapi import Request, Response
@@ -6,6 +7,8 @@
67

78
from ..version import get_version
89

10+
logger = logging.getLogger(__name__)
11+
912

1013
def lbheartbeat():
1114
return {"status": "ok"}
@@ -42,3 +45,11 @@ def version(request: Request):
4245
else:
4346
root = "/app"
4447
return get_version(root)
48+
49+
50+
def error(request: Request):
51+
"""
52+
A view that raises an exception, used to test error handling.
53+
"""
54+
logger.error("The __error__ endpoint was called")
55+
raise Exception("This is a test exception from the /__error__ endpoint.")

src/dockerflow/flask/app.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ def init_app(self, app):
177177
("/__version__", "version", self._version_view),
178178
("/__heartbeat__", "heartbeat", self._heartbeat_view),
179179
("/__lbheartbeat__", "lbheartbeat", self._lbheartbeat_view),
180+
("/__error__", "error", self._error_view),
180181
):
181182
self._blueprint.add_url_rule(*view)
182183
self._blueprint.before_app_request(self._before_request)
@@ -346,6 +347,13 @@ def render(status_code):
346347
heartbeat_failed.send(self, level=check_results.level)
347348
raise HeartbeatFailure(response=render(status_code))
348349

350+
def _error_view(self):
351+
"""
352+
A view that raises an exception, used to test error handling.
353+
"""
354+
self.logger.error("The __error__ endpoint was called")
355+
raise Exception("This is a test exception from the /__error__ endpoint.")
356+
349357
def version_callback(self, func):
350358
"""
351359
A decorator to optionally register a new Dockerflow version callback

src/dockerflow/sanic/app.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def init_app(self, app):
130130
("/__version__", "version", self._version_view),
131131
("/__heartbeat__", "heartbeat", self._heartbeat_view),
132132
("/__lbheartbeat__", "lbheartbeat", self._lbheartbeat_view),
133+
("/__error__", "error", self._error_view),
133134
):
134135
app.add_route(handler, uri, name="dockerflow." + name)
135136
app.middleware("request")(extract_request_id)
@@ -200,6 +201,13 @@ async def _version_view(self, request):
200201
else:
201202
return response.json(version_json)
202203

204+
async def _error_view(self, request):
205+
"""
206+
A view that raises an exception, used to test error handling.
207+
"""
208+
self.logger.error("The __error__ endpoint was called")
209+
raise Exception("This is a test exception from the /__error__ endpoint.")
210+
203211
async def _lbheartbeat_view(self, request):
204212
"""
205213
Lets the load balancer know the application is running and available.

tests/django/test_django.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,17 @@ def test_lbheartbeat_makes_no_db_queries(dockerflow_middleware, rf):
134134
assert len(queries) == 0
135135

136136

137+
def test_error_returns_500_and_logs_error(dockerflow_middleware, rf, caplog):
138+
request = rf.get("/__error__")
139+
with caplog.at_level(logging.INFO, logger="dockerflow.django"):
140+
with pytest.raises(Exception, match="__error__ endpoint"):
141+
dockerflow_middleware.process_request(request)
142+
assert len(caplog.records) == 1
143+
record = caplog.records[0]
144+
assert record.getMessage() == "The __error__ endpoint was called"
145+
assert record.levelno == logging.ERROR
146+
147+
137148
@pytest.mark.django_db()
138149
def test_redis_check(client, settings):
139150
settings.DOCKERFLOW_CHECKS = ["dockerflow.django.checks.check_redis_connected"]

tests/fastapi/test_fastapi.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,14 @@ def return_error():
262262

263263
response = client.get("/__heartbeat__")
264264
assert response.json()["checks"]["my_check_name"]
265+
266+
267+
def test_error_returns_500_and_logs_error(caplog):
268+
client = TestClient(app, raise_server_exceptions=False)
269+
response = client.get("/__error__")
270+
assert response.status_code == 500
271+
assert len(caplog.records) == 1
272+
record = caplog.records[0]
273+
assert record.name == "dockerflow.fastapi.views"
274+
assert record.getMessage() == "The __error__ endpoint was called"
275+
assert record.levelno == logging.ERROR

tests/flask/test_flask.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,17 @@ def test_lbheartbeat_makes_no_db_queries(dockerflow, app):
198198
assert len(get_recorded_queries()) == 0
199199

200200

201+
def test_error_returns_500_and_logs_error(dockerflow, app, caplog):
202+
with app.app_context():
203+
with caplog.at_level(logging.INFO, logger="dockerflow.flask"):
204+
response = app.test_client().get("/__error__")
205+
assert response.status_code == 500
206+
assert len(caplog.records) >= 1
207+
record = caplog.records[0]
208+
assert record.getMessage() == "The __error__ endpoint was called"
209+
assert record.levelno == logging.ERROR
210+
211+
201212
def test_full_redis_check(mocker):
202213
app = Flask("redis-check")
203214
app.debug = True

tests/sanic/test_sanic.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,16 @@ def test_lbheartbeat(dockerflow, test_client):
136136
assert response.body == b""
137137

138138

139+
def test_error_returns_500_and_logs_error(dockerflow, test_client, caplog):
140+
with caplog.at_level(logging.INFO, logger="dockerflow.sanic"):
141+
_, response = test_client.get("/__error__")
142+
assert response.status_code == 500
143+
assert len(caplog.records) >= 1
144+
record = caplog.records[0]
145+
assert record.getMessage() == "The __error__ endpoint was called"
146+
assert record.levelno == logging.ERROR
147+
148+
139149
def test_heartbeat(dockerflow, test_client):
140150
_, response = test_client.get("/__heartbeat__")
141151
assert response.status == 200
@@ -214,7 +224,7 @@ def test_redis_check(dockerflow_redis, mocker, test_client):
214224
[
215225
(
216226
"connection",
217-
{health.ERROR_CANNOT_CONNECT_REDIS: "Could not connect to " "redis: fake"},
227+
{health.ERROR_CANNOT_CONNECT_REDIS: "Could not connect to redis: fake"},
218228
),
219229
("redis", {health.ERROR_REDIS_EXCEPTION: 'Redis error: "fake"'}),
220230
("malformed", {health.ERROR_REDIS_PING_FAILED: "Redis ping failed"}),

0 commit comments

Comments
 (0)