pax_global_header 0000666 0000000 0000000 00000000064 14305372012 0014507 g ustar 00root root 0000000 0000000 52 comment=f42257ae117b6fa18a7b4a2cfa1fb5efd3b824f9
django-health-check-3.17.0/ 0000775 0000000 0000000 00000000000 14305372012 0015357 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/.bandit 0000664 0000000 0000000 00000000030 14305372012 0016612 0 ustar 00root root 0000000 0000000 [bandit]
exclude: tests
django-health-check-3.17.0/.editorconfig 0000664 0000000 0000000 00000001305 14305372012 0020033 0 ustar 00root root 0000000 0000000 # http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.py]
indent_style = space
indent_size = 4
# isort config
atomic = true
multi_line_output = 5
line_length = 80
combine_as_imports = true
skip = wsgi.py,docs,env,.eggs
known_first_party = health_check,tests
known_third_party = django,celery,psutil
default_section=THIRDPARTY
not_skip = __init__.py
[*.{rst,ini}]
indent_style = space
indent_size = 4
[*.{yml,html,xml,xsl,json}]
indent_style = space
indent_size = 2
[*.{css,less}]
indent_style = space
indent_size = 2
[*.{js,coffee}]
indent_style = space
indent_size = 4
[Makefile]
indent_style = tab
indent_size = 1
django-health-check-3.17.0/.github/ 0000775 0000000 0000000 00000000000 14305372012 0016717 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/.github/FUNDING.yml 0000664 0000000 0000000 00000000072 14305372012 0020533 0 ustar 00root root 0000000 0000000 github: codingjoe
custom: https://www.paypal.me/codingjoe
django-health-check-3.17.0/.github/dependabot.yml 0000664 0000000 0000000 00000000263 14305372012 0021550 0 ustar 00root root 0000000 0000000 version: 2
updates:
- package-ecosystem: pip
directory: "/"
schedule:
interval: daily
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
django-health-check-3.17.0/.github/workflows/ 0000775 0000000 0000000 00000000000 14305372012 0020754 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/.github/workflows/ci.yml 0000664 0000000 0000000 00000003606 14305372012 0022077 0 ustar 00root root 0000000 0000000 name: CI
on:
push:
branches:
- main
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
strategy:
matrix:
lint-command:
- bandit -r . -x ./tests
- black --check --diff .
- flake8 .
- isort --check-only --diff .
- pydocstyle .
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.10"
cache: 'pip'
cache-dependency-path: 'linter-requirements.txt'
- run: python -m pip install -r linter-requirements.txt
- run: ${{ matrix.lint-command }}
dist:
runs-on: ubuntu-latest
needs: [lint]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.x"
- run: python -m pip install --upgrade pip build wheel twine
- run: python -m build --sdist --wheel
- run: python -m twine check dist/*
docs:
runs-on: ubuntu-latest
needs: [lint]
steps:
- uses: actions/checkout@v3
- name: setup Python
uses: actions/setup-python@v4
with:
python-version: "3.10"
- run: python -m pip install -e .[docs]
- run: python -m sphinx -b html -W docs docs/_build
PyTest:
runs-on: ubuntu-latest
needs: [lint]
strategy:
matrix:
python-version:
- "3.8"
- "3.9"
- "3.10"
django-version:
- "2.2"
- "4.0"
- "4.1"
steps:
- uses: actions/checkout@v3
- name: Setup Python version ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- run: python -m pip install .[test]
- run: python -m pip install Django~="${{ matrix.django-version }}.0"
- run: python -m pytest
- uses: codecov/codecov-action@v3
django-health-check-3.17.0/.github/workflows/release.yml 0000664 0000000 0000000 00000000721 14305372012 0023117 0 ustar 00root root 0000000 0000000 name: Release
on:
release:
types: [published]
jobs:
PyPi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.x"
- run: python -m pip install --upgrade pip build wheel twine
- run: python -m build --sdist --wheel
- run: python -m twine upload dist/*
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
django-health-check-3.17.0/.gitignore 0000664 0000000 0000000 00000002236 14305372012 0017352 0 ustar 00root root 0000000 0000000 # Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# pytest
.pytest_cache/
django-health-check-3.17.0/.readthedocs.yaml 0000664 0000000 0000000 00000000513 14305372012 0020605 0 ustar 00root root 0000000 0000000 # .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
version: 2
build:
os: ubuntu-20.04
tools:
python: "3.10"
sphinx:
configuration: docs/conf.py
python:
install:
- method: pip
path: .
extra_requirements:
- docs
django-health-check-3.17.0/LICENSE 0000664 0000000 0000000 00000002075 14305372012 0016370 0 ustar 00root root 0000000 0000000 Copyright (C) 2011-2019 Kristian Øllegaard and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
django-health-check-3.17.0/MANIFEST.in 0000664 0000000 0000000 00000000047 14305372012 0017116 0 ustar 00root root 0000000 0000000 include LICENSE
prune tests
prune docs
django-health-check-3.17.0/README.rst 0000664 0000000 0000000 00000023143 14305372012 0017051 0 ustar 00root root 0000000 0000000 ===================
django-health-check
===================
|version| |pyversion| |djversion| |coverage| |license|
This project checks for various conditions and provides reports when anomalous
behavior is detected.
The following health checks are bundled with this project:
- cache
- database
- storage
- disk and memory utilization (via ``psutil``)
- AWS S3 storage
- Celery task queue
- Celery ping
- RabbitMQ
- Migrations
Writing your own custom health checks is also very quick and easy.
We also like contributions, so don't be afraid to make a pull request.
Use Cases
---------
The primary intended use case is to monitor conditions via HTTP(S), with
responses available in HTML and JSON formats. When you get back a response that
includes one or more problems, you can then decide the appropriate course of
action, which could include generating notifications and/or automating the
replacement of a failing node with a new one. If you are monitoring health in a
high-availability environment with a load balancer that returns responses from
multiple nodes, please note that certain checks (e.g., disk and memory usage)
will return responses specific to the node selected by the load balancer.
Supported Versions
------------------
We officially only support the latest version of Python as well as the
latest version of Django and the latest Django LTS version.
Installation
------------
First install the ``django-health-check`` package:
.. code::
pip install django-health-check
Add the health checker to a URL you want to use:
.. code:: python
urlpatterns = [
# ...
url(r'^ht/', include('health_check.urls')),
]
Add the ``health_check`` applications to your ``INSTALLED_APPS``:
.. code:: python
INSTALLED_APPS = [
# ...
'health_check', # required
'health_check.db', # stock Django health checkers
'health_check.cache',
'health_check.storage',
'health_check.contrib.migrations',
'health_check.contrib.celery', # requires celery
'health_check.contrib.celery_ping', # requires celery
'health_check.contrib.psutil', # disk and memory utilization; requires psutil
'health_check.contrib.s3boto3_storage', # requires boto3 and S3BotoStorage backend
'health_check.contrib.rabbitmq', # requires RabbitMQ broker
'health_check.contrib.redis', # requires Redis broker
]
Note : If using ``boto 2.x.x`` use ``health_check.contrib.s3boto_storage``
(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``.
.. code:: python
HEALTH_CHECK = {
'DISK_USAGE_MAX': 90, # percent
'MEMORY_MIN': 100, # in MB
}
If using the DB check, run migrations:
.. code::
django-admin migrate
To use the RabbitMQ healthcheck, please make sure that there is a variable named ``BROKER_URL``
on django.conf.settings with the required format to connect to your rabbit server. For example:
.. code::
BROKER_URL = amqp://myuser:mypassword@localhost:5672/myvhost
To use the Redis healthcheck, please make sure that there is a variable named ``REDIS_URL``
on django.conf.settings with the required format to connect to your redis server. For example:
.. code::
REDIS_URL = redis://localhost:6370
Setting up monitoring
---------------------
You can use tools like Pingdom_ or other uptime robots to monitor service status.
The ``/ht/`` endpoint will respond a HTTP 200 if all checks passed
and a HTTP 500 if any of the tests failed.
.. code::
$ curl -v -X GET -H http://www.example.com/ht/
> GET /ht/ HTTP/1.1
> Host: www.example.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=utf-8
System status
|
CacheBackend |
working |
|
DatabaseBackend |
working |
|
S3BotoStorageHealthCheck |
working |
Getting machine readable JSON reports
-------------------------------------
If you want machine readable status reports you can request the ``/ht/``
endpoint with the ``Accept`` HTTP header set to ``application/json``
or pass ``format=json`` as a query parameter.
The backend will return a JSON response:
.. code::
$ curl -v -X GET -H "Accept: application/json" http://www.example.com/ht/
> GET /ht/ HTTP/1.1
> Host: www.example.com
> Accept: application/json
>
< HTTP/1.1 200 OK
< Content-Type: application/json
{
"CacheBackend": "working",
"DatabaseBackend": "working",
"S3BotoStorageHealthCheck": "working"
}
$ curl -v -X GET http://www.example.com/ht/?format=json
> GET /ht/?format=json HTTP/1.1
> Host: www.example.com
>
< HTTP/1.1 200 OK
< Content-Type: application/json
{
"CacheBackend": "working",
"DatabaseBackend": "working",
"S3BotoStorageHealthCheck": "working"
}
Writing a custom health check
-----------------------------
Writing a health check is quick and easy:
.. code:: python
from health_check.backends import BaseHealthCheckBackend
class MyHealthCheckBackend(BaseHealthCheckBackend):
#: The status endpoints will respond with a 200 status code
#: even if the check errors.
critical_service = False
def check_status(self):
# The test code goes here.
# You can use `self.add_error` or
# raise a `HealthCheckException`,
# similar to Django's form validation.
pass
def identifier(self):
return self.__class__.__name__ # Display name on the endpoint.
After writing a custom checker, register it in your app configuration:
.. code:: python
from django.apps import AppConfig
from health_check.plugins import plugin_dir
class MyAppConfig(AppConfig):
name = 'my_app'
def ready(self):
from .backends import MyHealthCheckBackend
plugin_dir.register(MyHealthCheckBackend)
Make sure the application you write the checker into is registered in your ``INSTALLED_APPS``.
Customizing output
------------------
You can customize HTML or JSON rendering by inheriting from ``MainView`` in ``health_check.views``
and customizing the ``template_name``, ``get``, ``render_to_response`` and ``render_to_response_json`` properties:
.. code:: python
# views.py
from health_check.views import MainView
class HealthCheckCustomView(MainView):
template_name = 'myapp/health_check_dashboard.html' # customize the used templates
def get(self, request, *args, **kwargs):
plugins = []
status = 200 # needs to be filled status you need
# ...
if 'application/json' in request.META.get('HTTP_ACCEPT', ''):
return self.render_to_response_json(plugins, status)
return self.render_to_response(plugins, status)
def render_to_response(self, plugins, status): # customize HTML output
return HttpResponse('COOL' if status == 200 else 'SWEATY', status=status)
def render_to_response_json(self, plugins, status): # customize JSON output
return JsonResponse(
{str(p.identifier()): 'COOL' if status == 200 else 'SWEATY' for p in plugins},
status=status
)
# urls.py
import views
urlpatterns = [
# ...
url(r'^ht/$', views.HealthCheckCustomView.as_view(), name='health_check_custom'),
]
Django command
--------------
You can run the Django command `health_check` to perform your health checks via the command line,
or periodically with a cron, as follow:
.. code::
django-admin health_check
This should yield the following output:
.. code::
DatabaseHealthCheck ... working
CustomHealthCheck ... unavailable: Something went wrong!
Similar to the http version, a critical error will cause the command to quit with the exit code `1`.
Other resources
---------------
- django-watchman_ is a package that does some of the same things in a slightly different way.
- See this weblog_ about configuring Django and health checking with AWS Elastic Load Balancer.
.. |version| image:: https://img.shields.io/pypi/v/django-health-check.svg
:target: https://pypi.python.org/pypi/django-health-check/
.. |pyversion| image:: https://img.shields.io/pypi/pyversions/django-health-check.svg
:target: https://pypi.python.org/pypi/django-health-check/
.. |djversion| image:: https://img.shields.io/pypi/djversions/django-health-check.svg
:target: https://pypi.python.org/pypi/django-health-check/
.. |coverage| image:: https://codecov.io/gh/KristianOellegaard/django-health-check/branch/master/graph/badge.svg
:target: https://codecov.io/gh/KristianOellegaard/django-health-check
.. |license| image:: https://img.shields.io/badge/license-MIT-blue.svg
:target: LICENSE
.. _Pingdom: https://www.pingdom.com/
.. _django-watchman: https://github.com/mwarkentin/django-watchman
.. _weblog: https://www.vincit.fi/en/blog/deploying-django-to-elastic-beanstalk-with-https-redirects-and-functional-health-checks/
django-health-check-3.17.0/docs/ 0000775 0000000 0000000 00000000000 14305372012 0016307 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/docs/changelog.rst 0000664 0000000 0000000 00000000314 14305372012 0020766 0 ustar 00root root 0000000 0000000 ChangeLog
=========
This package is released on GitHub. Please refer to the GitHub
release page to review the changes in each version.
https://github.com/KristianOellegaard/django-health-check/releases
django-health-check-3.17.0/docs/conf.py 0000664 0000000 0000000 00000000340 14305372012 0017603 0 ustar 00root root 0000000 0000000 try:
import sphinx_rtd_theme
except ImportError:
sphinx_rtd_theme = None
master_doc = "index"
if sphinx_rtd_theme:
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
django-health-check-3.17.0/docs/contrib.rst 0000664 0000000 0000000 00000004115 14305372012 0020502 0 ustar 00root root 0000000 0000000 contrib
=======
``psutil``
----------
Full disks and out-of-memory conditions are common causes of service outages.
These situations can be averted by checking disk and memory utilization via the
``psutil`` package:
.. code::
pip install psutil
Once that dependency has been installed, make sure that the corresponding Django
app has been added to ``INSTALLED_APPS``:
.. code:: python
INSTALLED_APPS = [
# ...
'health_check', # required
'health_check.contrib.psutil', # disk and memory utilization; requires psutil
# ...
]
The following default settings will be used to check for disk and memory
utilization. If you would prefer different thresholds, you can add the dictionary
below to your Django settings file and adjust the values accordingly. If you want
to disable any of these checks, set its value to ``None``.
.. code:: python
HEALTH_CHECK = {
'DISK_USAGE_MAX': 90, # percent
'MEMORY_MIN' = 100, # in MB
}
``celery``
----------
If you are using Celery you may choose between two different Celery checks.
`health_check.contrib.celery` sends a task to the queue and it expects that task
to be executed in `HEALTHCHECK_CELERY_TIMEOUT` seconds which by default is three seconds.
You may override that in your Django settings module. This check is suitable for use cases
which require that tasks can be processed frequently all the time.
`health_check.contrib.celery_ping` is a different check. It checks that each predefined
Celery task queue has a consumer (i.e. worker) that responds `{"ok": "pong"}` in
`HEALTHCHECK_CELERY_PING_TIMEOUT` seconds. The default for this is one second.
You may override that in your Django settings module. This check is suitable for use cases
which don't require that tasks are executed almost instantly but require that they are going
to be executed in sometime the future i.e. that the worker process is alive and processing tasks
all the time.
You may also use both of them. To use these checks add them to `INSTALLED_APPS` in your
Django settings module.
django-health-check-3.17.0/docs/index.rst 0000664 0000000 0000000 00000000535 14305372012 0020153 0 ustar 00root root 0000000 0000000 django-health-check
-------------------
This project checks for various conditions and provides reports when anomalous
behavior is detected. Many of these checks involve connecting to back-end
services and ensuring basic operations are successful.
.. toctree::
:maxdepth: 2
:caption: Contents:
readme
contrib
settings
changelog
django-health-check-3.17.0/docs/readme.rst 0000664 0000000 0000000 00000000033 14305372012 0020272 0 ustar 00root root 0000000 0000000 .. include:: ../README.rst
django-health-check-3.17.0/docs/settings.rst 0000664 0000000 0000000 00000005375 14305372012 0020713 0 ustar 00root root 0000000 0000000 Settings
========
Settings can be configured via the ``HEALTH_CHECK`` dictionary.
.. data:: WARNINGS_AS_ERRORS
Treats :class:`ServiceWarning` as errors, meaning they will cause the views
to respond with a 500 status code. Default is ``True``. If set to
``False`` warnings will be displayed in the template on in the JSON
response but the status code will remain a 200.
Security
--------
Django health check can be used as a possible DOS attack vector as it can put
your system under a lot of stress. As a default the view is also not cached by
CDNs. Therefore we recommend to use a secure token to protect you application
servers from an attacker.
1. Setup HTTPS. Seriously...
2. Add a secure token to your URL.
Create a secure token:
.. code:: shell
python -c "import secrets; print(secrets.token_urlsafe())"
Add it to your URL:
.. code:: python
urlpatterns = [
# ...
url(r'^ht/super_secret_token/'), include('health_check.urls')),
]
You can still use any uptime bot that is URL based while enjoying token protection.
.. warning::
Do NOT use Django's `SECRET_KEY` setting. This should never be exposed,
to any third party. Not even your trusted uptime bot.
``psutil``
----------
The following default settings will be used to check for disk and memory
utilization. If you would prefer different thresholds, you can add the dictionary
below to your Django settings file and adjust the values accordingly. If you want
to disable any of these checks, set its value to ``None``.
.. code:: python
HEALTH_CHECK = {
'DISK_USAGE_MAX': 90, # percent
'MEMORY_MIN' = 100, # in MB
}
With the above default settings, warnings will be reported when disk utilization
exceeds 90% or available memory drops below 100 MB.
.. data:: DISK_USAGE_MAX
Specify the desired disk utilization threshold, in percent. When disk usage
exceeds the specified value, a warning will be reported.
.. data:: MEMORY_MIN
Specify the desired memory utilization threshold, in megabytes. When available
memory falls below the specified value, a warning will be reported.
Celery Health Check
----------------------
Using `django.settings` you may exert more fine-grained control over the behavior of the celery health check
.. list-table:: Additional Settings
:widths: 25 10 10 55
:header-rows: 1
* - Name
- Type
- Default
- Description
* - `HEALTHCHECK_CELERY_QUEUE_TIMEOUT`
- Number
- 3
- Specifies the maximum amount of time a task may spend in the queue before being automatically revoked with a `TaskRevokedError`.
* - `HEALTHCHECK_CELERY_RESULT_TIMEOUT`
- Number
- 3
- Specifies the maximum total time for a task to complete and return a result, including queue time.
django-health-check-3.17.0/health_check/ 0000775 0000000 0000000 00000000000 14305372012 0017761 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/__init__.py 0000664 0000000 0000000 00000000000 14305372012 0022060 0 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/backends.py 0000664 0000000 0000000 00000003252 14305372012 0022107 0 ustar 00root root 0000000 0000000 import logging
from timeit import default_timer as timer
from django.utils.translation import gettext_lazy as _ # noqa: N812
from health_check.exceptions import HealthCheckException
logger = logging.getLogger("health-check")
class BaseHealthCheckBackend:
critical_service = True
"""
Define if service is critical to the operation of the site.
If set to ``True`` service failures return 500 response code on the
health check endpoint.
"""
def __init__(self):
self.errors = []
def check_status(self):
raise NotImplementedError
def run_check(self):
start = timer()
self.errors = []
try:
self.check_status()
except HealthCheckException as e:
self.add_error(e, e)
except BaseException:
logger.exception("Unexpected Error!")
raise
finally:
self.time_taken = timer() - start
def add_error(self, error, cause=None):
if isinstance(error, HealthCheckException):
pass
elif isinstance(error, str):
msg = error
error = HealthCheckException(msg)
else:
msg = _("unknown error")
error = HealthCheckException(msg)
if isinstance(cause, BaseException):
logger.exception(str(error))
else:
logger.error(str(error))
self.errors.append(error)
def pretty_status(self):
if self.errors:
return "\n".join(str(e) for e in self.errors)
return _("working")
@property
def status(self):
return int(not self.errors)
def identifier(self):
return self.__class__.__name__
django-health-check-3.17.0/health_check/cache/ 0000775 0000000 0000000 00000000000 14305372012 0021024 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/cache/__init__.py 0000664 0000000 0000000 00000000160 14305372012 0023132 0 ustar 00root root 0000000 0000000 import django
if django.VERSION < (3, 2):
default_app_config = "health_check.cache.apps.HealthCheckConfig"
django-health-check-3.17.0/health_check/cache/apps.py 0000664 0000000 0000000 00000000537 14305372012 0022346 0 ustar 00root root 0000000 0000000 from django.apps import AppConfig
from django.conf import settings
from health_check.plugins import plugin_dir
class HealthCheckConfig(AppConfig):
name = "health_check.cache"
def ready(self):
from .backends import CacheBackend
for backend in settings.CACHES:
plugin_dir.register(CacheBackend, backend=backend)
django-health-check-3.17.0/health_check/cache/backends.py 0000664 0000000 0000000 00000002027 14305372012 0023151 0 ustar 00root root 0000000 0000000 from django.core.cache import CacheKeyWarning, caches
from health_check.backends import BaseHealthCheckBackend
from health_check.exceptions import ServiceReturnedUnexpectedResult, ServiceUnavailable
class CacheBackend(BaseHealthCheckBackend):
def __init__(self, backend="default"):
super().__init__()
self.backend = backend
def identifier(self):
return f"Cache backend: {self.backend}"
def check_status(self):
cache = caches[self.backend]
try:
cache.set("djangohealtcheck_test", "itworks")
if not cache.get("djangohealtcheck_test") == "itworks":
raise ServiceUnavailable("Cache key does not match")
except CacheKeyWarning as e:
self.add_error(ServiceReturnedUnexpectedResult("Cache key warning"), e)
except ValueError as e:
self.add_error(ServiceReturnedUnexpectedResult("ValueError"), e)
except ConnectionError as e:
self.add_error(ServiceReturnedUnexpectedResult("Connection Error"), e)
django-health-check-3.17.0/health_check/conf.py 0000664 0000000 0000000 00000000344 14305372012 0021261 0 ustar 00root root 0000000 0000000 from django.conf import settings
HEALTH_CHECK = getattr(settings, "HEALTH_CHECK", {})
HEALTH_CHECK.setdefault("DISK_USAGE_MAX", 90)
HEALTH_CHECK.setdefault("MEMORY_MIN", 100)
HEALTH_CHECK.setdefault("WARNINGS_AS_ERRORS", True)
django-health-check-3.17.0/health_check/contrib/ 0000775 0000000 0000000 00000000000 14305372012 0021421 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/contrib/__init__.py 0000664 0000000 0000000 00000000000 14305372012 0023520 0 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/contrib/celery/ 0000775 0000000 0000000 00000000000 14305372012 0022704 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/contrib/celery/__init__.py 0000664 0000000 0000000 00000000171 14305372012 0025014 0 ustar 00root root 0000000 0000000 import django
if django.VERSION < (3, 2):
default_app_config = "health_check.contrib.celery.apps.HealthCheckConfig"
django-health-check-3.17.0/health_check/contrib/celery/apps.py 0000664 0000000 0000000 00000001671 14305372012 0024226 0 ustar 00root root 0000000 0000000 import warnings
from celery import current_app
from django.apps import AppConfig
from django.conf import settings
from health_check.plugins import plugin_dir
class HealthCheckConfig(AppConfig):
name = "health_check.contrib.celery"
def ready(self):
from .backends import CeleryHealthCheck
if hasattr(settings, "HEALTHCHECK_CELERY_TIMEOUT"):
warnings.warn(
"HEALTHCHECK_CELERY_TIMEOUT is depricated and may be removed in the "
"future. Please use HEALTHCHECK_CELERY_RESULT_TIMEOUT and "
"HEALTHCHECK_CELERY_QUEUE_TIMEOUT instead.",
DeprecationWarning,
)
for queue in current_app.amqp.queues:
celery_class_name = "CeleryHealthCheck" + queue.title()
celery_class = type(
celery_class_name, (CeleryHealthCheck,), {"queue": queue}
)
plugin_dir.register(celery_class)
django-health-check-3.17.0/health_check/contrib/celery/backends.py 0000664 0000000 0000000 00000003533 14305372012 0025034 0 ustar 00root root 0000000 0000000 from celery.exceptions import TaskRevokedError, TimeoutError
from django.conf import settings
from health_check.backends import BaseHealthCheckBackend
from health_check.exceptions import ServiceReturnedUnexpectedResult, ServiceUnavailable
from .tasks import add
class CeleryHealthCheck(BaseHealthCheckBackend):
def check_status(self):
timeout = getattr(settings, "HEALTHCHECK_CELERY_TIMEOUT", 3)
result_timeout = getattr(settings, "HEALTHCHECK_CELERY_RESULT_TIMEOUT", timeout)
queue_timeout = getattr(settings, "HEALTHCHECK_CELERY_QUEUE_TIMEOUT", timeout)
try:
result = add.apply_async(
args=[4, 4], expires=queue_timeout, queue=self.queue
)
result.get(timeout=result_timeout)
if result.result != 8:
self.add_error(
ServiceReturnedUnexpectedResult("Celery returned wrong result")
)
except IOError as e:
self.add_error(ServiceUnavailable("IOError"), e)
except NotImplementedError as e:
self.add_error(
ServiceUnavailable(
"NotImplementedError: Make sure CELERY_RESULT_BACKEND is set"
),
e,
)
except TaskRevokedError as e:
self.add_error(
ServiceUnavailable(
"TaskRevokedError: The task was revoked, likely because it spent "
"too long in the queue"
),
e,
)
except TimeoutError as e:
self.add_error(
ServiceUnavailable(
"TimeoutError: The task took too long to return a result"
),
e,
)
except BaseException as e:
self.add_error(ServiceUnavailable("Unknown error"), e)
django-health-check-3.17.0/health_check/contrib/celery/tasks.py 0000664 0000000 0000000 00000000143 14305372012 0024401 0 ustar 00root root 0000000 0000000 from celery import shared_task
@shared_task(ignore_result=False)
def add(x, y):
return x + y
django-health-check-3.17.0/health_check/contrib/celery_ping/ 0000775 0000000 0000000 00000000000 14305372012 0023721 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/contrib/celery_ping/__init__.py 0000664 0000000 0000000 00000000176 14305372012 0026036 0 ustar 00root root 0000000 0000000 import django
if django.VERSION < (3, 2):
default_app_config = "health_check.contrib.celery_ping.apps.HealthCheckConfig"
django-health-check-3.17.0/health_check/contrib/celery_ping/apps.py 0000664 0000000 0000000 00000000441 14305372012 0025235 0 ustar 00root root 0000000 0000000 from django.apps import AppConfig
from health_check.plugins import plugin_dir
class HealthCheckConfig(AppConfig):
name = "health_check.contrib.celery_ping"
def ready(self):
from .backends import CeleryPingHealthCheck
plugin_dir.register(CeleryPingHealthCheck)
django-health-check-3.17.0/health_check/contrib/celery_ping/backends.py 0000664 0000000 0000000 00000004370 14305372012 0026051 0 ustar 00root root 0000000 0000000 from celery.app import default_app as app
from django.conf import settings
from health_check.backends import BaseHealthCheckBackend
from health_check.exceptions import ServiceUnavailable
class CeleryPingHealthCheck(BaseHealthCheckBackend):
CORRECT_PING_RESPONSE = {"ok": "pong"}
def check_status(self):
timeout = getattr(settings, "HEALTHCHECK_CELERY_PING_TIMEOUT", 1)
try:
ping_result = app.control.ping(timeout=timeout)
except IOError as e:
self.add_error(ServiceUnavailable("IOError"), e)
except NotImplementedError as exc:
self.add_error(
ServiceUnavailable(
"NotImplementedError: Make sure CELERY_RESULT_BACKEND is set"
),
exc,
)
except BaseException as exc:
self.add_error(ServiceUnavailable("Unknown error"), exc)
else:
if not ping_result:
self.add_error(
ServiceUnavailable("Celery workers unavailable"),
)
else:
self._check_ping_result(ping_result)
def _check_ping_result(self, ping_result):
active_workers = []
for result in ping_result:
worker, response = list(result.items())[0]
if response != self.CORRECT_PING_RESPONSE:
self.add_error(
ServiceUnavailable(
f"Celery worker {worker} response was incorrect"
),
)
continue
active_workers.append(worker)
if not self.errors:
self._check_active_queues(active_workers)
def _check_active_queues(self, active_workers):
defined_queues = app.conf.CELERY_QUEUES
if not defined_queues:
return
defined_queues = set([queue.name for queue in defined_queues])
active_queues = set()
for queues in app.control.inspect(active_workers).active_queues().values():
active_queues.update([queue.get("name") for queue in queues])
for queue in defined_queues.difference(active_queues):
self.add_error(
ServiceUnavailable(f"No worker for Celery task queue {queue}"),
)
django-health-check-3.17.0/health_check/contrib/migrations/ 0000775 0000000 0000000 00000000000 14305372012 0023575 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/contrib/migrations/__init__.py 0000664 0000000 0000000 00000000175 14305372012 0025711 0 ustar 00root root 0000000 0000000 import django
if django.VERSION < (3, 2):
default_app_config = "health_check.contrib.migrations.apps.HealthCheckConfig"
django-health-check-3.17.0/health_check/contrib/migrations/apps.py 0000664 0000000 0000000 00000000440 14305372012 0025110 0 ustar 00root root 0000000 0000000 from django.apps import AppConfig
from health_check.plugins import plugin_dir
class HealthCheckConfig(AppConfig):
name = "health_check.contrib.migrations"
def ready(self):
from .backends import MigrationsHealthCheck
plugin_dir.register(MigrationsHealthCheck)
django-health-check-3.17.0/health_check/contrib/migrations/backends.py 0000664 0000000 0000000 00000002044 14305372012 0025721 0 ustar 00root root 0000000 0000000 import logging
from django.conf import settings
from django.db import DEFAULT_DB_ALIAS, DatabaseError, connections
from django.db.migrations.executor import MigrationExecutor
from health_check.backends import BaseHealthCheckBackend
from health_check.exceptions import ServiceUnavailable
logger = logging.getLogger(__name__)
class MigrationsHealthCheck(BaseHealthCheckBackend):
def get_migration_plan(self, executor):
return executor.migration_plan(executor.loader.graph.leaf_nodes())
def check_status(self):
db_alias = getattr(settings, "HEALTHCHECK_MIGRATIONS_DB", DEFAULT_DB_ALIAS)
try:
executor = MigrationExecutor(connections[db_alias])
plan = self.get_migration_plan(executor)
if plan:
self.add_error(ServiceUnavailable("There are migrations to apply"))
except DatabaseError as e:
self.add_error(ServiceUnavailable("Database is not ready"), e)
except Exception as e:
self.add_error(ServiceUnavailable("Unexpected error"), e)
django-health-check-3.17.0/health_check/contrib/psutil/ 0000775 0000000 0000000 00000000000 14305372012 0022741 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/contrib/psutil/__init__.py 0000664 0000000 0000000 00000000171 14305372012 0025051 0 ustar 00root root 0000000 0000000 import django
if django.VERSION < (3, 2):
default_app_config = "health_check.contrib.psutil.apps.HealthCheckConfig"
django-health-check-3.17.0/health_check/contrib/psutil/apps.py 0000664 0000000 0000000 00000001561 14305372012 0024261 0 ustar 00root root 0000000 0000000 from django.apps import AppConfig
from django.conf import settings
from health_check.plugins import plugin_dir
class HealthCheckConfig(AppConfig):
name = "health_check.contrib.psutil"
def ready(self):
from .backends import DiskUsage, MemoryUsage
# Ensure checks haven't been explicitly disabled before registering
if (
hasattr(settings, "HEALTH_CHECK")
and ("DISK_USAGE_MAX" in settings.HEALTH_CHECK)
and (settings.HEALTH_CHECK["DISK_USAGE_MAX"] is None)
):
pass
else:
plugin_dir.register(DiskUsage)
if (
hasattr(settings, "HEALTH_CHECK")
and ("DISK_USAGE_MAX" in settings.HEALTH_CHECK)
and (settings.HEALTH_CHECK["MEMORY_MIN"] is None)
):
pass
else:
plugin_dir.register(MemoryUsage)
django-health-check-3.17.0/health_check/contrib/psutil/backends.py 0000664 0000000 0000000 00000003112 14305372012 0025062 0 ustar 00root root 0000000 0000000 import locale
import socket
import psutil
from health_check.backends import BaseHealthCheckBackend
from health_check.conf import HEALTH_CHECK
from health_check.exceptions import ServiceReturnedUnexpectedResult, ServiceWarning
host = socket.gethostname()
DISK_USAGE_MAX = HEALTH_CHECK["DISK_USAGE_MAX"]
MEMORY_MIN = HEALTH_CHECK["MEMORY_MIN"]
class DiskUsage(BaseHealthCheckBackend):
def check_status(self):
try:
du = psutil.disk_usage("/")
if DISK_USAGE_MAX and du.percent >= DISK_USAGE_MAX:
raise ServiceWarning(
"{host} {percent}% disk usage exceeds {disk_usage}%".format(
host=host, percent=du.percent, disk_usage=DISK_USAGE_MAX
)
)
except ValueError as e:
self.add_error(ServiceReturnedUnexpectedResult("ValueError"), e)
class MemoryUsage(BaseHealthCheckBackend):
def check_status(self):
try:
memory = psutil.virtual_memory()
if MEMORY_MIN and memory.available < (MEMORY_MIN * 1024 * 1024):
locale.setlocale(locale.LC_ALL, "")
avail = "{:n}".format(int(memory.available / 1024 / 1024))
threshold = "{:n}".format(MEMORY_MIN)
raise ServiceWarning(
"{host} {avail} MB available RAM below {threshold} MB".format(
host=host, avail=avail, threshold=threshold
)
)
except ValueError as e:
self.add_error(ServiceReturnedUnexpectedResult("ValueError"), e)
django-health-check-3.17.0/health_check/contrib/rabbitmq/ 0000775 0000000 0000000 00000000000 14305372012 0023222 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/contrib/rabbitmq/__init__.py 0000664 0000000 0000000 00000000173 14305372012 0025334 0 ustar 00root root 0000000 0000000 import django
if django.VERSION < (3, 2):
default_app_config = "health_check.contrib.rabbitmq.apps.HealthCheckConfig"
django-health-check-3.17.0/health_check/contrib/rabbitmq/apps.py 0000664 0000000 0000000 00000000432 14305372012 0024536 0 ustar 00root root 0000000 0000000 from django.apps import AppConfig
from health_check.plugins import plugin_dir
class HealthCheckConfig(AppConfig):
name = "health_check.contrib.rabbitmq"
def ready(self):
from .backends import RabbitMQHealthCheck
plugin_dir.register(RabbitMQHealthCheck)
django-health-check-3.17.0/health_check/contrib/rabbitmq/backends.py 0000664 0000000 0000000 00000003254 14305372012 0025352 0 ustar 00root root 0000000 0000000 import logging
from amqp.exceptions import AccessRefused
from django.conf import settings
from kombu import Connection
from health_check.backends import BaseHealthCheckBackend
from health_check.exceptions import ServiceUnavailable
logger = logging.getLogger(__name__)
class RabbitMQHealthCheck(BaseHealthCheckBackend):
"""Health check for RabbitMQ."""
def check_status(self):
"""Check RabbitMQ service by opening and closing a broker channel."""
logger.debug("Checking for a broker_url on django settings...")
broker_url = getattr(settings, "BROKER_URL", None)
logger.debug("Got %s as the broker_url. Connecting to rabbit...", broker_url)
logger.debug("Attempting to connect to rabbit...")
try:
# conn is used as a context to release opened resources later
with Connection(broker_url) as conn:
conn.connect() # exceptions may be raised upon calling connect
except ConnectionRefusedError as e:
self.add_error(
ServiceUnavailable(
"Unable to connect to RabbitMQ: Connection was refused."
),
e,
)
except AccessRefused as e:
self.add_error(
ServiceUnavailable(
"Unable to connect to RabbitMQ: Authentication error."
),
e,
)
except IOError as e:
self.add_error(ServiceUnavailable("IOError"), e)
except BaseException as e:
self.add_error(ServiceUnavailable("Unknown error"), e)
else:
logger.debug("Connection established. RabbitMQ is healthy.")
django-health-check-3.17.0/health_check/contrib/redis/ 0000775 0000000 0000000 00000000000 14305372012 0022527 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/contrib/redis/__init__.py 0000664 0000000 0000000 00000000170 14305372012 0024636 0 ustar 00root root 0000000 0000000 import django
if django.VERSION < (3, 2):
default_app_config = "health_check.contrib.redis.apps.HealthCheckConfig"
django-health-check-3.17.0/health_check/contrib/redis/apps.py 0000664 0000000 0000000 00000000421 14305372012 0024041 0 ustar 00root root 0000000 0000000 from django.apps import AppConfig
from health_check.plugins import plugin_dir
class HealthCheckConfig(AppConfig):
name = "health_check.contrib.redis"
def ready(self):
from .backends import RedisHealthCheck
plugin_dir.register(RedisHealthCheck)
django-health-check-3.17.0/health_check/contrib/redis/backends.py 0000664 0000000 0000000 00000003130 14305372012 0024650 0 ustar 00root root 0000000 0000000 import logging
from django.conf import settings
from redis import exceptions, from_url
from health_check.backends import BaseHealthCheckBackend
from health_check.exceptions import ServiceUnavailable
logger = logging.getLogger(__name__)
class RedisHealthCheck(BaseHealthCheckBackend):
"""Health check for Redis."""
redis_url = getattr(settings, "REDIS_URL", "redis://localhost/1")
def check_status(self):
"""Check Redis service by pinging the redis instance with a redis connection."""
logger.debug("Got %s as the redis_url. Connecting to redis...", self.redis_url)
logger.debug("Attempting to connect to redis...")
try:
# conn is used as a context to release opened resources later
with from_url(self.redis_url) as conn:
conn.ping() # exceptions may be raised upon ping
except ConnectionRefusedError as e:
self.add_error(
ServiceUnavailable(
"Unable to connect to Redis: Connection was refused."
),
e,
)
except exceptions.TimeoutError as e:
self.add_error(
ServiceUnavailable("Unable to connect to Redis: Timeout."), e
)
except exceptions.ConnectionError as e:
self.add_error(
ServiceUnavailable("Unable to connect to Redis: Connection Error"), e
)
except BaseException as e:
self.add_error(ServiceUnavailable("Unknown error"), e)
else:
logger.debug("Connection established. Redis is healthy.")
django-health-check-3.17.0/health_check/contrib/s3boto3_storage/ 0000775 0000000 0000000 00000000000 14305372012 0024441 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/contrib/s3boto3_storage/__init__.py 0000664 0000000 0000000 00000000202 14305372012 0026544 0 ustar 00root root 0000000 0000000 import django
if django.VERSION < (3, 2):
default_app_config = "health_check.contrib.s3boto3_storage.apps.HealthCheckConfig"
django-health-check-3.17.0/health_check/contrib/s3boto3_storage/apps.py 0000664 0000000 0000000 00000000455 14305372012 0025762 0 ustar 00root root 0000000 0000000 from django.apps import AppConfig
from health_check.plugins import plugin_dir
class HealthCheckConfig(AppConfig):
name = "health_check.contrib.s3boto3_storage"
def ready(self):
from .backends import S3Boto3StorageHealthCheck
plugin_dir.register(S3Boto3StorageHealthCheck)
django-health-check-3.17.0/health_check/contrib/s3boto3_storage/backends.py 0000664 0000000 0000000 00000001437 14305372012 0026572 0 ustar 00root root 0000000 0000000 import logging
from health_check.storage.backends import StorageHealthCheck
class S3Boto3StorageHealthCheck(StorageHealthCheck):
"""
Tests the status of a `S3BotoStorage` file storage backend.
S3BotoStorage is included in the `django-storages` package
and recommended by for example Amazon and Heroku for Django
static and media file storage on cloud platforms.
``django-storages`` can be found at https://git.io/v1lGx
``S3Boto3Storage`` can be found at
https://github.com/jschneier/django-storages/blob/master/storages/backends/s3boto3.py
"""
logger = logging.getLogger(__name__)
storage = "storages.backends.s3boto3.S3Boto3Storage"
def check_delete(self, file_name):
storage = self.get_storage()
storage.delete(file_name)
django-health-check-3.17.0/health_check/contrib/s3boto_storage/ 0000775 0000000 0000000 00000000000 14305372012 0024356 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/contrib/s3boto_storage/__init__.py 0000664 0000000 0000000 00000000201 14305372012 0026460 0 ustar 00root root 0000000 0000000 import django
if django.VERSION < (3, 2):
default_app_config = "health_check.contrib.s3boto_storage.apps.HealthCheckConfig"
django-health-check-3.17.0/health_check/contrib/s3boto_storage/apps.py 0000664 0000000 0000000 00000000452 14305372012 0025674 0 ustar 00root root 0000000 0000000 from django.apps import AppConfig
from health_check.plugins import plugin_dir
class HealthCheckConfig(AppConfig):
name = "health_check.contrib.s3boto_storage"
def ready(self):
from .backends import S3BotoStorageHealthCheck
plugin_dir.register(S3BotoStorageHealthCheck)
django-health-check-3.17.0/health_check/contrib/s3boto_storage/backends.py 0000664 0000000 0000000 00000001322 14305372012 0026500 0 ustar 00root root 0000000 0000000 import logging
from health_check.storage.backends import StorageHealthCheck
class S3BotoStorageHealthCheck(StorageHealthCheck):
"""
Tests the status of a `S3BotoStorage` file storage backend.
S3BotoStorage is included in the `django-storages` package
and recommended by for example Amazon and Heroku for Django
static and media file storage on cloud platforms.
``django-storages`` can be found at https://git.io/v1lGx
``S3BotoStorage`` can be found at https://git.io/v1lGF
"""
logger = logging.getLogger(__name__)
storage = "storages.backends.s3boto.S3BotoStorage"
def check_delete(self, file_name):
storage = self.get_storage()
storage.delete(file_name)
django-health-check-3.17.0/health_check/db/ 0000775 0000000 0000000 00000000000 14305372012 0020346 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/db/__init__.py 0000664 0000000 0000000 00000000155 14305372012 0022460 0 ustar 00root root 0000000 0000000 import django
if django.VERSION < (3, 2):
default_app_config = "health_check.db.apps.HealthCheckConfig"
django-health-check-3.17.0/health_check/db/apps.py 0000664 0000000 0000000 00000000472 14305372012 0021666 0 ustar 00root root 0000000 0000000 from django.apps import AppConfig
from health_check.plugins import plugin_dir
class HealthCheckConfig(AppConfig):
default_auto_field = "django.db.models.AutoField"
name = "health_check.db"
def ready(self):
from .backends import DatabaseBackend
plugin_dir.register(DatabaseBackend)
django-health-check-3.17.0/health_check/db/backends.py 0000664 0000000 0000000 00000001203 14305372012 0022466 0 ustar 00root root 0000000 0000000 from django.db import DatabaseError, IntegrityError
from health_check.backends import BaseHealthCheckBackend
from health_check.exceptions import ServiceReturnedUnexpectedResult, ServiceUnavailable
from .models import TestModel
class DatabaseBackend(BaseHealthCheckBackend):
def check_status(self):
try:
obj = TestModel.objects.create(title="test")
obj.title = "newtest"
obj.save()
obj.delete()
except IntegrityError:
raise ServiceReturnedUnexpectedResult("Integrity Error")
except DatabaseError:
raise ServiceUnavailable("Database error")
django-health-check-3.17.0/health_check/db/migrations/ 0000775 0000000 0000000 00000000000 14305372012 0022522 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/db/migrations/0001_initial.py 0000664 0000000 0000000 00000001543 14305372012 0025170 0 ustar 00root root 0000000 0000000 # Generated by Django 1.10.1 on 2016-09-26 18:46
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
replaces = [
("health_check_db", "0001_initial"),
]
dependencies = []
operations = [
migrations.CreateModel(
name="TestModel",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("title", models.CharField(max_length=128)),
],
options={
"db_table": "health_check_db_testmodel",
},
),
]
django-health-check-3.17.0/health_check/db/migrations/__init__.py 0000664 0000000 0000000 00000000000 14305372012 0024621 0 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/db/models.py 0000664 0000000 0000000 00000000253 14305372012 0022203 0 ustar 00root root 0000000 0000000 from django.db import models
class TestModel(models.Model):
title = models.CharField(max_length=128)
class Meta:
db_table = "health_check_db_testmodel"
django-health-check-3.17.0/health_check/exceptions.py 0000664 0000000 0000000 00000001355 14305372012 0022520 0 ustar 00root root 0000000 0000000 from django.utils.translation import gettext_lazy as _ # noqa: N812
class HealthCheckException(Exception):
message_type = _("unknown error")
def __init__(self, message):
self.message = message
def __str__(self):
return "%s: %s" % (self.message_type, self.message)
class ServiceWarning(HealthCheckException):
"""
Warning of service misbehavior.
If the ``HEALTH_CHECK['WARNINGS_AS_ERRORS']`` is set to ``False``,
these exceptions will not case a 500 status response.
"""
message_type = _("warning")
class ServiceUnavailable(HealthCheckException):
message_type = _("unavailable")
class ServiceReturnedUnexpectedResult(HealthCheckException):
message_type = _("unexpected result")
django-health-check-3.17.0/health_check/management/ 0000775 0000000 0000000 00000000000 14305372012 0022075 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/management/__init__.py 0000664 0000000 0000000 00000000000 14305372012 0024174 0 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/management/commands/ 0000775 0000000 0000000 00000000000 14305372012 0023676 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/management/commands/__init__.py 0000664 0000000 0000000 00000000000 14305372012 0025775 0 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/management/commands/health_check.py 0000664 0000000 0000000 00000001231 14305372012 0026647 0 ustar 00root root 0000000 0000000 import sys
from django.core.management.base import BaseCommand
from health_check.mixins import CheckMixin
class Command(CheckMixin, BaseCommand):
help = "Run health checks and exit 0 if everything went well."
def handle(self, *args, **options):
# perform all checks
errors = self.errors
for plugin in self.plugins:
style_func = self.style.SUCCESS if not plugin.errors else self.style.ERROR
self.stdout.write(
"{:<24} ... {}\n".format(
plugin.identifier(), style_func(plugin.pretty_status())
)
)
if errors:
sys.exit(1)
django-health-check-3.17.0/health_check/mixins.py 0000664 0000000 0000000 00000003070 14305372012 0021642 0 ustar 00root root 0000000 0000000 import copy
from concurrent.futures import ThreadPoolExecutor
from health_check.conf import HEALTH_CHECK
from health_check.exceptions import ServiceWarning
from health_check.plugins import plugin_dir
class CheckMixin:
_errors = None
_plugins = None
@property
def errors(self):
if not self._errors:
self._errors = self.run_check()
return self._errors
@property
def plugins(self):
if not self._plugins:
self._plugins = sorted(
(
plugin_class(**copy.deepcopy(options))
for plugin_class, options in plugin_dir._registry
),
key=lambda plugin: plugin.identifier(),
)
return self._plugins
def run_check(self):
errors = []
def _run(plugin):
plugin.run_check()
try:
return plugin
finally:
from django.db import connections
connections.close_all()
with ThreadPoolExecutor(max_workers=len(self.plugins) or 1) as executor:
for plugin in executor.map(_run, self.plugins):
if plugin.critical_service:
if not HEALTH_CHECK["WARNINGS_AS_ERRORS"]:
errors.extend(
e
for e in plugin.errors
if not isinstance(e, ServiceWarning)
)
else:
errors.extend(plugin.errors)
return errors
django-health-check-3.17.0/health_check/plugins.py 0000664 0000000 0000000 00000001157 14305372012 0022020 0 ustar 00root root 0000000 0000000 class AlreadyRegistered(Exception):
pass
class NotRegistered(Exception):
pass
class HealthCheckPluginDirectory:
"""Django health check registry."""
def __init__(self):
self._registry = [] # plugin_class class -> plugin options
def reset(self):
"""Reset registry state, e.g. for testing purposes."""
self._registry = []
def register(self, plugin, **options):
"""Add the given plugin from the registry."""
# Instantiate the admin class to save in the registry
self._registry.append((plugin, options))
plugin_dir = HealthCheckPluginDirectory()
django-health-check-3.17.0/health_check/storage/ 0000775 0000000 0000000 00000000000 14305372012 0021425 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/storage/__init__.py 0000664 0000000 0000000 00000000162 14305372012 0023535 0 ustar 00root root 0000000 0000000 import django
if django.VERSION < (3, 2):
default_app_config = "health_check.storage.apps.HealthCheckConfig"
django-health-check-3.17.0/health_check/storage/apps.py 0000664 0000000 0000000 00000000445 14305372012 0022745 0 ustar 00root root 0000000 0000000 from django.apps import AppConfig
from health_check.plugins import plugin_dir
class HealthCheckConfig(AppConfig):
name = "health_check.storage"
def ready(self):
from .backends import DefaultFileStorageHealthCheck
plugin_dir.register(DefaultFileStorageHealthCheck)
django-health-check-3.17.0/health_check/storage/backends.py 0000664 0000000 0000000 00000004553 14305372012 0023560 0 ustar 00root root 0000000 0000000 import uuid
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.files.storage import get_storage_class
from health_check.backends import BaseHealthCheckBackend
from health_check.exceptions import ServiceUnavailable
class StorageHealthCheck(BaseHealthCheckBackend):
"""
Tests the status of a `StorageBackend`.
Can be extended to test any storage backend by subclassing:
class MyStorageHealthCheck(StorageHealthCheck):
storage = 'some.other.StorageBackend'
plugin_dir.register(MyStorageHealthCheck)
storage must be either a string pointing to a storage class
(e.g 'django.core.files.storage.FileSystemStorage') or a Storage instance.
"""
storage = None
def get_storage(self):
if isinstance(self.storage, str):
return get_storage_class(self.storage)()
else:
return self.storage
def get_file_name(self):
return "health_check_storage_test/test-%s.txt" % uuid.uuid4()
def get_file_content(self):
return b"this is the healthtest file content"
def check_save(self, file_name, file_content):
storage = self.get_storage()
# save the file
file_name = storage.save(file_name, ContentFile(content=file_content))
# read the file and compare
if not storage.exists(file_name):
raise ServiceUnavailable("File does not exist")
with storage.open(file_name) as f:
if not f.read() == file_content:
raise ServiceUnavailable("File content does not match")
return file_name
def check_delete(self, file_name):
storage = self.get_storage()
# delete the file and make sure it is gone
storage.delete(file_name)
if storage.exists(file_name):
raise ServiceUnavailable("File was not deleted")
def check_status(self):
try:
# write the file to the storage backend
file_name = self.get_file_name()
file_content = self.get_file_content()
file_name = self.check_save(file_name, file_content)
self.check_delete(file_name)
return True
except Exception as e:
raise ServiceUnavailable("Unknown exception") from e
class DefaultFileStorageHealthCheck(StorageHealthCheck):
storage = settings.DEFAULT_FILE_STORAGE
django-health-check-3.17.0/health_check/templates/ 0000775 0000000 0000000 00000000000 14305372012 0021757 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/templates/health_check/ 0000775 0000000 0000000 00000000000 14305372012 0024361 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/health_check/templates/health_check/index.html 0000664 0000000 0000000 00000003250 14305372012 0026356 0 ustar 00root root 0000000 0000000
{% block title %}System status{% endblock title %}
{% block extra_head %}{% endblock extra_head %}
{% block content %}
System status
Service |
Status |
Time Taken |
{% for plugin in plugins %}
{% if plugin.status %}
✅
{% else %}
❌
{% endif %}
|
{{ plugin.identifier }} |
{{ plugin.pretty_status | linebreaks }} |
{{ plugin.time_taken|floatformat:4 }} seconds |
{% endfor %}
{% endblock content %}
django-health-check-3.17.0/health_check/urls.py 0000664 0000000 0000000 00000000260 14305372012 0021316 0 ustar 00root root 0000000 0000000 from django.urls import path
from health_check.views import MainView
app_name = "health_check"
urlpatterns = [
path("", MainView.as_view(), name="health_check_home"),
]
django-health-check-3.17.0/health_check/views.py 0000664 0000000 0000000 00000010100 14305372012 0021460 0 ustar 00root root 0000000 0000000 import re
from django.http import HttpResponse, JsonResponse
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
from django.views.generic import TemplateView
from health_check.mixins import CheckMixin
class MediaType:
"""
Sortable object representing HTTP's accept header.
.. seealso:: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept
"""
pattern = re.compile(
r"""
^
(?P
(\w+|\*) # Media type, or wildcard
/
([\w\d\-+.]+|\*) # subtype, or wildcard
)
(
\s*;\s* # parameter separator with optional whitespace
q= # q is expected to be the first parameter, by RFC2616
(?P
1([.]0{1,3})? # 1 with up to three digits of precision
|
0([.]\d{1,3})? # 0.000 to 0.999 with optional precision
)
)?
(
\s*;\s* # parameter separator with optional whitespace
[-!#$%&'*+.^_`|~0-9a-zA-Z]+ # any token from legal characters
=
[-!#$%&'*+.^_`|~0-9a-zA-Z]+ # any value from legal characters
)*
$
""",
re.VERBOSE,
)
def __init__(self, mime_type, weight=1.0):
self.mime_type = mime_type
self.weight = float(weight)
@classmethod
def from_string(cls, value):
"""Return single instance parsed from given accept header string."""
match = cls.pattern.search(value)
if match is None:
raise ValueError('"%s" is not a valid media type' % value)
try:
return cls(match.group("mime_type"), float(match.group("weight") or 1))
except ValueError:
return cls(value)
@classmethod
def parse_header(cls, value="*/*"):
"""Parse HTTP accept header and return instances sorted by weight."""
yield from sorted(
(
cls.from_string(token.strip())
for token in value.split(",")
if token.strip()
),
reverse=True,
)
def __str__(self):
return "%s; q=%s" % (self.mime_type, self.weight)
def __repr__(self):
return "%s: %s" % (type(self).__name__, self.__str__())
def __eq__(self, other):
return self.weight == other.weight and self.mime_type == other.mime_type
def __lt__(self, other):
return self.weight.__lt__(other.weight)
class MainView(CheckMixin, TemplateView):
template_name = "health_check/index.html"
@method_decorator(never_cache)
def get(self, request, *args, **kwargs):
status_code = 500 if self.errors else 200
format_override = request.GET.get("format")
if format_override == "json":
return self.render_to_response_json(self.plugins, status_code)
accept_header = request.META.get("HTTP_ACCEPT", "*/*")
for media in MediaType.parse_header(accept_header):
if media.mime_type in (
"text/html",
"application/xhtml+xml",
"text/*",
"*/*",
):
context = self.get_context_data(**kwargs)
return self.render_to_response(context, status=status_code)
elif media.mime_type in ("application/json", "application/*"):
return self.render_to_response_json(self.plugins, status_code)
return HttpResponse(
"Not Acceptable: Supported content types: text/html, application/json",
status=406,
content_type="text/plain",
)
def get_context_data(self, **kwargs):
return {**super().get_context_data(**kwargs), "plugins": self.plugins}
def render_to_response_json(self, plugins, status):
return JsonResponse(
{str(p.identifier()): str(p.pretty_status()) for p in plugins},
status=status,
)
django-health-check-3.17.0/linter-requirements.txt 0000664 0000000 0000000 00000000120 14305372012 0022127 0 ustar 00root root 0000000 0000000 bandit==1.7.4
black==22.8.0
flake8==5.0.4
isort==5.10.1
pydocstyle[toml]==6.1.1
django-health-check-3.17.0/setup.cfg 0000664 0000000 0000000 00000004334 14305372012 0017204 0 ustar 00root root 0000000 0000000 [metadata]
name = django-health-check
author = Kristian Ollegaard
author_email = kristian@oellegaard.com
description = Run checks on services like databases, queue servers, celery processes, etc.
long_description = file: README.rst
url = https://github.com/KristianOellegaard/django-health-check
license = MIT License
license_file = LICENSE
classifier =
Development Status :: 5 - Production/Stable
Framework :: Django
Framework :: Django :: 2.2
Framework :: Django :: 3.1
Framework :: Django :: 3.2
Intended Audience :: Developers
License :: OSI Approved :: MIT License
Operating System :: OS Independent
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Topic :: Software Development :: Quality Assurance
Framework :: Django
Framework :: Django :: 2.2
Framework :: Django :: 4.0
Framework :: Django :: 4.1
Topic :: System :: Logging
Topic :: System :: Monitoring
Topic :: Utilities
keywords =
django
postgresql
[options]
python_requires = >=3.8
include_package_data = True
packages = health_check
install_requires =
django>=2.2
setup_requires =
setuptools_scm
[options.extras_require]
test =
pytest
pytest-cov
pytest-django
celery
redis
docs =
sphinx
[bdist_wheel]
universal = 1
[bdist_rpm]
requires =
python-django-appconf >= 2.0
python-django-appconf >= 0.6
[aliases]
test = pytest
[tool:pytest]
norecursedirs=venv env .eggs
DJANGO_SETTINGS_MODULE=tests.testapp.settings
addopts =
--tb=short
-rxs
--cov=health_check
[flake8]
max-line-length = 88
select = C,E,F,W,B,B950
ignore = E203, E501, W503, E731
[isort]
atomic = true
line_length = 88
multi_line_output = 3
include_trailing_comma = True
force_grid_wrap = 0
use_parentheses = True
known_first_party = health_check, tests
default_section = THIRDPARTY
combine_as_imports = true
[pydocstyle]
add-ignore = D1
match-dir = (?!tests|env|.eggs|\.).*
[coverage:run]
branch = True
omit =
*/migrations/*
*/tests/*
*/test_*.py
.eggs/*
[coverage:report]
ignore_errors = True
show_missing = True
skip_covered = True
sort = Cover
django-health-check-3.17.0/setup.py 0000775 0000000 0000000 00000000154 14305372012 0017074 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
from setuptools import setup
setup(name="django-health-check", use_scm_version=True)
django-health-check-3.17.0/tests/ 0000775 0000000 0000000 00000000000 14305372012 0016521 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/tests/__init__.py 0000664 0000000 0000000 00000000000 14305372012 0020620 0 ustar 00root root 0000000 0000000 django-health-check-3.17.0/tests/test_autodiscover.py 0000664 0000000 0000000 00000002100 14305372012 0022632 0 ustar 00root root 0000000 0000000 from celery import current_app
from django.conf import settings
from health_check.contrib.celery.backends import CeleryHealthCheck
from health_check.contrib.celery_ping.backends import CeleryPingHealthCheck
from health_check.plugins import plugin_dir
class TestAutoDiscover:
def test_autodiscover(self):
health_check_plugins = list(
filter(
lambda x: x.startswith("health_check.") and "celery" not in x,
settings.INSTALLED_APPS,
)
)
non_celery_plugins = [
x
for x in plugin_dir._registry
if not issubclass(x[0], (CeleryHealthCheck, CeleryPingHealthCheck))
]
# The number of installed apps excluding celery should equal to all plugins except celery
assert len(non_celery_plugins) == len(health_check_plugins)
def test_discover_celery_queues(self):
celery_plugins = [
x for x in plugin_dir._registry if issubclass(x[0], CeleryHealthCheck)
]
assert len(celery_plugins) == len(current_app.amqp.queues)
django-health-check-3.17.0/tests/test_backends.py 0000664 0000000 0000000 00000005675 14305372012 0021721 0 ustar 00root root 0000000 0000000 import logging
from io import StringIO
import pytest
from health_check.backends import BaseHealthCheckBackend
from health_check.exceptions import HealthCheckException
class TestBaseHealthCheckBackend:
def test_run_check(self):
with pytest.raises(NotImplementedError):
BaseHealthCheckBackend().run_check()
def test_identifier(self):
assert BaseHealthCheckBackend().identifier() == "BaseHealthCheckBackend"
class MyHeathCheck(BaseHealthCheckBackend):
pass
assert MyHeathCheck().identifier() == "MyHeathCheck"
class MyHeathCheck(BaseHealthCheckBackend):
foo = "bar"
def identifier(self):
return self.foo
assert MyHeathCheck().identifier() == "bar"
def test_status(self):
ht = BaseHealthCheckBackend()
assert ht.status == 1
ht.errors = [1]
assert ht.status == 0
def test_pretty_status(self):
ht = BaseHealthCheckBackend()
assert ht.pretty_status() == "working"
ht.errors = ["foo"]
assert ht.pretty_status() == "foo"
ht.errors.append("bar")
assert ht.pretty_status() == "foo\nbar"
ht.errors.append(123)
assert ht.pretty_status() == "foo\nbar\n123"
def test_add_error(self):
ht = BaseHealthCheckBackend()
e = HealthCheckException("foo")
ht.add_error(e)
assert ht.errors[0] is e
ht = BaseHealthCheckBackend()
ht.add_error("bar")
assert isinstance(ht.errors[0], HealthCheckException)
assert str(ht.errors[0]) == "unknown error: bar"
ht = BaseHealthCheckBackend()
ht.add_error(type)
assert isinstance(ht.errors[0], HealthCheckException)
assert str(ht.errors[0]) == "unknown error: unknown error"
def test_add_error_cause(self):
ht = BaseHealthCheckBackend()
logger = logging.getLogger("health-check")
with StringIO() as stream:
stream_handler = logging.StreamHandler(stream)
logger.addHandler(stream_handler)
try:
raise Exception("bar")
except Exception as e:
ht.add_error("foo", e)
stream.seek(0)
log = stream.read()
assert "foo" in log
assert "bar" in log
assert "Traceback" in log
assert "Exception: bar" in log
logger.removeHandler(stream_handler)
with StringIO() as stream:
stream_handler = logging.StreamHandler(stream)
logger.addHandler(stream_handler)
try:
raise Exception("bar")
except Exception:
ht.add_error("foo")
stream.seek(0)
log = stream.read()
assert "foo" in log
assert "bar" not in log
assert "Traceback" not in log
assert "Exception: bar" not in log
logger.removeHandler(stream_handler)
django-health-check-3.17.0/tests/test_cache.py 0000664 0000000 0000000 00000007244 14305372012 0021204 0 ustar 00root root 0000000 0000000 from unittest.mock import patch
from django.core.cache.backends.base import BaseCache, CacheKeyWarning
from django.test import TestCase
from health_check.cache.backends import CacheBackend
# A Mock version of the cache to use for testing
class MockCache(BaseCache):
"""
A Mock Cache used for testing.
set_works - set to False to make the mocked set method fail, but not raise
set_raises - The Exception to be raised when set() is called, if any
"""
key = None
value = None
set_works = None
set_raises = None
def __init__(self, set_works=True, set_raises=None):
super(MockCache, self).__init__(params={})
self.set_works = set_works
self.set_raises = set_raises
def set(self, key, value, *args, **kwargs):
if self.set_raises is not None:
raise self.set_raises
elif self.set_works:
self.key = key
self.value = value
else:
self.key = key
self.value = None
def get(self, key, *args, **kwargs):
if key == self.key:
return self.value
else:
return None
class HealthCheckCacheTests(TestCase):
"""
Tests health check behavior with a mocked cache backend.
Ensures check_status returns/raises the expected result when the cache works, fails, or raises exceptions.
"""
@patch("health_check.cache.backends.caches", dict(default=MockCache()))
def test_check_status_working(self):
cache_backend = CacheBackend()
cache_backend.run_check()
self.assertFalse(cache_backend.errors)
@patch(
"health_check.cache.backends.caches",
dict(default=MockCache(), broken=MockCache(set_works=False)),
)
def test_multiple_backends_check_default(self):
# default backend works while other is broken
cache_backend = CacheBackend("default")
cache_backend.run_check()
self.assertFalse(cache_backend.errors)
@patch(
"health_check.cache.backends.caches",
dict(default=MockCache(), broken=MockCache(set_works=False)),
)
def test_multiple_backends_check_broken(self):
cache_backend = CacheBackend("broken")
cache_backend.run_check()
self.assertTrue(cache_backend.errors)
self.assertIn(
"unavailable: Cache key does not match", cache_backend.pretty_status()
)
# check_status should raise ServiceUnavailable when values at cache key do not match
@patch(
"health_check.cache.backends.caches", dict(default=MockCache(set_works=False))
)
def test_set_fails(self):
cache_backend = CacheBackend()
cache_backend.run_check()
self.assertTrue(cache_backend.errors)
self.assertIn(
"unavailable: Cache key does not match", cache_backend.pretty_status()
)
# check_status should catch generic exceptions raised by set and convert to ServiceUnavailable
@patch(
"health_check.cache.backends.caches",
dict(default=MockCache(set_raises=Exception)),
)
def test_set_raises_generic(self):
cache_backend = CacheBackend()
with self.assertRaises(Exception):
cache_backend.run_check()
# check_status should catch CacheKeyWarning and convert to ServiceReturnedUnexpectedResult
@patch(
"health_check.cache.backends.caches",
dict(default=MockCache(set_raises=CacheKeyWarning)),
)
def test_set_raises_cache_key_warning(self):
cache_backend = CacheBackend()
cache_backend.check_status()
cache_backend.run_check()
self.assertIn(
"unexpected result: Cache key warning", cache_backend.pretty_status()
)
django-health-check-3.17.0/tests/test_celery_ping.py 0000664 0000000 0000000 00000011103 14305372012 0022426 0 ustar 00root root 0000000 0000000 from unittest.mock import patch
import pytest
from django.apps import apps
from django.conf import settings
from health_check.contrib.celery_ping.apps import HealthCheckConfig
from health_check.contrib.celery_ping.backends import CeleryPingHealthCheck
class TestCeleryPingHealthCheck:
CELERY_APP_CONTROL_PING = (
"health_check.contrib.celery_ping.backends.app.control.ping"
)
CELERY_APP_CONTROL_INSPECT_ACTIVE_QUEUES = (
"health_check.contrib.celery_ping.backends.app.control.inspect.active_queues"
)
@pytest.fixture
def health_check(self):
return CeleryPingHealthCheck()
def test_check_status_doesnt_add_errors_when_ping_successful(self, health_check):
celery_worker = "celery@4cc150a7b49b"
with patch(
self.CELERY_APP_CONTROL_PING,
return_value=[
{celery_worker: CeleryPingHealthCheck.CORRECT_PING_RESPONSE},
{f"{celery_worker}-2": CeleryPingHealthCheck.CORRECT_PING_RESPONSE},
],
), patch(
self.CELERY_APP_CONTROL_INSPECT_ACTIVE_QUEUES,
return_value={
celery_worker: [
{"name": queue.name} for queue in settings.CELERY_QUEUES
]
},
):
health_check.check_status()
assert not health_check.errors
def test_check_status_reports_errors_if_ping_responses_are_incorrect(
self, health_check
):
with patch(
self.CELERY_APP_CONTROL_PING,
return_value=[
{"celery1@4cc150a7b49b": CeleryPingHealthCheck.CORRECT_PING_RESPONSE},
{"celery2@4cc150a7b49b": {}},
{"celery3@4cc150a7b49b": {"error": "pong"}},
],
):
health_check.check_status()
assert len(health_check.errors) == 2
def test_check_status_adds_errors_when_ping_successfull_but_not_all_defined_queues_have_consumers(
self,
health_check,
):
celery_worker = "celery@4cc150a7b49b"
queues = list(settings.CELERY_QUEUES)
with patch(
self.CELERY_APP_CONTROL_PING,
return_value=[{celery_worker: CeleryPingHealthCheck.CORRECT_PING_RESPONSE}],
), patch(
self.CELERY_APP_CONTROL_INSPECT_ACTIVE_QUEUES,
return_value={celery_worker: [{"name": queues.pop().name}]},
):
health_check.check_status()
assert len(health_check.errors) == len(queues)
@pytest.mark.parametrize(
"exception_to_raise",
[
IOError,
TimeoutError,
],
)
def test_check_status_add_error_when_io_error_raised_from_ping(
self, exception_to_raise, health_check
):
with patch(self.CELERY_APP_CONTROL_PING, side_effect=exception_to_raise):
health_check.check_status()
assert len(health_check.errors) == 1
assert "ioerror" in health_check.errors[0].message.lower()
@pytest.mark.parametrize(
"exception_to_raise", [ValueError, SystemError, IndexError, MemoryError]
)
def test_check_status_add_error_when_any_exception_raised_from_ping(
self, exception_to_raise, health_check
):
with patch(self.CELERY_APP_CONTROL_PING, side_effect=exception_to_raise):
health_check.check_status()
assert len(health_check.errors) == 1
assert health_check.errors[0].message.lower() == "unknown error"
def test_check_status_when_raised_exception_notimplementederror(self, health_check):
expected_error_message = (
"notimplementederror: make sure celery_result_backend is set"
)
with patch(self.CELERY_APP_CONTROL_PING, side_effect=NotImplementedError):
health_check.check_status()
assert len(health_check.errors) == 1
assert health_check.errors[0].message.lower() == expected_error_message
@pytest.mark.parametrize("ping_result", [None, list()])
def test_check_status_add_error_when_ping_result_failed(
self, ping_result, health_check
):
with patch(self.CELERY_APP_CONTROL_PING, return_value=ping_result):
health_check.check_status()
assert len(health_check.errors) == 1
assert "workers unavailable" in health_check.errors[0].message.lower()
class TestCeleryPingHealthCheckApps:
def test_apps(self):
assert HealthCheckConfig.name == "health_check.contrib.celery_ping"
celery_ping = apps.get_app_config("celery_ping")
assert celery_ping.name == "health_check.contrib.celery_ping"
django-health-check-3.17.0/tests/test_commands.py 0000664 0000000 0000000 00000001647 14305372012 0021743 0 ustar 00root root 0000000 0000000 from io import StringIO
import pytest
from django.core.management import call_command
from health_check.backends import BaseHealthCheckBackend
from health_check.plugins import plugin_dir
class FailPlugin(BaseHealthCheckBackend):
def check_status(self):
self.add_error("Oops")
class OkPlugin(BaseHealthCheckBackend):
def check_status(self):
pass
class TestCommand:
@pytest.fixture(autouse=True)
def setup(self):
plugin_dir.reset()
plugin_dir.register(FailPlugin)
plugin_dir.register(OkPlugin)
yield
plugin_dir.reset()
def test_command(self):
stdout = StringIO()
with pytest.raises(SystemExit):
call_command("health_check", stdout=stdout)
stdout.seek(0)
assert stdout.read() == (
"FailPlugin ... unknown error: Oops\n"
"OkPlugin ... working\n"
)
django-health-check-3.17.0/tests/test_db.py 0000664 0000000 0000000 00000004466 14305372012 0020531 0 ustar 00root root 0000000 0000000 from unittest.mock import patch
from django.db import DatabaseError, IntegrityError
from django.db.models import Model
from django.test import TestCase
from health_check.db.backends import DatabaseBackend
class MockDBModel(Model):
"""
A Mock database used for testing.
error_thrown - The Exception to be raised when save() is called, if any
"""
error_thrown = None
def __init__(self, error_thrown=None, *args, **kwargs):
super(MockDBModel, self).__init__(*args, **kwargs)
self.error_thrown = error_thrown
def save(self, *args, **kwargs):
if self.error_thrown is not None:
raise self.error_thrown
else:
return True
def delete(self, *args, **kwargs):
return True
def raise_(ex):
raise ex
class HealthCheckDatabaseTests(TestCase):
"""
Tests health check behavior with a mocked database backend.
Ensures check_status returns/raises the expected result when the database works or raises exceptions.
"""
@patch(
"health_check.db.backends.TestModel.objects.create",
lambda title=None: MockDBModel(),
)
def test_check_status_works(self):
db_backend = DatabaseBackend()
db_backend.check_status()
self.assertFalse(db_backend.errors)
@patch(
"health_check.db.backends.TestModel.objects.create",
lambda title=None: raise_(IntegrityError),
)
def test_raise_integrity_error(self):
db_backend = DatabaseBackend()
db_backend.run_check()
self.assertTrue(db_backend.errors)
self.assertIn("unexpected result: Integrity Error", db_backend.pretty_status())
@patch(
"health_check.db.backends.TestModel.objects.create",
lambda title=None: MockDBModel(error_thrown=DatabaseError),
)
def test_raise_database_error(self):
db_backend = DatabaseBackend()
db_backend.run_check()
self.assertTrue(db_backend.errors)
self.assertIn("unavailable: Database error", db_backend.pretty_status())
@patch(
"health_check.db.backends.TestModel.objects.create",
lambda title=None: MockDBModel(error_thrown=Exception),
)
def test_raise_exception(self):
db_backend = DatabaseBackend()
with self.assertRaises(Exception):
db_backend.run_check()
django-health-check-3.17.0/tests/test_migrations.py 0000664 0000000 0000000 00000001702 14305372012 0022306 0 ustar 00root root 0000000 0000000 from unittest.mock import patch
from django.db.migrations import Migration
from django.test import TestCase
from health_check.contrib.migrations.backends import MigrationsHealthCheck
class MockMigration(Migration):
...
class TestMigrationsHealthCheck(TestCase):
def test_check_status_work(self):
with patch(
"health_check.contrib.migrations.backends.MigrationsHealthCheck.get_migration_plan",
return_value=[],
):
backend = MigrationsHealthCheck()
backend.run_check()
self.assertFalse(backend.errors)
def test_check_status_raises_error_if_there_are_migrations(self):
with patch(
"health_check.contrib.migrations.backends.MigrationsHealthCheck.get_migration_plan",
return_value=[(MockMigration, False)],
):
backend = MigrationsHealthCheck()
backend.run_check()
self.assertTrue(backend.errors)
django-health-check-3.17.0/tests/test_mixins.py 0000664 0000000 0000000 00000001474 14305372012 0021447 0 ustar 00root root 0000000 0000000 import pytest
from health_check.backends import BaseHealthCheckBackend
from health_check.mixins import CheckMixin
from health_check.plugins import plugin_dir
class FailPlugin(BaseHealthCheckBackend):
def check_status(self):
self.add_error("Oops")
class OkPlugin(BaseHealthCheckBackend):
def check_status(self):
pass
class Checker(CheckMixin):
pass
class TestCheckMixin:
@pytest.fixture(autouse=True)
def setup(self):
plugin_dir.reset()
plugin_dir.register(FailPlugin)
plugin_dir.register(OkPlugin)
yield
plugin_dir.reset()
def test_plugins(self):
assert len(Checker().plugins) == 2
def test_errors(self):
assert len(Checker().errors) == 1
def test_run_check(self):
assert len(Checker().run_check()) == 1
django-health-check-3.17.0/tests/test_plugins.py 0000664 0000000 0000000 00000001044 14305372012 0021612 0 ustar 00root root 0000000 0000000 import pytest
from health_check.backends import BaseHealthCheckBackend
from health_check.plugins import plugin_dir
class FakePlugin(BaseHealthCheckBackend):
def check_status(self):
pass
class Plugin(BaseHealthCheckBackend):
def check_status(self):
pass
class TestPlugin:
@pytest.fixture(autouse=True)
def setup(self):
plugin_dir.reset()
plugin_dir.register(FakePlugin)
yield
plugin_dir.reset()
def test_register_plugin(self):
assert len(plugin_dir._registry) == 1
django-health-check-3.17.0/tests/test_rabbitmq.py 0000664 0000000 0000000 00000005755 14305372012 0021747 0 ustar 00root root 0000000 0000000 from unittest import mock
from amqp.exceptions import AccessRefused
from health_check.contrib.rabbitmq.backends import RabbitMQHealthCheck
class TestRabbitMQHealthCheck:
"""Test RabbitMQ health check."""
@mock.patch("health_check.contrib.rabbitmq.backends.getattr")
@mock.patch("health_check.contrib.rabbitmq.backends.Connection")
def test_broker_refused_connection(self, mocked_connection, mocked_getattr):
"""Test when the connection to RabbitMQ is refused."""
mocked_getattr.return_value = "broker_url"
conn_exception = ConnectionRefusedError("Refused connection")
# mock returns
mocked_conn = mock.MagicMock()
mocked_connection.return_value.__enter__.return_value = mocked_conn
mocked_conn.connect.side_effect = conn_exception
# instantiates the class
rabbitmq_healthchecker = RabbitMQHealthCheck()
# invokes the method check_status()
rabbitmq_healthchecker.check_status()
assert len(rabbitmq_healthchecker.errors), 1
# mock assertions
mocked_connection.assert_called_once_with("broker_url")
@mock.patch("health_check.contrib.rabbitmq.backends.getattr")
@mock.patch("health_check.contrib.rabbitmq.backends.Connection")
def test_broker_auth_error(self, mocked_connection, mocked_getattr):
"""Test that the connection to RabbitMQ has an authentication error."""
mocked_getattr.return_value = "broker_url"
conn_exception = AccessRefused("Refused connection")
# mock returns
mocked_conn = mock.MagicMock()
mocked_connection.return_value.__enter__.return_value = mocked_conn
mocked_conn.connect.side_effect = conn_exception
# instantiates the class
rabbitmq_healthchecker = RabbitMQHealthCheck()
# invokes the method check_status()
rabbitmq_healthchecker.check_status()
assert len(rabbitmq_healthchecker.errors), 1
# mock assertions
mocked_connection.assert_called_once_with("broker_url")
@mock.patch("health_check.contrib.rabbitmq.backends.getattr")
@mock.patch("health_check.contrib.rabbitmq.backends.Connection")
def test_broker_connection_upon_none_url(self, mocked_connection, mocked_getattr):
"""Thest when the connection to RabbitMQ has no ``broker_url``."""
mocked_getattr.return_value = None
# if the variable BROKER_URL is not set, AccessRefused exception is raised
conn_exception = AccessRefused("Refused connection")
# mock returns
mocked_conn = mock.MagicMock()
mocked_connection.return_value.__enter__.return_value = mocked_conn
mocked_conn.connect.side_effect = conn_exception
# instantiates the class
rabbitmq_healthchecker = RabbitMQHealthCheck()
# invokes the method check_status()
rabbitmq_healthchecker.check_status()
assert len(rabbitmq_healthchecker.errors), 1
# mock assertions
mocked_connection.assert_called_once_with(None)
django-health-check-3.17.0/tests/test_redis.py 0000664 0000000 0000000 00000006632 14305372012 0021247 0 ustar 00root root 0000000 0000000 from unittest import mock
from redis.exceptions import ConnectionError, TimeoutError
from health_check.contrib.redis.backends import RedisHealthCheck
class TestRedisHealthCheck:
"""Test Redis health check."""
@mock.patch("health_check.contrib.redis.backends.getattr")
@mock.patch("health_check.contrib.redis.backends.from_url", autospec=True)
def test_redis_refused_connection(self, mocked_connection, mocked_getattr):
"""Test when the connection to Redis is refused."""
mocked_getattr.return_value = "redis_url"
# mock returns
mocked_connection.return_value = mock.MagicMock()
mocked_connection.return_value.__enter__.side_effect = ConnectionRefusedError(
"Refused connection"
)
# instantiates the class
redis_healthchecker = RedisHealthCheck()
# invokes the method check_status()
redis_healthchecker.check_status()
assert len(redis_healthchecker.errors), 1
# mock assertions
mocked_connection.assert_called_once_with("redis://localhost/1")
@mock.patch("health_check.contrib.redis.backends.getattr")
@mock.patch("health_check.contrib.redis.backends.from_url")
def test_redis_timeout_error(self, mocked_connection, mocked_getattr):
"""Test Redis TimeoutError."""
mocked_getattr.return_value = "redis_url"
# mock returns
mocked_connection.return_value = mock.MagicMock()
mocked_connection.return_value.__enter__.side_effect = TimeoutError(
"Timeout Error"
)
# instantiates the class
redis_healthchecker = RedisHealthCheck()
# invokes the method check_status()
redis_healthchecker.check_status()
assert len(redis_healthchecker.errors), 1
# mock assertions
mocked_connection.assert_called_once_with("redis://localhost/1")
@mock.patch("health_check.contrib.redis.backends.getattr")
@mock.patch("health_check.contrib.redis.backends.from_url")
def test_redis_con_limit_exceeded(self, mocked_connection, mocked_getattr):
"""Test Connection Limit Exceeded error."""
mocked_getattr.return_value = "redis_url"
# mock returns
mocked_connection.return_value = mock.MagicMock()
mocked_connection.return_value.__enter__.side_effect = ConnectionError(
"Connection Error"
)
# instantiates the class
redis_healthchecker = RedisHealthCheck()
# invokes the method check_status()
redis_healthchecker.check_status()
assert len(redis_healthchecker.errors), 1
# mock assertions
mocked_connection.assert_called_once_with("redis://localhost/1")
@mock.patch("health_check.contrib.redis.backends.getattr")
@mock.patch("health_check.contrib.redis.backends.from_url")
def test_redis_conn_ok(self, mocked_connection, mocked_getattr):
"""Test everything is OK."""
mocked_getattr.return_value = "redis_url"
# mock returns
mocked_connection.return_value = mock.MagicMock()
mocked_connection.return_value.__enter__.side_effect = True
# instantiates the class
redis_healthchecker = RedisHealthCheck()
# invokes the method check_status()
redis_healthchecker.check_status()
assert len(redis_healthchecker.errors), 0
# mock assertions
mocked_connection.assert_called_once_with("redis://localhost/1")
django-health-check-3.17.0/tests/test_storage.py 0000664 0000000 0000000 00000006762 14305372012 0021611 0 ustar 00root root 0000000 0000000 from unittest import mock
from django.core.files.storage import Storage
from django.test import TestCase
from health_check.exceptions import ServiceUnavailable
from health_check.storage.backends import (
DefaultFileStorageHealthCheck,
StorageHealthCheck,
)
class MockStorage(Storage):
"""
A Mock Storage backend used for testing.
saves - Determines whether save will mock a successful or unsuccessful save
deletes - Determines whether save will mock a successful or unsuccessful deletion
"""
MOCK_FILE_COUNT = 0
saves = None
deletes = None
def __init__(self, saves=True, deletes=True):
super(MockStorage, self).__init__()
self.MOCK_FILE_COUNT = 0
self.saves = saves
self.deletes = deletes
def exists(self, file_name):
return self.MOCK_FILE_COUNT != 0
def delete(self, name):
if self.deletes:
self.MOCK_FILE_COUNT -= 1
def save(self, name, content, max_length=None):
if self.saves:
self.MOCK_FILE_COUNT += 1
def get_file_name(*args, **kwargs):
return "mockfile.txt"
def get_file_content(*args, **kwargs):
return b"mockcontent"
@mock.patch(
"health_check.storage.backends.StorageHealthCheck.get_file_name", get_file_name
)
@mock.patch(
"health_check.storage.backends.StorageHealthCheck.get_file_content",
get_file_content,
)
class HealthCheckStorageTests(TestCase):
"""
Tests health check behavior with a mocked storage backend.
Ensures check_status returns/raises the expected result when the storage works or raises exceptions.
"""
def test_get_storage(self):
"""Test get_storage method returns None on the base class, but a Storage instance on default."""
base_storage = StorageHealthCheck()
self.assertIsNone(base_storage.get_storage())
default_storage = DefaultFileStorageHealthCheck()
self.assertIsInstance(default_storage.get_storage(), Storage)
@mock.patch(
"health_check.storage.backends.DefaultFileStorageHealthCheck.storage",
MockStorage(),
)
def test_check_status_working(self):
"""Test check_status returns True when storage is working properly."""
default_storage_health = DefaultFileStorageHealthCheck()
default_storage = default_storage_health.get_storage()
default_storage_open = "{}.{}.open".format(
default_storage.__module__, default_storage.__class__.__name__
)
with mock.patch(
default_storage_open,
mock.mock_open(read_data=default_storage_health.get_file_content()),
):
self.assertTrue(default_storage_health.check_status())
@mock.patch(
"health_check.storage.backends.DefaultFileStorageHealthCheck.storage",
MockStorage(saves=False),
)
def test_file_does_not_exist(self):
"""Test check_status raises ServiceUnavailable when file is not saved."""
default_storage_health = DefaultFileStorageHealthCheck()
with self.assertRaises(ServiceUnavailable):
default_storage_health.check_status()
@mock.patch(
"health_check.storage.backends.DefaultFileStorageHealthCheck.storage",
MockStorage(deletes=False),
)
def test_file_not_deleted(self):
"""Test check_status raises ServiceUnavailable when file is not deleted."""
default_storage_health = DefaultFileStorageHealthCheck()
with self.assertRaises(ServiceUnavailable):
default_storage_health.check_status()
django-health-check-3.17.0/tests/test_views.py 0000664 0000000 0000000 00000025047 14305372012 0021277 0 ustar 00root root 0000000 0000000 import json
import pytest
from health_check.backends import BaseHealthCheckBackend
from health_check.conf import HEALTH_CHECK
from health_check.exceptions import ServiceWarning
from health_check.plugins import plugin_dir
from health_check.views import MediaType
try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse
class TestMediaType:
def test_lt(self):
assert not MediaType("*/*") < MediaType("*/*")
assert not MediaType("*/*") < MediaType("*/*", 0.9)
assert MediaType("*/*", 0.9) < MediaType("*/*")
def test_str(self):
assert str(MediaType("*/*")) == "*/*; q=1.0"
assert str(MediaType("image/*", 0.6)) == "image/*; q=0.6"
def test_repr(self):
assert repr(MediaType("*/*")) == "MediaType: */*; q=1.0"
def test_eq(self):
assert MediaType("*/*") == MediaType("*/*")
assert MediaType("*/*", 0.9) != MediaType("*/*")
valid_strings = [
("*/*", MediaType("*/*")),
("*/*; q=0.9", MediaType("*/*", 0.9)),
("*/*; q=0", MediaType("*/*", 0.0)),
("*/*; q=0.0", MediaType("*/*", 0.0)),
("*/*; q=0.1", MediaType("*/*", 0.1)),
("*/*; q=0.12", MediaType("*/*", 0.12)),
("*/*; q=0.123", MediaType("*/*", 0.123)),
("*/*; q=1.000", MediaType("*/*", 1.0)),
("*/*; q=1", MediaType("*/*", 1.0)),
("*/*;q=0.9", MediaType("*/*", 0.9)),
("*/* ;q=0.9", MediaType("*/*", 0.9)),
("*/* ; q=0.9", MediaType("*/*", 0.9)),
("*/* ; q=0.9", MediaType("*/*", 0.9)),
("*/*;v=b3", MediaType("*/*")),
("*/*; q=0.5; v=b3", MediaType("*/*", 0.5)),
]
@pytest.mark.parametrize("type, expected", valid_strings)
def test_from_valid_strings(self, type, expected):
assert MediaType.from_string(type) == expected
invalid_strings = [
"*/*;0.9",
'text/html;z=""',
"text/html; xxx",
"text/html; =a",
]
@pytest.mark.parametrize("type", invalid_strings)
def test_from_invalid_strings(self, type):
with pytest.raises(ValueError) as e:
MediaType.from_string(type)
expected_error = '"%s" is not a valid media type' % type
assert expected_error in str(e.value)
def test_parse_header(self):
assert list(MediaType.parse_header()) == [
MediaType("*/*"),
]
assert list(
MediaType.parse_header(
"text/html; q=0.1, application/xhtml+xml; q=0.1 ,application/json"
)
) == [
MediaType("application/json"),
MediaType("text/html", 0.1),
MediaType("application/xhtml+xml", 0.1),
]
class TestMainView:
url = reverse("health_check:health_check_home")
def test_success(self, client):
response = client.get(self.url)
assert response.status_code == 200, response.content.decode("utf-8")
assert response["content-type"] == "text/html; charset=utf-8"
def test_error(self, client):
class MyBackend(BaseHealthCheckBackend):
def check_status(self):
self.add_error("Super Fail!")
plugin_dir.reset()
plugin_dir.register(MyBackend)
response = client.get(self.url)
assert response.status_code == 500, response.content.decode("utf-8")
assert response["content-type"] == "text/html; charset=utf-8"
assert b"Super Fail!" in response.content
def test_warning(self, client):
class MyBackend(BaseHealthCheckBackend):
def check_status(self):
raise ServiceWarning("so so")
plugin_dir.reset()
plugin_dir.register(MyBackend)
response = client.get(self.url)
assert response.status_code == 500, response.content.decode("utf-8")
assert b"so so" in response.content, response.content
HEALTH_CHECK["WARNINGS_AS_ERRORS"] = False
response = client.get(self.url)
assert response.status_code == 200, response.content.decode("utf-8")
assert response["content-type"] == "text/html; charset=utf-8"
assert b"so so" in response.content, response.content
def test_non_critical(self, client):
class MyBackend(BaseHealthCheckBackend):
critical_service = False
def check_status(self):
self.add_error("Super Fail!")
plugin_dir.reset()
plugin_dir.register(MyBackend)
response = client.get(self.url)
assert response.status_code == 200, response.content.decode("utf-8")
assert response["content-type"] == "text/html; charset=utf-8"
assert b"Super Fail!" in response.content
def test_success_accept_json(self, client):
class JSONSuccessBackend(BaseHealthCheckBackend):
def run_check(self):
pass
plugin_dir.reset()
plugin_dir.register(JSONSuccessBackend)
response = client.get(self.url, HTTP_ACCEPT="application/json")
assert response["content-type"] == "application/json"
assert response.status_code == 200
def test_success_prefer_json(self, client):
class JSONSuccessBackend(BaseHealthCheckBackend):
def run_check(self):
pass
plugin_dir.reset()
plugin_dir.register(JSONSuccessBackend)
response = client.get(
self.url, HTTP_ACCEPT="application/json; q=0.8, text/html; q=0.5"
)
assert response["content-type"] == "application/json"
assert response.status_code == 200
def test_success_accept_xhtml(self, client):
class SuccessBackend(BaseHealthCheckBackend):
def run_check(self):
pass
plugin_dir.reset()
plugin_dir.register(SuccessBackend)
response = client.get(self.url, HTTP_ACCEPT="application/xhtml+xml")
assert response["content-type"] == "text/html; charset=utf-8"
assert response.status_code == 200
def test_success_unsupported_accept(self, client):
class SuccessBackend(BaseHealthCheckBackend):
def run_check(self):
pass
plugin_dir.reset()
plugin_dir.register(SuccessBackend)
response = client.get(self.url, HTTP_ACCEPT="application/octet-stream")
assert response["content-type"] == "text/plain"
assert response.status_code == 406
assert (
response.content
== b"Not Acceptable: Supported content types: text/html, application/json"
)
def test_success_unsupported_and_supported_accept(self, client):
class SuccessBackend(BaseHealthCheckBackend):
def run_check(self):
pass
plugin_dir.reset()
plugin_dir.register(SuccessBackend)
response = client.get(
self.url, HTTP_ACCEPT="application/octet-stream, application/json; q=0.9"
)
assert response["content-type"] == "application/json"
assert response.status_code == 200
def test_success_accept_order(self, client):
class JSONSuccessBackend(BaseHealthCheckBackend):
def run_check(self):
pass
plugin_dir.reset()
plugin_dir.register(JSONSuccessBackend)
response = client.get(
self.url,
HTTP_ACCEPT="text/html, application/xhtml+xml, application/json; q=0.9, */*; q=0.1",
)
assert response["content-type"] == "text/html; charset=utf-8"
assert response.status_code == 200
def test_success_accept_order__reverse(self, client):
class JSONSuccessBackend(BaseHealthCheckBackend):
def run_check(self):
pass
plugin_dir.reset()
plugin_dir.register(JSONSuccessBackend)
response = client.get(
self.url,
HTTP_ACCEPT="text/html; q=0.1, application/xhtml+xml; q=0.1, application/json",
)
assert response["content-type"] == "application/json"
assert response.status_code == 200
def test_format_override(self, client):
class JSONSuccessBackend(BaseHealthCheckBackend):
def run_check(self):
pass
plugin_dir.reset()
plugin_dir.register(JSONSuccessBackend)
response = client.get(self.url + "?format=json", HTTP_ACCEPT="text/html")
assert response["content-type"] == "application/json"
assert response.status_code == 200
def test_format_no_accept_header(self, client):
class JSONSuccessBackend(BaseHealthCheckBackend):
def run_check(self):
pass
plugin_dir.reset()
plugin_dir.register(JSONSuccessBackend)
response = client.get(self.url)
assert response.status_code == 200, response.content.decode("utf-8")
assert response["content-type"] == "text/html; charset=utf-8"
def test_error_accept_json(self, client):
class JSONErrorBackend(BaseHealthCheckBackend):
def run_check(self):
self.add_error("JSON Error")
plugin_dir.reset()
plugin_dir.register(JSONErrorBackend)
response = client.get(self.url, HTTP_ACCEPT="application/json")
assert response.status_code == 500, response.content.decode("utf-8")
assert response["content-type"] == "application/json"
assert (
"JSON Error"
in json.loads(response.content.decode("utf-8"))[
JSONErrorBackend().identifier()
]
)
def test_success_param_json(self, client):
class JSONSuccessBackend(BaseHealthCheckBackend):
def run_check(self):
pass
plugin_dir.reset()
plugin_dir.register(JSONSuccessBackend)
response = client.get(self.url, {"format": "json"})
assert response.status_code == 200, response.content.decode("utf-8")
assert response["content-type"] == "application/json"
assert json.loads(response.content.decode("utf-8")) == {
JSONSuccessBackend().identifier(): JSONSuccessBackend().pretty_status()
}
def test_error_param_json(self, client):
class JSONErrorBackend(BaseHealthCheckBackend):
def run_check(self):
self.add_error("JSON Error")
plugin_dir.reset()
plugin_dir.register(JSONErrorBackend)
response = client.get(self.url, {"format": "json"})
assert response.status_code == 500, response.content.decode("utf-8")
assert response["content-type"] == "application/json"
assert (
"JSON Error"
in json.loads(response.content.decode("utf-8"))[
JSONErrorBackend().identifier()
]
)
django-health-check-3.17.0/tests/testapp/ 0000775 0000000 0000000 00000000000 14305372012 0020201 5 ustar 00root root 0000000 0000000 django-health-check-3.17.0/tests/testapp/__init__.py 0000664 0000000 0000000 00000000053 14305372012 0022310 0 ustar 00root root 0000000 0000000 from .celery import app
__all__ = ["app"]
django-health-check-3.17.0/tests/testapp/celery.py 0000664 0000000 0000000 00000000212 14305372012 0022031 0 ustar 00root root 0000000 0000000 from celery import Celery
app = Celery("testapp", broker="memory://")
app.config_from_object("django.conf:settings", namespace="CELERY")
django-health-check-3.17.0/tests/testapp/manage.py 0000775 0000000 0000000 00000000362 14305372012 0022007 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
django-health-check-3.17.0/tests/testapp/settings.py 0000664 0000000 0000000 00000002637 14305372012 0022423 0 ustar 00root root 0000000 0000000 import os.path
import uuid
from kombu import Queue
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DEBUG = True
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
},
"other": { # 2nd database conneciton to ensure proper connection handling
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":backup:",
},
}
INSTALLED_APPS = (
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.staticfiles",
"health_check",
"health_check.cache",
"health_check.db",
"health_check.storage",
"health_check.contrib.celery",
"health_check.contrib.migrations",
"health_check.contrib.celery_ping",
"health_check.contrib.s3boto_storage",
"tests",
)
MIDDLEWARE_CLASSES = (
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
)
STATIC_URL = "/static/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
SITE_ID = 1
ROOT_URLCONF = "tests.testapp.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"APP_DIRS": True,
"OPTIONS": {
"debug": True,
},
},
]
SECRET_KEY = uuid.uuid4().hex
USE_TZ = True
CELERY_QUEUES = [
Queue("default"),
Queue("queue2"),
]
django-health-check-3.17.0/tests/testapp/urls.py 0000664 0000000 0000000 00000000160 14305372012 0021535 0 ustar 00root root 0000000 0000000 from django.urls import include, re_path
urlpatterns = [
re_path(r"^ht/", include("health_check.urls")),
]