Skip to content

Configuration Health

A module can be installed and still do nothing: an empty setting, a missing database row or a dummy API key turns a feature off without an error until a background task trips over it. Configuration health makes that state visible before it costs anything (in the admin CMS, in the API and on the command line).

  • Admin CMS: a warning triangle next to the notification bell, only while a check fails. The panel lists each problem with a How to fix link; the user menu opens it any time, with the passing checks as a green grid. A screen that depends on a check (for example Leads → Review) shows the same text as a banner.
  • API: GET /api/munin/v2/health/ (admin) returns the rows; POST /api/munin/v2/health/check/ adds live probes such as an SMTP login.
  • CLI / CI gate:
Terminal window
python manage.py check --database default --tag entirius_config --fail-level WARNING
python manage.py check --deploy --tag entirius_probe # live probes, on demand

Configuration health is state, not an event: nothing is marked read, and a fixed setting clears on the next read. Runtime failures (a send that bounced, an outage) still go to the notification bar.

A check is a plain Django system check; django-munin aggregates them.

RuleWhy
Register with the tag entirius_config and a second tag that is the check code, <module>.<check>the code names the row in the CMS and works as check --tag <code>
Return [] when healthy; one checks.Warning per failing subject otherwisea passing code shows as a green tick
Never checks.Errorrunserver and migrate abort on errors. A config gap must never stop a boot
Warning(title, hint=detail, id=code, obj={"scope", "state", "severity", "fix_url"})scope = channel idx or ""; state = unconfigured, recording (a source reads recorded answers instead of the real API), unreachable or auth_failed (munin adds error when a check itself raises); severity = high, medium or low; fix_url = the module docs anchor or a CMS path (/…)
Read the database only when databases is passed and your table existsboot passes none; the endpoint and --database default do, and so does migrate, before it has created anything
No network in a config checka probe is a separate check: tag entirius_probe, register(..., deploy=True), never raises; its failures show until the next “Check again”. One exception: toolbox.status keeps the toolbox client’s cached 60 s probe on every read, so it also runs at boot and in manage.py check
# django_<module>/checks.py
from django.conf import settings
from django.core import checks
CODE = "mymodule.api_key"
FIX_URL = "https://github.com/entirius/entirius-django-mymodule/blob/master/docs/configuration.md#api-key"
@checks.register("entirius_config", CODE)
def api_key_set(app_configs=None, **kwargs):
if settings.MYMODULE_API_KEY:
return []
return [
checks.Warning(
"API key not set",
hint="Set MYMODULE_API_KEY; until then the sync task skips every run.",
id=CODE,
obj={"scope": "", "state": "unconfigured", "severity": "high", "fix_url": FIX_URL},
)
]

Import the module from AppConfig.ready(). Then:

  1. Document the setting and the check command in the module’s own docs/. The fix_url lands there.
  2. Add the CMS text: config_health.checks.<code with _>.<state> and config_health.names.<code with _> in entirius-pwa-cms en.json / pl.json. Without it the CMS shows your English title.
  3. Test the check function directly: healthy → [], each failing state → one warning with scope and fix_url.

Checks shipped so far: toolbox.status (django-munin), email.smtp (django-email), communicator.smtp (django-communicator), siteintel.sources and the siteintel.keys probe (django-siteintel). Each module’s docs/ describes its own.

Leads End-to-End Testing