Skip to content

Commit

Permalink
Exposing the final result to the entry point.
Browse files Browse the repository at this point in the history
- Command line arguments added to enable the manager (-m).
- Acceptance tests added for the above.
  • Loading branch information
metatoaster committed Nov 28, 2015
1 parent bfaf34e commit 82015b8
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 12 deletions.
43 changes: 33 additions & 10 deletions src/explosive/fuse/ctrl.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import sys
import logging
from os.path import abspath
from os.path import join
from os import getcwd

from argparse import ArgumentError
from argparse import ArgumentParser
Expand All @@ -10,6 +13,7 @@

from explosive.fuse import pathmaker
from explosive.fuse.fs import ExplosiveFUSE
from explosive.fuse.fs import ManagedExplosiveFUSE


class _Version(Action):
Expand Down Expand Up @@ -110,6 +114,16 @@ def get_argparse():
parser.add_argument(
'-d', '--debug', dest='debug', action='store_true',
help='Run with debug messages.')
parser.add_argument(
'-m', '--manager', dest='manager', action='store_true',
help='Enable the symlink manager directory, where all the archives '
'indexed and available within this ExplosiveFUSE instance are '
'exposed as symlinks. These symlinks can be removed to remove '
'the associated files from the filesystem, and new symlinks to '
'other archives can be created to add them to the filesystem.')
parser.add_argument(
'--manager-dir', dest='manager_dir', nargs='?', default='.manager',
help='Name of the symlink manager directory.')
parser.add_argument(
'dir',
help='The directory to mount the compressed archive(s) to.')
Expand Down Expand Up @@ -148,17 +162,26 @@ def main(args=None):
format='%(asctime)s %(levelname)s %(name)s %(message)s'
)

try:
FUSE(
ExplosiveFUSE(
parsed_args.archives,
_pathmaker=parsed_args.pathmaker,
overwrite=parsed_args.overwrite,
include_arcname=parsed_args.include_arcname,
),
parsed_args.dir,
foreground=parsed_args.foreground,
if parsed_args.manager:
mount_root = abspath(join(getcwd(), parsed_args.dir))
fuse = ManagedExplosiveFUSE(
mount_root,
parsed_args.manager_dir,
parsed_args.archives,
_pathmaker=parsed_args.pathmaker,
overwrite=parsed_args.overwrite,
include_arcname=parsed_args.include_arcname,
)
else:
fuse = ExplosiveFUSE(
parsed_args.archives,
_pathmaker=parsed_args.pathmaker,
overwrite=parsed_args.overwrite,
include_arcname=parsed_args.include_arcname,
)

try:
FUSE(fuse, parsed_args.dir, foreground=parsed_args.foreground)
except RuntimeError:
# assume error messages are properly handled.
sys.exit(255)
Expand Down
21 changes: 19 additions & 2 deletions src/explosive/fuse/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def statfs(self, path):
return dict(f_bsize=1024, f_blocks=1024, f_bavail=0)


class SymlinkFUSE(LoggingMixIn, Operations):
class _SymlinkFUSE(LoggingMixIn, Operations):
"""
A symlink only filesystem that exist in memory.
"""
Expand Down Expand Up @@ -211,18 +211,33 @@ def symlink(self, path, source):
symkey = basename(path)
target = abspath(join(self.mount_root, self.base_path[1:], source))
self.symlinks[symkey] = target
# Warning: non-standard return value
return target

def unlink(self, path):
symkey = basename(path)
if symkey not in self.symlinks:
raise FuseOSError(ENOTSUP)
# Warning: non-standard return value
return self.symlinks.pop(symkey)

def statfs(self, path):
return {'f_bsize': 0, 'f_blocks': 0, 'f_bavail': 0}


class SymlinkFUSE(_SymlinkFUSE):
"""
Standardized implementation for methods that return non-standard
values.
"""

def symlink(self, path, source):
super(SymlinkFUSE, self).symlink(path, source)

def unlink(self, path):
super(SymlinkFUSE, self).unlink(path)


class ManagedExplosiveFUSE(ExplosiveFUSE):
"""
ExplosiveFS with a management path
Expand All @@ -233,7 +248,7 @@ def __init__(self, mount_root, management_node, *a, **kw):
raise ValueError('Management node must be a valid directory name')
self.management_node = management_node
base_path = '/' + management_node
self.symlinkfs = SymlinkFUSE(mount_root, base_path)
self.symlinkfs = _SymlinkFUSE(mount_root, base_path)
super(ManagedExplosiveFUSE, self).__init__(*a, **kw)
self.symlinkfs.symlinks.update(
{'%d_%s' % (n, basename(k)): k
Expand All @@ -259,9 +274,11 @@ def __call__(self, op, path, *args):
self.symlinkfs.unlink(path)
# Assume I/O error due to archive inaccessible.
raise FuseOSError(EIO)
return None

elif op == 'unlink':
self.mapping.unload_archive(result)
return None

return result
return super(ManagedExplosiveFUSE, self).__call__(op, path, *args)
62 changes: 62 additions & 0 deletions tests/test_ctrl.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ def test_failure(self):
with self.assertRaises(SystemExit):
ctrl.main(['-d', '/tmp/to/no/such/dir', 'somezip.zip'])

def test_failure_with_manager(self):
with capture_stdio() as stdio:
with self.assertRaises(SystemExit):
ctrl.main(['-m', '-d', '/tmp/to/no/such/dir', 'somezip.zip'])

def test_invalid_layout_choice(self):
with capture_stdio() as stdio:
in_, out, err = stdio
Expand Down Expand Up @@ -164,3 +169,60 @@ def test_success_basic(self):
sorted(os.listdir(join(self.mountpoint, 'demo2.zip', 'demo'))),
['file1', 'file2', 'file3', 'file4', 'file5', 'file6']
)

def test_success_managed_default(self):
dummy1 = path('demo1.zip')
dummy2 = path('demo2.zip')

# This will terminate, so spawn a separate process.
p = Process(
target=ctrl.main, args=(['-md', self.mountpoint, dummy1, dummy2],))
p.start()
p.join()

self.assertEqual(
sorted(os.listdir(self.mountpoint)),
['.manager', 'demo1.zip', 'demo2.zip'],
)

self.assertEqual(
sorted(os.listdir(join(self.mountpoint, '.manager'))),
['0_demo1.zip', '1_demo2.zip']
)

self.assertEqual(
sorted(os.listdir(join(self.mountpoint, 'demo1.zip'))),
['file1', 'file2', 'file3', 'file4', 'file5', 'file6']
)

self.assertEqual(
sorted(os.listdir(join(self.mountpoint, 'demo2.zip'))),
['demo']
)

self.assertEqual(
sorted(os.listdir(join(self.mountpoint, 'demo2.zip', 'demo'))),
['file1', 'file2', 'file3', 'file4', 'file5', 'file6']
)

os.unlink(join(self.mountpoint, '.manager', '0_demo1.zip'))
self.assertEqual(
sorted(os.listdir(self.mountpoint)),
['.manager', 'demo2.zip'],
)

os.symlink(dummy1, join(self.mountpoint, '.manager', 'archive.zip'))

self.assertEqual(
sorted(os.listdir(join(self.mountpoint, '.manager'))),
['1_demo2.zip', 'archive.zip']
)

self.assertEqual(
sorted(os.listdir(self.mountpoint)),
['.manager', 'demo1.zip', 'demo2.zip'],
)

with self.assertRaises(OSError):
os.symlink(dummy1, join(
self.mountpoint, '.manager', 'alternate.zip'))

0 comments on commit 82015b8

Please sign in to comment.