Merge lp:~thisfred/desktopcouch/migrate_or_bust into lp:desktopcouch
- migrate_or_bust
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Eric Casteleijn |
Approved revision: | 237 |
Merged at revision: | 230 |
Proposed branch: | lp:~thisfred/desktopcouch/migrate_or_bust |
Merge into: | lp:desktopcouch |
Diff against target: |
356 lines (+109/-103) 6 files modified
bin/desktopcouch-get-port (+0/-2) desktopcouch/application/migration/__init__.py (+40/-35) desktopcouch/application/migration/tests/test_migration.py (+61/-60) desktopcouch/application/platform/linux/__init__.py (+1/-0) desktopcouch/application/start_local_couchdb.py (+6/-3) desktopcouch/records/database.py (+1/-3) |
To merge this branch: | bzr merge lp:~thisfred/desktopcouch/migrate_or_bust |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Albisetti (community) | Approve | ||
Vincenzo Di Somma (community) | Approve | ||
Review via email: mp+41974@code.launchpad.net |
Commit message
This adds the migration from 'deleted' flags in records, to moving records into the 'dc_trash' database, where deleted records now should live.
Description of the change
This adds the migration from 'deleted' flags in records, to moving records into the 'dc_trash' database, where deleted records now should live.
Vincenzo Di Somma (vds) : | # |
Eric Casteleijn (thisfred) wrote : | # |
Fixed!
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~thisfred/desktopcouch/migrate_or_bust into lp:desktopcouch failed. Below is the output from the failed tests.
Apache CouchDB has started, time to relax.
Browse your desktop CouchDB at file://
desktopcouch.
TestUpdateDes
test_
desktopcouch.
TestService
test_
test_
test_
desktopcouch.
TestKeyringIn
test_with_auth ... [OK]
test_
TestLocalFiles
test_
test_
test_
test_
desktopcouch.
TestReplication
test_creation ... Apache CouchDB has started, time to relax.
Browse your desktop CouchDB at file://
TestUbuntuone
test_exclusion ... Apache CouchDB has started, time to relax.
Browse your desktop CouchDB at file://
desktopcouch.
TestMigration
test_
Failure: testtools.
------------
Traceback (most recent call last):
File "/usr/lib/
return fn(*args)
File "/usr/lib/
testMethod()
File "/var/cache/
self.
AssertionError: 'test_migration
------------
[FAIL]
test_
test_
TestRegistration
test_
desktopcouch.
TestCouchdbIo
test_
test_
test_mkuri ... [OK]
test_
Eric Casteleijn (thisfred) wrote : | # |
Oops, forgot to exclude dc_trash from migration.
Preview Diff
1 | === modified file 'bin/desktopcouch-get-port' |
2 | --- bin/desktopcouch-get-port 2010-11-22 21:36:06 +0000 |
3 | +++ bin/desktopcouch-get-port 2010-11-26 19:54:11 +0000 |
4 | @@ -36,8 +36,6 @@ |
5 | # Don't let any of our output interfere |
6 | sys.stdout = DEVNULL |
7 | PORT = desktopcouch.application.platform.find_port() |
8 | - # FIXME log the result of the migration |
9 | - desktopcouch.application.migration.run_needed_migrations() |
10 | finally: |
11 | sys.stdout = SAVED_STDOUT |
12 | DEVNULL.close() |
13 | |
14 | === modified file 'desktopcouch/application/migration/__init__.py' |
15 | --- desktopcouch/application/migration/__init__.py 2010-11-23 18:47:46 +0000 |
16 | +++ desktopcouch/application/migration/__init__.py 2010-11-26 19:54:11 +0000 |
17 | @@ -21,64 +21,69 @@ |
18 | |
19 | import logging |
20 | |
21 | -from couchdb.client import ResourceConflict |
22 | - |
23 | from desktopcouch.records import database |
24 | from desktopcouch.application import server |
25 | from desktopcouch.application.platform import find_port |
26 | |
27 | MIGRATION_DESIGN_DOCUMENT = 'dc_migration' |
28 | |
29 | -MIGRATIONS_REGISTRY = {} |
30 | - |
31 | |
32 | def _all_dbs(ctx): |
33 | - """Get all the non private dbs from a server""" |
34 | + """Get all the non private dbs from a server.""" |
35 | port = find_port(ctx=ctx) |
36 | uri = "http://localhost:%s" % port |
37 | couchdb_server = server.DesktopServer(uri, oauth_tokens=None, ctx=ctx) |
38 | - return [x for x in couchdb_server.resource.get('/_all_dbs')[1]] |
39 | - |
40 | - |
41 | -def _add_view(view_name, view_code, dbs, ctx): |
42 | - """add the provided view to the dbs""" |
43 | - if not dbs: |
44 | - dbs = _all_dbs(ctx) |
45 | - for db_name in [db for db in dbs if not db.startswith('_')]: |
46 | - try: |
47 | - db = server.DesktopDatabase(database=db_name, ctx=ctx) |
48 | - except database.NoSuchDatabase: |
49 | - return |
50 | - try: |
51 | - db.add_view(view_name, map_js=view_code, reduce_js='', |
52 | - design_doc=MIGRATION_DESIGN_DOCUMENT) |
53 | - logging.info("Migrating DB: %s", db.db.name) |
54 | - except ResourceConflict: |
55 | - pass |
56 | - |
57 | - |
58 | -def register(view_name, view_code, migration_method, ctx, dbs=None): |
59 | - """register a migration script, with the view and the dbs it is meant to |
60 | - migrate""" |
61 | + return [x for x in couchdb_server.resource.get('/_all_dbs')[1] if not |
62 | + x.startswith('_') and not x == database.DCTRASH] |
63 | + |
64 | + |
65 | +def register(migration_method, dbs=None): |
66 | + """Register a migration script, with the view and the dbs it is meant to |
67 | + migrate.""" |
68 | if dbs is None: |
69 | dbs = [] |
70 | - MIGRATIONS_REGISTRY[view_name] = {'method': migration_method, 'dbs': dbs} |
71 | - _add_view(view_name, view_code, dbs, ctx) |
72 | + MIGRATIONS_REGISTRY.append( |
73 | + {'method': migration_method, 'dbs': dbs}) |
74 | |
75 | |
76 | def run_needed_migrations(ctx=None): |
77 | - """Run the actual migration""" |
78 | + """Run the actual migration.""" |
79 | from desktopcouch.application import local_files |
80 | if ctx is None: |
81 | ctx = local_files.DEFAULT_CONTEXT |
82 | all_dbs = _all_dbs(ctx) |
83 | for migration in MIGRATIONS_REGISTRY: |
84 | - dbs = MIGRATIONS_REGISTRY[migration]['dbs'] |
85 | + dbs = migration['dbs'] |
86 | if not dbs: |
87 | dbs = all_dbs |
88 | - for db in dbs: |
89 | + for db_name in dbs: |
90 | + db = server.DesktopDatabase(database=db_name, ctx=ctx) |
91 | try: |
92 | - MIGRATIONS_REGISTRY[migration]['method']( |
93 | - server.DesktopDatabase(database=db, ctx=ctx)) |
94 | + migration['method'](db) |
95 | + logging.info("Migrating DB: %s", db) |
96 | except database.NoSuchDatabase: |
97 | pass |
98 | + |
99 | +DELETION_VIEW_NAME = 'migrate_deleted' |
100 | + |
101 | +DELETION_VIEW_CODE = """ |
102 | + function(doc) { |
103 | + if (doc['application_annotations']['Ubuntu One'] |
104 | + ['private_application_annotations']['deleted']) { |
105 | + emit(doc.id, doc.id); |
106 | + } |
107 | + }""" |
108 | + |
109 | + |
110 | +def migrate_from_deleted_to_trash(migrateable_db): |
111 | + """Migrate from deleted flag to the dc_trash database.""" |
112 | + migrateable_db.add_view( |
113 | + DELETION_VIEW_NAME, map_js=DELETION_VIEW_CODE, |
114 | + design_doc=MIGRATION_DESIGN_DOCUMENT) |
115 | + view_result = migrateable_db.execute_view( |
116 | + view_name=DELETION_VIEW_NAME, design_doc=MIGRATION_DESIGN_DOCUMENT) |
117 | + for result in view_result: |
118 | + migrateable_db.delete_record(result.id) |
119 | + |
120 | + |
121 | +MIGRATIONS_REGISTRY = [{'method': migrate_from_deleted_to_trash, 'dbs': []}] |
122 | |
123 | === modified file 'desktopcouch/application/migration/tests/test_migration.py' |
124 | --- desktopcouch/application/migration/tests/test_migration.py 2010-11-23 18:47:46 +0000 |
125 | +++ desktopcouch/application/migration/tests/test_migration.py 2010-11-26 19:54:11 +0000 |
126 | @@ -27,7 +27,8 @@ |
127 | from desktopcouch.application import migration |
128 | import desktopcouch.application.tests as test_environment |
129 | from desktopcouch.application.server import DesktopDatabase |
130 | -from desktopcouch.records import Record |
131 | +from desktopcouch.records import Record, NoRecordTypeSpecified |
132 | +from desktopcouch.records.database import DCTRASH |
133 | from desktopcouch.application.stop_local_couchdb import stop_couchdb |
134 | |
135 | |
136 | @@ -77,55 +78,25 @@ |
137 | |
138 | def test_register_migration_is_added_to_the_registry(self): |
139 | """Test that the migration script is correctly registered.""" |
140 | - fake_view_code = '' |
141 | size = len(migration.MIGRATIONS_REGISTRY) |
142 | - migration.register(view_name=self.dbname, |
143 | - view_code=fake_view_code, |
144 | - migration_method=fake_migration, |
145 | - dbs=[self.dbname], |
146 | - ctx=self.ctx) |
147 | - self.assertEqual( |
148 | - {'method': fake_migration, |
149 | - 'dbs': [self.dbname]}, |
150 | - migration.MIGRATIONS_REGISTRY[self.dbname]) |
151 | + migration.register(migration_method=fake_migration, dbs=[self.dbname]) |
152 | + self.assertEqual([{'method': fake_migration, 'dbs': [self.dbname]}], |
153 | + migration.MIGRATIONS_REGISTRY) |
154 | self.assertEqual(size + 1, len(migration.MIGRATIONS_REGISTRY)) |
155 | |
156 | - def test_register_migration_add_view_to_a_given_db(self): |
157 | - """Test that the migration is correctly registered.""" |
158 | - fake_view_code = '' |
159 | - migration.register(view_name=self.dbname, |
160 | - view_code=fake_view_code, |
161 | - migration_method=fake_migration, |
162 | - dbs=[self.dbname], |
163 | - ctx=self.ctx) |
164 | - self.assert_(self.database.view_exists(self.dbname, 'dc_migration')) |
165 | - self.failIf(self.one_more_database.view_exists( |
166 | - self.dbname, 'dc_migration')) |
167 | - |
168 | - def test_register_migration_add_view_to_all_the_dbs(self): |
169 | - """Test that the migration is correctly registered.""" |
170 | - fake_view_code = '' |
171 | - migration.register(view_name=self.dbname, |
172 | - view_code=fake_view_code, |
173 | - migration_method=fake_migration, |
174 | - dbs=[], |
175 | - ctx=self.ctx) |
176 | - self.assertEqual( |
177 | - {'method': fake_migration, |
178 | - 'dbs': []}, |
179 | - migration.MIGRATIONS_REGISTRY[self.dbname]) |
180 | - for db in [db for db in self.server if not db.startswith('_')]: |
181 | - target = DesktopDatabase(database=db, ctx=self.ctx) |
182 | - self.assert_(target.view_exists(self.dbname, 'dc_migration')) |
183 | - |
184 | |
185 | class TestMigration(MigrationBase): |
186 | """Tests the actual migration script""" |
187 | |
188 | + def setUp(self): |
189 | + super(TestMigration, self).setUp() |
190 | + self.trash = DesktopDatabase(DCTRASH, create=True, ctx=self.ctx) |
191 | + |
192 | def tearDown(self): |
193 | """tear down each test""" |
194 | + del self.database._server[DCTRASH] |
195 | super(TestMigration, self).tearDown() |
196 | - migration.MIGRATIONS_REGISTRY = {} |
197 | + migration.MIGRATIONS_REGISTRY = [] |
198 | |
199 | def test_migration_script_is_run(self): |
200 | """Test that the migration script is run.""" |
201 | @@ -136,19 +107,20 @@ |
202 | # and get the right db as argument |
203 | self.assertEqual(self.dbname, database.db.name) |
204 | |
205 | - fake_view_code = '' |
206 | - migration.register(view_name=self.dbname, |
207 | - view_code=fake_view_code, |
208 | - migration_method=simple_migration, |
209 | - dbs=[self.dbname], |
210 | - ctx=self.ctx) |
211 | + migration.register( |
212 | + migration_method=simple_migration, dbs=[self.dbname]) |
213 | migration.run_needed_migrations(ctx=self.ctx) |
214 | |
215 | def test_migration_script_is_run_and_can_access_view(self): |
216 | """Test that the migration script is run.""" |
217 | |
218 | + simple_view_code = 'function(doc){emit(doc._id, doc.record_type)}' |
219 | + |
220 | def simple_migration(database): |
221 | """simple migration code""" |
222 | + database.add_view( |
223 | + 'simple_view', map_js=simple_view_code, |
224 | + design_doc=migration.MIGRATION_DESIGN_DOCUMENT) |
225 | results = database.execute_view( |
226 | 'simple_view', |
227 | design_doc='dc_migration') |
228 | @@ -161,17 +133,46 @@ |
229 | self.database.put_record(Record({ |
230 | 'key13_1': 'va31_1', 'record_type': 'test.com'})) |
231 | |
232 | - simple_view_code = 'function(doc){emit(doc._id, doc.record_type)}' |
233 | - migration.register(view_name='simple_view', |
234 | - view_code=simple_view_code, |
235 | - migration_method=simple_migration, |
236 | - dbs=[self.dbname], |
237 | - ctx=self.ctx) |
238 | - migration.run_needed_migrations(ctx=self.ctx) |
239 | - migration.MIGRATIONS_REGISTRY = {} |
240 | - migration.register(view_name='simple_view', |
241 | - view_code=simple_view_code, |
242 | - migration_method=simple_migration, |
243 | - dbs=['db_that_doesnt_exixts'], |
244 | - ctx=self.ctx) |
245 | - self.assertIs(None, migration.run_needed_migrations(ctx=self.ctx)) |
246 | + migration.register( |
247 | + migration_method=simple_migration, dbs=[self.dbname]) |
248 | + migration.run_needed_migrations(ctx=self.ctx) |
249 | + |
250 | + def test_migration_deleted_flag_to_trash(self): |
251 | + """Test that the migration script is run.""" |
252 | + record_id = self.database.put_record( |
253 | + Record({ |
254 | + 'key1_1': 'val1_1', |
255 | + 'record_type': 'test.com', |
256 | + 'application_annotations': { |
257 | + 'Ubuntu One': { |
258 | + 'private_application_annotations': { |
259 | + 'deleted': True}}}})) |
260 | + undeleted_record_id1 = self.database.put_record( |
261 | + Record({ |
262 | + 'key1_1': 'val1_1', |
263 | + 'record_type': 'test.com', |
264 | + 'application_annotations': { |
265 | + 'Ubuntu One': { |
266 | + 'private_application_annotations': { |
267 | + 'deleted': False}}}})) |
268 | + undeleted_record_id2 = self.database.put_record( |
269 | + Record({ |
270 | + 'key1_1': 'val1_1', |
271 | + 'record_type': 'test.com'})) |
272 | + migration.run_needed_migrations(ctx=self.ctx) |
273 | + # Record no longer exists in database |
274 | + self.assertIs(None, self.database.get_record(record_id)) |
275 | + # Undeleted records still exist |
276 | + self.assertIsNot(None, self.database.get_record(undeleted_record_id1)) |
277 | + self.assertIsNot(None, self.database.get_record(undeleted_record_id2)) |
278 | + |
279 | + # Known deleted record is only record in trash |
280 | + for document_id in self.trash.db: |
281 | + try: |
282 | + record = self.trash.get_record(document_id) |
283 | + except NoRecordTypeSpecified: |
284 | + continue |
285 | + private = record.application_annotations['desktopcouch'][ |
286 | + 'private_application_annotations'] |
287 | + self.assertEqual(self.dbname, private['original_database_name']) |
288 | + self.assertEqual(record_id, private['original_id']) |
289 | |
290 | === modified file 'desktopcouch/application/platform/linux/__init__.py' |
291 | --- desktopcouch/application/platform/linux/__init__.py 2010-11-23 18:47:46 +0000 |
292 | +++ desktopcouch/application/platform/linux/__init__.py 2010-11-26 19:54:11 +0000 |
293 | @@ -78,6 +78,7 @@ |
294 | # now load the design documents and pair records updates, |
295 | # because it's started |
296 | start_local_couchdb.update_design_documents() |
297 | + start_local_couchdb.run_needed_migrations() |
298 | if not process_is_couchdb(pid): |
299 | logging.error("CouchDB process did not start up") |
300 | raise RuntimeError("desktop-couch not started") |
301 | |
302 | === modified file 'desktopcouch/application/start_local_couchdb.py' |
303 | --- desktopcouch/application/start_local_couchdb.py 2010-11-24 14:40:03 +0000 |
304 | +++ desktopcouch/application/start_local_couchdb.py 2010-11-26 19:54:11 +0000 |
305 | @@ -46,7 +46,7 @@ |
306 | import sys |
307 | import time |
308 | |
309 | -from desktopcouch.application import local_files |
310 | +from desktopcouch.application import local_files, migration |
311 | from desktopcouch.application.platform import ( |
312 | read_pidfile, process_is_couchdb, find_port) |
313 | from desktopcouch.application.server import DesktopDatabase |
314 | @@ -118,7 +118,6 @@ |
315 | return pid, port |
316 | |
317 | |
318 | -# pylint: disable=R0914 |
319 | def update_design_documents(ctx=local_files.DEFAULT_CONTEXT): |
320 | """Check system design documents and update any that need updating |
321 | |
322 | @@ -176,7 +175,11 @@ |
323 | else: |
324 | db.add_view( |
325 | view_name, mapjs, design_doc=dd_name) |
326 | -# pylint: enable=R0914 |
327 | + |
328 | + |
329 | +def run_needed_migrations(ctx=local_files.DEFAULT_CONTEXT): |
330 | + """Run any necessary migrations.""" |
331 | + migration.run_needed_migrations(ctx=ctx) |
332 | |
333 | |
334 | def update_bookmark_file(port, ctx=local_files.DEFAULT_CONTEXT): |
335 | |
336 | === modified file 'desktopcouch/records/database.py' |
337 | --- desktopcouch/records/database.py 2010-11-24 19:31:39 +0000 |
338 | +++ desktopcouch/records/database.py 2010-11-26 19:54:11 +0000 |
339 | @@ -219,7 +219,7 @@ |
340 | return self.db.query(map_fun, reduce_fun, language, |
341 | wrapper, **options) |
342 | |
343 | - def get_record(self, record_id, hide_deleted=True): |
344 | + def get_record(self, record_id): |
345 | """Get a record from back end storage.""" |
346 | |
347 | def make_getter(source_db, document_id, attachment_name, content_type): |
348 | @@ -239,8 +239,6 @@ |
349 | return None |
350 | data = {} |
351 | |
352 | - if row_is_deleted(couch_record) and hide_deleted: |
353 | - return None |
354 | data.update(couch_record) |
355 | record = self.record_factory(data=data) |
356 | if "_attachments" in data: |
+ """Migrate from deleted falg to the _trash database"""
Typo! You must be so embarrassed....