Merge lp:~james-w/python-oops-dictconfig/new-publisher-api into lp:python-oops-dictconfig
- new-publisher-api
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | James Westby |
Approved revision: | 21 |
Merged at revision: | 18 |
Proposed branch: | lp:~james-w/python-oops-dictconfig/new-publisher-api |
Merge into: | lp:python-oops-dictconfig |
Diff against target: |
366 lines (+229/-29) 7 files modified
README (+48/-16) oops_dictconfig/configglue_options.py (+24/-1) oops_dictconfig/dictconfig.py (+27/-11) oops_dictconfig/tests/__init__.py (+1/-0) oops_dictconfig/tests/test_configglue_options.py (+114/-0) oops_dictconfig/tests/test_dictconfig.py (+14/-0) tarmac_tests.sh (+1/-1) |
To merge this branch: | bzr merge lp:~james-w/python-oops-dictconfig/new-publisher-api |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Westby (community) | Approve | ||
Diogo Baeder (community) | Approve | ||
Review via email: mp+159677@code.launchpad.net |
Commit message
Add a better way to specify fallback publishers now that oops allows it.
Description of the change
Hi,
Now that oops supports the better publish_
that to be specified in the config, and deprecate the 'new_only' method
of doing fallback (as it is deprecated in oops itself).
Thanks,
James
Diogo Baeder (diogobaeder) : | # |
James Westby (james-w) : | # |
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~james-w/python-oops-dictconfig/new-publisher-api into lp:python-oops-dictconfig failed. Below is the output from the failed tests.
Downloading/
Running setup.py egg_info for package testtools
no previously-included directories found matching 'doc/_build'
Downloading/
Downloading oops_amqp-
Running setup.py egg_info for package oops-amqp
Downloading/
Downloading oops_datedir_
Running setup.py egg_info for package oops-datedir-repo
Downloading/
Downloading configglue-
Running setup.py egg_info for package configglue
Downloading/
Downloading extras-0.0.3.tar.gz
Running setup.py egg_info for package extras
Downloading/
Downloading python-
Running setup.py egg_info for package python-mimeparse
Downloading/
Running setup.py egg_info for package pymongo
Requirement already satisfied (use --upgrade to upgrade): oops>=0.0.11 in ./virtualenv/
Downloading/
Running setup.py egg_info for package amqplib
Downloading/
Downloading bson-0.3.3.tar.gz
Running setup.py egg_info for package bson
Downloading/
Downloading iso8601-
Running setup.py egg_info for package iso8601
Downloading/
Running setup.py egg_info for package launchpadlib
Requirement already satisfied (use --upgrade to upgrade): pytz in ./virtualenv/
Downloading/
Running setup.py egg_info for package pyxdg
Downloading/
Running setup.py egg_info for package httplib2
Downloading/
Running setup.py egg_info for package keyring
zip_safe flag not set; analyzing archive contents...
Installed /tmp/easy_
Installed /mnt/tarmac/
warning: no previously-included files found matching '.hg/last-
Downloading/
Running setup.py egg_info for package lazr.restfulclient
Downloading/
Downloading lazr.uri-
Running setup.py egg_info for package lazr.uri
Downloading/
Error <urlopen error timed out> while getting http://
Exception:
Traceback (most recent call l...
Diogo Baeder (diogobaeder) wrote : | # |
Em 01-05-2013 13:30, Ubuntu One Auto Pilot escreveu:
> The proposal to merge lp:~james-w/python-oops-dictconfig/new-publisher-api into lp:python-oops-dictconfig has been updated.
>
> Status: Approved => Needs review
>
> For more details, see:
> https:/
http://
Preview Diff
1 | === modified file 'README' |
2 | --- README 2013-02-06 17:00:45 +0000 |
3 | +++ README 2013-05-01 15:40:31 +0000 |
4 | @@ -41,7 +41,8 @@ |
5 | * type: the type of publisher, currently `"datedir"` and `"amqp"` are |
6 | supported. |
7 | |
8 | -All publishers support the following optional key: |
9 | +All publishers support the following (deprecated) optional key. Instead of |
10 | +using this, you should see the section on fallback publishers: |
11 | |
12 | * new_only: if this is `True` then the publisher will be wrapped in |
13 | `oops.publish_only_new`, meaning that the oops will only |
14 | @@ -93,6 +94,32 @@ |
15 | to a user is being published (but uniqueness cannot be |
16 | guaranteed). |
17 | |
18 | +Fallback publishers |
19 | +------------------- |
20 | + |
21 | +Sometimes it is desirable to have a chain of publishers, where the next in |
22 | +the chain is used if the current attempt reported failure. For example, you |
23 | +can have an amqp publisher with a datedir fallback, so that if the amqp |
24 | +broker is not reachable the oops are saved to a datedir, where they can |
25 | +be forwarded later. |
26 | + |
27 | +To do this create a publisher definition with a 'fallback_chain' key, |
28 | +whose value is a list of publishers to chain, e.g. |
29 | + |
30 | + { |
31 | + 'fallback_chain': [ |
32 | + {'type': 'amqp', |
33 | + ... |
34 | + }, |
35 | + {'type': 'datedir', |
36 | + ... |
37 | + } |
38 | + ] |
39 | + } |
40 | + |
41 | +Note that these chains can't be nested, so 'fallback_chain' can only appear |
42 | +at the top level of the publishers list. |
43 | + |
44 | Examples |
45 | -------- |
46 | |
47 | @@ -129,18 +156,21 @@ |
48 | { |
49 | 'publishers': [ |
50 | { |
51 | - 'type': 'amqp', |
52 | - 'host': 'amqp.example.com:5302', |
53 | - 'user': 'oopsuser', |
54 | - 'password': 'oopspassword', |
55 | - 'vhost': 'oopses', |
56 | - 'exchange_name': 'oopses', |
57 | - 'routing_key': 'oopses', |
58 | - }, |
59 | - { |
60 | - 'type': 'datedir', |
61 | - 'error_dir': '/var/log/oopses/', |
62 | - 'new_only': True, |
63 | + 'fallback_chain': [ |
64 | + { |
65 | + 'type': 'amqp', |
66 | + 'host': 'amqp.example.com:5302', |
67 | + 'user': 'oopsuser', |
68 | + 'password': 'oopspassword', |
69 | + 'vhost': 'oopses', |
70 | + 'exchange_name': 'oopses', |
71 | + 'routing_key': 'oopses', |
72 | + }, |
73 | + { |
74 | + 'type': 'datedir', |
75 | + 'error_dir': '/var/log/oopses/', |
76 | + }, |
77 | + ], |
78 | }, |
79 | ], |
80 | } |
81 | @@ -183,8 +213,11 @@ |
82 | oopses = oopses_config |
83 | |
84 | [oopses_config] |
85 | - publishers = amqp_publisher |
86 | - datedir_publisher |
87 | + publishers = fallback_chain |
88 | + |
89 | + [fallback_chain] |
90 | + fallback_chain = amqp_publisher |
91 | + datedir_publisher |
92 | |
93 | [amqp_publisher] |
94 | type = amqp |
95 | @@ -198,7 +231,6 @@ |
96 | [datedir_publisher] |
97 | type = datedir |
98 | error_dir = /var/log/oopses |
99 | - only_new = True |
100 | |
101 | |
102 | Running Tests |
103 | |
104 | === modified file 'oops_dictconfig/configglue_options.py' |
105 | --- oops_dictconfig/configglue_options.py 2013-02-26 16:15:03 +0000 |
106 | +++ oops_dictconfig/configglue_options.py 2013-05-01 15:40:31 +0000 |
107 | @@ -16,7 +16,7 @@ |
108 | from configglue import schema |
109 | |
110 | |
111 | -class PublisherDescriptionOption(schema.DictOption): |
112 | +class BasePublisherDescriptionOption(schema.DictOption): |
113 | |
114 | def __init__(self, **kwargs): |
115 | if 'spec' not in kwargs: |
116 | @@ -55,7 +55,30 @@ |
117 | ) |
118 | if 'help' not in kwargs: |
119 | kwargs['help'] = "Config for an oops publisher." |
120 | + super(BasePublisherDescriptionOption, self).__init__(**kwargs) |
121 | + |
122 | + |
123 | +class FallbackChainOption(schema.ListOption): |
124 | + |
125 | + def __init__(self, **kwargs): |
126 | + if 'item' not in kwargs: |
127 | + kwargs['item'] = BasePublisherDescriptionOption() |
128 | + if 'help' not in kwargs: |
129 | + kwargs['help'] = ("List of oops publishers in order to publish to." |
130 | + " Each publisher will only be used if all of the publishers" |
131 | + " earlier in the list reported a failure to publish.") |
132 | + super(FallbackChainOption, self).__init__(**kwargs) |
133 | + |
134 | + |
135 | +class PublisherDescriptionOption(BasePublisherDescriptionOption): |
136 | + |
137 | + def __init__(self, **kwargs): |
138 | + add_fallback_chain = False |
139 | + if 'spec' not in kwargs: |
140 | + add_fallback_chain = True |
141 | super(PublisherDescriptionOption, self).__init__(**kwargs) |
142 | + if add_fallback_chain: |
143 | + self.spec['fallback_chain'] = FallbackChainOption() |
144 | |
145 | |
146 | class PublishersOption(schema.ListOption): |
147 | |
148 | === modified file 'oops_dictconfig/dictconfig.py' |
149 | --- oops_dictconfig/dictconfig.py 2013-04-17 16:39:49 +0000 |
150 | +++ oops_dictconfig/dictconfig.py 2013-05-01 15:40:31 +0000 |
151 | @@ -60,6 +60,28 @@ |
152 | } |
153 | |
154 | |
155 | +def publisher_from_definition(publisher_defn): |
156 | + publisher_type = publisher_defn.get('type') |
157 | + if publisher_type is None: |
158 | + raise AssertionError( |
159 | + "Missing publisher type: %s" % str(publisher_defn)) |
160 | + publisher_factory = PUBLISHERS.get(publisher_type) |
161 | + if publisher_factory is None: |
162 | + raise AssertionError( |
163 | + "Unknown publisher type: %s" % str(publisher_type)) |
164 | + publish_method = publisher_factory(publisher_defn) |
165 | + if 'new_only' in publisher_defn and publisher_defn['new_only']: |
166 | + publish_method = oops.publish_new_only(publish_method) |
167 | + return publish_method |
168 | + |
169 | + |
170 | +def make_fallback_chain(publisher_defns): |
171 | + publishers = [] |
172 | + for publisher_defn in publisher_defns: |
173 | + publishers.append(publisher_from_definition(publisher_defn)) |
174 | + return oops.publish_with_fallback(*publishers) |
175 | + |
176 | + |
177 | def config_from_dict(dict_config): |
178 | """Create an `oops.Config` object from `dict_config`. |
179 | |
180 | @@ -88,17 +110,11 @@ |
181 | publishers = [] |
182 | if 'publishers' in dict_config: |
183 | for publisher_defn in dict_config['publishers']: |
184 | - publisher_type = publisher_defn.get('type') |
185 | - if publisher_type is None: |
186 | - raise AssertionError( |
187 | - "Missing publisher type: %s" % str(publisher_defn)) |
188 | - publisher_factory = PUBLISHERS.get(publisher_type) |
189 | - if publisher_factory is None: |
190 | - raise AssertionError( |
191 | - "Unknown publisher type: %s" % str(publisher_type)) |
192 | - publish_method = publisher_factory(publisher_defn) |
193 | - if 'new_only' in publisher_defn and publisher_defn['new_only']: |
194 | - publish_method = oops.publish_new_only(publish_method) |
195 | + if 'fallback_chain' in publisher_defn: |
196 | + publish_method = make_fallback_chain( |
197 | + publisher_defn['fallback_chain']) |
198 | + else: |
199 | + publish_method = publisher_from_definition(publisher_defn) |
200 | publishers.append(publish_method) |
201 | oops_config.publisher = oops.publish_to_many(*publishers) |
202 | oops_config.template.update(dict_config.get('template', {})) |
203 | |
204 | === modified file 'oops_dictconfig/tests/__init__.py' |
205 | --- oops_dictconfig/tests/__init__.py 2012-02-02 22:18:43 +0000 |
206 | +++ oops_dictconfig/tests/__init__.py 2013-05-01 15:40:31 +0000 |
207 | @@ -17,6 +17,7 @@ |
208 | |
209 | def test_suite(): |
210 | module_names = [ |
211 | + 'oops_dictconfig.tests.test_configglue_options', |
212 | 'oops_dictconfig.tests.test_dictconfig', |
213 | ] |
214 | loader = unittest.TestLoader() |
215 | |
216 | === added file 'oops_dictconfig/tests/test_configglue_options.py' |
217 | --- oops_dictconfig/tests/test_configglue_options.py 1970-01-01 00:00:00 +0000 |
218 | +++ oops_dictconfig/tests/test_configglue_options.py 2013-05-01 15:40:31 +0000 |
219 | @@ -0,0 +1,114 @@ |
220 | +# Copyright (c) 2013, Canonical Ltd |
221 | +# |
222 | +# This program is free software: you can redistribute it and/or modify |
223 | +# it under the terms of the GNU Lesser General Public License as published by |
224 | +# the Free Software Foundation, version 3 only. |
225 | +# |
226 | +# This program is distributed in the hope that it will be useful, |
227 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
228 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
229 | +# GNU Lesser General Public License for more details. |
230 | +# |
231 | +# You should have received a copy of the GNU Lesser General Public License |
232 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
233 | +# GNU Lesser General Public License version 3 (see the file LICENSE). |
234 | + |
235 | +from StringIO import StringIO |
236 | + |
237 | +from configglue.glue import schemaconfigglue |
238 | +from configglue.parser import SchemaConfigParser |
239 | +from configglue.schema import Schema, Section |
240 | +from testtools import TestCase |
241 | + |
242 | +from oops_dictconfig.configglue_options import OopsOption |
243 | + |
244 | + |
245 | +class BasicSchema(Schema): |
246 | + |
247 | + class oops(Section): |
248 | + |
249 | + oops = OopsOption() |
250 | + |
251 | + |
252 | +class ConfigglueOptionsTests(TestCase): |
253 | + |
254 | + def get_parser(self, schema_class): |
255 | + return SchemaConfigParser(schema_class()) |
256 | + |
257 | + def get_options_from_config(self, schema_class, config): |
258 | + """`config` is a file-like object containing the config""" |
259 | + parser = self.get_parser(schema_class) |
260 | + parser.readfp(config) |
261 | + return schemaconfigglue(parser, argv=[])[1] |
262 | + |
263 | + def test_emtpy_gives_empty(self): |
264 | + options = self.get_options_from_config(BasicSchema, |
265 | + StringIO("")) |
266 | + self.assertEqual([], options.oops_oops['publishers']) |
267 | + self.assertEqual({}, options.oops_oops['template']) |
268 | + |
269 | + def test_basic_amqp_definition(self): |
270 | + options = self.get_options_from_config(BasicSchema, |
271 | + StringIO(""" |
272 | +[oops] |
273 | +oops = oops_config |
274 | + |
275 | +[oops_config] |
276 | +publishers = amqp_publisher |
277 | + |
278 | +[amqp_publisher] |
279 | +type = amqp |
280 | +host = example.com |
281 | +""")) |
282 | + publishers = options.oops_oops['publishers'] |
283 | + self.assertEqual(1, len(publishers)) |
284 | + self.assertEqual('amqp', publishers[0]['type']) |
285 | + self.assertEqual('example.com', publishers[0]['host']) |
286 | + |
287 | + def test_basic_datedir_definition(self): |
288 | + options = self.get_options_from_config(BasicSchema, |
289 | + StringIO(""" |
290 | +[oops] |
291 | +oops = oops_config |
292 | + |
293 | +[oops_config] |
294 | +publishers = datedir_publisher |
295 | + |
296 | +[datedir_publisher] |
297 | +type = datedir |
298 | +error_dir = some/dir |
299 | +""")) |
300 | + publishers = options.oops_oops['publishers'] |
301 | + self.assertEqual(1, len(publishers)) |
302 | + self.assertEqual('datedir', publishers[0]['type']) |
303 | + self.assertEqual('some/dir', publishers[0]['error_dir']) |
304 | + |
305 | + def test_fallback_chain(self): |
306 | + options = self.get_options_from_config(BasicSchema, |
307 | + StringIO(""" |
308 | +[oops] |
309 | +oops = oops_config |
310 | + |
311 | +[oops_config] |
312 | +publishers = fallback_chain |
313 | + |
314 | +[fallback_chain] |
315 | +fallback_chain = amqp_publisher |
316 | + datedir_publisher |
317 | + |
318 | +[amqp_publisher] |
319 | +type = amqp |
320 | +host = example.com |
321 | + |
322 | +[datedir_publisher] |
323 | +type = datedir |
324 | +error_dir = some/dir |
325 | +""")) |
326 | + publishers = options.oops_oops['publishers'] |
327 | + self.assertEqual(1, len(publishers)) |
328 | + self.assertEqual(2, len(publishers[0]['fallback_chain'])) |
329 | + fallback_chain = publishers[0]['fallback_chain'] |
330 | + self.assertEqual('amqp', fallback_chain[0]['type']) |
331 | + self.assertEqual('example.com', fallback_chain[0]['host']) |
332 | + self.assertEqual('datedir', fallback_chain[1]['type']) |
333 | + self.assertEqual('some/dir', fallback_chain[1]['error_dir']) |
334 | |
335 | === modified file 'oops_dictconfig/tests/test_dictconfig.py' |
336 | --- oops_dictconfig/tests/test_dictconfig.py 2013-04-17 16:39:49 +0000 |
337 | +++ oops_dictconfig/tests/test_dictconfig.py 2013-05-01 15:40:31 +0000 |
338 | @@ -271,3 +271,17 @@ |
339 | def test_non_empty_template_gives_non_empty_template(self): |
340 | config = config_from_dict(dict(template=dict(foo='bar'))) |
341 | self.assertEqual(dict(foo='bar'), config.template) |
342 | + |
343 | + def test_fallback_chain_creates_fallback_chain(self): |
344 | + amqp_defn = self.get_basic_ampq_definition() |
345 | + datedir_defn = dict(type='datedir', error_dir='error_dir') |
346 | + config = self.get_config_from_dict( |
347 | + [dict(fallback_chain=[amqp_defn, datedir_defn])]) |
348 | + fallback_fn = get_publishers_from_function(config.publisher)[0] |
349 | + # FIXME: brittle test, dependent on implementation details of |
350 | + # oops.publish_with_fallback. Find a better way of testing |
351 | + # that the function is used. |
352 | + self.assertEquals('result', fallback_fn.func_name) |
353 | + chain = get_publishers_from_function(fallback_fn) |
354 | + self.assertIsInstance(chain[0], oops_amqp.Publisher) |
355 | + self.assertIsInstance(chain[1].im_self, DateDirRepo) |
356 | |
357 | === modified file 'tarmac_tests.sh' |
358 | --- tarmac_tests.sh 2012-02-24 21:22:56 +0000 |
359 | +++ tarmac_tests.sh 2013-05-01 15:40:31 +0000 |
360 | @@ -6,5 +6,5 @@ |
361 | virtualenv --no-site-packages virtualenv > log |
362 | . virtualenv/bin/activate >> log |
363 | python setup.py develop >> log |
364 | -pip install testtools oops_amqp oops_datedir_repo |
365 | +pip install testtools oops_amqp oops_datedir_repo configglue |
366 | python -m testtools.run oops_dictconfig.tests.test_suite |
The attempt to merge lp:~james-w/python-oops-dictconfig/new-publisher-api into lp:python-oops-dictconfig failed. Below is the output from the failed tests.
Downloading/ unpacking testtools
Running setup.py egg_info for package testtools
no previously-included directories found matching 'doc/_build' unpacking oops-amqp 0.0.7.tar. gz
Downloading/
Downloading oops_amqp-
Running setup.py egg_info for package oops-amqp
Downloading/ unpacking oops-datedir-repo repo-0. 0.21.tar. gz
Downloading oops_datedir_
Running setup.py egg_info for package oops-datedir-repo
Downloading/ unpacking extras (from testtools)
Downloading extras-0.0.3.tar.gz
Running setup.py egg_info for package extras
Downloading/ unpacking python-mimeparse (from testtools) mimeparse- 0.1.4.tar. gz
Downloading python-
Running setup.py egg_info for package python-mimeparse
Downloading/ unpacking pymongo (from oops-amqp)
Running setup.py egg_info for package pymongo
Requirement already satisfied (use --upgrade to upgrade): oops>=0.0.11 in ./virtualenv/ lib/python2. 7/site- packages/oops-0.0.13-py2.7.egg (from oops-amqp) unpacking amqplib (from oops-amqp)
Downloading/
Running setup.py egg_info for package amqplib
Downloading/ unpacking bson (from oops-datedir-repo)
Downloading bson-0.3.3.tar.gz
Running setup.py egg_info for package bson
Downloading/ unpacking iso8601 (from oops-datedir-repo) 0.1.4.tar. gz
Downloading iso8601-
Running setup.py egg_info for package iso8601
Downloading/ unpacking launchpadlib (from oops-datedir-repo)
Running setup.py egg_info for package launchpadlib
Requirement already satisfied (use --upgrade to upgrade): pytz in ./virtualenv/ lib/python2. 7/site- packages/ pytz-2013b- py2.7.egg (from oops-datedir-repo) unpacking httplib2 (from launchpadlib->oops-datedir-repo)
Downloading/
Running setup.py egg_info for package httplib2
Downloading/ unpacking keyring (from launchpadlib->oops-datedir-repo)
Running setup.py egg_info for package keyring
zip_safe flag not set; analyzing archive contents...
Installed /tmp/easy_ install- bwAGVc/ pytest- runner- 1.2/hgtools- 3.0.2-py2. 7.egg
Installed /mnt/tarmac/ cache/python-oops-dictconfig/virtualenv/ build/keyring/ pytest_ runner- 1.2-py2. 7.egg
warning: no previously-included files found matching '.hg/last- message. txt' unpacking lazr.restfulcli ent>=0. 9.19 (from launchpadlib->oops-datedir-repo)
Downloading/
Running setup.py egg_info for package lazr.restfulclient
Downloading/ unpacking lazr.uri (from launchpadlib->oops-datedir-repo) 1.0.3.tar. gz
Downloading lazr.uri-
Running setup.py egg_info for package lazr.uri
Downloading/ unpacking oauth (from launchpadlib->oops-datedir-repo)
Downloading oauth-1.0.1.tar.gz
Running setup.py egg_info for package oauth
Requirement already satisfied (use --upgrade to upgrade): distribute in ./virtualenv/ lib/python2. 7/site- packages/ distribute- 0.6.24- py2.7.egg (from launchpadlib->oops-datedir-repo) unpacking simplejson (from launchpadlib->oops-datedir-repo)
Downloading/
Running setup.py egg_info for package simplejson
Downloading/ unpacking testresources (from launchpadlib->oops-datedi...