Merge lp:~wgrant/launchpad/connectionstring-hates-production into lp:launchpad

Proposed by William Grant
Status: Merged
Approved by: Ian Booth
Approved revision: no longer in the source branch.
Merged at revision: 13890
Proposed branch: lp:~wgrant/launchpad/connectionstring-hates-production
Merge into: lp:launchpad
Diff against target: 76 lines (+46/-1)
2 files modified
lib/canonical/database/postgresql.py (+6/-1)
lib/canonical/database/tests/test_connectionstring.py (+40/-0)
To merge this branch: bzr merge lp:~wgrant/launchpad/connectionstring-hates-production
Reviewer Review Type Date Requested Status
Ian Booth (community) code Approve
Review via email: mp+74321@code.launchpad.net

Commit message

ConnectionString now assumes values continue until a space character, and rejects values that seem to be quoted or escaped. Also now has better tests.

Description of the change

This branch fixes ConnectionString to not truncate production DB hostnames. See bug #843412 for details of the problem.

As I suggested in the bug, I've not implemented escaping or quoting since we don't need them in production, and they would greatly increase complexity and time to fix. I have, however, added assertions so we crash instead of misparse.

I used gina against the production configs to verify the fix:

$ LPCONFIG=gina scripts/gina.py -vv lenny # before
2011-09-07 00:30:15 DEBUG Cronscript control file not found at file:cronscripts.ini
2011-09-07 00:30:15 INFO Creating lockfile: /var/lock/launchpad-gina.lock
2011-09-07 00:30:21 INFO
2011-09-07 00:30:21 INFO === Processing debian/lenny/release ===
2011-09-07 00:30:21 DEBUG Packages read from: /srv/debian-import.launchpad.net/mirror/
2011-09-07 00:30:21 DEBUG Keyrings read from: /usr/share/keyrings
2011-09-07 00:30:21 INFO Components to import: main, contrib, non-free
2011-09-07 00:30:21 INFO Architectures to import: i386, powerpc, amd64
2011-09-07 00:30:21 DEBUG Launchpad database: launchpad_prod_3
2011-09-07 00:30:21 DEBUG Launchpad database host: lp
2011-09-07 00:30:21 DEBUG Launchpad database user: gina

$ LPCONFIG=gina scripts/gina.py -vv lenny # after
2011-09-07 00:29:36 DEBUG Cronscript control file not found at file:cronscripts.ini
2011-09-07 00:29:36 INFO Creating lockfile: /var/lock/launchpad-gina.lock
2011-09-07 00:29:41 INFO
2011-09-07 00:29:41 INFO === Processing debian/lenny/release ===
2011-09-07 00:29:41 DEBUG Packages read from: /srv/debian-import.launchpad.net/mirror/
2011-09-07 00:29:41 DEBUG Keyrings read from: /usr/share/keyrings
2011-09-07 00:29:41 INFO Components to import: main, contrib, non-free
2011-09-07 00:29:41 INFO Architectures to import: i386, powerpc, amd64
2011-09-07 00:29:41 DEBUG Launchpad database: launchpad_prod_3
2011-09-07 00:29:41 DEBUG Launchpad database host: lp-db-master.canonical.com
2011-09-07 00:29:41 DEBUG Launchpad database user: gina

To post a comment you must log in.
Revision history for this message
Ian Booth (wallyworld) wrote :

Looks good

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/database/postgresql.py'
2--- lib/canonical/database/postgresql.py 2009-09-21 08:13:40 +0000
3+++ lib/canonical/database/postgresql.py 2011-09-07 00:46:24 +0000
4@@ -482,6 +482,8 @@
5 need the components seperated out (such as pg_dump command line
6 arguments). This class allows you to switch easily between formats.
7
8+ Quoted or escaped values are not supported.
9+
10 >>> cs = ConnectionString('user=foo dbname=launchpad_dev')
11 >>> cs.dbname
12 'launchpad_dev'
13@@ -496,6 +498,9 @@
14 'dbname', 'user', 'host', 'port', 'connect_timeout', 'sslmode']
15
16 def __init__(self, conn_str):
17+ if "'" in conn_str or "\\" in conn_str:
18+ raise AssertionError("quoted or escaped values are not supported")
19+
20 if '=' not in conn_str:
21 # Just a dbname
22 for key in self.CONNECTION_KEYS:
23@@ -507,7 +512,7 @@
24 # be added after construction or not actually required
25 # at all in some instances.
26 for key in self.CONNECTION_KEYS:
27- match = re.search(r'%s=(\w+)' % key, conn_str)
28+ match = re.search(r'%s=([^ ]+)' % key, conn_str)
29 if match is None:
30 setattr(self, key, None)
31 else:
32
33=== added file 'lib/canonical/database/tests/test_connectionstring.py'
34--- lib/canonical/database/tests/test_connectionstring.py 1970-01-01 00:00:00 +0000
35+++ lib/canonical/database/tests/test_connectionstring.py 2011-09-07 00:46:24 +0000
36@@ -0,0 +1,40 @@
37+# Copyright 2011 Canonical Ltd. This software is licensed under the
38+# GNU Affero General Public License version 3 (see the file LICENSE).
39+
40+from canonical.database.postgresql import ConnectionString
41+from lp.testing import TestCase
42+
43+
44+class TestConnectionString(TestCase):
45+
46+ def test_relevant_fields_parsed(self):
47+ s = ('dbname=dbname user=user host=host port=port '
48+ 'connect_timeout=timeout sslmode=mode')
49+ cs = ConnectionString(s)
50+ self.assertEqual('dbname', cs.dbname)
51+ self.assertEqual('user', cs.user)
52+ self.assertEqual('host', cs.host)
53+ self.assertEqual('port', cs.port)
54+ self.assertEqual('timeout', cs.connect_timeout)
55+ self.assertEqual('mode', cs.sslmode)
56+
57+ # and check that str/repr have the same keys and values.
58+ self.assertContentEqual(s.split(), str(cs).split())
59+ self.assertContentEqual(s.split(), repr(cs).split())
60+
61+ def test_hyphens_in_values(self):
62+ cs = ConnectionString('user=foo-bar host=foo.bar-baz.quux')
63+ self.assertEqual('foo-bar', cs.user)
64+ self.assertEqual('foo.bar-baz.quux', cs.host)
65+
66+ def test_str_with_changes(self):
67+ initial = 'dbname=foo host=bar'
68+ expected = 'dbname=foo user=baz host=blah'
69+ cs = ConnectionString(initial)
70+ cs.host = 'blah'
71+ cs.user = 'baz'
72+ self.assertEqual(expected, str(cs))
73+
74+ def test_rejects_quoted_strings(self):
75+ self.assertRaises(
76+ AssertionError, ConnectionString, "dbname='quoted string'")