diff --git a/docs/settings.md b/docs/settings.md index dea89211..299fbfc4 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -75,11 +75,28 @@ HEALTH_CHECK = { With the above default settings, warnings will be reported when disk utilization exceeds 90% or available memory drops below 100 MB. +By default `psutil` assume the disk to check is available at `/` path. +To use a different disk e.g. when the app is deployed in a +container but the relevant disk is the host disk, update the dictionary +above to include `DISK_USAGE_PATH` setting. + +```python +HEALTH_CHECK = { + # existing settings + "DISK_USAGE_PATH": "/host/", # string path to disk +} +``` + ### `DISK_USAGE_MAX` Specify the desired disk utilization threshold, in percent. When disk usage exceeds the specified value, a warning will be reported. +### `DISK_USAGE_PATH` + +Specify the path to the desired disk for which utilization needs to be +checked. + ### `MEMORY_MIN` Specify the desired memory utilization threshold, in megabytes. When diff --git a/health_check/conf.py b/health_check/conf.py index 10d0c48c..eed7d8f9 100644 --- a/health_check/conf.py +++ b/health_check/conf.py @@ -2,6 +2,7 @@ HEALTH_CHECK = getattr(settings, "HEALTH_CHECK", {}) HEALTH_CHECK.setdefault("DISK_USAGE_MAX", 90) +HEALTH_CHECK.setdefault("DISK_USAGE_PATH", "/") HEALTH_CHECK.setdefault("MEMORY_MIN", 100) HEALTH_CHECK.setdefault("WARNINGS_AS_ERRORS", True) HEALTH_CHECK.setdefault("SUBSETS", {}) diff --git a/health_check/contrib/psutil/backends.py b/health_check/contrib/psutil/backends.py index ada96da1..7c1e4911 100644 --- a/health_check/contrib/psutil/backends.py +++ b/health_check/contrib/psutil/backends.py @@ -10,13 +10,14 @@ host = socket.gethostname() DISK_USAGE_MAX = HEALTH_CHECK["DISK_USAGE_MAX"] +DISK_USAGE_PATH = HEALTH_CHECK["DISK_USAGE_PATH"] MEMORY_MIN = HEALTH_CHECK["MEMORY_MIN"] class DiskUsage(BaseHealthCheckBackend): def check_status(self): try: - du = psutil.disk_usage("/") + du = psutil.disk_usage(DISK_USAGE_PATH) if DISK_USAGE_MAX and du.percent >= DISK_USAGE_MAX: raise ServiceWarning(f"{host} {du.percent}% disk usage exceeds {DISK_USAGE_MAX}%") except ValueError as e: diff --git a/pyproject.toml b/pyproject.toml index a02eff72..1bbffc1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,6 +61,7 @@ test = [ "pytest-cov", "pytest-django", "celery", + "psutil", "redis", "django-storages", "boto3", diff --git a/tests/test_psutil.py b/tests/test_psutil.py new file mode 100644 index 00000000..ae006df6 --- /dev/null +++ b/tests/test_psutil.py @@ -0,0 +1,43 @@ +from collections import namedtuple +from unittest import mock + +import pytest + +from health_check.contrib.psutil.backends import DiskUsage + +# psutil.disk_usage() returns a namedtuple +sdiskusage = namedtuple("sdiskusage", ("total", "used", "free", "percent")) + + +def mock_disk_usage(path): + """Mock function to simulate psutil.disk_usage() in a container/host environment.""" + if path == "/": + # Default path (container) with high disk space + return sdiskusage(total=100, used=97, free=3, percent=97) + elif path == "/host/": + # Custom path (host) with low disk usage + return sdiskusage(total=100, used=5, free=95, percent=5) + else: + # No other disk in the system + raise FileNotFoundError(f"No such file or directory: '{path}'") + + +@mock.patch("health_check.contrib.psutil.backends.psutil.disk_usage", mock_disk_usage) +class TestDiskUsage: + def test_default_path(self): + du = DiskUsage() + du.run_check() + assert du.errors + assert "97% disk usage exceeds 90%" in str(du.errors[0]) + + @mock.patch.multiple("health_check.contrib.psutil.backends", DISK_USAGE_PATH="/host/") + def test_custom_path_when_valid(self): + du = DiskUsage() + du.run_check() + assert not du.errors + + @mock.patch.multiple("health_check.contrib.psutil.backends", DISK_USAGE_PATH="*NOT A PATH*") + def test_custom_path_when_invalid(self): + du = DiskUsage() + with pytest.raises(FileNotFoundError): + du.run_check() diff --git a/tests/testapp/settings.py b/tests/testapp/settings.py index cf95982c..0f8577b4 100644 --- a/tests/testapp/settings.py +++ b/tests/testapp/settings.py @@ -28,6 +28,7 @@ "health_check.storage", "health_check.contrib.celery", "health_check.contrib.migrations", + "health_check.contrib.psutil", "health_check.contrib.celery_ping", "health_check.contrib.s3boto_storage", "health_check.contrib.db_heartbeat",