Merge lp:~michael.nelson/ubuntu-webcatalog/render_404_rather_than_raise_exception into lp:ubuntu-webcatalog

Proposed by Michael Nelson
Status: Merged
Approved by: Michael Nelson
Approved revision: 170
Merged at revision: 167
Proposed branch: lp:~michael.nelson/ubuntu-webcatalog/render_404_rather_than_raise_exception
Merge into: lp:ubuntu-webcatalog
Diff against target: 254 lines (+148/-18)
7 files modified
django_project/config/main.cfg (+10/-3)
setup.py (+3/-0)
src/webcatalog/schema.py (+8/-3)
src/webcatalog/templates/500.html (+0/-3)
src/webcatalog/tests/__init__.py (+1/-0)
src/webcatalog/tests/test_oops_wsgi_config.py (+110/-0)
src/webcatalog/wsgi.py (+16/-9)
To merge this branch: bzr merge lp:~michael.nelson/ubuntu-webcatalog/render_404_rather_than_raise_exception
Reviewer Review Type Date Requested Status
James Westby (community) Approve
Review via email: mp+121016@code.launchpad.net

Commit message

Use oops-wsgi for errors.

Description of the change

Overview
========

Contrary to the name, this branch adds and configures oops-wsgi for use with ubuntu-webcatalog.

`fab test`

To post a comment you must log in.
Revision history for this message
James Westby (james-w) wrote :

68 + 'instance_id': 'dev',

It is preferred not to set instance_id at all.

16 +[oops_datedir_publisher]
17 +type = datedir
18 +error_dir = /tmp/uwc-oops/
19 +instance_id = dev

Why override from the default here? Having the oopses in-tree seems
preferable to me anyway.

Other than that this looks excellent.

Thanks,

James

review: Approve
169. By Michael Nelson

REFACTOR: updated oops-dictconfig version to that used on our precise production boxes.

170. By Michael Nelson

REFACTOR: remove instance_id setting and update eg config to match the default.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'django_project/config/main.cfg'
2--- django_project/config/main.cfg 2012-07-02 13:41:19 +0000
3+++ django_project/config/main.cfg 2012-08-23 15:14:17 +0000
4@@ -106,8 +106,15 @@
5 canonical-losas = admin
6 canonical-ca-hackers = developers
7
8-[oops]
9-oops_dir = /srv/%(hostname)s/staging-logs/www-oops
10+[oops_wsgi]
11+oopses = oops_publishers
12+
13+[oops_publishers]
14+publishers = oops_datedir_publisher
15+
16+[oops_datedir_publisher]
17+type = datedir
18+error_dir = oopses
19
20 [webcatalog]
21 serve_site_media = True
22@@ -142,4 +149,4 @@
23 broker_url = amqp://guest:guest@localhost:5672//
24 celery_imports = webcatalog.tasks
25 celery_result_backend = database
26-celerybeat_scheduler = djcelery.schedulers.DatabaseScheduler
27\ No newline at end of file
28+celerybeat_scheduler = djcelery.schedulers.DatabaseScheduler
29
30=== modified file 'setup.py'
31--- setup.py 2012-07-02 13:51:23 +0000
32+++ setup.py 2012-08-23 15:14:17 +0000
33@@ -52,6 +52,9 @@
34 'PIL',
35 'celery',
36 'django-celery',
37+ 'oops-wsgi==0.0.10',
38+ 'oops-dictconfig==0.0.2',
39+ 'oops-datedir_repo==0.0.17',
40 ],
41 package_data = find_packages_data('src'),
42 dependency_links = [
43
44=== modified file 'src/webcatalog/schema.py'
45--- src/webcatalog/schema.py 2012-08-21 13:57:11 +0000
46+++ src/webcatalog/schema.py 2012-08-23 15:14:17 +0000
47@@ -21,6 +21,7 @@
48 import django
49 from configglue import schema
50 from django_configglue.schema import schemas
51+from oops_dictconfig.configglue_options import OopsOption
52
53 DjangoSchema = schemas.get('1.3.1')
54
55@@ -32,9 +33,13 @@
56 pgconnect_timeout = schema.IntOption(default=10)
57 should_serve_https = schema.BoolOption()
58
59- class oops(schema.Section):
60- oops_dir = schema.StringOption(help='Absolute path to the directory'
61- ' oops reports will be stored in')
62+ class oops_wsgi(schema.Section):
63+ oopses = OopsOption(default={
64+ 'publishers': [{
65+ 'type': 'datedir',
66+ 'error_dir': 'oopses',
67+ }],
68+ })
69
70 class openid(schema.Section):
71 openid_sso_server_url = schema.StringOption()
72
73=== modified file 'src/webcatalog/templates/500.html'
74--- src/webcatalog/templates/500.html 2012-03-08 18:13:11 +0000
75+++ src/webcatalog/templates/500.html 2012-08-23 15:14:17 +0000
76@@ -7,7 +7,4 @@
77 <h1>{% trans 'Server Error <em>(500)</em>' %}</h1>
78 {% if errormsg %}<p>{{ errormsg }}</p>{% endif %}
79 <p>{% blocktrans %}An error occurred which prevented the page you requested from loading. If this problem continues, please consider reporting this problem by sending an e-mail to <a href="mailto:webmaster@canonical.com">webmaster@canonical.com</a>{% endblocktrans %}</p>
80-<p>
81-{% blocktrans %}(Error <abbr>ID</abbr>: {{oopsid}}){% endblocktrans %}
82-</p>
83 {% endblock %}
84
85=== modified file 'src/webcatalog/tests/__init__.py'
86--- src/webcatalog/tests/__init__.py 2012-06-28 18:26:25 +0000
87+++ src/webcatalog/tests/__init__.py 2012-08-23 15:14:17 +0000
88@@ -25,6 +25,7 @@
89 from .test_models import *
90 from .test_managers import *
91 from .test_migrations import *
92+from .test_oops_wsgi_config import *
93 from .test_pep8 import *
94 from .test_preflight import *
95 from .test_tasks import *
96
97=== added file 'src/webcatalog/tests/test_oops_wsgi_config.py'
98--- src/webcatalog/tests/test_oops_wsgi_config.py 1970-01-01 00:00:00 +0000
99+++ src/webcatalog/tests/test_oops_wsgi_config.py 2012-08-23 15:14:17 +0000
100@@ -0,0 +1,110 @@
101+# -*- coding: utf-8 -*-
102+# This file is part of the Ubuntu Web Catalog
103+# Copyright (C) 2011-2012 Canonical Ltd.
104+#
105+# This program is free software: you can redistribute it and/or modify
106+# it under the terms of the GNU Affero General Public License as
107+# published by the Free Software Foundation, either version 3 of the
108+# License, or (at your option) any later version.
109+#
110+# This program is distributed in the hope that it will be useful,
111+# but WITHOUT ANY WARRANTY; without even the implied warranty of
112+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
113+# GNU Affero General Public License for more details.
114+#
115+# You should have received a copy of the GNU Affero General Public License
116+# along with this program. If not, see <http://www.gnu.org/licenses/>.
117+
118+"""Tests for the oops_wsgi setup."""
119+
120+from __future__ import absolute_import
121+
122+__metaclass__ = type
123+__all__ = [
124+ 'OopsWsgiTestCase',
125+]
126+
127+import bson
128+import os
129+from shutil import rmtree
130+from tempfile import mkdtemp
131+
132+from django.core.cache import cache
133+from django.core.urlresolvers import reverse
134+from django.conf import settings
135+from django.test import TestCase
136+from mock import (
137+ Mock,
138+ patch,
139+)
140+from webcatalog.wsgi import make_app
141+from webcatalog.tests.helpers import patch_settings
142+
143+
144+class OopsWsgiTestCase(TestCase):
145+
146+ def setUp(self):
147+ super(OopsWsgiTestCase, self).setUp()
148+ self.oops_dir = mkdtemp()
149+ self.addCleanup(rmtree, self.oops_dir)
150+ self.environ = {
151+ 'PATH_INFO': '/',
152+ 'QUERY_STRING': '',
153+ 'REMOTE_ADDR': '127.0.0.1',
154+ 'REQUEST_METHOD': 'GET',
155+ 'SCRIPT_NAME': '',
156+ 'SERVER_NAME': 'testserver',
157+ 'SERVER_PORT': '80',
158+ 'SERVER_PROTOCOL': 'HTTP/1.1',
159+ 'wsgi.version': (1, 0),
160+ 'wsgi.url_scheme': 'http',
161+ 'wsgi.multiprocess': True,
162+ 'wsgi.multithread': False,
163+ 'wsgi.run_once': False,
164+ 'wsgi.input': '',
165+ }
166+ cache.clear()
167+
168+ def request_wsgi_response(self, environ):
169+ start_response = Mock()
170+ with patch_settings(OOPSES={
171+ 'publishers': [{
172+ 'type': 'datedir',
173+ 'error_dir': self.oops_dir,
174+ 'instance_id': 'dev',
175+ }],
176+ }):
177+ app = make_app()
178+ response = app(environ, start_response).next()
179+ return response
180+
181+ def read_oops_from_disk(self):
182+ # Helper to read in the oops written to disk.
183+ # There is only one date directory in the oops directory.
184+ dated_dirs = os.listdir(self.oops_dir)
185+ self.assertEqual(1, len(dated_dirs))
186+
187+ # There is only one oops.
188+ todays_oops_path = dated_dirs[0]
189+ oops_list = os.listdir(self.oops_dir + '/' + todays_oops_path)
190+ self.assertEqual(1, len(oops_list))
191+
192+ oops_path = self.oops_dir + '/' + todays_oops_path + '/' + oops_list[0]
193+ with open(oops_path, "r") as oops_file:
194+ oops_file_contents = oops_file.read()
195+ return bson.loads(oops_file_contents)
196+
197+ def request_with_exception(self):
198+ self.environ['PATH_INFO'] = reverse('wc-index')
199+ view_fn_path = 'webcatalog.views.render_to_response'
200+ with patch(view_fn_path) as mock_render:
201+ mock_render.side_effect = ZeroDivisionError
202+ response = self.request_wsgi_response(self.environ)
203+ return response
204+
205+ def test_oops_on_disk_on_error(self):
206+ response = self.request_with_exception()
207+
208+ self.assertIn('Server Error <em>(500)</em>', response)
209+ oops_data = self.read_oops_from_disk()
210+ self.assertEqual('ZeroDivisionError', oops_data['type'])
211
212=== modified file 'src/webcatalog/wsgi.py'
213--- src/webcatalog/wsgi.py 2012-06-06 17:42:04 +0000
214+++ src/webcatalog/wsgi.py 2012-08-23 15:14:17 +0000
215@@ -17,13 +17,13 @@
216
217 """wsgi stack for the Apps Directory."""
218
219+import logging.config
220+import oops_dictconfig
221+import oops_wsgi
222+import oops_wsgi.django
223 import os
224-import logging.config
225 import platform
226
227-from canonical.oops.serializer import OOPSRFC822Serializer
228-from canonical.oops.wsgi import OopsWare
229-from django.core.handlers.wsgi import WSGIHandler
230 from django.conf import settings
231
232
233@@ -34,9 +34,16 @@
234
235 default_id = ''.join(x for x in platform.node() if x.isalpha())
236 appserver_id = getattr(settings, 'APPSERVER_ID', default_id)
237- logging.config.fileConfig(settings.WEBAPP_LOGGING_CONFIG)
238- oops_dir = getattr(settings, 'OOPS_DIR', '/tmp')
239- oops_app = OopsWare(WSGIHandler(), oops_dir=oops_dir,
240- key=appserver_id, hide_meta=True)
241- oops_app.serial = OOPSRFC822Serializer(appserver_id, oops_dir, None)
242+ logging_config = settings.WEBAPP_LOGGING_CONFIG
243+ if logging_config:
244+ logging.config.fileConfig(logging_config)
245+
246+ # This is needed to workaround a bug in Django, see the file it is
247+ # implemented in for more details.
248+ non_oops_app = oops_wsgi.django.OOPSWSGIHandler()
249+
250+ config = oops_dictconfig.config_from_dict(settings.OOPSES)
251+ oops_wsgi.install_hooks(config)
252+ oops_app = oops_wsgi.make_app(non_oops_app, config,
253+ oops_on_status=['500'])
254 return oops_app

Subscribers

People subscribed via source and target branches