Merge lp:~jderose/dmedia/simple-default into lp:dmedia

Proposed by Jason Gerard DeRose
Status: Merged
Merged at revision: 524
Proposed branch: lp:~jderose/dmedia/simple-default
Merge into: lp:dmedia
Diff against target: 668 lines (+233/-250)
8 files modified
debian/dmedia.postinst (+0/-10)
dmedia-cli (+3/-3)
dmedia-service (+6/-14)
dmedia/core.py (+66/-36)
dmedia/tests/base.py (+5/-0)
dmedia/tests/test_core.py (+118/-186)
dmedia/tests/test_util.py (+26/-1)
dmedia/util.py (+9/-0)
To merge this branch: bzr merge lp:~jderose/dmedia/simple-default
Reviewer Review Type Date Requested Status
James Raymond Approve
Review via email: mp+136073@code.launchpad.net

Description of the change

For background, see this bug:
https://bugs.launchpad.net/dmedia/+bug/1082864

Changes include:

 * Removed debian/dmedia.postinst script so /home/.dmedia FileStore is no longer created at package install

 * Reworked Core so it always has a default file store, doesn't use the shared store in /home/.dmedia anymore

 * Moved the private store from ~/.dmedia to ~/.local/share/dmedia/.dmedia

 * Removed SetDefaultStore() DBus method

 * Added Core.purge_store() implementation and test

 * Added PurgeStore() DBus method

 * Added quick and dirty migration functionality for the default store changes, which:

1) If ~/.dmedia exists, it is move to ~/.local/share/dmedia/.dmedia

2) If /home/.dmedia exists, all files there are moved (or copied if needed) into ~/.local/share/dmedia/.dmedia

3) Finally, calls Core.purge_store() to remove any references to copies in /home/.dmedia

To post a comment you must log in.
Revision history for this message
James Raymond (jamesmr) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== removed file 'debian/dmedia.postinst'
--- debian/dmedia.postinst 2012-07-26 21:05:25 +0000
+++ debian/dmedia.postinst 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
1#!/bin/sh -e
2
3case $1 in
4 configure)
5 # Initalize shared file-store in /home
6 /usr/lib/dmedia/init-filestore /home
7esac
8
9
10#DEBHELPER#
110
=== modified file 'dmedia-cli'
--- dmedia-cli 2012-11-01 11:06:53 +0000
+++ dmedia-cli 2012-11-26 00:24:26 +0000
@@ -134,10 +134,10 @@
134 return [path.abspath(directory)]134 return [path.abspath(directory)]
135135
136136
137class SetDefaultStore(_Method):137class PurgeStore(_Method):
138 "Set default store to 'private', 'shared', or 'none'"138 'Purge references to a store'
139139
140 args = ['value']140 args = ['store_id']
141141
142142
143class Resolve(_Method):143class Resolve(_Method):
144144
=== modified file 'dmedia-service'
--- dmedia-service 2012-11-25 07:25:50 +0000
+++ dmedia-service 2012-11-26 00:24:26 +0000
@@ -181,11 +181,6 @@
181 self.snapshot_queue_in.put(dbname)181 self.snapshot_queue_in.put(dbname)
182 return True182 return True
183183
184 @dbus.service.method(IFACE, in_signature='as', out_signature='')
185 def SnapshotMany(self, names):
186 for dbname in names:
187 self.Snapshot(dbname)
188
189 @dbus.service.method(IFACE, in_signature='', out_signature='')184 @dbus.service.method(IFACE, in_signature='', out_signature='')
190 def SnapshotAll(self):185 def SnapshotAll(self):
191 log.info('Dmedia.SnapshotAll()')186 log.info('Dmedia.SnapshotAll()')
@@ -209,9 +204,7 @@
209 log.info('Starting CouchDB took %.3f', time.time() - start)204 log.info('Starting CouchDB took %.3f', time.time() - start)
210 self.core = Core(env)205 self.core = Core(env)
211 self.core.load_identity(self.couch.machine, self.couch.user)206 self.core.load_identity(self.couch.machine, self.couch.user)
212 self.core.init_default_store()207 self.core.load_default_filestore(self.couch.basedir)
213 if self.core.local.get('default_store') is None:
214 self.core.set_default_store('shared')
215 self.env_s = dumps(self.core.env, pretty=True)208 self.env_s = dumps(self.core.env, pretty=True)
216 log.info('Finished core startup in %.3f', time.time() - start)209 log.info('Finished core startup in %.3f', time.time() - start)
217 GObject.timeout_add(250, self.on_idle1)210 GObject.timeout_add(250, self.on_idle1)
@@ -468,12 +461,11 @@
468 self.core.create_filestore(parentdir)461 self.core.create_filestore(parentdir)
469 return self.LocalDmedia()462 return self.LocalDmedia()
470463
471 @dbus.service.method(IFACE, in_signature='s', out_signature='s')464 @dbus.service.method(IFACE, in_signature='s', out_signature='')
472 def SetDefaultStore(self, value):465 def PurgeStore(self, store_id):
473 value = str(value)466 store_id = str(store_id)
474 log.info('Dmedia.SetDefaultStore(%r)', value)467 log.info('Dmedia.PurgeStore(%r)', store_id)
475 self.core.set_default_store(value)468 start_thread(self.core.purge_store, store_id)
476 return self.LocalDmedia()
477469
478 @dbus.service.method(IFACE, in_signature='s', out_signature='s')470 @dbus.service.method(IFACE, in_signature='s', out_signature='s')
479 def Resolve(self, _id):471 def Resolve(self, _id):
480472
=== modified file 'dmedia/core.py'
--- dmedia/core.py 2012-11-18 11:57:51 +0000
+++ dmedia/core.py 2012-11-26 00:24:26 +0000
@@ -42,7 +42,7 @@
42from base64 import b64encode42from base64 import b64encode
4343
44from microfiber import Server, Database, NotFound, Conflict, BulkConflict44from microfiber import Server, Database, NotFound, Conflict, BulkConflict
45from filestore import FileStore, check_root_hash, check_id45from filestore import FileStore, check_root_hash, check_id, DOTNAME
4646
47import dmedia47import dmedia
48from dmedia.parallel import start_thread, start_process48from dmedia.parallel import start_thread, start_process
@@ -54,8 +54,10 @@
5454
55log = logging.getLogger()55log = logging.getLogger()
56LOCAL_ID = '_local/dmedia'56LOCAL_ID = '_local/dmedia'
57HOME = path.abspath(os.environ['HOME'])
58if not path.isdir(HOME):
59 raise Exception('$HOME is not a directory: {!r}'.format(HOME))
57SHARED = '/home'60SHARED = '/home'
58PRIVATE = path.abspath(os.environ['HOME'])
5961
6062
61def start_httpd(couch_env, ssl_config):63def start_httpd(couch_env, ssl_config):
@@ -185,18 +187,37 @@
185 log.exception('Error updating project stats for %r', project_id)187 log.exception('Error updating project stats for %r', project_id)
186188
187189
190def migrate_shared(srcdir, dstdir):
191 try:
192 count = 0
193 src = FileStore(srcdir)
194 dst = FileStore(dstdir)
195 log.info('Migrating files from %r to %r', src, dst)
196 for st in src:
197 if dst.exists(st.id):
198 continue
199 log.info('Migrating %s %s', st.id, st.size)
200 try:
201 os.rename(st.name, dst.path(st.id))
202 except OSError:
203 src.copy(st.id, dst)
204 src.remove(st.id)
205 count += 1
206 log.info('Migrating %d files from %r to %r', count, src, dst)
207 except Exception:
208 log.exception('Error migrating files from shared FileStore')
209 return count
210
211
188class Core:212class Core:
189 def __init__(self, env, private=None, shared=None):213 def __init__(self, env):
190 self.env = env214 self.env = env
191 self._private = (PRIVATE if private is None else private)
192 self._shared = (SHARED if shared is None else shared)
193 self.db = util.get_db(env, init=True)215 self.db = util.get_db(env, init=True)
194 self.server = self.db.server()216 self.server = self.db.server()
195 self.ms = MetaStore(self.db)217 self.ms = MetaStore(self.db)
196 self.stores = LocalStores()218 self.stores = LocalStores()
197 self.queue = Queue()219 self.queue = Queue()
198 self.thread = None220 self.thread = None
199 self.default = None
200 try:221 try:
201 self.local = self.db.get(LOCAL_ID)222 self.local = self.db.get(LOCAL_ID)
202 except NotFound:223 except NotFound:
@@ -230,23 +251,18 @@
230 self.local['user_id'] = user['_id']251 self.local['user_id'] = user['_id']
231 self.save_local()252 self.save_local()
232253
233 def init_default_store(self):254 def load_default_filestore(self, parentdir):
234 value = self.local.get('default_store')255 if util.isfilestore(HOME) and not util.isfilestore(parentdir):
235 if value not in ('private', 'shared'):256 src = FileStore(HOME)
236 log.info('no default FileStore')257 src.init_dirs()
237 self.default = None258 dstdir = path.join(parentdir, DOTNAME)
238 self._sync_stores()259 log.info('Moving %r to %r', src.basedir, dstdir)
239 return260 os.rename(src.basedir, dstdir)
240 if value == 'shared' and not util.isfilestore(self._shared):261 self.parentdir = parentdir
241 log.warning('Switching to private, no shared FileStore at %r', self._shared)
242 value = 'private'
243 self.local['default_store'] = value
244 self.db.save(self.local)
245 parentdir = (self._private if value == 'private' else self._shared)
246 (fs, doc) = util.init_filestore(parentdir)262 (fs, doc) = util.init_filestore(parentdir)
247 self.default = fs263 log.info('Default FileStore %s at %r', doc['_id'], parentdir)
248 log.info('Connecting default FileStore %r at %r', fs.id, fs.parentdir)
249 self._add_filestore(fs, doc)264 self._add_filestore(fs, doc)
265 return fs
250266
251 def _sync_stores(self):267 def _sync_stores(self):
252 self.local['stores'] = self.stores.local_stores()268 self.local['stores'] = self.stores.local_stores()
@@ -270,6 +286,13 @@
270 self._sync_stores()286 self._sync_stores()
271287
272 def _background_worker(self):288 def _background_worker(self):
289 if util.isfilestore(SHARED):
290 log.info('Running migration...')
291 process = start_process(migrate_shared, SHARED, self.parentdir)
292 process.join()
293 store_id = util.get_filestore_id(SHARED)
294 if store_id is not None:
295 self.purge_store(store_id)
273 log.info('Background worker listing to queue...')296 log.info('Background worker listing to queue...')
274 while True:297 while True:
275 try:298 try:
@@ -327,19 +350,6 @@
327 def update_project(self, project_id):350 def update_project(self, project_id):
328 update_project(self.db, project_id) 351 update_project(self.db, project_id)
329352
330 def set_default_store(self, value):
331 if value not in ('private', 'shared', 'none'):
332 raise ValueError(
333 "need 'private', 'shared', or 'none'; got {!r}".format(value)
334 )
335 if self.local.get('default_store') != value:
336 self.local['default_store'] = value
337 self.db.save(self.local)
338 if self.default is not None:
339 self.disconnect_filestore(self.default.parentdir, self.default.id)
340 self.default = None
341 self.init_default_store()
342
343 def create_filestore(self, parentdir):353 def create_filestore(self, parentdir):
344 """354 """
345 Create a new file-store in *parentdir*.355 Create a new file-store in *parentdir*.
@@ -412,16 +422,36 @@
412 * ``doc['partial'][store_id]``422 * ``doc['partial'][store_id]``
413423
414 Some scenarios in which you might want to do this:424 Some scenarios in which you might want to do this:
415 425
416 1. The HDD was run over by a bus, the data is gone. We need to426 1. The HDD was run over by a bus, the data is gone. We need to
417 embrace reality, the sooner the better.427 embrace reality, the sooner the better.
418428
419 2. We're going to format or otherwise repurpose an HDD. Ideally, we429 2. We're going to format or otherwise repurpose an HDD. Ideally, we
420 would have called `Core2.downgrade_store()` first.430 would have called `Core2.downgrade_store()` first.
421 431
422 Note that this method makes sense for remote cloud stores as well as for432 Note that this method makes sense for remote cloud stores as well as for
423 local file-stores433 local file-stores
424 """434 """
435 log.info('Purging store %s', store_id)
436 ids = []
437 while True:
438 rows = self.db.view('file', 'stored',
439 key=store_id,
440 include_docs=True,
441 limit=25,
442 )['rows']
443 if not rows:
444 break
445 ids.extend(r['id'] for r in rows)
446 docs = [r['doc'] for r in rows]
447 for doc in docs:
448 del doc['stored'][store_id]
449 try:
450 self.db.save_many(docs)
451 except BulkConflict:
452 log.exception('Conflict purging %s', store_id)
453 log.info('Purged %d references to %s', len(ids), store_id)
454 return ids
425455
426 def stat(self, _id):456 def stat(self, _id):
427 doc = self.db.get(_id)457 doc = self.db.get(_id)
428458
=== modified file 'dmedia/tests/base.py'
--- dmedia/tests/base.py 2012-05-06 22:10:00 +0000
+++ dmedia/tests/base.py 2012-11-26 00:24:26 +0000
@@ -33,6 +33,7 @@
33from random import SystemRandom33from random import SystemRandom
34from zipfile import ZipFile34from zipfile import ZipFile
3535
36import filestore
36from filestore import File, Leaf, ContentHash, Batch, Hasher, LEAF_SIZE37from filestore import File, Leaf, ContentHash, Batch, Hasher, LEAF_SIZE
37from filestore import scandir38from filestore import scandir
38from microfiber import random_id39from microfiber import random_id
@@ -42,6 +43,10 @@
42random = SystemRandom()43random = SystemRandom()
4344
4445
46def random_file_id():
47 return random_id(filestore.DIGEST_BYTES)
48
49
45class DummyQueue(object):50class DummyQueue(object):
46 def __init__(self):51 def __init__(self):
47 self.items = []52 self.items = []
4853
=== modified file 'dmedia/tests/test_core.py'
--- dmedia/tests/test_core.py 2012-10-31 20:42:27 +0000
+++ dmedia/tests/test_core.py 2012-11-26 00:24:26 +0000
@@ -41,7 +41,7 @@
41from dmedia import util, core41from dmedia import util, core
4242
43from .couch import CouchCase43from .couch import CouchCase
44from .base import TempDir44from .base import TempDir, random_file_id
4545
4646
47class TestFunctions(TestCase):47class TestFunctions(TestCase):
@@ -64,6 +64,32 @@
64 }64 }
65 )65 )
6666
67 def test_migrate_shared(self):
68 tmp1 = TempDir()
69 tmp2 = TempDir()
70 src = filestore.FileStore(tmp1.dir)
71 dst = filestore.FileStore(tmp2.dir)
72 st_list = []
73 for i in range(10):
74 (file, ch) = tmp1.random_file()
75 os.rename(file.name, src.path(ch.id))
76 st = src.stat(ch.id)
77 assert st.size == ch.file_size
78 st_list.append(st)
79 st_list.sort(key=lambda st: st.id)
80 self.assertEqual(list(src), st_list)
81 self.assertEqual(list(dst), [])
82 self.assertEqual(core.migrate_shared(tmp1.dir, tmp2.dir), 10)
83 self.assertEqual(list(src), [])
84 self.assertEqual(
85 [st.id for st in dst],
86 [st.id for st in st_list]
87 )
88 for st in st_list:
89 ch = dst.verify(st.id)
90 self.assertEqual(st.size, ch.file_size)
91 self.assertEqual(dst.stat(st.id).mtime, st.mtime)
92
6793
68class TestCouchFunctions(CouchCase):94class TestCouchFunctions(CouchCase):
69 def test_projects_iter(self):95 def test_projects_iter(self):
@@ -135,191 +161,6 @@
135 }161 }
136 )162 )
137163
138 def test_init_default_store(self):
139 private = TempDir()
140 shared = TempDir()
141 machine_id = random_id()
142
143 # Test when default_store is missing
144 inst = core.Core(self.env, private.dir, shared.dir)
145 self.assertEqual(inst._private, private.dir)
146 self.assertEqual(inst._shared, shared.dir)
147 inst.init_default_store()
148 self.assertIsNone(inst.default)
149 self.assertEqual(inst.local,
150 {
151 '_id': '_local/dmedia',
152 'stores': {},
153 }
154 )
155
156 # Test when default_store is 'private'
157 inst = core.Core(self.env, private.dir, shared.dir)
158 self.assertEqual(inst._private, private.dir)
159 self.assertEqual(inst._shared, shared.dir)
160 inst.local['default_store'] = 'private'
161 self.assertFalse(util.isfilestore(private.dir))
162 inst.init_default_store()
163 self.assertEqual(
164 set(inst.local['stores']),
165 set([private.dir])
166 )
167 store_id = inst.local['stores'][private.dir]['id']
168 (fs1, doc) = util.get_filestore(private.dir, store_id)
169 self.assertEqual(inst.local,
170 {
171 '_id': '_local/dmedia',
172 '_rev': '0-1',
173 'default_store': 'private',
174 'stores': {
175 fs1.parentdir: {
176 'id': fs1.id,
177 'copies': fs1.copies,
178 }
179 }
180 }
181 )
182
183 # Again test when default_store is 'private' to make sure local isn't
184 # updated needlessly
185 inst = core.Core(self.env, private.dir, shared.dir)
186 self.assertEqual(inst._private, private.dir)
187 self.assertEqual(inst._shared, shared.dir)
188 inst.init_default_store()
189 self.assertEqual(inst.local,
190 {
191 '_id': '_local/dmedia',
192 '_rev': '0-1',
193 'default_store': 'private',
194 'stores': {
195 fs1.parentdir: {
196 'id': fs1.id,
197 'copies': fs1.copies,
198 }
199 }
200 }
201 )
202
203 # Test when default_store is 'shared' (which we're assuming exists)
204 self.assertFalse(util.isfilestore(shared.dir))
205 (fs2, doc) = util.init_filestore(shared.dir)
206 inst = core.Core(self.env, private.dir, shared.dir)
207 self.assertEqual(inst._private, private.dir)
208 self.assertEqual(inst._shared, shared.dir)
209 inst.local['default_store'] = 'shared'
210 inst.init_default_store()
211 self.assertEqual(inst.local,
212 {
213 '_id': '_local/dmedia',
214 '_rev': '0-2',
215 'default_store': 'shared',
216 'stores': {
217 fs2.parentdir: {
218 'id': fs2.id,
219 'copies': fs2.copies,
220 }
221 }
222 }
223 )
224
225 # Test when default_store is 'shared' and it doesn't exist, in which
226 # case it should automatically switch to 'private' instead
227 nope = TempDir()
228 inst = core.Core(self.env, private.dir, nope.dir)
229 self.assertEqual(inst._private, private.dir)
230 self.assertEqual(inst._shared, nope.dir)
231 inst.local['default_store'] = 'shared'
232 inst.init_default_store()
233 self.assertEqual(inst.local,
234 {
235 '_id': '_local/dmedia',
236 '_rev': '0-4',
237 'default_store': 'private',
238 'stores': {
239 fs1.parentdir: {
240 'id': fs1.id,
241 'copies': fs1.copies,
242 }
243 }
244 }
245 )
246
247 def test_set_default_store(self):
248 private = TempDir()
249 shared = TempDir()
250 (fs1, doc1) = util.init_filestore(private.dir)
251 (fs2, doc2) = util.init_filestore(shared.dir)
252 inst = core.Core(self.env, private.dir, shared.dir)
253 self.assertEqual(inst._private, private.dir)
254 self.assertEqual(inst._shared, shared.dir)
255
256 with self.assertRaises(ValueError) as cm:
257 inst.set_default_store('foobar')
258 self.assertEqual(
259 str(cm.exception),
260 "need 'private', 'shared', or 'none'; got 'foobar'"
261 )
262 self.assertEqual(inst.local,
263 {
264 '_id': '_local/dmedia',
265 'stores': {},
266 }
267 )
268
269 # Test with 'private'
270 inst.set_default_store('private')
271 self.assertEqual(inst.local,
272 {
273 '_id': '_local/dmedia',
274 '_rev': '0-2',
275 'default_store': 'private',
276 'stores': {
277 fs1.parentdir: {'id': fs1.id, 'copies': 1},
278 },
279 }
280 )
281
282 # Test with 'shared'
283 inst.set_default_store('shared')
284 self.assertEqual(inst.local,
285 {
286 '_id': '_local/dmedia',
287 '_rev': '0-5',
288 'default_store': 'shared',
289 'stores': {
290 fs2.parentdir: {'id': fs2.id, 'copies': 1},
291 },
292 }
293 )
294
295 # Test with 'none'
296 inst.set_default_store('none')
297 self.assertEqual(inst.local,
298 {
299 '_id': '_local/dmedia',
300 '_rev': '0-7',
301 'default_store': 'none',
302 'stores': {},
303 }
304 )
305
306 # Test with 'shared' when it doesn't exist
307 nope = TempDir()
308 inst = core.Core(self.env, private.dir, nope.dir)
309 self.assertEqual(inst._private, private.dir)
310 self.assertEqual(inst._shared, nope.dir)
311 inst.set_default_store('shared')
312 self.assertEqual(inst.local,
313 {
314 '_id': '_local/dmedia',
315 '_rev': '0-10',
316 'default_store': 'private',
317 'stores': {
318 fs1.parentdir: {'id': fs1.id, 'copies': 1},
319 },
320 }
321 )
322
323 def test_create_filestore(self):164 def test_create_filestore(self):
324 inst = core.Core(self.env)165 inst = core.Core(self.env)
325166
@@ -502,6 +343,97 @@
502 inst.disconnect_filestore(fs1.parentdir, fs1.id)343 inst.disconnect_filestore(fs1.parentdir, fs1.id)
503 self.assertEqual(str(cm.exception), repr(fs1.parentdir))344 self.assertEqual(str(cm.exception), repr(fs1.parentdir))
504345
346 def test_purge_store(self):
347 store_id1 = random_id()
348 store_id2 = random_id()
349 store_id3 = random_id()
350 inst = core.Core(self.env)
351 db = inst.db
352
353 # Test when empty
354 self.assertEqual(inst.purge_store(store_id1), [])
355
356 docs = [
357 {
358 '_id': random_file_id(),
359 'type': 'dmedia/file',
360 'bytes': 1776,
361 'stored': {
362 store_id1: {
363 'copies': 1,
364 'mtime': 1234567890,
365 },
366 store_id2: {
367 'copies': 2,
368 'mtime': 1234567891,
369 },
370 },
371 }
372 for i in range(533)
373 ]
374 ids = [doc['_id'] for doc in docs]
375 ids.sort()
376 db.save_many(docs)
377
378 # Test when store isn't present
379 self.assertEqual(inst.purge_store(store_id3), [])
380 for doc in docs:
381 self.assertEqual(db.get(doc['_id']), doc)
382
383 # Purge one of the stores, make sure the other remains
384 self.assertEqual(inst.purge_store(store_id1), ids)
385 for doc in db.get_many(ids):
386 _id = doc['_id']
387 rev = doc.pop('_rev')
388 self.assertTrue(rev.startswith('2-'))
389 self.assertEqual(
390 doc,
391 {
392 '_id': _id,
393 'type': 'dmedia/file',
394 'bytes': 1776,
395 'stored': {
396 store_id2: {
397 'copies': 2,
398 'mtime': 1234567891,
399 },
400 },
401 }
402 )
403
404 # Purge the other store
405 self.assertEqual(inst.purge_store(store_id2), ids)
406 for doc in db.get_many(ids):
407 _id = doc['_id']
408 rev = doc.pop('_rev')
409 self.assertTrue(rev.startswith('3-'))
410 self.assertEqual(
411 doc,
412 {
413 '_id': _id,
414 'type': 'dmedia/file',
415 'bytes': 1776,
416 'stored': {},
417 }
418 )
419
420 # Purge both again, make sure no doc changes result:
421 self.assertEqual(inst.purge_store(store_id1), [])
422 self.assertEqual(inst.purge_store(store_id2), [])
423 for doc in db.get_many(ids):
424 _id = doc['_id']
425 rev = doc.pop('_rev')
426 self.assertTrue(rev.startswith('3-'))
427 self.assertEqual(
428 doc,
429 {
430 '_id': _id,
431 'type': 'dmedia/file',
432 'bytes': 1776,
433 'stored': {},
434 }
435 )
436
505 def test_update_atime(self):437 def test_update_atime(self):
506 inst = core.Core(self.env)438 inst = core.Core(self.env)
507 _id = random_id()439 _id = random_id()
508440
=== modified file 'dmedia/tests/test_util.py'
--- dmedia/tests/test_util.py 2012-07-09 14:45:38 +0000
+++ dmedia/tests/test_util.py 2012-11-26 00:24:26 +0000
@@ -51,7 +51,32 @@
51 tmp.makedirs('.dmedia')51 tmp.makedirs('.dmedia')
52 self.assertTrue(util.isfilestore(tmp.dir))52 self.assertTrue(util.isfilestore(tmp.dir))
5353
54 def test_getfilestore(self):54 def test_get_filestore_id(self):
55 tmp = TempDir()
56 doc = schema.create_filestore(1)
57 _id = doc['_id']
58
59 # Test when .dmedia/ doesn't exist
60 self.assertIsNone(util.get_filestore_id(tmp.dir))
61
62 # Test when .dmedia/ exists, but store.json doesn't:
63 tmp.makedirs('.dmedia')
64 self.assertIsNone(util.get_filestore_id(tmp.dir))
65
66 # Test when .dmedia/store.json exists
67 store = tmp.join('.dmedia', 'store.json')
68 json.dump(doc, open(store, 'w'))
69 self.assertEqual(util.get_filestore_id(tmp.dir), _id)
70
71 # Test with bad JSON
72 open(store, 'wb').write(b'bad JSON, no cookie for you')
73 self.assertIsNone(util.get_filestore_id(tmp.dir))
74
75 # Test with non-dict
76 json.dump('hello', open(store, 'w'))
77 self.assertIsNone(util.get_filestore_id(tmp.dir))
78
79 def test_get_filestore(self):
55 tmp = TempDir()80 tmp = TempDir()
56 doc = schema.create_filestore(1)81 doc = schema.create_filestore(1)
5782
5883
=== modified file 'dmedia/util.py'
--- dmedia/util.py 2012-10-19 00:11:46 +0000
+++ dmedia/util.py 2012-11-26 00:24:26 +0000
@@ -43,6 +43,15 @@
43 return path.isdir(path.join(parentdir, DOTNAME))43 return path.isdir(path.join(parentdir, DOTNAME))
4444
4545
46def get_filestore_id(parentdir):
47 store = path.join(parentdir, DOTNAME, 'store.json')
48 try:
49 doc = json.load(open(store, 'r'))
50 return doc['_id']
51 except Exception:
52 pass
53
54
46def get_filestore(parentdir, store_id, copies=None):55def get_filestore(parentdir, store_id, copies=None):
47 store = path.join(parentdir, DOTNAME, 'store.json')56 store = path.join(parentdir, DOTNAME, 'store.json')
48 doc = json.load(open(store, 'r'))57 doc = json.load(open(store, 'r'))

Subscribers

People subscribed via source and target branches