Skip to content

Commit ee91632

Browse files
authored
Upgrade django-health-check from pinned git commit to PyPI 3.24.0 (#3556)
* Upgrade django-health-check from pinned git commit to PyPI 3.24.0 Replaces the stale codingjoe/django-health-check@53f9bdc3 (dev build of 3.20.1) git dependency with the newest PyPI release compatible with Django==4.2.30: 3.24.0. 4.0.0+ requires Django>=5.2 so isn't available here yet. Verified the settings.py integration (bare check class names in HEALTH_CHECK["SUBSETS"], contrib.migrations/celery_ping/redis/db_heartbeat apps, health_check.urls include) is unaffected other than new deprecation warnings for APIs slated for removal in v4. * Fix silently-broken health check SUBSETS matching after 3.24.0 bump The previous commit upgraded django-health-check from a stale git pin to PyPI 3.24.0, which (unbeknownst at the time) silently broke this repo's HEALTH_CHECK["SUBSETS"] configuration: 3.24.0's health_check/__init__.py now does `Cache.__qualname__ = "Cache"` (and similarly for Database) as a global, process-wide mutation of the shared CacheBackend/ DatabaseHeartBeatCheck classes, so their dataclass-generated __repr__ no longer matches the bare class names ("CacheBackend", "DatabaseHeartBeatCheck") used in SUBSETS. Confirmed empirically: only "MigrationsHealthCheck" (the one check with no reprable fields and no top-level alias) still matched; Cache, Database, Redis, and CeleryPing were silently excluded from every subset, meaning /health/liveness/, /health/readiness/, and most of /health/startup/ and /health/full/ were vacuously always-200 regardless of actual DB/cache/redis/celery status. This was a regression introduced by the dependency bump, not a pre-existing issue: the old pinned commit's health_check/__init__.py did no such aliasing. Also, every one of the old INSTALLED_APPS contrib sub-apps (health_check.contrib.{redis,db_heartbeat,celery_ping,migrations}) emits its own DeprecationWarning at app-ready time pointing at this exact fix: 'checks are now configured via HealthCheckView... add the appropriate check to your HealthCheckView.checks.' Replaces the whole SUBSETS + INSTALLED_APPS-registration mechanism with the explicit, non-fragile HealthCheckView(checks=[...]) API (already available in 3.24.0, not just v4), matching the pattern already used in mitxonline/micromasters/ocw-studio/mitxpro. Also fixes the CacheBackend check to target the "redis" cache alias instead of the always-available in-memory "default" alias, which made it a no-op regardless of the SUBSETS bug. Verified live via Django's test client: /health/liveness/ returns 200 {"Database(alias='default')": "OK"} against sqlite, and /health/readiness/ correctly returns a 500 ServiceUnavailable when pointed at an unreachable Redis instead of silently passing. * Add bare /health/ route and URL-resolution tests Addresses Copilot review feedback: the rewritten urls_healthcheck.py only defined /health/{startup,liveness,readiness,full}/ subset routes, dropping the bare /health/ index that include("health_check.urls") previously provided. Added it back, mapped to the full check list. Also adds main/urls_healthcheck_test.py asserting all five paths resolve to HealthCheckView. Resolves directly against the main.urls_healthcheck sub-urlconf (via get_resolver) rather than django.urls.resolve() against the full project urlconf, since the latter transitively imports the tika client (via learning_resources' ETL utils) for the first time in a fresh test process, which triggers a pkg_resources.declare_namespace() DeprecationWarning that this repo's autouse warnings-as-errors fixture turns into a failure -- a pre-existing, unrelated issue in the tika dependency, not something to paper over by scoping down what this test actually needs to exercise.
1 parent 0bca7e4 commit ee91632

6 files changed

Lines changed: 89 additions & 47 deletions

File tree

main/settings.py

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -140,48 +140,10 @@
140140
"ol_hubspot",
141141
"mitol.scim.apps.ScimApp",
142142
"health_check",
143-
"health_check.cache",
144-
"health_check.contrib.migrations",
145-
"health_check.contrib.celery_ping",
146-
"health_check.contrib.redis",
147-
"health_check.contrib.db_heartbeat",
148143
)
149144

150145
WEBHOOK_SECRET = get_string("WEBHOOK_SECRET", "please-change-this")
151146

152-
HEALTH_CHECK = {
153-
"SUBSETS": {
154-
# The 'startup' subset includes checks that must pass before the application can
155-
# start.
156-
"startup": [
157-
"MigrationsHealthCheck", # Ensures database migrations are applied.
158-
"CacheBackend", # Verifies the cache backend is operational.
159-
"RedisHealthCheck", # Confirms Redis is reachable and functional.
160-
"DatabaseHeartBeatCheck", # Checks the database connection is alive.
161-
],
162-
# The 'liveness' subset includes checks to determine if the application is
163-
# running.
164-
"liveness": ["DatabaseHeartBeatCheck"], # Minimal check to ensure the app is
165-
# alive.
166-
# The 'readiness' subset includes checks to determine if the application is
167-
# ready to serve requests.
168-
"readiness": [
169-
"CacheBackend", # Ensures the cache is ready for use.
170-
"RedisHealthCheck", # Confirms Redis is ready for use.
171-
"DatabaseHeartBeatCheck", # Verifies the database is ready for queries.
172-
],
173-
# The 'full' subset includes all available health checks for a comprehensive
174-
# status report.
175-
"full": [
176-
"MigrationsHealthCheck", # Ensures database migrations are applied.
177-
"CacheBackend", # Verifies the cache backend is operational.
178-
"RedisHealthCheck", # Confirms Redis is reachable and functional.
179-
"DatabaseHeartBeatCheck", # Checks the database connection is alive.
180-
"CeleryPingHealthCheck", # Verifies Celery workers are responsive.
181-
],
182-
}
183-
}
184-
185147
if not get_bool("RUN_DATA_MIGRATIONS", default=False):
186148
MIGRATION_MODULES = {"data_fixtures": None}
187149

main/urls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from django.conf import settings
1818
from django.conf.urls.static import static
1919
from django.contrib import admin
20-
from django.urls import include, re_path
20+
from django.urls import include, path, re_path
2121
from django.views.generic.base import RedirectView
2222
from rest_framework.routers import DefaultRouter
2323

@@ -60,7 +60,7 @@
6060
re_path(r"", include("webhooks.urls", namespace="webhooks")),
6161
re_path(r"", include(features_router.urls)),
6262
re_path(r"^app", RedirectView.as_view(url=settings.APP_BASE_URL)),
63-
re_path(r"^health/", include("health_check.urls")),
63+
path("", include("main.urls_healthcheck")),
6464
]
6565
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
6666
+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

main/urls_healthcheck.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""Healthcheck urls"""
2+
3+
from django.urls import include, path
4+
from health_check.views import HealthCheckView
5+
6+
MIGRATIONS_CHECK = "health_check.contrib.migrations.backends.MigrationsHealthCheck"
7+
8+
BASE_CHECKS = [
9+
# "default" is in-memory and always available; "redis" is the real backing cache.
10+
("health_check.Cache", {"alias": "redis"}),
11+
"health_check.Database",
12+
"health_check.contrib.redis.Redis",
13+
]
14+
15+
urlpatterns = [
16+
path(
17+
"health/",
18+
include(
19+
[
20+
path(
21+
"",
22+
HealthCheckView.as_view(
23+
checks=[
24+
*BASE_CHECKS,
25+
MIGRATIONS_CHECK,
26+
"health_check.contrib.celery.Ping",
27+
]
28+
),
29+
),
30+
path(
31+
"startup/",
32+
HealthCheckView.as_view(checks=[*BASE_CHECKS, MIGRATIONS_CHECK]),
33+
),
34+
path(
35+
"liveness/",
36+
HealthCheckView.as_view(checks=["health_check.Database"]),
37+
),
38+
path(
39+
"readiness/",
40+
HealthCheckView.as_view(checks=[*BASE_CHECKS]),
41+
),
42+
path(
43+
"full/",
44+
HealthCheckView.as_view(
45+
checks=[
46+
*BASE_CHECKS,
47+
MIGRATIONS_CHECK,
48+
"health_check.contrib.celery.Ping",
49+
]
50+
),
51+
),
52+
]
53+
),
54+
),
55+
]

main/urls_healthcheck_test.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""Tests for the healthcheck urls"""
2+
3+
import pytest
4+
from django.urls import get_resolver
5+
from health_check.views import HealthCheckView
6+
7+
8+
@pytest.mark.parametrize(
9+
"path",
10+
[
11+
"/health/",
12+
"/health/startup/",
13+
"/health/liveness/",
14+
"/health/readiness/",
15+
"/health/full/",
16+
],
17+
)
18+
def test_healthcheck_urls_resolve(path):
19+
"""All healthcheck endpoints should resolve to HealthCheckView"""
20+
# Resolve directly against this urlconf module rather than django.urls.resolve()
21+
# (which walks the full project urlconf) to avoid pulling in unrelated apps.
22+
match = get_resolver("main.urls_healthcheck").resolve(path)
23+
assert match.func.view_class is HealthCheckView

pyproject.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ dependencies = [
3232
"django-cors-headers>=4.0.0,<5",
3333
"django-filter>=2.4.0,<3",
3434
"django-guardian>=3.0.0,<4",
35-
"django-health-check",
35+
"django-health-check>=3.24.0,<4",
3636
"django-imagekit>=6.0.0,<7",
3737
"django-ipware>=7.0.0,<8",
3838
"django-json-widget>=2.0.0,<3",
@@ -154,9 +154,6 @@ package = false
154154
default-groups = "all"
155155
override-dependencies = ["setuptools<80"]
156156

157-
[tool.uv.sources]
158-
django-health-check = { git = "https://github.com/revsys/django-health-check", rev = "53f9bdc3a7acc8a577319987fef0bd3040eef4b4" } # pragma: allowlist secret
159-
160157
[tool.uv.build-backend]
161158
module-root = ""
162159

uv.lock

Lines changed: 8 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)