10587: Added --version to arv-mount command.
[arvados.git] / services / fuse / tests / test_command_args.py
1 import arvados
2 import arvados_fuse
3 import arvados_fuse.command
4 import contextlib
5 import functools
6 import json
7 import llfuse
8 import logging
9 import mock
10 import os
11 import run_test_server
12 import sys
13 import tempfile
14 import unittest
15
16 def noexit(func):
17     """If argparse or arvados_fuse tries to exit, fail the test instead"""
18     class SystemExitCaught(StandardError):
19         pass
20     @functools.wraps(func)
21     def wrapper(*args, **kwargs):
22         try:
23             return func(*args, **kwargs)
24         except SystemExit:
25             raise SystemExitCaught
26     return wrapper
27
28 @contextlib.contextmanager
29 def nostderr():
30     orig, sys.stderr = sys.stderr, open(os.devnull, 'w')
31     try:
32         yield
33     finally:
34         sys.stderr = orig
35
36
37 class MountArgsTest(unittest.TestCase):
38     def setUp(self):
39         self.mntdir = tempfile.mkdtemp()
40         run_test_server.authorize_with('active')
41
42     def tearDown(self):
43         os.rmdir(self.mntdir)
44
45     def lookup(self, mnt, *path):
46         ent = mnt.operations.inodes[llfuse.ROOT_INODE]
47         for p in path:
48             ent = ent[p]
49         return ent
50
51     def check_ent_type(self, cls, *path):
52         ent = self.lookup(self.mnt, *path)
53         self.assertEqual(ent.__class__, cls)
54         return ent
55
56     @noexit
57     def test_default_all(self):
58         args = arvados_fuse.command.ArgumentParser().parse_args([
59             '--foreground', self.mntdir])
60         self.assertEqual(args.mode, None)
61         self.mnt = arvados_fuse.command.Mount(args)
62         e = self.check_ent_type(arvados_fuse.ProjectDirectory, 'home')
63         self.assertEqual(e.project_object['uuid'],
64                          run_test_server.fixture('users')['active']['uuid'])
65         e = self.check_ent_type(arvados_fuse.MagicDirectory, 'by_id')
66
67         e = self.check_ent_type(arvados_fuse.StringFile, 'README')
68         readme = e.readfrom(0, -1)
69         self.assertRegexpMatches(readme, r'active-user@arvados\.local')
70         self.assertRegexpMatches(readme, r'\n$')
71
72         e = self.check_ent_type(arvados_fuse.StringFile, 'by_id', 'README')
73         txt = e.readfrom(0, -1)
74         self.assertRegexpMatches(txt, r'portable data hash')
75         self.assertRegexpMatches(txt, r'\n$')
76
77     @noexit
78     def test_by_id(self):
79         args = arvados_fuse.command.ArgumentParser().parse_args([
80             '--by-id',
81             '--foreground', self.mntdir])
82         self.assertEqual(args.mode, 'by_id')
83         self.mnt = arvados_fuse.command.Mount(args)
84         e = self.check_ent_type(arvados_fuse.MagicDirectory)
85         self.assertEqual(e.pdh_only, False)
86         self.assertEqual(True, self.mnt.listen_for_events)
87
88     @noexit
89     def test_by_pdh(self):
90         args = arvados_fuse.command.ArgumentParser().parse_args([
91             '--by-pdh',
92             '--foreground', self.mntdir])
93         self.assertEqual(args.mode, 'by_pdh')
94         self.mnt = arvados_fuse.command.Mount(args)
95         e = self.check_ent_type(arvados_fuse.MagicDirectory)
96         self.assertEqual(e.pdh_only, True)
97         self.assertEqual(False, self.mnt.listen_for_events)
98
99     @noexit
100     def test_by_tag(self):
101         args = arvados_fuse.command.ArgumentParser().parse_args([
102             '--by-tag',
103             '--foreground', self.mntdir])
104         self.assertEqual(args.mode, 'by_tag')
105         self.mnt = arvados_fuse.command.Mount(args)
106         e = self.check_ent_type(arvados_fuse.TagsDirectory)
107         self.assertEqual(True, self.mnt.listen_for_events)
108
109     @noexit
110     def test_collection(self, id_type='uuid'):
111         c = run_test_server.fixture('collections')['public_text_file']
112         cid = c[id_type]
113         args = arvados_fuse.command.ArgumentParser().parse_args([
114             '--collection', cid,
115             '--foreground', self.mntdir])
116         self.mnt = arvados_fuse.command.Mount(args)
117         e = self.check_ent_type(arvados_fuse.CollectionDirectory)
118         self.assertEqual(e.collection_locator, cid)
119         self.assertEqual(id_type == 'uuid', self.mnt.listen_for_events)
120
121     def test_collection_pdh(self):
122         self.test_collection('portable_data_hash')
123
124     @noexit
125     def test_home(self):
126         args = arvados_fuse.command.ArgumentParser().parse_args([
127             '--home',
128             '--foreground', self.mntdir])
129         self.assertEqual(args.mode, 'home')
130         self.mnt = arvados_fuse.command.Mount(args)
131         e = self.check_ent_type(arvados_fuse.ProjectDirectory)
132         self.assertEqual(e.project_object['uuid'],
133                          run_test_server.fixture('users')['active']['uuid'])
134         self.assertEqual(True, self.mnt.listen_for_events)
135
136     def test_mutually_exclusive_args(self):
137         cid = run_test_server.fixture('collections')['public_text_file']['uuid']
138         gid = run_test_server.fixture('groups')['aproject']['uuid']
139         for badargs in [
140                 ['--mount-tmp', 'foo', '--collection', cid],
141                 ['--mount-tmp', 'foo', '--project', gid],
142                 ['--collection', cid, '--project', gid],
143                 ['--by-id', '--project', gid],
144                 ['--mount-tmp', 'foo', '--by-id'],
145         ]:
146             with nostderr():
147                 with self.assertRaises(SystemExit):
148                     args = arvados_fuse.command.ArgumentParser().parse_args(
149                         badargs + ['--foreground', self.mntdir])
150                     arvados_fuse.command.Mount(args)
151     @noexit
152     def test_project(self):
153         uuid = run_test_server.fixture('groups')['aproject']['uuid']
154         args = arvados_fuse.command.ArgumentParser().parse_args([
155             '--project', uuid,
156             '--foreground', self.mntdir])
157         self.mnt = arvados_fuse.command.Mount(args)
158         e = self.check_ent_type(arvados_fuse.ProjectDirectory)
159         self.assertEqual(e.project_object['uuid'], uuid)
160
161     @noexit
162     def test_shared(self):
163         args = arvados_fuse.command.ArgumentParser().parse_args([
164             '--shared',
165             '--foreground', self.mntdir])
166         self.assertEqual(args.mode, 'shared')
167         self.mnt = arvados_fuse.command.Mount(args)
168         e = self.check_ent_type(arvados_fuse.SharedDirectory)
169         self.assertEqual(e.current_user['uuid'],
170                          run_test_server.fixture('users')['active']['uuid'])
171         self.assertEqual(True, self.mnt.listen_for_events)
172
173     @noexit
174     @mock.patch('arvados.events.subscribe')
175     def test_disable_event_listening(self, mock_subscribe):
176         args = arvados_fuse.command.ArgumentParser().parse_args([
177             '--disable-event-listening',
178             '--by-id',
179             '--foreground', self.mntdir])
180         self.mnt = arvados_fuse.command.Mount(args)
181         self.assertEqual(True, self.mnt.listen_for_events)
182         self.assertEqual(True, self.mnt.args.disable_event_listening)
183         with self.mnt:
184             pass
185         self.assertEqual(0, mock_subscribe.call_count)
186
187     @noexit
188     @mock.patch('arvados.events.subscribe')
189     def test_custom(self, mock_subscribe):
190         args = arvados_fuse.command.ArgumentParser().parse_args([
191             '--mount-tmp', 'foo',
192             '--mount-tmp', 'bar',
193             '--mount-home', 'my_home',
194             '--foreground', self.mntdir])
195         self.assertEqual(args.mode, None)
196         self.mnt = arvados_fuse.command.Mount(args)
197         self.check_ent_type(arvados_fuse.Directory)
198         self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'foo')
199         self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'bar')
200         e = self.check_ent_type(arvados_fuse.ProjectDirectory, 'my_home')
201         self.assertEqual(e.project_object['uuid'],
202                          run_test_server.fixture('users')['active']['uuid'])
203         self.assertEqual(True, self.mnt.listen_for_events)
204         with self.mnt:
205             pass
206         self.assertEqual(1, mock_subscribe.call_count)
207
208     @noexit
209     @mock.patch('arvados.events.subscribe')
210     def test_custom_no_listen(self, mock_subscribe):
211         args = arvados_fuse.command.ArgumentParser().parse_args([
212             '--mount-by-pdh', 'pdh',
213             '--mount-tmp', 'foo',
214             '--mount-tmp', 'bar',
215             '--foreground', self.mntdir])
216         self.mnt = arvados_fuse.command.Mount(args)
217         self.assertEqual(False, self.mnt.listen_for_events)
218         with self.mnt:
219             pass
220         self.assertEqual(0, mock_subscribe.call_count)
221
222     def test_custom_unsupported_layouts(self):
223         for name in ['.', '..', '', 'foo/bar', '/foo']:
224             with nostderr():
225                 with self.assertRaises(SystemExit):
226                     args = arvados_fuse.command.ArgumentParser().parse_args([
227                         '--mount-tmp', name,
228                         '--foreground', self.mntdir])
229                     arvados_fuse.command.Mount(args)
230
231 class MountErrorTest(unittest.TestCase):
232     def setUp(self):
233         self.mntdir = tempfile.mkdtemp()
234         run_test_server.run()
235         run_test_server.authorize_with("active")
236         self.logger = logging.getLogger("null")
237         self.logger.setLevel(logging.CRITICAL+1)
238
239     def tearDown(self):
240         if os.path.exists(self.mntdir):
241             # If the directory was not unmounted, this will raise an exception.
242             os.rmdir(self.mntdir)
243         run_test_server.reset()
244
245     def test_no_token(self):
246         del arvados.config._settings["ARVADOS_API_TOKEN"]
247         arvados.config._settings = {}
248         with self.assertRaises(SystemExit) as ex:
249             args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
250             arvados_fuse.command.Mount(args, logger=self.logger).run()
251         self.assertEqual(1, ex.exception.code)
252
253     def test_no_host(self):
254         del arvados.config._settings["ARVADOS_API_HOST"]
255         with self.assertRaises(SystemExit) as ex:
256             args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
257             arvados_fuse.command.Mount(args, logger=self.logger).run()
258         self.assertEqual(1, ex.exception.code)
259
260     def test_bogus_host(self):
261         arvados.config._settings["ARVADOS_API_HOST"] = "100::"
262         with self.assertRaises(SystemExit) as ex:
263             args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
264             arvados_fuse.command.Mount(args, logger=self.logger).run()
265         self.assertEqual(1, ex.exception.code)
266
267     def test_bogus_token(self):
268         arvados.config._settings["ARVADOS_API_TOKEN"] = "zzzzzzzzzzzzz"
269         with self.assertRaises(SystemExit) as ex:
270             args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
271             arvados_fuse.command.Mount(args, logger=self.logger).run()
272         self.assertEqual(1, ex.exception.code)
273
274     def test_bogus_mount_dir(self):
275         # All FUSE errors in llfuse.init() are raised as RuntimeError
276         # An easy error to trigger is to supply a nonexistent mount point,
277         # so test that one.
278         #
279         # Other possible errors that also raise RuntimeError (but are much
280         # harder to test automatically because they depend on operating
281         # system configuration):
282         #
283         # The user doesn't have permission to use FUSE
284         # The user specified --allow-other but user_allow_other is not set
285         # in /etc/fuse.conf
286         os.rmdir(self.mntdir)
287         with self.assertRaises(SystemExit) as ex:
288             args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
289             arvados_fuse.command.Mount(args, logger=self.logger).run()
290         self.assertEqual(1, ex.exception.code)
291
292     def test_unreadable_collection(self):
293         with self.assertRaises(SystemExit) as ex:
294             args = arvados_fuse.command.ArgumentParser().parse_args([
295                 "--collection", "zzzzz-4zz18-zzzzzzzzzzzzzzz", self.mntdir])
296             arvados_fuse.command.Mount(args, logger=self.logger).run()
297         self.assertEqual(1, ex.exception.code)
298
299     def test_unreadable_project(self):
300         with self.assertRaises(SystemExit) as ex:
301             args = arvados_fuse.command.ArgumentParser().parse_args([
302                 "--project", "zzzzz-j7d0g-zzzzzzzzzzzzzzz", self.mntdir])
303             arvados_fuse.command.Mount(args, logger=self.logger).run()
304         self.assertEqual(1, ex.exception.code)