Collect and run healthchecks

Given you have healthchecks, there are two main ways to collect and run healthchecks:

  1. run a shell command:
  2. perform GET requests to a web service: using hospital.wsgi module...

If you want to setup another runner, notice that you can use the is_healthcheck() function to verify whether an object is a healthcheck or not.

hospital-cli

You can use hospital-cli to collect and run healthchecks in a shell session:

$ hospital-cli hospital.healthchecks.predictable
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

See hospital-cli --help for detailed usage and options.

Nose

Here are guidelines to collect and run healthchecks with Nose test runner [1]. In the examples below, we run health checks of hospital project.

$ nosetests --all-modules --attr='is_healthcheck' hospital.healthchecks.predictable
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

Reciprocally, you may want to skip healthchecks when you run tests. With nose, it could be:

$ nosetests --all-modules --attr='!is_healthcheck' hospital.healthchecks

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Tip

–all-modules option [2] makes Nose collect tests in all modules. Without the option, it would have collected modules or packages named “tests”, and not “healthchecks”.

py.test

hospital provides a pytest plugin to run healthchecks. It registers a pytest marker [3] called “healthcheck”.

$ py.test -m healthcheck foo
======================================== test session starts ========================================
platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2
plugins: hospital
collected 3 items

foo/test_foo.py ..

============================= 1 tests deselected by "-m 'healthcheck'" ==============================
============================== 2 passed, 1 deselected in 0.01 seconds ===============================

In the example above, we see that three tests were collected, and two of those were health checks.

Tests that use hospital’s healthcheck() decorator are automatically marked with @pytest.mark.healthcheck.

As another example, here is how to run only non-healthchecks:

$ py.test -m "not healthcheck" foo
======================================== test session starts ========================================
platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2
plugins: hospital
collected 3 items

foo/test_foo.py .

=========================== 2 tests deselected by "-m 'not healthcheck'" ============================
============================== 1 passed, 2 deselected in 0.01 seconds ===============================

hospital-serve

hospital.wsgi embeds a basic HTTP server:

$ hospital-serve hospital.healthchecks.predictable
Serving on 0.0.0.0 port 1515...

See hospital-serve --help for detailed usage and options.

Each time you perform a GET on the server’s root, healthchecks are collected and run.

The status code of the response is 200 in case all healthchecks passed, else it is 500.

$ curl -X GET -I http://localhost:1515/
HTTP/1.0 200 OK
Date: Fri, 28 Feb 2014 13:19:08 GMT
Server: WSGIServer/0.1 Python/2.7.5+
Content-Type: application/json; charset=utf-8
Content-Length: 520

The output is JSON:

{
    "status": "pass",
    "details": [
        {
            "test": "Health checks are collected.",
            "status": "pass"
        }
    ],
    "summary": {
        "skip": 0,
        "pass": 1,
        "expected_failure": 0,
        "error": 0,
        "fail": 0,
        "total": 1,
        "unexpected_success": 0
    }
}

Gunicorn

You can use hospital.wsgi.application WSGI endpoint with Gunicorn. Setup the list of healthchecks to run in HEALTHCHECKS environment variable (space separated).

export HEALTHCHECKS='hospital.healthchecks.predictable'
gunicorn -w 4 hospital.wsgi:application

Which can also be written in a single line:

HEALTHCHECKS='hospital.healthchecks.predictable' gunicorn -w 4 hospital.wsgi:application

See Gunicorn documentation [4] for details.

Chaussette

You can use hospital.wsgi.application WSGI endpoint with Chaussette. Setup the list of healthchecks to run in HEALTHCHECKS environment variable (space separated).

export HEALTHCHECKS='hospital.healthchecks.predictable'
chaussette hospital.wsgi.application

Which can also be written in a single line:

HEALTHCHECKS='hospital.healthchecks.predictable' chaussette hospital.wsgi.application

See Chaussette documentation [5] for details.

Circus

Here is a sample Circus configuration serving “hospital.healthchecks.predictable” healthchecks on port 2014, using Chaussette and a virtual environnement.

[watcher:yourproject-healthchecks]
# Service command.
cmd = bin/chaussette --fd $(circus.sockets.yourproject-healthchecks) hospital.wsgi.application
numprocesses = 1
use_sockets = True
copy_env = True
working_dir = path/to/your-virtualenv
# Stdout logs.
stdout_stream.class = FileStream
stdout_stream.filename = /var/log/chaussette/wsgi_healthchecks_out.log
stdout_stream.time_format = [%Y/%m/%d | %H:%M:%S]
# Stderr logs.
stderr_stream.class = FileStream
stderr_stream.filename = /var/log/chaussette/wsgi_healthchecks_err.log
stderr_stream.time_format = [%Y/%m/%d | %H:%M:%S]

[socket:yourproject-healthchecks]
host = 127.0.0.1
port = 2014

[env:yourproject-healthchecks]
HEALTHCHECKS = hospital.healthchecks.predictable

See Circus documentation [6] for details.

twod.wsgi and Django

You can use twod.wsgi and hospital.wsgi.HealthCheckApp to serve healthchecks with Django [7]. In your project’s urls.py module:

from django.core.urlresolvers import patterns
from twod.wsgi import make_wsgi_view
from hospital.wsgi import HealthCheckApp

urlpatterns = patterns('',
    # ...
    (r'^healthchecks(/.*)$', make_wsgi_view(HealthCheckApp(discover=['hospital.healthchecks.predictable']))),
    # ...
)

See twod.wsgi documentation [8] for details.

Notes & references

[1]https://pypi.python.org/pypi/nose
[2]http://nose.readthedocs.org/en/latest/usage.html#cmdoption–all-modules
[3]http://pytest.org/latest/mark.html
[4]http://gunicorn.org/
[5]https://pypi.python.org/pypi/chaussette
[6]https://circus.readthedocs.org/en/latest/
[7]https://www.djangoproject.com/
[8]https://pythonhosted.org/twod.wsgi/embedded-apps.html