diff --git a/README.md b/README.md index 87680e86..22f45850 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,14 @@ Add the `health_check` applications to your `INSTALLED_APPS`: **Note:** If using `boto 2.x.x` use `health_check.contrib.s3boto_storage` +If you want to customize some settings, you can do so by adding a `HEALTH_CHECK` dictionary to your `settings.py` file: + +```python + HEALTH_CHECK = { + 'STORAGE_DIR': 'another-dir-under-my-project/', # needs to be in a permitted directory like MEDIA_ROOT + } +``` + (Optional) If using the `psutil` app, you can configure disk and memory threshold settings; otherwise below defaults are assumed. If you want to disable one of these checks, set its value to `None`. diff --git a/health_check/conf.py b/health_check/conf.py index 10d0c48c..207be756 100644 --- a/health_check/conf.py +++ b/health_check/conf.py @@ -6,3 +6,4 @@ HEALTH_CHECK.setdefault("WARNINGS_AS_ERRORS", True) HEALTH_CHECK.setdefault("SUBSETS", {}) HEALTH_CHECK.setdefault("DISABLE_THREADING", False) +HEALTH_CHECK.setdefault("STORAGE_DIR", "") diff --git a/health_check/storage/backends.py b/health_check/storage/backends.py index 4c0e1cbc..25c23949 100644 --- a/health_check/storage/backends.py +++ b/health_check/storage/backends.py @@ -1,11 +1,17 @@ +import os import uuid +from django.conf import settings + +from health_check.conf import HEALTH_CHECK from django.core.files.base import ContentFile from django.core.files.storage import InvalidStorageError, default_storage, storages from health_check.backends import BaseHealthCheckBackend from health_check.exceptions import ServiceUnavailable +STORAGE_DIR = HEALTH_CHECK["STORAGE_DIR"] + class StorageHealthCheck(BaseHealthCheckBackend): """ @@ -31,7 +37,14 @@ def get_storage(self): return None def get_file_name(self): - return f"health_check_storage_test/test-{uuid.uuid4()}.txt" + """Return a unique file name for the health check. + + It needs to be a relative path to the storage directory to ensure + that the file can be created in the storage backend.""" + relative_file_path = f"health_check_storage_test/test-{uuid.uuid4()}.txt" + if STORAGE_DIR: + return os.path.relpath(f"{STORAGE_DIR}/{relative_file_path}", settings.BASE_DIR) + return relative_file_path def get_file_content(self): return b"this is the healthtest file content" diff --git a/tests/test_storage.py b/tests/test_storage.py index 90142ba8..ee526b0d 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -1,8 +1,10 @@ +import tempfile import unittest from io import BytesIO from unittest import mock import django +from django.conf import settings from django.core.files.base import File from django.core.files.storage import Storage from django.test import TestCase, override_settings @@ -14,6 +16,8 @@ StorageHealthCheck, ) +original_get_file_name = StorageHealthCheck.get_file_name + class CustomStorage(Storage): pass @@ -105,8 +109,7 @@ def exists(self, name): return name in self.files -def get_file_name(*args, **kwargs): - return "mockfile.txt" +get_file_name = mock.MagicMock(return_value="mockfile.txt") def get_file_content(*args, **kwargs): @@ -167,6 +170,27 @@ def test_check_status_working(self): ): self.assertTrue(default_storage_health.check_status()) + def test_check_status_working_with_custom_dir(self): + default_storage = DefaultFileStorageHealthCheck() + # restore the original get_file_name method + get_file_name.side_effect = lambda *args, **kwargs: original_get_file_name(default_storage, *args, **kwargs) + + # check that it will be stored in the relative path + self.assertTrue(default_storage.check_status()) + expected_pattern = r"health_check_storage_test\/test.*\.txt" + self.assertRegex(original_get_file_name(default_storage), expected_pattern) + + tmp_dir = tempfile.mkdtemp(dir=settings.MEDIA_ROOT) + with mock.patch("health_check.storage.backends.STORAGE_DIR", tmp_dir): + # check that it will be stored in the custom path + default_storage = DefaultFileStorageHealthCheck() + get_file_name.side_effect = lambda *args, **kwargs: original_get_file_name(default_storage, *args, **kwargs) + self.assertTrue(default_storage.check_status()) + + # relative path in media directory + expected_pattern = r"media\/.*\/health_check_storage_test\/test.*\.txt" + self.assertRegex(original_get_file_name(default_storage), expected_pattern) + @mock.patch( "health_check.storage.backends.storages", {"default": MockStorage(saves=False)},