Merge lp:~bloodearnest/django-preflight/gargoyle into lp:~canonical-isd-hackers/django-preflight/gargoyle

Proposed by Simon Davy
Status: Superseded
Proposed branch: lp:~bloodearnest/django-preflight/gargoyle
Merge into: lp:~canonical-isd-hackers/django-preflight/gargoyle
Diff against target: 1016 lines (+274/-327)
20 files modified
MANIFEST.in (+1/-0)
debian/changelog (+9/-0)
debian/rules (+0/-1)
doc/conf.py (+1/-1)
doc/install.rst (+13/-9)
example_project/app/preflight.py (+0/-38)
example_project/app/templates/base.html (+0/-57)
example_project/manage.py (+0/-13)
example_project/run.py (+0/-31)
example_project/settings.py (+0/-44)
example_project/urls.py (+0/-15)
preflight/__init__.py (+1/-1)
preflight/conf.py (+6/-0)
preflight/models.py (+41/-8)
preflight/templates/preflight/overview.html (+32/-14)
preflight/tests.py (+117/-31)
preflight/views.py (+8/-9)
setup.py (+17/-13)
tox (+0/-6)
tox.ini (+28/-36)
To merge this branch: bzr merge lp:~bloodearnest/django-preflight/gargoyle
Reviewer Review Type Date Requested Status
Canonical ISD hackers Pending
Review via email: mp+166838@code.launchpad.net

Description of the change

Add support for showing switches. Initially just gargoyle.

To post a comment you must log in.

Unmerged revisions

26. By Simon Davy

more generic (less gargoyle specific), more gargoyle tests, embedded json switch data for automated scraping

25. By Simon Davy

Merge mfoord's branch into trunk, and make gargoyle optional in the process

24. By Łukasz Czyżykowski

[r=ricardokirkner] Updated code and tests to work with Django 1.5; dropped support for Python 2.5

23. By Anthony Lenton

[r=james-w] Fixed a silly bug that meant that hidden settings weren't really configurable via the PREFLIGHT_HIDDEN_SETTINGS setting.

22. By Łukasz Czyżykowski

Updated debian/rules to be compatible with precise.

21. By Łukasz Czyżykowski

Added entry in the changelog

20. By Łukasz Czyżykowski

Merged the 1.4-release branch

19. By Anthony Lenton

[r=lukasz-czyzykowski] Added configurable hidden settings.

18. By Māris Fogels

[r=lukasz-czyzykowski] Changed the default display template to the Django admin theme.

17. By Jonathan Lange

[r=lukasz-czyzykowski] Add platform to default versions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'MANIFEST.in'
--- MANIFEST.in 1970-01-01 00:00:00 +0000
+++ MANIFEST.in 2013-05-31 15:53:26 +0000
@@ -0,0 +1,1 @@
1include preflight/templates/preflight/*.html
0\ No newline at end of file2\ No newline at end of file
13
=== modified file 'debian/changelog'
--- debian/changelog 2011-10-04 14:33:24 +0000
+++ debian/changelog 2013-05-31 15:53:26 +0000
@@ -1,3 +1,12 @@
1django-preflight (0.1.4) precise; urgency=low
2
3 * Added display of settings on the preflight page
4 * Added configurable list of settings to hide
5 * Changed default base tamplate to Django admin base
6 * Added platform to default versions
7
8 -- Łukasz Czyżykowski <lukasz.czyzykowski@canonical.com> Wed, 03 Oct 2012 09:45:22 +0000
9
1django-preflight (0.1.1) lucid; urgency=low10django-preflight (0.1.1) lucid; urgency=low
211
3 * Use RequestContext instance when rendering the overview view12 * Use RequestContext instance when rendering the overview view
413
=== modified file 'debian/rules'
--- debian/rules 2011-02-07 16:06:51 +0000
+++ debian/rules 2013-05-31 15:53:26 +0000
@@ -4,4 +4,3 @@
44
5include /usr/share/cdbs/1/rules/debhelper.mk5include /usr/share/cdbs/1/rules/debhelper.mk
6include /usr/share/cdbs/1/class/python-distutils.mk6include /usr/share/cdbs/1/class/python-distutils.mk
7include /usr/share/cdbs/1/rules/langpack.mk
87
=== modified file 'doc/conf.py'
--- doc/conf.py 2011-02-09 16:26:04 +0000
+++ doc/conf.py 2013-05-31 15:53:26 +0000
@@ -18,7 +18,7 @@
18# documentation root, use os.path.abspath to make it absolute, like shown here.18# documentation root, use os.path.abspath to make it absolute, like shown here.
19sys.path.append(os.path.abspath('..'))19sys.path.append(os.path.abspath('..'))
2020
21os.environ['DJANGO_SETTINGS_MODULE'] = 'example_project.settings'21os.environ['DJANGO_SETTINGS_MODULE'] = 'preflight_example_project.settings'
2222
23# -- General configuration -----------------------------------------------------23# -- General configuration -----------------------------------------------------
2424
2525
=== modified file 'doc/install.rst'
--- doc/install.rst 2012-05-25 16:34:25 +0000
+++ doc/install.rst 2013-05-31 15:53:26 +0000
@@ -38,19 +38,21 @@
3838
39django-preflight by itself doesn't have any extra dependecies.39django-preflight by itself doesn't have any extra dependecies.
4040
41Because this project doesn't include any database models, there's no41Because this project doesn't include any models, there's no need of
42need of updating your database schema.42updating your database schema.
4343
4444
45Update ``urls.py``45Update ``urls.py``
46------------------46------------------
4747
48Last bit of configuration is to include django-preflight into the48Last bit of configuration is to run preflight discovery code and to
49project's ``urls.py`` file. It should look like the following:49include it somewhere in the url's definition. The easiest way for
50doing both of those steps is to modify project's ``urls.py`` file. It
51should look like the following:
5052
51.. testcode::53.. testcode::
5254
53 from django.conf.urls.defaults import *55 from django.conf.urls import *
5456
55 import preflight57 import preflight
56 import preflight.urls58 import preflight.urls
@@ -78,10 +80,12 @@
78Compatibility80Compatibility
79-------------81-------------
8082
81django-preflight is tested with released Django versions from83django-preflight is compatible with released Django versions since
821.1 to 1.4. To be sure about this it's tested841.1, up to current 1.5. To be sure about this it's tested using tox_
83with tox_ against all of these versions.85against all of them.
8486
85Additionally it's tested on Python 2.5, 2.6 and 2.7, all on Ubuntu Linux.87Additionally it's tested on Python 2.6 and 2.7, all of that on `Ubuntu
88Linux`_.
8689
87.. _tox: http://codespeak.net/tox/90.. _tox: http://codespeak.net/tox/
91 _Ubuntu Linux: http://www.ubuntu.com
8892
=== removed directory 'example_project'
=== removed file 'example_project/__init__.py'
=== removed directory 'example_project/app'
=== removed file 'example_project/app/__init__.py'
=== removed file 'example_project/app/models.py'
=== removed file 'example_project/app/preflight.py'
--- example_project/app/preflight.py 2011-02-07 16:06:51 +0000
+++ example_project/app/preflight.py 1970-01-01 00:00:00 +0000
@@ -1,38 +0,0 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4from __future__ import absolute_import
5
6from django.conf import settings
7
8import os.path
9import preflight
10import tempfile
11
12
13class AppPreflight(preflight.Preflight):
14
15 def authenticate(self, request):
16 # Allow everybody to access this page. Default implementation restricts
17 # this to people who can access /admin/ pages (user.is_stuff == True).
18 return True
19
20 def versions(self):
21 return [{'name': 'foo-bar', 'version': '1.2.3'}]
22
23 def check_media_root_is_writable(self):
24 """Check if current MEDIA_ROOT directory is writable."""
25 path = os.path.realpath(settings.MEDIA_ROOT)
26
27 # Try writing to temporary file in MEDIA_ROOT
28 tmp = tempfile.TemporaryFile(dir=path)
29 tmp.write("test")
30 tmp.close()
31
32 # If we reached this point this means the directory itself is
33 # writable. In case of any errors an exception would cause the check to
34 # fail. Return value informs preflight that everything went right.
35 return True
36
37
38preflight.register(AppPreflight)
390
=== removed directory 'example_project/app/templates'
=== removed file 'example_project/app/templates/base.html'
--- example_project/app/templates/base.html 2011-02-07 16:06:51 +0000
+++ example_project/app/templates/base.html 1970-01-01 00:00:00 +0000
@@ -1,57 +0,0 @@
1{# Copyright 2010 Canonical Ltd. This software is licensed under the #}
2{# GNU Affero General Public License version 3 (see the file LICENSE). #}
3<html>
4 <head>
5 <title>{% block title %}{% endblock %}</title>
6 <style>
7 #content {
8 font: 13.34px helvetica, arial, freesans, clean, sans-serif;
9 width: 600px;
10 margin: 32px auto;
11 border: 1px solid #ddd;
12 padding: 2em;
13 background-color: #f0f0f0;
14 }
15 #content p {
16 margin-top: 32px;
17 border-top: 1px solid #999;
18 text-align: center;
19 }
20 #content h1 {
21 margin: 64px 0 32px;
22 border-bottom: 3px solid #555;
23 }
24 #content h2 {
25 margin: 32px 0;
26 border-bottom: 3px solid #999;
27 }
28 #content table {
29 padding: 0;
30 border-spacing: 0;
31 width: 100%;
32 }
33 #content table th {
34 border-bottom: 1px solid #aaa;
35 }
36 #content table td,
37 #content table th {
38 padding: 0.3em;
39 }
40 #content .ok {
41 color: green;
42 }
43 #content .error {
44 color: red;
45 }
46 #content td.status {
47 text-align: center;
48 }
49 #content td.description {
50 font-size: 80%;
51 }
52 </style>
53 </head>
54 <body>
55 <div id="content">{% block content %}{% endblock %}</div>
56 </body>
57</html>
580
=== removed file 'example_project/manage.py'
--- example_project/manage.py 2011-02-07 16:06:51 +0000
+++ example_project/manage.py 1970-01-01 00:00:00 +0000
@@ -1,13 +0,0 @@
1#!/usr/bin/python
2import sys
3sys.path.append('..')
4
5from django.core.management import execute_manager
6try:
7 import settings # Assumed to be in the same directory.
8except ImportError:
9 sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
10 sys.exit(1)
11
12if __name__ == "__main__":
13 execute_manager(settings)
140
=== removed file 'example_project/run.py'
--- example_project/run.py 2011-02-07 16:06:51 +0000
+++ example_project/run.py 1970-01-01 00:00:00 +0000
@@ -1,31 +0,0 @@
1import os
2import sys
3
4os.environ['DJANGO_SETTINGS_MODULE'] = 'example_project.settings'
5sys.path.insert(0, 'example_project')
6
7try:
8 from django.test.utils import get_runner
9except ImportError:
10 from django.test.simple import run_tests
11 get_runner = lambda s: run_tests
12
13from django.conf import settings
14
15
16
17def tests():
18 TestRunner = get_runner(settings)
19 if hasattr(TestRunner, 'func_name'):
20 # Before change in Django 1.2
21 failures = TestRunner(['preflight'])
22 else:
23 test_runner = TestRunner()
24 failures = test_runner.run_tests(['preflight'])
25
26 sys.exit(bool(failures))
27
28
29
30if __name__ == '__main__':
31 tests()
320
=== removed file 'example_project/settings.py'
--- example_project/settings.py 2012-05-25 16:31:15 +0000
+++ example_project/settings.py 1970-01-01 00:00:00 +0000
@@ -1,44 +0,0 @@
1import os
2
3DEBUG = True
4TEMPLATE_DEBUG = DEBUG
5
6ADMINS = ()
7MANAGERS = ADMINS
8
9DATABASES = {
10 'default': {
11 'ENGINE': 'django.db.backends.sqlite3',
12 'NAME': 'database.sqlit3',
13 }
14}
15
16# Old db setup
17DATABASE_ENGINE = 'sqlite3'
18DATABASE_NAME = 'database.sqlite3'
19
20TIME_ZONE = 'America/Chicago'
21LANGUAGE_CODE = 'en-us'
22SITE_ID = 1
23MEDIA_ROOT = ''
24MEDIA_URL = ''
25ADMIN_MEDIA_PREFIX = '/media/'
26SECRET_KEY = 'j0-wo!y4zwi)tejv1u(0skc41_379ls^@qbh91%#5o=greje6('
27
28ROOT_URLCONF = 'example_project.urls'
29
30TEMPLATE_DIRS = (
31 os.path.join(os.path.realpath(os.path.dirname(__file__)), 'templates'),
32)
33
34INSTALLED_APPS = (
35 'django.contrib.auth',
36 'django.contrib.contenttypes',
37 'django.contrib.sessions',
38 'django.contrib.sites',
39 'preflight',
40 'app',
41 'gargoyle',
42)
43
44PREFLIGHT_BASE_TEMPLATE = 'base.html'
450
=== removed file 'example_project/urls.py'
--- example_project/urls.py 2011-10-04 15:54:50 +0000
+++ example_project/urls.py 1970-01-01 00:00:00 +0000
@@ -1,15 +0,0 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4from django.conf.urls.defaults import (
5 handler404, handler500, include, patterns
6)
7
8import preflight
9
10
11preflight.autodiscover()
12
13urlpatterns = patterns('',
14 (r'^preflight/', include('preflight.urls')),
15)
160
=== modified file 'preflight/__init__.py'
--- preflight/__init__.py 2011-03-19 15:15:41 +0000
+++ preflight/__init__.py 2013-05-31 15:53:26 +0000
@@ -5,7 +5,7 @@
55
6from .models import REGISTRY6from .models import REGISTRY
77
8__version__ = "0.1.1"8__version__ = "0.1.5"
99
1010
11def autodiscover():11def autodiscover():
1212
=== added file 'preflight/conf.py'
--- preflight/conf.py 1970-01-01 00:00:00 +0000
+++ preflight/conf.py 2013-05-31 15:53:26 +0000
@@ -0,0 +1,6 @@
1from django.conf import settings
2
3
4BASE_TEMPLATE = getattr(
5 settings, 'PREFLIGHT_BASE_TEMPLATE', "admin/base.html")
6TABLE_CLASS = getattr(settings, 'PREFLIGHT_TABLE_CLASS', "listing")
07
=== modified file 'preflight/models.py'
--- preflight/models.py 2012-05-25 16:31:15 +0000
+++ preflight/models.py 2013-05-31 15:53:26 +0000
@@ -2,6 +2,8 @@
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4from __future__ import absolute_import4from __future__ import absolute_import
5
6import re
5from django.conf import settings7from django.conf import settings
6from django.views.debug import HIDDEN_SETTINGS8from django.views.debug import HIDDEN_SETTINGS
79
@@ -75,11 +77,13 @@
75 data.77 data.
7678
77 """79 """
80 import platform
78 import sys81 import sys
79 import django82 import django
80 import preflight83 import preflight
8184
82 items = [85 items = [
86 {'name': 'Platform', 'version': platform.platform()},
83 {'name': 'Django', 'version': django.get_version()},87 {'name': 'Django', 'version': django.get_version()},
84 {'name': 'Python', 'version': sys.version},88 {'name': 'Python', 'version': sys.version},
85 {'name': 'preflight', 'version': preflight.__version__}89 {'name': 'preflight', 'version': preflight.__version__}
@@ -115,8 +119,13 @@
115 names = sorted([x for x in settings._wrapped.__dict__119 names = sorted([x for x in settings._wrapped.__dict__
116 if x.isupper() and not x.startswith('_')])120 if x.isupper() and not x.startswith('_')])
117 settings_list = []121 settings_list = []
122 hidden_settings = getattr(settings, 'PREFLIGHT_HIDDEN_SETTINGS', None)
123 if hidden_settings:
124 hidden_settings = re.compile(hidden_settings)
125 else:
126 hidden_settings = HIDDEN_SETTINGS
118 for name in names:127 for name in names:
119 if HIDDEN_SETTINGS.match(name):128 if hidden_settings.search(name):
120 value = '******************'129 value = '******************'
121 else:130 else:
122 value = getattr(settings, name, None)131 value = getattr(settings, name, None)
@@ -135,16 +144,40 @@
135 return all(class_().authenticate(request) for class_ in REGISTRY)144 return all(class_().authenticate(request) for class_ in REGISTRY)
136145
137146
147def gather_switches():
148 """Gather all switches into one list."""
149 switches = {}
150 if settings.USE_GARGOYLE:
151 gargoyle_switches = gather_gargoyle()
152 if gargoyle_switches:
153 switches['gargoyle'] = gargoyle_switches
154 return switches
155
156
138def gather_gargoyle():157def gather_gargoyle():
158 if not settings.USE_GARGOYLE:
159 return None
139 try:160 try:
140 from gargoyle.models import Switch161 from gargoyle.models import Switch
141 except ImportError:162 except ImportError:
142 return None163 return None
143 switches = Switch.objects.all()164 return [_format_gargoyle(switch) for switch in Switch.objects.all()]
144 return [_format_switch(switch) for switch in switches]165
145166
146def _format_switch(switch):167def _format_gargoyle(switch):
168 from gargoyle import gargoyle
169 """Formats a gargoyle switch into std format for display"""
170 d = switch.to_dict(gargoyle)
171 conditions = []
172 for condition in d['conditions']:
173 params = ','.join('%s=%s' % (c[0], c[2])
174 for c in condition['conditions'])
175 conditions.append("%s(%s)" % (condition['label'], params))
176
147 return dict(177 return dict(
148 name=switch.key, description=switch.description or '',
149 value=switch.value, status=switch.get_status_label()
150 )
151\ No newline at end of file178\ No newline at end of file
179 name=d['key'],
180 description=d['description'],
181 status=d['status'],
182 status_text=d['statusLabel'],
183 conditions=conditions,
184 )
152185
=== modified file 'preflight/templates/preflight/overview.html'
--- preflight/templates/preflight/overview.html 2012-05-25 16:31:15 +0000
+++ preflight/templates/preflight/overview.html 2013-05-31 15:53:26 +0000
@@ -17,7 +17,9 @@
17 {% endfor %}17 {% endfor %}
18 <li><a href="#versions">Versions</a></li>18 <li><a href="#versions">Versions</a></li>
19 <li><a href="#settings">Settings</a></li>19 <li><a href="#settings">Settings</a></li>
20 <li><a href="#gargoyle">Gargoyle</a></li>20 {% if switches %}
21 <li><a href="#switches">Switches</a></li>
22 {% endif %}
21 </ul>23 </ul>
2224
23 {% for application in applications %}25 {% for application in applications %}
@@ -81,32 +83,48 @@
81 {% endfor %}83 {% endfor %}
82 </dl>84 </dl>
8385
86 {% if switches %}
84 <a href="#links" style="text-decoration : none;">87 <a href="#links" style="text-decoration : none;">
85 <h1 id="gargoyle">{% trans "Gargoyle Switches" %}</h1>88 <h1 id="switches">{% trans "Switches" %}</h1>
86 </a>89 </a>
87 {% if gargoyle %}90 <table id="switches-table" class="{{ preflight_table_class }}">
88 <table id="gargoyle-table" class="{{ preflight_table_class }}">
89 <thead>91 <thead>
90 <tr>92 <tr>
91 <th>{% trans "Name" %}</th>93 <th>{% trans "Name" %}</th>
92 <th>{% trans "Description" %}</th>94 <th>{% trans "Description" %}</th>
93 <th>{% trans "value" %}</th>95 <th>{% trans "Conditions" %}</th>
94 <th>{% trans "Status" %}</th>96 <th>{% trans "Status" %}</th>
95 </tr>97 </tr>
96 </thead>98 </thead>
97 <tbody>99 <tbody>
98 {% for item in gargoyle %}100 {% for type, type_switches in switches.items %}
99 <tr>101 <tr><th colspan="4">{{ type }}</th></tr>
100 <td>{{ item.name }}</td>102 {% for switch in type_switches %}
101 <td>{{ item.description }}</td>103 <tr class="switch">
102 <td>{{ item.value }}</td>104 <td>{{ switch.name }}</td>
103 <td>{{ item.status }}</td>105 <td>{{ switch.description }}</td>
104 </tr>106 <td>
107 {% if switch.conditions %}
108 <ul>
109 {% for condition in switch.conditions %}
110 <li>{{ condition|first }}: {{ condition|last }}</li>
111 {% endfor %}
112 </ul>
113 {% endif %}
114 </td>
115 <td>{{ switch.status_text }}</td>
116 {% endfor %}
117 </tr>
105 {% endfor %}118 {% endfor %}
106 </tbody>119 </tbody>
107 </table>120 </table>
121 <p>Switch data is included in JSON format in a script tag with id
122 "switches-json" to aid automated scripting</p>
123 <script id="switches-json">
124 {{ switches_json|safe }}
125 </script>
108 {% else %}126 {% else %}
109 <p>No gargoyle switches.</p>127 <p>No switches.</p>
110 {% endif %}128 {% endif %}
111</div>129</div>
112130
113131
=== modified file 'preflight/tests.py'
--- preflight/tests.py 2012-05-25 17:19:29 +0000
+++ preflight/tests.py 2013-05-31 15:53:26 +0000
@@ -1,19 +1,26 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
3import sys3import sys
4import json
5try:
6 from unittest import skipIf
7except ImportError:
8 from unittest2 import skipIf # NOQA
49
10from django.conf import settings
5from django.core.management import call_command11from django.core.management import call_command
6from django.core.urlresolvers import reverse12from django.core.urlresolvers import reverse
7from django.http import HttpResponse13from django.http import HttpResponse
8from django.test import TestCase14from django.test import TestCase
9from django.template import RequestContext, TemplateDoesNotExist15from django.template import RequestContext
1016from django.template.loader import render_to_string
11from mock import (17from mock import (
12 Mock,18 Mock,
13 patch,19 patch,
14)20)
1521
16from cStringIO import StringIO22from cStringIO import StringIO
23from pyquery import PyQuery
1724
18from . import Preflight25from . import Preflight
19from .models import (26from .models import (
@@ -22,11 +29,12 @@
22 REGISTRY,29 REGISTRY,
23 authenticate,30 authenticate,
24 gather_checks,31 gather_checks,
32 gather_switches,
25 gather_gargoyle,33 gather_gargoyle,
26 gather_settings,34 gather_settings,
27 gather_versions,35 gather_versions,
28)36)
2937from preflight.conf import BASE_TEMPLATE, TABLE_CLASS
3038
3139
32class CheckTestCase(TestCase):40class CheckTestCase(TestCase):
@@ -48,7 +56,8 @@
48 self.assertEquals(check.description, "description")56 self.assertEquals(check.description, "description")
4957
50 def test_initialisation_when_doc_is_none(self):58 def test_initialisation_when_doc_is_none(self):
51 def method(): pass59 def method():
60 pass
5261
53 check = Check("check_name", method)62 check = Check("check_name", method)
5463
@@ -144,6 +153,7 @@
144 def authenticate(self, request):153 def authenticate(self, request):
145 return False154 return False
146155
156
147def clear_registry():157def clear_registry():
148 while REGISTRY:158 while REGISTRY:
149 REGISTRY.pop()159 REGISTRY.pop()
@@ -184,6 +194,7 @@
184 def versions(self):194 def versions(self):
185 return [{'name': 'spam', 'version': 'ni'}]195 return [{'name': 'spam', 'version': 'ni'}]
186196
197
187class ExtraVersionAsList(Preflight):198class ExtraVersionAsList(Preflight):
188199
189 versions = [{'name': 'eggs', 'version': 'peng'}]200 versions = [{'name': 'eggs', 'version': 'peng'}]
@@ -196,7 +207,7 @@
196207
197 versions = gather_versions()208 versions = gather_versions()
198209
199 self.assertEquals(len(versions), 3)210 self.assertEquals(len(versions), 4)
200 for item in versions:211 for item in versions:
201 self.assertTrue('name' in item and 'version' in item)212 self.assertTrue('name' in item and 'version' in item)
202213
@@ -206,7 +217,7 @@
206217
207 versions = gather_versions()218 versions = gather_versions()
208219
209 self.assertEquals(len(versions), 4)220 self.assertEquals(len(versions), 5)
210 self.assertEquals(versions[-1]['name'], 'spam')221 self.assertEquals(versions[-1]['name'], 'spam')
211222
212 def test_get_extra_version_information_from_class_attribute(self):223 def test_get_extra_version_information_from_class_attribute(self):
@@ -215,7 +226,7 @@
215226
216 versions = gather_versions()227 versions = gather_versions()
217228
218 self.assertEquals(len(versions), 4)229 self.assertEquals(len(versions), 5)
219 self.assertEquals(versions[-1]['version'], 'peng')230 self.assertEquals(versions[-1]['version'], 'peng')
220231
221232
@@ -241,8 +252,9 @@
241 def test_overview_when_not_authenticated(self, mock_authenticate):252 def test_overview_when_not_authenticated(self, mock_authenticate):
242 mock_authenticate.return_value = False253 mock_authenticate.return_value = False
243254
244 self.assertRaises(TemplateDoesNotExist, self.client.get,255 response = self.client.get(reverse('preflight-overview'))
245 reverse('preflight-overview'))256
257 self.assertEqual(response.status_code, 404)
246258
247 @patch('preflight.views.render_to_response')259 @patch('preflight.views.render_to_response')
248 @patch('preflight.views.authenticate')260 @patch('preflight.views.authenticate')
@@ -262,6 +274,7 @@
262 def test_gather_settings_no_location(self, mock_settings):274 def test_gather_settings_no_location(self, mock_settings):
263 mock_settings._wrapped.FOO = 'bar'275 mock_settings._wrapped.FOO = 'bar'
264 mock_settings.FOO = 'bar'276 mock_settings.FOO = 'bar'
277 mock_settings.PREFLIGHT_HIDDEN_SETTINGS = ''
265 settings = gather_settings()278 settings = gather_settings()
266 expected = [{'name': 'FOO', 'value': 'bar', 'location': ''}]279 expected = [{'name': 'FOO', 'value': 'bar', 'location': ''}]
267 self.assertEqual(expected, settings)280 self.assertEqual(expected, settings)
@@ -270,6 +283,7 @@
270 def test_gather_settings_with_location(self, mock_settings):283 def test_gather_settings_with_location(self, mock_settings):
271 mock_settings._wrapped.FOO = 'bar'284 mock_settings._wrapped.FOO = 'bar'
272 mock_settings.FOO = 'bar'285 mock_settings.FOO = 'bar'
286 mock_settings.PREFLIGHT_HIDDEN_SETTINGS = ''
273 parser = Mock()287 parser = Mock()
274 parser.locate.return_value = '/tmp/baz'288 parser.locate.return_value = '/tmp/baz'
275 mock_settings.__CONFIGGLUE_PARSER__ = parser289 mock_settings.__CONFIGGLUE_PARSER__ = parser
@@ -279,43 +293,115 @@
279293
280 @patch('preflight.models.settings')294 @patch('preflight.models.settings')
281 def test_gather_settings_hidden(self, mock_settings):295 def test_gather_settings_hidden(self, mock_settings):
282 mock_settings._wrapped.SECRET_FOO = 'bar'296 mock_settings._wrapped.FOO_ZOGGLES = 'bar'
283 mock_settings.SECRET_FOO = 'bar'297 mock_settings.FOO_ZOGGLES = 'bar'
298 mock_settings.PREFLIGHT_HIDDEN_SETTINGS = 'ZOGGLES'
284 settings = gather_settings()299 settings = gather_settings()
285 expected = [{'name': 'SECRET_FOO', 'value': '*' * 18, 'location': ''}]300 expected = [{'name': 'FOO_ZOGGLES', 'value': '*' * 18, 'location': ''}]
286 self.assertEqual(expected, settings)301 self.assertEqual(expected, settings)
287302
288 @patch('preflight.models.settings')303 @patch('preflight.models.settings')
289 def test_gather_settings_omitted(self, mock_settings):304 def test_gather_settings_omitted(self, mock_settings):
290 mock_settings._wrapped._FOO = 'bar'305 mock_settings._wrapped._FOO = 'bar'
291 mock_settings._FOO = 'bar'306 mock_settings._FOO = 'bar'
307 mock_settings.PREFLIGHT_HIDDEN_SETTINGS = ''
292 settings = gather_settings()308 settings = gather_settings()
293 self.assertEqual([], settings)309 self.assertEqual([], settings)
294310
295311
312@skipIf(not settings.USE_GARGOYLE, 'skipping for Django 1.1')
296class GargoyleTestCase(TestCase):313class GargoyleTestCase(TestCase):
314 from gargoyle import gargoyle # NOQA
315 from gargoyle.models import ( # NOQA
316 Switch,
317 DISABLED,
318 SELECTIVE,
319 GLOBAL,
320 INHERIT
321 )
322
323 def setUp(self):
324 super(GargoyleTestCase, self).setUp()
325 from gargoyle.builtins import IPAddressConditionSet
326
327 self.gargoyle.register(IPAddressConditionSet())
328
329 def get_switches(self):
330 switches = [
331 self.Switch(key='DISABLED', status=self.DISABLED,
332 description='switch 1'),
333 self.Switch(key='SELECTIVE_1', status=self.SELECTIVE),
334 self.Switch(key='SELECTIVE_2', status=self.SELECTIVE),
335 self.Switch(key='GLOBAL', status=self.GLOBAL),
336 self.Switch(key='INHERIT', status=self.INHERIT),
337 ]
338 selective = switches[2]
339 selective.add_condition(
340 self.gargoyle,
341 condition_set='gargoyle.builtins.IPAddressConditionSet',
342 field_name='ip_address',
343 condition='127.0.0.1',
344 )
345 return switches
297346
298 @patch.dict(sys.modules, **{'gargoyle.models': None})347 @patch.dict(sys.modules, **{'gargoyle.models': None})
299 def test_gather_gargoyle_no_gargoyle(self):348 def test_gather_switches_no_gargoyle(self):
300 self.assertEqual(gather_gargoyle(), None)349 self.assertEqual(gather_gargoyle(), None)
301350
302 @patch.dict(sys.modules, **{'gargoyle.models': Mock()})351 def assert_switches_dict(self, actual):
303 def test_gather_gargoyle(self):
304 # Inner import to get the mocked version
305 from gargoyle.models import Switch as MockSwitch
306
307 mocks = [Mock(), Mock(), Mock()]
308 MockSwitch.objects.all.return_value = mocks
309 switches = [
310
311 ]
312 for switch, mock in zip(switches, mocks):
313 for attr, value in switch:
314 setattr(mock, attr, value)
315
316 expected = [352 expected = [
317 {},353 dict(name='DISABLED',
318 {},354 status=self.DISABLED,
319 {}355 description='switch 1',
356 status_text=self.Switch.STATUS_LABELS[self.DISABLED],
357 conditions=[]),
358 dict(name='SELECTIVE_1',
359 status=self.SELECTIVE,
360 description=None,
361 status_text=self.Switch.STATUS_LABELS[self.GLOBAL],
362 conditions=[]),
363 dict(name='SELECTIVE_2', status=self.SELECTIVE,
364 description=None,
365 status_text=self.Switch.STATUS_LABELS[self.SELECTIVE],
366 conditions=['IP Address(ip_address=127.0.0.1)']),
367 dict(name='GLOBAL', status=self.GLOBAL,
368 description=None,
369 status_text=self.Switch.STATUS_LABELS[self.GLOBAL],
370 conditions=[]),
371 dict(name='INHERIT', status=self.INHERIT,
372 description=None,
373 status_text=self.Switch.STATUS_LABELS[self.INHERIT],
374 conditions=[]),
320 ]375 ]
321 #self.assertEqual(gather_gargoyle(), expected)376 self.assertEqual(actual, expected)
377
378 @patch('gargoyle.models.Switch.objects.all')
379 def test_gather_switches(self, mock_all):
380 mock_all.return_value = self.get_switches()
381 self.assert_switches_dict(gather_gargoyle())
382
383 @patch('gargoyle.models.Switch.objects.all')
384 def test_gargoyle_template(self, mock_all):
385 switches = self.get_switches()
386 mock_all.return_value = switches
387 the_switches = gather_switches()
388 context = {
389 "switches": the_switches,
390 "switches_json": json.dumps(the_switches),
391 "preflight_base_template": BASE_TEMPLATE,
392 "preflight_table_class": TABLE_CLASS,
393 }
394 response = render_to_string('preflight/overview.html', context)
395 dom = PyQuery(response)
396 table = dom.find('#switches-table tbody')
397 self.assertEqual(table.find('tr')[0][0].text, 'gargoyle')
398
399 for row, switch in zip(table.find('tr.switch'), switches):
400 self.assertEqual(row[0].text, switch.key)
401 self.assertEqual(row[1].text, str(switch.description))
402 self.assertEqual(row[3].text, switch.get_status_label())
403
404 data = json.loads(dom.find('#switches-json').text())
405 self.assertTrue('gargoyle' in data)
406 json_switches = data['gargoyle']
407 self.assert_switches_dict(json_switches)
322408
=== modified file 'preflight/views.py'
--- preflight/views.py 2012-05-18 13:13:41 +0000
+++ preflight/views.py 2013-05-31 15:53:26 +0000
@@ -2,10 +2,10 @@
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4from datetime import datetime4from datetime import datetime
5import json
56
6from django.views.decorators.cache import never_cache7from django.views.decorators.cache import never_cache
7from django.http import Http4048from django.http import Http404
8from django.conf import settings
9from django.shortcuts import render_to_response9from django.shortcuts import render_to_response
10from django.template import RequestContext10from django.template import RequestContext
1111
@@ -14,8 +14,9 @@
14 gather_checks,14 gather_checks,
15 gather_settings,15 gather_settings,
16 gather_versions,16 gather_versions,
17 gather_gargoyle,17 gather_switches,
18)18)
19from .conf import BASE_TEMPLATE, TABLE_CLASS
1920
2021
21@never_cache22@never_cache
@@ -23,17 +24,15 @@
23 if not authenticate(request):24 if not authenticate(request):
24 raise Http40425 raise Http404
2526
26 base_template = getattr(settings, 'PREFLIGHT_BASE_TEMPLATE',27 switches = gather_switches()
27 "index.1col.html")
28 table_class = getattr(settings, 'PREFLIGHT_TABLE_CLASS', "listing")
29
30 context = RequestContext(request, {28 context = RequestContext(request, {
31 "applications": gather_checks(),29 "applications": gather_checks(),
32 "versions": gather_versions(),30 "versions": gather_versions(),
33 "settings": gather_settings(),31 "settings": gather_settings(),
34 "gargoyle": gather_gargoyle(),32 "switches": switches,
33 "switches_json": json.dumps(switches),
35 "now": datetime.now(),34 "now": datetime.now(),
36 "preflight_base_template": base_template,35 "preflight_base_template": BASE_TEMPLATE,
37 'preflight_table_class': table_class,36 "preflight_table_class": TABLE_CLASS,
38 })37 })
39 return render_to_response("preflight/overview.html", context)38 return render_to_response("preflight/overview.html", context)
4039
=== modified file 'setup.py'
--- setup.py 2011-02-11 13:06:42 +0000
+++ setup.py 2013-05-31 15:53:26 +0000
@@ -1,6 +1,8 @@
1# -*- encoding: utf-8 -*-1# -*- encoding: utf-8 -*-
2# Copyright 2010 Canonical Ltd. This software is licensed under the2# Copyright 2010 Canonical Ltd. This software is licensed under the
3# GNU Affero General Public License version 3 (see the file LICENSE).3# GNU Affero General Public License version 3 (see the file LICENSE).
4import sys
5
4try:6try:
5 from ast import PyCF_ONLY_AST7 from ast import PyCF_ONLY_AST
6except ImportError:8except ImportError:
@@ -14,24 +16,31 @@
14 for line in open('preflight/__init__.py')16 for line in open('preflight/__init__.py')
15 if line.startswith('__version__')][0]17 if line.startswith('__version__')][0]
1618
19tests_require = [
20 'mock > 0.6',
21 'gargoyle >= 0.6.0',
22 'pyquery',
23]
24
25if sys.version_info[:2] < (2, 7):
26 tests_require.append('unittest2')
1727
18setup(28setup(
19 name='django-preflight',29 name='django-preflight',
20 version=get_version(),30 version=get_version(),
21 author='Łukasz Czyżykowski',31 author='Lukasz Czyzykowski',
22 author_email='lukasz.czyzykowski@canonical.com',32 author_email='lukasz.czyzykowski@canonical.com',
23 description="Create a page for making sure all settings are correct.",33 description="Create a page for making sure all settings are correct.",
24 long_description=open('README').read(),34 long_description=open('README').read(),
25 url='https://launchpad.net/django-preflight',35 url='https://launchpad.net/django-preflight',
26 download_url='https://launchpad.net/django-preflight/+download',36 download_url='https://launchpad.net/django-preflight/+download',
27 classifiers=[37 classifiers=[
28 "Development Status :: 4 - Beta",38 "Development Status :: 5 - Production/Stable",
29 "Environment :: Web Environment",39 "Environment :: Web Environment",
30 "Framework :: Django",40 "Framework :: Django",
31 "Intended Audience :: Developers",41 "Intended Audience :: Developers",
32 "License :: OSI Approved :: GNU Affero General Public License v3",42 "License :: OSI Approved :: GNU Affero General Public License v3",
33 "Programming Language :: Python",43 "Programming Language :: Python",
34 "Programming Language :: Python :: 2.5",
35 "Programming Language :: Python :: 2.6",44 "Programming Language :: Python :: 2.6",
36 "Programming Language :: Python :: 2.7",45 "Programming Language :: Python :: 2.7",
37 "Topic :: Internet :: WWW/HTTP :: Site Management",46 "Topic :: Internet :: WWW/HTTP :: Site Management",
@@ -44,19 +53,14 @@
44 'preflight.management.commands',53 'preflight.management.commands',
45 ),54 ),
46 package_data={55 package_data={
47 'preflight': [56 'preflight': ['templates/preflight/*.html'],
48 'templates/preflight/*',
49 ],
50 },57 },
51 zip_safe=False,
52 install_requires=[58 install_requires=[
53 'django >= 1.0'59 'django >= 1.1',
54 ],60 ],
55 tests_require=[61 tests_require=tests_require,
56 'mock > 0.6'
57 ],
58 extras_require={62 extras_require={
59 'docs': ['Sphinx'],63 'docs': ['Sphinx'],
60 },64 },
61 test_suite='example_project.run.tests',65 test_suite='preflight_example_project.run.tests',
62)66)
6367
=== removed file 'tox'
--- tox 2011-02-10 12:43:01 +0000
+++ tox 1970-01-01 00:00:00 +0000
@@ -1,6 +0,0 @@
1#!/usr/bin/env python
2import urllib
3url = "https://pytox.googlecode.com/hg/toxbootstrap.py"
4d = dict(__file__='toxbootstrap.py')
5exec urllib.urlopen(url).read() in d
6d['cmdline'](['--recreate'])
7\ No newline at end of file0\ No newline at end of file
81
=== modified file 'tox.ini'
--- tox.ini 2012-05-18 13:13:41 +0000
+++ tox.ini 2013-05-31 15:53:26 +0000
@@ -1,8 +1,10 @@
1[tox]1[tox]
2envlist =2envlist =
3 py2.5-django1.2, py2.5-django1.1, py2.5-django1.4, py2.5-django1.3,3 py2.6-django1.1, py2.7-django1.1,
4 py2.6-django1.2, py2.6-django1.1, py2.6-django1.4, py2.6-django1.3,4 py2.6-django1.2, py2.7-django1.2,
5 py2.7-django1.2, py2.7-django1.1, py2.7-django1.4, py2.7-django1.3,5 py2.6-django1.3, py2.7-django1.3,
6 py2.6-django1.4, py2.7-django1.4,
7 py2.6-django1.5, py2.7-django1.5,
6 docs8 docs
79
8[testenv]10[testenv]
@@ -16,54 +18,44 @@
16 sphinx-build -b doctest -d {envtmpdir}/doctrees . {envtmpdir}/doctest18 sphinx-build -b doctest -d {envtmpdir}/doctrees . {envtmpdir}/doctest
17 sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html19 sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
1820
19# Python 2.5
20[testenv:py2.5-django1.2]
21basepython = python2.5
22deps = django >= 1.2, < 1.3
23
24[testenv:py2.5-django1.1]
25basepython = python2.5
26deps = django >= 1.1, < 1.2
27
28[testenv:py2.5-django1.4]
29basetpython = python2.5
30deps = django >= 1.4, < 1.1
31
32[testenv:py2.5-django1.3]
33basepython = python2.5
34deps = django >= 1.3, < 1.4
35
36# Python 2.621# Python 2.6
22[testenv:py2.6-django1.1]
23basepython = python2.6
24deps = django >= 1.1, < 1.2
25
37[testenv:py2.6-django1.2]26[testenv:py2.6-django1.2]
38basepython = python2.627basepython = python2.6
39deps = django >= 1.2, < 1.328deps = django >= 1.2, < 1.3
4029
41[testenv:py2.6-django1.1]30[testenv:py2.6-django1.3]
42basepython = python2.631basepython = python2.6
43deps = django >= 1.1, < 1.232deps = django >= 1.3, < 1.4
4433
45[testenv:py2.6-django1.4]34[testenv:py2.6-django1.4]
46basetpython = python2.635basepython = python2.6
47deps = django >= 1.4, < 1.136deps = django >= 1.4, < 1.5
4837
49[testenv:py2.6-django1.3]38[testenv:py2.6-django1.5]
50basepython = python2.639basepython = python2.6
51deps = django >= 1.3, < 1.440deps = django >= 1.5, < 1.6
5241
53# Python 2.742# Python 2.7
43[testenv:py2.7-django1.1]
44basepython = python2.7
45deps = django >= 1.1, < 1.2
46
54[testenv:py2.7-django1.2]47[testenv:py2.7-django1.2]
55basepython = python2.748basepython = python2.7
56deps = django >= 1.2, < 1.349deps = django >= 1.2, < 1.3
5750
58[testenv:py2.7-django1.1]51[testenv:py2.7-django1.3]
59basepython = python2.752basepython = python2.7
60deps = django >= 1.1, < 1.253deps = django >= 1.3, < 1.4
6154
62[testenv:py2.7-django1.4]55[testenv:py2.7-django1.4]
63basetpython = python2.756basepython = python2.7
64deps = django >= 1.4, < 1.157deps = django >= 1.4, < 1.5
6558
66[testenv:py2.7-django1.3]59[testenv:py2.7-django1.5]
67basepython = python2.760basepython = python2.7
68deps = django >= 1.3, < 1.461deps = django >= 1.5, < 1.6
69

Subscribers

People subscribed via source and target branches