Merge lp:~ev/oops-repository/whoopsie-daisy into lp:oops-repository
- whoopsie-daisy
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | j.c.sackett | ||||
Approved revision: | 28 | ||||
Merged at revision: | 13 | ||||
Proposed branch: | lp:~ev/oops-repository/whoopsie-daisy | ||||
Merge into: | lp:oops-repository | ||||
Diff against target: |
454 lines (+94/-114) 8 files modified
oopsrepository/cassandra.py (+1/-1) oopsrepository/oopses.py (+0/-94) oopsrepository/schema.py (+17/-1) oopsrepository/testing/cassandra.py (+1/-1) oopsrepository/testing/matchers.py (+11/-2) oopsrepository/tests/test_matchers.py (+1/-1) oopsrepository/tests/test_oopses.py (+62/-13) oopsrepository/tests/test_schema.py (+1/-1) |
||||
To merge this branch: | bzr merge lp:~ev/oops-repository/whoopsie-daisy | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
j.c.sackett (community) | Approve | ||
Robert Collins | Pending | ||
Review via email: mp+97714@code.launchpad.net |
Commit message
Description of the change
Apologies for the accidental deletion there.
This branch:
- Brings oops-repository up to the latest pycassa API.
- Replaces the insert_bson function with insert_dict, as we want to inspect the contents of the OOPS in the WSGI code to make a determination as to how we're going to process it, and it doesn't make sense to parse the BSON string twice.
- Adds a bucket method on the oops module to store crashes that are of the same issue in a single collection.
- Counts the additions to these buckets at a day interval.
- Adds tests.
j.c.sackett (jcsackett) wrote : | # |
Thanks so much.
This looks good to me.
I've added a commit to handle unicode data in OOPSes by setting a default_
https:/
I originally implemented this by encoding unicode dictionary keys and values as utf8 in oops-repository, but then I noticed the comment in DESIGN.txt about the columns all being strings. If that interpretation is incorrect, do feel free to take r27 instead of r28.
Robert Collins (lifeless) wrote : | # |
This is landable - we need to get you direct push rights, for now, perhaps jc can land it for you.
j.c.sackett (jcsackett) wrote : | # |
I'll assume that counts as a vote for "Approve" on your pending, Robert, and I'm happy to land it.
Preview Diff
1 | === modified file 'oopsrepository/cassandra.py' | |||
2 | --- oopsrepository/cassandra.py 2011-02-26 19:47:58 +0000 | |||
3 | +++ oopsrepository/cassandra.py 2012-03-23 23:37:20 +0000 | |||
4 | @@ -6,7 +6,7 @@ | |||
5 | 6 | 6 | ||
6 | 7 | """Things to ease working with cassandra.""" | 7 | """Things to ease working with cassandra.""" |
7 | 8 | 8 | ||
9 | 9 | from pycassa.cassandra.Cassandra import InvalidRequestException | 9 | from pycassa.cassandra.ttypes import InvalidRequestException |
10 | 10 | 10 | ||
11 | 11 | def workaround_1779(callable, *args, **kwargs): | 11 | def workaround_1779(callable, *args, **kwargs): |
12 | 12 | """Workaround cassandra not being able to do concurrent schema edits. | 12 | """Workaround cassandra not being able to do concurrent schema edits. |
13 | 13 | 13 | ||
14 | === added file 'oopsrepository/oopses.py' | |||
15 | --- oopsrepository/oopses.py 1970-01-01 00:00:00 +0000 | |||
16 | +++ oopsrepository/oopses.py 2012-03-23 23:37:20 +0000 | |||
17 | @@ -0,0 +1,116 @@ | |||
18 | 1 | # oops-repository is Copyright 2011 Canonical Ltd. | ||
19 | 2 | # | ||
20 | 3 | # Canonical Ltd ("Canonical") distributes the oops-repository source code under | ||
21 | 4 | # the GNU Affero General Public License, version 3 ("AGPLv3"). See the file | ||
22 | 5 | # LICENSE in the source tree for more information. | ||
23 | 6 | |||
24 | 7 | """basic operations on oopses in the db.""" | ||
25 | 8 | |||
26 | 9 | import json | ||
27 | 10 | import bson | ||
28 | 11 | import time | ||
29 | 12 | import uuid | ||
30 | 13 | |||
31 | 14 | import pycassa | ||
32 | 15 | from pycassa.index import create_index_expression, LT, create_index_clause | ||
33 | 16 | from pycassa.cassandra.ttypes import IndexExpression | ||
34 | 17 | |||
35 | 18 | DAY = 60*60*24 | ||
36 | 19 | MONTH = DAY*30 | ||
37 | 20 | |||
38 | 21 | def prune(config): | ||
39 | 22 | """Remove OOPSES that are over 30 days old.""" | ||
40 | 23 | pool = pycassa.pool.ConnectionPool(config['keyspace'], config['host']) | ||
41 | 24 | dayoops_cf = pycassa.ColumnFamily(pool, 'DayOOPS') | ||
42 | 25 | oops_cf = pycassa.ColumnFamily(pool, 'OOPS') | ||
43 | 26 | # Find days to prune | ||
44 | 27 | days = set() | ||
45 | 28 | prune_to = time.strftime('%Y%m%d', time.gmtime(time.time() - MONTH)) | ||
46 | 29 | for key, _ in dayoops_cf.get_range(): | ||
47 | 30 | if key < prune_to: | ||
48 | 31 | days.add(key) | ||
49 | 32 | if not days: | ||
50 | 33 | return | ||
51 | 34 | # collect all the oopses (buffers all in memory; may want to make | ||
52 | 35 | # incremental in future) | ||
53 | 36 | batch_size = 10000 | ||
54 | 37 | oopses = [] | ||
55 | 38 | for day in days: | ||
56 | 39 | columns_found = batch_size | ||
57 | 40 | start_col = '' | ||
58 | 41 | while columns_found==batch_size: | ||
59 | 42 | columns = dayoops_cf.get(day, column_start=start_col) | ||
60 | 43 | columns_found = len(columns) | ||
61 | 44 | for column, oopsid in columns.items(): | ||
62 | 45 | start_col = column | ||
63 | 46 | oopses.append(oopsid) | ||
64 | 47 | # Remove the oopses | ||
65 | 48 | batch = oops_cf.batch() | ||
66 | 49 | map(batch.remove, oopses) | ||
67 | 50 | batch.send() | ||
68 | 51 | # Clean out the days aggregates | ||
69 | 52 | # Clean out the days index | ||
70 | 53 | batch = dayoops_cf.batch() | ||
71 | 54 | map(batch.remove, days) | ||
72 | 55 | batch.send() | ||
73 | 56 | |||
74 | 57 | def insert(config, oopsid, oops_json, user_token=None): | ||
75 | 58 | """Insert an OOPS into the system. | ||
76 | 59 | |||
77 | 60 | :return: The day which the oops was filed under. | ||
78 | 61 | """ | ||
79 | 62 | # make sure the oops report is a json dict, and break out each key to a | ||
80 | 63 | # separate column. For now, rather than worrying about typed column values | ||
81 | 64 | # we just coerce them all to strings. | ||
82 | 65 | oops_dict = json.loads(oops_json) | ||
83 | 66 | assert isinstance(oops_dict, dict) | ||
84 | 67 | insert_dict = {} | ||
85 | 68 | for key, value in oops_dict.items(): | ||
86 | 69 | insert_dict[key] = json.dumps(value) | ||
87 | 70 | return _insert(config, oopsid, insert_dict, user_token) | ||
88 | 71 | |||
89 | 72 | def insert_dict(config, oopsid, oops_dict, user_token=None): | ||
90 | 73 | """Insert an OOPS into the system. | ||
91 | 74 | |||
92 | 75 | :return: The day which the oops was filed under. | ||
93 | 76 | """ | ||
94 | 77 | assert isinstance(oops_dict, dict) | ||
95 | 78 | return _insert(config, oopsid, oops_dict, user_token) | ||
96 | 79 | |||
97 | 80 | def _insert(config, oopsid, insert_dict, user_token=None): | ||
98 | 81 | pool = pycassa.pool.ConnectionPool(config['keyspace'], config['host']) | ||
99 | 82 | dayoops_cf = pycassa.ColumnFamily(pool, 'DayOOPS') | ||
100 | 83 | oops_cf = pycassa.ColumnFamily(pool, 'OOPS') | ||
101 | 84 | day_key = time.strftime('%Y%m%d', time.gmtime()) | ||
102 | 85 | now_uuid = uuid.uuid1() | ||
103 | 86 | |||
104 | 87 | oops_cf.insert(oopsid, insert_dict) | ||
105 | 88 | dayoops_cf.insert(day_key, {now_uuid:oopsid}) | ||
106 | 89 | if user_token: | ||
107 | 90 | useroops_cf = pycassa.ColumnFamily(pool, 'UserOOPS') | ||
108 | 91 | useroops_cf.insert(user_token, {oopsid : ''}) | ||
109 | 92 | |||
110 | 93 | return day_key | ||
111 | 94 | |||
112 | 95 | def bucket(config, oopsid, bucketid): | ||
113 | 96 | """Adds an OOPS to a bucket, a collection of OOPSes that form a single | ||
114 | 97 | issue. If the bucket does not exist, it will be created. | ||
115 | 98 | |||
116 | 99 | :return: The day which the bucket was filed under. | ||
117 | 100 | """ | ||
118 | 101 | pool = pycassa.pool.ConnectionPool(config['keyspace'], config['host']) | ||
119 | 102 | bucket_cf = pycassa.ColumnFamily(pool, 'Buckets') | ||
120 | 103 | daybucket_cf = pycassa.ColumnFamily(pool, 'DayBuckets') | ||
121 | 104 | daybucketcount_cf = pycassa.ColumnFamily(pool, 'DayBucketsCount') | ||
122 | 105 | day_key = time.strftime('%Y%m%d', time.gmtime()) | ||
123 | 106 | |||
124 | 107 | bucket_cf.insert(bucketid, {oopsid : ''}) | ||
125 | 108 | daybucket_cf.insert((day_key, bucketid), {oopsid : ''}) | ||
126 | 109 | # We have no way of knowing whether an increment has been performed if the | ||
127 | 110 | # write fails unexpectedly (CASSANDRA-2495). We will apply eventual | ||
128 | 111 | # consistency to this problem and tolerate slightly inaccurate counts for | ||
129 | 112 | # the span of a single day, cleaning up once this period has passed. This | ||
130 | 113 | # will be done by counting the number of columns in DayBuckets for the day | ||
131 | 114 | # and bucket ID. | ||
132 | 115 | daybucketcount_cf.add(day_key, bucketid) | ||
133 | 116 | return day_key | ||
134 | 0 | 117 | ||
135 | === removed file 'oopsrepository/oopses.py' | |||
136 | --- oopsrepository/oopses.py 2012-02-21 10:56:50 +0000 | |||
137 | +++ oopsrepository/oopses.py 1970-01-01 00:00:00 +0000 | |||
138 | @@ -1,94 +0,0 @@ | |||
139 | 1 | # oops-repository is Copyright 2011 Canonical Ltd. | ||
140 | 2 | # | ||
141 | 3 | # Canonical Ltd ("Canonical") distributes the oops-repository source code under | ||
142 | 4 | # the GNU Affero General Public License, version 3 ("AGPLv3"). See the file | ||
143 | 5 | # LICENSE in the source tree for more information. | ||
144 | 6 | |||
145 | 7 | """basic operations on oopses in the db.""" | ||
146 | 8 | |||
147 | 9 | import json | ||
148 | 10 | import bson | ||
149 | 11 | import time | ||
150 | 12 | import uuid | ||
151 | 13 | |||
152 | 14 | import pycassa | ||
153 | 15 | from pycassa.index import create_index_expression, LT, create_index_clause | ||
154 | 16 | from pycassa.cassandra.ttypes import IndexExpression | ||
155 | 17 | |||
156 | 18 | DAY = 60*60*24 | ||
157 | 19 | MONTH = DAY*30 | ||
158 | 20 | |||
159 | 21 | def prune(config): | ||
160 | 22 | """Remove OOPSES that are over 30 days old.""" | ||
161 | 23 | pool = pycassa.connect(config['keyspace'], config['host']) | ||
162 | 24 | dayoops_cf = pycassa.ColumnFamily(pool, 'DayOOPS') | ||
163 | 25 | oops_cf = pycassa.ColumnFamily(pool, 'OOPS') | ||
164 | 26 | # Find days to prune | ||
165 | 27 | days = set() | ||
166 | 28 | prune_to = time.strftime('%Y%m%d', time.gmtime(time.time() - MONTH)) | ||
167 | 29 | for key, _ in dayoops_cf.get_range(columns=()): | ||
168 | 30 | if key < prune_to: | ||
169 | 31 | days.add(key) | ||
170 | 32 | if not days: | ||
171 | 33 | return | ||
172 | 34 | # collect all the oopses (buffers all in memory; may want to make | ||
173 | 35 | # incremental in future) | ||
174 | 36 | batch_size = 10000 | ||
175 | 37 | oopses = [] | ||
176 | 38 | for day in days: | ||
177 | 39 | columns_found = batch_size | ||
178 | 40 | start_col = '' | ||
179 | 41 | while columns_found==batch_size: | ||
180 | 42 | columns = dayoops_cf.get(day, column_start=start_col) | ||
181 | 43 | columns_found = len(columns) | ||
182 | 44 | for column, oopsid in columns.items(): | ||
183 | 45 | start_col = column | ||
184 | 46 | oopses.append(oopsid) | ||
185 | 47 | # Remove the oopses | ||
186 | 48 | batch = oops_cf.batch() | ||
187 | 49 | map(batch.remove, oopses) | ||
188 | 50 | batch.send() | ||
189 | 51 | # Clean out the days aggregates | ||
190 | 52 | # Clean out the days index | ||
191 | 53 | batch = dayoops_cf.batch() | ||
192 | 54 | map(batch.remove, days) | ||
193 | 55 | batch.send() | ||
194 | 56 | |||
195 | 57 | def insert(config, oopsid, oops_json, user_token=None): | ||
196 | 58 | """Insert an OOPS into the system. | ||
197 | 59 | |||
198 | 60 | :return: The day which the oops was filed under. | ||
199 | 61 | """ | ||
200 | 62 | # make sure the oops report is a json dict, and break out each key to a | ||
201 | 63 | # separate column. For now, rather than worrying about typed column values | ||
202 | 64 | # we just coerce them all to strings. | ||
203 | 65 | oops_dict = json.loads(oops_json) | ||
204 | 66 | assert isinstance(oops_dict, dict) | ||
205 | 67 | insert_dict = {} | ||
206 | 68 | for key, value in oops_dict.items(): | ||
207 | 69 | insert_dict[key] = json.dumps(value) | ||
208 | 70 | return _insert(config, oopsid, insert_dict, user_token) | ||
209 | 71 | |||
210 | 72 | def insert_bson(config, oopsid, oops_bson, user_token=None): | ||
211 | 73 | """Insert an OOPS into the system. | ||
212 | 74 | |||
213 | 75 | :return: The day which the oops was filed under. | ||
214 | 76 | """ | ||
215 | 77 | oops_dict = bson.BSON(oops_bson) | ||
216 | 78 | insert_dict = oops_dict.to_dict() | ||
217 | 79 | return _insert(config, oopsid, insert_dict, user_token) | ||
218 | 80 | |||
219 | 81 | def _insert(config, oopsid, insert_dict, user_token=None): | ||
220 | 82 | pool = pycassa.connect(config['keyspace'], config['host']) | ||
221 | 83 | dayoops_cf = pycassa.ColumnFamily(pool, 'DayOOPS') | ||
222 | 84 | oops_cf = pycassa.ColumnFamily(pool, 'OOPS') | ||
223 | 85 | day_key = time.strftime('%Y%m%d', time.gmtime()) | ||
224 | 86 | now_uuid = uuid.uuid1() | ||
225 | 87 | |||
226 | 88 | oops_cf.insert(oopsid, insert_dict) | ||
227 | 89 | dayoops_cf.insert(day_key, {now_uuid:oopsid}) | ||
228 | 90 | if user_token: | ||
229 | 91 | useroops_cf = pycassa.ColumnFamily(pool, 'UserOOPS') | ||
230 | 92 | useroops_cf.insert(user_token, {oopsid : ''}) | ||
231 | 93 | |||
232 | 94 | return day_key | ||
233 | 95 | 0 | ||
234 | === modified file 'oopsrepository/schema.py' | |||
235 | --- oopsrepository/schema.py 2011-04-03 11:14:12 +0000 | |||
236 | +++ oopsrepository/schema.py 2012-03-23 23:37:20 +0000 | |||
237 | @@ -6,6 +6,11 @@ | |||
238 | 6 | 6 | ||
239 | 7 | """The schema for oopsrepository.""" | 7 | """The schema for oopsrepository.""" |
240 | 8 | 8 | ||
241 | 9 | from pycassa.types import ( | ||
242 | 10 | CompositeType, | ||
243 | 11 | UTF8Type, | ||
244 | 12 | CounterColumnType, | ||
245 | 13 | ) | ||
246 | 9 | from pycassa.system_manager import ( | 14 | from pycassa.system_manager import ( |
247 | 10 | LONG_TYPE, | 15 | LONG_TYPE, |
248 | 11 | SystemManager, | 16 | SystemManager, |
249 | @@ -28,9 +33,20 @@ | |||
250 | 28 | mgr = SystemManager() | 33 | mgr = SystemManager() |
251 | 29 | try: | 34 | try: |
252 | 30 | workaround_1779(mgr.create_column_family, keyspace, 'OOPS', | 35 | workaround_1779(mgr.create_column_family, keyspace, 'OOPS', |
254 | 31 | comparator_type=UTF8_TYPE) | 36 | comparator_type=UTF8_TYPE, default_validation_class=UTF8_TYPE) |
255 | 32 | workaround_1779(mgr.create_column_family, keyspace, 'DayOOPS', | 37 | workaround_1779(mgr.create_column_family, keyspace, 'DayOOPS', |
256 | 33 | comparator_type=TIME_UUID_TYPE) | 38 | comparator_type=TIME_UUID_TYPE) |
257 | 39 | workaround_1779(mgr.create_column_family, keyspace, 'UserOOPS', | ||
258 | 40 | comparator_type=UTF8_TYPE) | ||
259 | 41 | workaround_1779(mgr.create_column_family, keyspace, 'Buckets', | ||
260 | 42 | comparator_type=UTF8_TYPE) | ||
261 | 43 | # TODO It might be more performant to use just the date for the key and | ||
262 | 44 | # a composite key of the bucket_id and the oops_id as the column name. | ||
263 | 45 | composite = CompositeType(UTF8Type(), UTF8Type()) | ||
264 | 46 | workaround_1779(mgr.create_column_family, keyspace, 'DayBuckets', | ||
265 | 47 | comparator_type=UTF8_TYPE, key_validation_class=composite) | ||
266 | 48 | workaround_1779(mgr.create_column_family, keyspace, 'DayBucketsCount', | ||
267 | 49 | comparator_type=UTF8_TYPE, default_validation_class=CounterColumnType()) | ||
268 | 34 | finally: | 50 | finally: |
269 | 35 | mgr.close() | 51 | mgr.close() |
270 | 36 | 52 | ||
271 | 37 | 53 | ||
272 | === modified file 'oopsrepository/testing/cassandra.py' | |||
273 | --- oopsrepository/testing/cassandra.py 2011-02-27 04:46:26 +0000 | |||
274 | +++ oopsrepository/testing/cassandra.py 2012-03-23 23:37:20 +0000 | |||
275 | @@ -28,7 +28,7 @@ | |||
276 | 28 | self.keyspace = os.path.basename(tempdir.path) | 28 | self.keyspace = os.path.basename(tempdir.path) |
277 | 29 | self.mgr = SystemManager() | 29 | self.mgr = SystemManager() |
278 | 30 | workaround_1779(self.mgr.create_keyspace, self.keyspace, | 30 | workaround_1779(self.mgr.create_keyspace, self.keyspace, |
280 | 31 | replication_factor=1) | 31 | pycassa.SIMPLE_STRATEGY, {'replication_factor' : '1'}) |
281 | 32 | self.addCleanup(workaround_1779, self.mgr.drop_keyspace, | 32 | self.addCleanup(workaround_1779, self.mgr.drop_keyspace, |
282 | 33 | self.keyspace) | 33 | self.keyspace) |
283 | 34 | 34 | ||
284 | 35 | 35 | ||
285 | === modified file 'oopsrepository/testing/matchers.py' | |||
286 | --- oopsrepository/testing/matchers.py 2011-04-03 11:14:12 +0000 | |||
287 | +++ oopsrepository/testing/matchers.py 2012-03-23 23:37:20 +0000 | |||
288 | @@ -11,7 +11,7 @@ | |||
289 | 11 | import uuid | 11 | import uuid |
290 | 12 | 12 | ||
291 | 13 | import pycassa | 13 | import pycassa |
293 | 14 | from pycassa.cassandra.Cassandra import NotFoundException | 14 | from pycassa.cassandra.ttypes import NotFoundException |
294 | 15 | from testtools.matchers import Matcher, Mismatch | 15 | from testtools.matchers import Matcher, Mismatch |
295 | 16 | 16 | ||
296 | 17 | 17 | ||
297 | @@ -22,12 +22,21 @@ | |||
298 | 22 | """ | 22 | """ |
299 | 23 | 23 | ||
300 | 24 | def match(self, keyspace): | 24 | def match(self, keyspace): |
302 | 25 | pool = pycassa.connect(keyspace) | 25 | pool = pycassa.ConnectionPool(keyspace, ['localhost:9160']) |
303 | 26 | try: | 26 | try: |
304 | 27 | cf = pycassa.ColumnFamily(pool, 'OOPS') | 27 | cf = pycassa.ColumnFamily(pool, 'OOPS') |
305 | 28 | cf.insert('key', | 28 | cf.insert('key', |
306 | 29 | {"date":json.dumps(time.time()), "URL":'a bit boring'}) | 29 | {"date":json.dumps(time.time()), "URL":'a bit boring'}) |
307 | 30 | cf = pycassa.ColumnFamily(pool, 'DayOOPS') | 30 | cf = pycassa.ColumnFamily(pool, 'DayOOPS') |
308 | 31 | cf.insert('20100212', {uuid.uuid1(): 'key'}) | 31 | cf.insert('20100212', {uuid.uuid1(): 'key'}) |
309 | 32 | cf = pycassa.ColumnFamily(pool, 'UserOOPS') | ||
310 | 33 | cf.insert('user-token', {'key':''}) | ||
311 | 34 | |||
312 | 35 | cf = pycassa.ColumnFamily(pool, 'Buckets') | ||
313 | 36 | cf.insert('/bin/bash:11:x86_64:[vdso]+70c:...', {'key':''}) | ||
314 | 37 | cf = pycassa.ColumnFamily(pool, 'DayBuckets') | ||
315 | 38 | cf.insert(('20100212', '/bin/bash:11:x86_64:[vdso]+70c:...'), {'key':''}) | ||
316 | 39 | cf = pycassa.ColumnFamily(pool, 'DayBucketsCount') | ||
317 | 40 | cf.add('20100212', '/bin/bash:11:x86_64:[vdso]+70c:...', 13) | ||
318 | 32 | except NotFoundException as e: | 41 | except NotFoundException as e: |
319 | 33 | return Mismatch(e.why) | 42 | return Mismatch(e.why) |
320 | 34 | 43 | ||
321 | === modified file 'oopsrepository/tests/test_matchers.py' | |||
322 | --- oopsrepository/tests/test_matchers.py 2011-02-27 04:46:26 +0000 | |||
323 | +++ oopsrepository/tests/test_matchers.py 2012-03-23 23:37:20 +0000 | |||
324 | @@ -16,6 +16,6 @@ | |||
325 | 16 | def test_creates_columnfamily(self): | 16 | def test_creates_columnfamily(self): |
326 | 17 | keyspace = self.useFixture(TemporaryKeyspace()).keyspace | 17 | keyspace = self.useFixture(TemporaryKeyspace()).keyspace |
327 | 18 | self.assertNotEqual(None, HasOOPSSchema().match(keyspace)) | 18 | self.assertNotEqual(None, HasOOPSSchema().match(keyspace)) |
329 | 19 | config = dict(keyspace=keyspace) | 19 | config = dict(keyspace=keyspace, host=['localhost:9160']) |
330 | 20 | schema.create(config) | 20 | schema.create(config) |
331 | 21 | self.assertThat(keyspace, HasOOPSSchema()) | 21 | self.assertThat(keyspace, HasOOPSSchema()) |
332 | 22 | 22 | ||
333 | === modified file 'oopsrepository/tests/test_oopses.py' | |||
334 | --- oopsrepository/tests/test_oopses.py 2011-04-03 11:14:12 +0000 | |||
335 | +++ oopsrepository/tests/test_oopses.py 2012-03-23 23:37:20 +0000 | |||
336 | @@ -1,3 +1,4 @@ | |||
337 | 1 | # -*- coding: utf-8; -*- | ||
338 | 1 | # oops-repository is Copyright 2011 Canonical Ltd. | 2 | # oops-repository is Copyright 2011 Canonical Ltd. |
339 | 2 | # | 3 | # |
340 | 3 | # Canonical Ltd ("Canonical") distributes the oops-repository source code under | 4 | # Canonical Ltd ("Canonical") distributes the oops-repository source code under |
341 | @@ -19,10 +20,10 @@ | |||
342 | 19 | 20 | ||
343 | 20 | def test_fresh_oops_kept(self): | 21 | def test_fresh_oops_kept(self): |
344 | 21 | keyspace = self.useFixture(TemporaryOOPSDB()).keyspace | 22 | keyspace = self.useFixture(TemporaryOOPSDB()).keyspace |
346 | 22 | config = dict(keyspace=keyspace) | 23 | config = dict(keyspace=keyspace, host=['localhost:9160']) |
347 | 23 | day_key = oopses.insert(config, 'key', | 24 | day_key = oopses.insert(config, 'key', |
348 | 24 | json.dumps({"date":time.time(), "URL":'a bit boring'})) | 25 | json.dumps({"date":time.time(), "URL":'a bit boring'})) |
350 | 25 | pool = pycassa.connect(keyspace) | 26 | pool = pycassa.ConnectionPool(keyspace, ['localhost:9160']) |
351 | 26 | dayoopses_cf = pycassa.ColumnFamily(pool, 'DayOOPS') | 27 | dayoopses_cf = pycassa.ColumnFamily(pool, 'DayOOPS') |
352 | 27 | oopses_cf = pycassa.ColumnFamily(pool, 'OOPS') | 28 | oopses_cf = pycassa.ColumnFamily(pool, 'OOPS') |
353 | 28 | oopses.prune(config) | 29 | oopses.prune(config) |
354 | @@ -32,8 +33,8 @@ | |||
355 | 32 | 33 | ||
356 | 33 | def test_old_oops_deleted(self): | 34 | def test_old_oops_deleted(self): |
357 | 34 | keyspace = self.useFixture(TemporaryOOPSDB()).keyspace | 35 | keyspace = self.useFixture(TemporaryOOPSDB()).keyspace |
360 | 35 | config = dict(keyspace=keyspace) | 36 | config = dict(keyspace=keyspace, host=['localhost:9160']) |
361 | 36 | pool = pycassa.connect(keyspace) | 37 | pool = pycassa.ConnectionPool(keyspace, ['localhost:9160']) |
362 | 37 | dayoopses_cf = pycassa.ColumnFamily(pool, 'DayOOPS') | 38 | dayoopses_cf = pycassa.ColumnFamily(pool, 'DayOOPS') |
363 | 38 | datestamp = time.time() - oopses.MONTH - oopses.DAY | 39 | datestamp = time.time() - oopses.MONTH - oopses.DAY |
364 | 39 | day_key = time.strftime('%Y%m%d', time.gmtime(datestamp)) | 40 | day_key = time.strftime('%Y%m%d', time.gmtime(datestamp)) |
365 | @@ -50,20 +51,68 @@ | |||
366 | 50 | 51 | ||
367 | 51 | class TestInsert(TestCase): | 52 | class TestInsert(TestCase): |
368 | 52 | 53 | ||
376 | 53 | def test_insert_oops(self): | 54 | def _test_insert_check(self, keyspace, oopsid, day_key, value=None): |
377 | 54 | keyspace = self.useFixture(TemporaryOOPSDB()).keyspace | 55 | pool = pycassa.ConnectionPool(keyspace, host=['localhost:9160']) |
371 | 55 | oopsid = 'booyah' | ||
372 | 56 | oops = json.dumps({'duration': 13000}) | ||
373 | 57 | config = dict(keyspace=keyspace) | ||
374 | 58 | day_key = oopses.insert(config, oopsid, oops) | ||
375 | 59 | pool = pycassa.connect(keyspace) | ||
378 | 60 | oopses_cf = pycassa.ColumnFamily(pool, 'OOPS') | 56 | oopses_cf = pycassa.ColumnFamily(pool, 'OOPS') |
379 | 57 | if value is None: | ||
380 | 58 | value = '13000' | ||
381 | 61 | # The oops is retrievable | 59 | # The oops is retrievable |
382 | 62 | columns = oopses_cf.get(oopsid) | 60 | columns = oopses_cf.get(oopsid) |
384 | 63 | self.assertEqual('13000', columns['duration']) | 61 | self.assertEqual(value, columns['duration']) |
385 | 64 | # The oops has been indexed by day | 62 | # The oops has been indexed by day |
387 | 65 | pool = pycassa.connect(keyspace) | 63 | pool = pycassa.ConnectionPool(keyspace, host=['localhost:9160']) |
388 | 66 | dayoops_cf = pycassa.ColumnFamily(pool, 'DayOOPS') | 64 | dayoops_cf = pycassa.ColumnFamily(pool, 'DayOOPS') |
389 | 67 | oops_refs = dayoops_cf.get(day_key) | 65 | oops_refs = dayoops_cf.get(day_key) |
390 | 68 | self.assertEqual([oopsid], oops_refs.values()) | 66 | self.assertEqual([oopsid], oops_refs.values()) |
391 | 69 | ## TODO - the aggregates for the OOPS have been updated. | 67 | ## TODO - the aggregates for the OOPS have been updated. |
392 | 68 | |||
393 | 69 | def test_insert_oops(self): | ||
394 | 70 | keyspace = self.useFixture(TemporaryOOPSDB()).keyspace | ||
395 | 71 | oopsid = 'booyah' | ||
396 | 72 | oops = json.dumps({'duration': 13000}) | ||
397 | 73 | config = dict(keyspace=keyspace, host=['localhost:9160']) | ||
398 | 74 | day_key = oopses.insert(config, oopsid, oops) | ||
399 | 75 | self._test_insert_check(keyspace, oopsid, day_key) | ||
400 | 76 | |||
401 | 77 | def test_insert_oops_dict(self): | ||
402 | 78 | keyspace = self.useFixture(TemporaryOOPSDB()).keyspace | ||
403 | 79 | oopsid = 'booyah' | ||
404 | 80 | oops = {'duration': '13000'} | ||
405 | 81 | config = dict(keyspace=keyspace, host=['localhost:9160']) | ||
406 | 82 | day_key = oopses.insert_dict(config, oopsid, oops) | ||
407 | 83 | self._test_insert_check(keyspace, oopsid, day_key) | ||
408 | 84 | |||
409 | 85 | def test_insert_unicode(self): | ||
410 | 86 | keyspace = self.useFixture(TemporaryOOPSDB()).keyspace | ||
411 | 87 | oopsid = 'booyah' | ||
412 | 88 | oops = {'duration': u'♥'} | ||
413 | 89 | config = dict(keyspace=keyspace, host=['localhost:9160']) | ||
414 | 90 | day_key = oopses.insert_dict(config, oopsid, oops) | ||
415 | 91 | self._test_insert_check(keyspace, oopsid, day_key, value=u'♥') | ||
416 | 92 | |||
417 | 93 | class TestBucket(TestCase): | ||
418 | 94 | |||
419 | 95 | def test_insert_bucket(self): | ||
420 | 96 | keyspace = self.useFixture(TemporaryOOPSDB()).keyspace | ||
421 | 97 | config = dict(keyspace=keyspace, host=['localhost:9160']) | ||
422 | 98 | oopsid = 'booyah' | ||
423 | 99 | oops = json.dumps({'duration': 13000}) | ||
424 | 100 | oopses.insert(config, oopsid, oops) | ||
425 | 101 | day_key = oopses.bucket(config, oopsid, 'bucket-key') | ||
426 | 102 | |||
427 | 103 | pool = pycassa.ConnectionPool(keyspace, ['localhost:9160']) | ||
428 | 104 | bucket_cf = pycassa.ColumnFamily(pool, 'Buckets') | ||
429 | 105 | daybucketcount_cf = pycassa.ColumnFamily(pool, 'DayBucketsCount') | ||
430 | 106 | |||
431 | 107 | oops_refs = bucket_cf.get('bucket-key') | ||
432 | 108 | self.assertEqual([oopsid], oops_refs.keys()) | ||
433 | 109 | self.assertEqual( | ||
434 | 110 | daybucketcount_cf.get(day_key, ['bucket-key']).values(), [1]) | ||
435 | 111 | |||
436 | 112 | oopsid = 'foobar' | ||
437 | 113 | oops = json.dumps({'wibbles': 12}) | ||
438 | 114 | oopses.insert(config, oopsid, oops) | ||
439 | 115 | day_key = oopses.bucket(config, oopsid, 'bucket-key') | ||
440 | 116 | self.assertEqual( | ||
441 | 117 | daybucketcount_cf.get(day_key, ['bucket-key']).values(), [2]) | ||
442 | 118 | |||
443 | 70 | 119 | ||
444 | === modified file 'oopsrepository/tests/test_schema.py' | |||
445 | --- oopsrepository/tests/test_schema.py 2011-02-27 04:46:26 +0000 | |||
446 | +++ oopsrepository/tests/test_schema.py 2012-03-23 23:37:20 +0000 | |||
447 | @@ -16,6 +16,6 @@ | |||
448 | 16 | 16 | ||
449 | 17 | def test_creates_columnfamily(self): | 17 | def test_creates_columnfamily(self): |
450 | 18 | keyspace = self.useFixture(TemporaryKeyspace()).keyspace | 18 | keyspace = self.useFixture(TemporaryKeyspace()).keyspace |
452 | 19 | config = dict(keyspace=keyspace) | 19 | config = dict(keyspace=keyspace, host=['localhost:9160']) |
453 | 20 | schema.create(config) | 20 | schema.create(config) |
454 | 21 | self.assertThat(keyspace, HasOOPSSchema()) | 21 | self.assertThat(keyspace, HasOOPSSchema()) |
This largely looks okay to me, but I'm holding off on voting b/c I'm not certain what has changed in oopses.py; I can see the LoC count is different, and I think everything looks alright, but could you post a diff here of just that file against the current trunk?
Or, if someone with greater knowledge of the oops-repository code thinks this is good, that person is welcome to just mark approved and run with it.