Merge lp:~james-w/canonical-identity-provider/django-timeline into lp:canonical-identity-provider/release

Proposed by James Westby
Status: Merged
Approved by: James Westby
Approved revision: no longer in the source branch.
Merged at revision: 992
Proposed branch: lp:~james-w/canonical-identity-provider/django-timeline
Merge into: lp:canonical-identity-provider/release
Diff against target: 159 lines (+91/-4)
5 files modified
config-manager.txt (+3/-0)
django_project/wsgi.py (+16/-4)
src/identityprovider/oops_config.py (+10/-0)
src/identityprovider/tests/test_oops.py (+26/-0)
src/identityprovider/timeline_filters.py (+36/-0)
To merge this branch: bzr merge lp:~james-w/canonical-identity-provider/django-timeline
Reviewer Review Type Date Requested Status
Natalia Bidart (community) Approve
Review via email: mp+178791@code.launchpad.net

Commit message

Add timeline-django to record sql queries in oopses.

Description of the change

Hi,

This adds SQL queries to the generated oopses, using django-timeline.

The majority of the change is adding the new dependencies and wiring it up.
The rest is configuring the code to redact queries against the tables that
contain sensitive information.

Thanks,

James

To post a comment you must log in.
Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

For tracking dependencies in a way that is auditable we prefer to track branches owned by ~ubuntuone-pqm-team. Please use the following branches instead of trunk:

lp:~ubuntuone-pqm-team/python-oops-timeline/stable
lp:~ubuntuone-pqm-team/python-timeline/stable
lp:~ubuntuone-pqm-team/python-timeline-django/stable

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Looks good!

review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

The attempt to merge lp:~james-w/canonical-identity-provider/django-timeline into lp:canonical-identity-provider failed. Below is the output from the failed tests.

Updating download cache at dir /mnt/tarmac/cache/canonical-identity-provider/isd-download-cache
Using saved parent location: bzr+ssh://bazaar.launchpad.net/~canonical-isd-hackers/+junk/download-cache/
No revisions or tags to pull.
[localhost] local: which virtualenv
[localhost] local: /usr/bin/python /usr/bin/virtualenv --distribute --clear --system-site-packages .env
Not deleting .env/bin
New python executable in .env/bin/python
Installing distribute.............................................................................................................................................................................................done.
Installing pip...............done.
[localhost] local: dpkg -l libpq-dev 2> /dev/null | grep '^ii' | wc -l
[localhost] local: dpkg -l libxml2-dev 2> /dev/null | grep '^ii' | wc -l
[localhost] local: dpkg -l libxslt1-dev 2> /dev/null | grep '^ii' | wc -l
[localhost] local: dpkg -l memcached 2> /dev/null | grep '^ii' | wc -l
[localhost] local: dpkg -l postgresql-plpython-9.1 2> /dev/null | grep '^ii' | wc -l
[localhost] local: dpkg -l python-dev 2> /dev/null | grep '^ii' | wc -l
[localhost] local: dpkg -l python-m2crypto 2> /dev/null | grep '^ii' | wc -l
[localhost] local: dpkg -l swig 2> /dev/null | grep '^ii' | wc -l
[localhost] local: dpkg -l config-manager 2> /dev/null | grep '^ii' | wc -l
[localhost] local: dpkg -l python-egenix-mx-base-dev 2> /dev/null | grep '^ii' | wc -l
[localhost] local: /usr/lib/config-manager/cm.py update /tmp/tmpO1bi_a
UnknownErrorFromSmartServer(Server sent an unexpected error: ('error', 'GhostRevisionsHaveNoRevno', 'Could not determine revno for {10} because its ancestry shows a ghost at {<email address hidden>}')) Server sent an unexpected error: ('error', 'GhostRevisionsHaveNoRevno', 'Could not determine revno for {10} because its ancestry shows a ghost at {<email address hidden>}')

Traceback (most recent call last):
  File "/usr/lib/config-manager/cm.py", line 30, in <module>
    main(sys.argv)
  File "/usr/lib/pymodules/python2.7/config_manager/__init__.py", line 384, in main
    config.update(os.path.abspath(os.curdir))
  File "/usr/lib/pymodules/python2.7/config_manager/__init__.py", line 91, in update
    entry.update(dir)
  File "/usr/lib/pymodules/python2.7/config_manager/__init__.py", line 306, in update
    return self.build(path)
  File "/usr/lib/pymodules/python2.7/config_manager/__init__.py", line 280, in build
    raise ValueError("unknown url type '%s'" % self.url)
ValueError: unknown url type 'bzr+ssh://bazaar.launchpad.net/~ubuntuone-pqm-team/python-timeline/stable;revno=10'

Fatal error: local() encountered an error (return code 1) while executing '/usr/lib/config-manager/cm.py update /tmp/tmpO1bi_a'

Aborting.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'config-manager.txt'
--- config-manager.txt 2013-08-05 15:26:51 +0000
+++ config-manager.txt 2013-08-07 15:56:25 +0000
@@ -34,6 +34,7 @@
34canonical-identity-provider/branches/oops-amqp bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-oops-amqp/stable;revno=0.0.734canonical-identity-provider/branches/oops-amqp bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-oops-amqp/stable;revno=0.0.7
35canonical-identity-provider/branches/oops-datedir-repo bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-oops-datedir-repo/stable;revno=0.0.2035canonical-identity-provider/branches/oops-datedir-repo bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-oops-datedir-repo/stable;revno=0.0.20
36canonical-identity-provider/branches/oops-dictconfig bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-oops-dictconfig/stable;revno=0.0.436canonical-identity-provider/branches/oops-dictconfig bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-oops-dictconfig/stable;revno=0.0.4
37canonical-identity-provider/branches/oops-timeline bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-oops-timeline/stable;revno=3
37canonical-identity-provider/branches/oops-wsgi bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-oops-wsgi/stable;revno=3638canonical-identity-provider/branches/oops-wsgi bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-oops-wsgi/stable;revno=36
38canonical-identity-provider/branches/pystatsd bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/pystatsd/stable;revno=5339canonical-identity-provider/branches/pystatsd bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/pystatsd/stable;revno=53
39canonical-identity-provider/branches/python-oath bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-oath/stable;revno=5340canonical-identity-provider/branches/python-oath bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-oath/stable;revno=53
@@ -42,4 +43,6 @@
42canonical-identity-provider/branches/requests bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/requests/stable;revno=v1.1.043canonical-identity-provider/branches/requests bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/requests/stable;revno=v1.1.0
43canonical-identity-provider/branches/requests-oauthlib bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/requests-oauthlib/stable;revno=1644canonical-identity-provider/branches/requests-oauthlib bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/requests-oauthlib/stable;revno=16
44canonical-identity-provider/branches/ssoclient bzr+ssh://bazaar.isd/~canonical-isd-hackers/canonical-identity-provider/ssoclient;revno=745canonical-identity-provider/branches/ssoclient bzr+ssh://bazaar.isd/~canonical-isd-hackers/canonical-identity-provider/ssoclient;revno=7
46canonical-identity-provider/branches/timeline bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-timeline/stable;revno=10
47canonical-identity-provider/branches/timeline-django bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/python-timeline-django/stable;revno=17
45canonical-identity-provider/branches/yui-3.10.0 bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/yui/stable;revno=v3.10.048canonical-identity-provider/branches/yui-3.10.0 bzr+ssh://bazaar.isd/~ubuntuone-pqm-team/yui/stable;revno=v3.10.0
4649
=== modified file 'django_project/wsgi.py'
--- django_project/wsgi.py 2013-08-02 16:09:22 +0000
+++ django_project/wsgi.py 2013-08-07 15:56:25 +0000
@@ -1,5 +1,4 @@
1import os1import os
2import platform
3import sys2import sys
43
5# before doing anything else, patch stdout to avoid breakage in production4# before doing anything else, patch stdout to avoid breakage in production
@@ -16,7 +15,6 @@
16paths.setup_paths()15paths.setup_paths()
1716
18from django.conf import settings17from django.conf import settings
19from django.core.handlers.wsgi import WSGIHandler
2018
21os.environ['PGCONNECT_TIMEOUT'] = str(settings.PGCONNECT_TIMEOUT)19os.environ['PGCONNECT_TIMEOUT'] = str(settings.PGCONNECT_TIMEOUT)
22os.environ['PGSSLMODE'] = 'allow'20os.environ['PGSSLMODE'] = 'allow'
@@ -27,13 +25,27 @@
2725
28# Wrap the application in the Oops wsgi app to catch unhandled exceptions26# Wrap the application in the Oops wsgi app to catch unhandled exceptions
29# and create oops for them.27# and create oops for them.
30#28
31# First we create the config that defines what to do with the oopses.29from identityprovider.oops_config import install_timeline_hooks
32import oops_dictconfig30import oops_dictconfig
33from oops_wsgi import make_app, install_hooks31from oops_wsgi import make_app, install_hooks
32from timeline_django import setup as timeline_django_setup
33from timeline_django.wsgi import make_app as timeline_django_make_app
34from timeline.wsgi import make_app as timeline_make_app
3435
36# First we create the config that defines what to do with the oopses.
35config = oops_dictconfig.config_from_dict(settings.OOPSES)37config = oops_dictconfig.config_from_dict(settings.OOPSES)
36install_hooks(config)38install_hooks(config)
3739
40# Install the hooks to record the sql queries
41timeline_django_setup.setup_for_requests()
42
43# Set up the timeline in the django and wsgi envs
44app = timeline_django_make_app(app)
45app = timeline_make_app(app)
46
47# Add the hooks to collect and sanitise the timeline
48install_timeline_hooks()
49
38# Then we wrap the django app in the oops one50# Then we wrap the django app in the oops one
39application = make_app(app, config, oops_on_status=['500'])51application = make_app(app, config, oops_on_status=['500'])
4052
=== added symlink 'lib/oops_timeline'
=== target is u'../branches/oops-timeline/oops_timeline/'
=== added symlink 'lib/timeline'
=== target is u'../branches/timeline/timeline'
=== added symlink 'lib/timeline_django'
=== target is u'../branches/timeline-django/timeline_django/'
=== added file 'src/identityprovider/oops_config.py'
--- src/identityprovider/oops_config.py 1970-01-01 00:00:00 +0000
+++ src/identityprovider/oops_config.py 2013-08-07 15:56:25 +0000
@@ -0,0 +1,10 @@
1import oops_timeline
2import timeline_django.filters
3
4from identityprovider import timeline_filters
5
6
7def install_timeline_hooks(config):
8 oops_timeline.install_hooks(config)
9 timeline_django.filters.install_hooks(config)
10 timeline_filters.install_hooks(config)
011
=== added file 'src/identityprovider/tests/test_oops.py'
--- src/identityprovider/tests/test_oops.py 1970-01-01 00:00:00 +0000
+++ src/identityprovider/tests/test_oops.py 2013-08-07 15:56:25 +0000
@@ -0,0 +1,26 @@
1import oops
2import timeline as mod_timeline
3from unittest import TestCase
4
5from identityprovider import oops_config
6
7
8class OopsTests(TestCase):
9
10 def test_timeline_filters_sensitive(self):
11 reports = []
12
13 def capture(report):
14 reports.append(report)
15 return []
16 config = oops.Config()
17 oops_config.install_timeline_hooks(config)
18 config.publisher = capture
19 timeline = mod_timeline.Timeline()
20 timeline.start('SQL-master', 'SELECT * from accountpassword').finish()
21 report = config.create(context=dict(timeline=timeline))
22 config.publish(report)
23 self.assertEqual(1, len(reports))
24 report = reports[0]
25 self.assertEqual('SELECT <accountpassword query redacted>',
26 report['timeline'][0][3])
027
=== added file 'src/identityprovider/timeline_filters.py'
--- src/identityprovider/timeline_filters.py 1970-01-01 00:00:00 +0000
+++ src/identityprovider/timeline_filters.py 2013-08-07 15:56:25 +0000
@@ -0,0 +1,36 @@
1# Copyright 2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4from timeline_django import filters
5
6
7def filter_table(table_name, reason):
8 def do_filter(query):
9 return filters._filter_table(query, table_name, reason)
10
11 def filter_query(report, context):
12 return filters._filter_query(report, do_filter)
13 return filter_query
14
15
16def install_hooks(config):
17 # Filter queries for tables that contain sensitive information
18 config.on_create.append(
19 filter_table('accountpassword', 'accountpassword'))
20 config.on_create.append(
21 filter_table('api_user', 'api_user'))
22 config.on_create.append(
23 filter_table('authtoken', 'authtoken'))
24 config.on_create.append(
25 filter_table('identityprovider_authenticationdevice',
26 'authenticationdevice'))
27 config.on_create.append(
28 filter_table('oauth_consumer', 'oauth_consumer'))
29 config.on_create.append(
30 filter_table('oauth_token', 'oauth_token'))
31 config.on_create.append(
32 filter_table('openidauthorization', 'openidauthorization'))
33 config.on_create.append(
34 filter_table('piston_consumer', 'piston_consumer'))
35 config.on_create.append(
36 filter_table('piston_token', 'piston_token'))