Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Lønne committed Sep 19, 2012
0 parents commit e66fc87
Show file tree
Hide file tree
Showing 16 changed files with 537 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist/
*.egg-info/
*.egg/
24 changes: 24 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Copyright (c) 2012, Funkbit AS.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Dokus service nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY FUNKBIT AS ''AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL FUNKBIT AS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# django-doctor

django-doctor is a Django app for checking the operational status of a Django
installation. It includes checking that caching and storage is correctly
set up, that email is working, etc.

This is an early draft, so use it at your own risk.


## Installation

Install `django-doctor` (available on PyPi):

pip install django-doctor

Add it to `INSTALLED_APPS` in your `settings.py`:

INSTALLED_APPS += ['doctor']

And add it to your root urls.py file:

url(r'^doctor/', include('doctor.urls')),


## Settings

These are the available configurable settings, along with their default values:

<table>
<tr>
<th align="left">Name</th>
<th align="left">Default</th>
<th align="left">Description</th>
</tr>
<tr>
<td><code>DOCTOR_BASE_TEMPLATE</code></td>
<td><code>'base.html'</code></td>
<td>The template all the doctor templates should inherit from</td>
</tr>
</table>

## Tests

Run unit tests by running <code>python setup.py test</code>

2 changes: 2 additions & 0 deletions doctor/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
version_info = (0, 1, 0)
__version__ = '.'.join(map(str, version_info))
Empty file added doctor/management/__init__.py
Empty file.
Empty file.
28 changes: 28 additions & 0 deletions doctor/management/commands/test_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from django.contrib.sites.models import Site
from django.core.mail import mail_admins
from django.core.management.base import BaseCommand


class Command(BaseCommand):
"""
Test the sending of email with the mail_admins command.
"""

help = 'Test sending of email.'

def handle(self, *args, **options):

verbosity = int(options.get('verbosity', 1))

message = 'This is a test mail from %(site_name)s. If you see this, mail is working :)' % {
'site_name': Site.objects.get_current().name,
}

try:
mail_admins('Test mail', message, fail_silently=False)

if verbosity > 0:
self.stdout.write('Mail successfully sent to admins.\n')

except Exception as exc:
self.stderr.write('Sending test mail failed! Exception was: %s\n' % str(exc))
45 changes: 45 additions & 0 deletions doctor/management/commands/test_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.core.management.base import BaseCommand


class Command(BaseCommand):
"""
Test various file storage operations, to ensure that integrations (with ie.
Amazon S3) is working properly.
"""

help = 'Test file storage operations.'

def handle(self, *args, **options):

verbosity = int(options.get('verbosity', 1))
filename = 'storage_test'

if verbosity > 0:
self.stdout.write('Storage used: %s\n' % settings.DEFAULT_FILE_STORAGE)

# Create a file
default_storage.save(filename, ContentFile('We are testing, 1 2 three.'))

# Check for existence
file_exists = default_storage.exists(filename)

if verbosity > 0:
self.stdout.write('Does newly created file exist? %s\n' % file_exists)

# Read back the file
f = default_storage.open(filename, 'r')
file_contents = f.read()
f.close()

if verbosity > 0:
self.stdout.write('Contents: "%s"\n' % file_contents)

# Delete the file
default_storage.delete(filename)
file_exists = default_storage.exists(filename)

if verbosity > 0:
self.stdout.write('Does file exist after deletion? %s\n' % file_exists)
Empty file added doctor/models.py
Empty file.
1 change: 1 addition & 0 deletions doctor/templates/doctor/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% extends base_template %}
57 changes: 57 additions & 0 deletions doctor/templates/doctor/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{% extends "doctor/base.html" %}
{% load i18n %}

{% block title %}{% trans 'Health checks' %} &mdash; {{ block.super }}{% endblock %}

{% block content %}
<h2>
{% trans 'Health checks' %}
</h2>

<h3>
{% trans 'Cache' %}
</h3>

<table class="table">
<tbody>
{% for cache_name, info in cache.iteritems %}
<tr>
<td style="width: 40%;">
<h4>
{{ cache_name }}
</h4>

{% if info.is_working %}
<strong>{% trans 'Status:' %}</strong> {% trans 'OK' %}<br>
<small class="muted">{{ info.message }}</small>
{% else %}
<div class="alert alert-error">
<h4>{% trans 'Error' %}</h4>
{{ info.message }}
</div>
{% endif %}
</td>
<td>

<table class="table table-bordered table-condensed">
<thead>
<th>{% trans 'Setting' %}</th>
<th>{% trans 'Value' %}</th>
</thead>
<tbody>
{% for key, val in info.settings.iteritems %}
<tr>
<td><code>{{ key }}</code></td>
<td><code>{{ val }}</code></td>
</tr>
{% endfor %}
</tbody>
</table>

</td>
</tr>
{% endfor %}
</tbody>
</table>

{% endblock %}
47 changes: 47 additions & 0 deletions doctor/templates/doctor/technical_info.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{% extends "doctor/base.html" %}
{% load i18n %}

{% block title %}{% trans 'Technical information' %} &mdash; {{ block.super }}{% endblock %}

{% block content %}
<h2>
{% trans 'Technical information' %}
</h2>


<h3>
{% trans 'Module versions' %}
</h3>

<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Version' %}</th>
</tr>
</thead>
<tbody>
{% for package, version in versions.iteritems %}
<tr>
<td>{{ package }}</td>
<td>{{ version }}</td>
</tr>
{% endfor %}
</tbody>
</table>


<h3>
{% trans 'Environment' %}
</h3>

<pre>{% for key,val in environ.items %}{{ key }}: {{ val }}<br>{% endfor %}</pre>


<h3>
{% trans 'Paths' %}
</h3>

<pre>{% for path in paths %}{{ path }}<br>{% endfor %}</pre>

{% endblock %}
94 changes: 94 additions & 0 deletions doctor/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.test import TestCase


class DoctorAuthorizationTests(TestCase):

def setUp(self):

# Create active user to test with
user = User(username='example_user', is_active=True)
user.set_password('1234')
user.save()

self.user = user

def testAnonymous(self):

# Available for all users (for now)
response = self.client.get(reverse('doctor-health-check'))
self.assertEquals(response.status_code, 200)
self.assertEquals(response['Content-Type'], 'text/plain')

# Check non-accessable pages
response = self.client.get(reverse('doctor-index'))
self.assertEquals(response.status_code, 404)

response = self.client.get(reverse('doctor-technical'))
self.assertEquals(response.status_code, 404)

response = self.client.get(reverse('doctor-server-error'))
self.assertEquals(response.status_code, 404)

def testNonPrivileged(self):

# Sign in user
self.client.login(username=self.user.username, password='1234')

# Available for all users (for now)
response = self.client.get(reverse('doctor-health-check'))
self.assertEquals(response.status_code, 200)
self.assertEquals(response['Content-Type'], 'text/plain')

# Check non-accessable pages
response = self.client.get(reverse('doctor-index'))
self.assertEquals(response.status_code, 404)

response = self.client.get(reverse('doctor-technical'))
self.assertEquals(response.status_code, 404)

response = self.client.get(reverse('doctor-server-error'))
self.assertEquals(response.status_code, 404)


class DoctorViewTests(TestCase):

def setUp(self):

# Create super user to test with
user = User(username='example_user', is_active=True, is_superuser=True)
user.set_password('1234')
user.save()

self.user = user

def testPrivileged(self):

# Sign in user
self.client.login(username=self.user.username, password='1234')

# Check pages
response = self.client.get(reverse('doctor-index'))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'doctor/index.html')

response = self.client.get(reverse('doctor-health-check'))
self.assertEquals(response.status_code, 200)
self.assertEquals(response['Content-Type'], 'text/plain')

response = self.client.get(reverse('doctor-technical'))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'doctor/technical_info.html')

def testForceServerError(self):

# Make superuser
self.user.is_superuser = True
self.user.save()

# Sign in user
self.client.login(username=self.user.username, password='1234')

with self.assertRaises(Exception):
response = self.client.get(reverse('doctor-server-error'))
11 changes: 11 additions & 0 deletions doctor/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.conf.urls import patterns, include, url

urlpatterns = patterns('doctor.views',

url(r'^health-check/$', 'health_check', name='doctor-health-check'),
url(r'^technical/$', 'technical_info', name='doctor-technical'),
url(r'^server-error/$', 'force_server_error', name='doctor-server-error'),

url(r'^$', 'index', name='doctor-index'),

)
Loading

0 comments on commit e66fc87

Please sign in to comment.