1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
5 from __future__ import absolute_import
6 from __future__ import print_function
7 from six import assertRegex
10 import arvados_fuse.command
19 from . import run_test_server
25 """If argparse or arvados_fuse tries to exit, fail the test instead"""
26 class SystemExitCaught(Exception):
28 @functools.wraps(func)
29 def wrapper(*args, **kwargs):
31 return func(*args, **kwargs)
33 raise SystemExitCaught
36 @contextlib.contextmanager
38 with open(os.devnull, 'w') as dn:
39 orig, sys.stderr = sys.stderr, dn
46 class MountArgsTest(unittest.TestCase):
48 self.mntdir = tempfile.mkdtemp()
49 run_test_server.authorize_with('active')
54 def lookup(self, mnt, *path):
55 ent = mnt.operations.inodes[llfuse.ROOT_INODE]
60 @contextlib.contextmanager
61 def stderrMatches(self, stderr):
62 orig, sys.stderr = sys.stderr, stderr
68 def check_ent_type(self, cls, *path):
69 ent = self.lookup(self.mnt, *path)
70 self.assertEqual(ent.__class__, cls)
74 def test_default_all(self):
75 args = arvados_fuse.command.ArgumentParser().parse_args([
76 '--foreground', self.mntdir])
77 self.assertEqual(args.mode, None)
78 self.mnt = arvados_fuse.command.Mount(args)
79 e = self.check_ent_type(arvados_fuse.ProjectDirectory, 'home')
80 self.assertEqual(e.project_object['uuid'],
81 run_test_server.fixture('users')['active']['uuid'])
82 e = self.check_ent_type(arvados_fuse.MagicDirectory, 'by_id')
84 e = self.check_ent_type(arvados_fuse.StringFile, 'README')
85 readme = e.readfrom(0, -1).decode()
86 assertRegex(self, readme, r'active-user@arvados\.local')
87 assertRegex(self, readme, r'\n$')
89 e = self.check_ent_type(arvados_fuse.StringFile, 'by_id', 'README')
90 txt = e.readfrom(0, -1).decode()
91 assertRegex(self, txt, r'portable data hash')
92 assertRegex(self, txt, r'\n$')
96 args = arvados_fuse.command.ArgumentParser().parse_args([
98 '--foreground', self.mntdir])
99 self.assertEqual(args.mode, 'by_id')
100 self.mnt = arvados_fuse.command.Mount(args)
101 e = self.check_ent_type(arvados_fuse.MagicDirectory)
102 self.assertEqual(e.pdh_only, False)
103 self.assertEqual(True, self.mnt.listen_for_events)
106 def test_by_pdh(self):
107 args = arvados_fuse.command.ArgumentParser().parse_args([
109 '--foreground', self.mntdir])
110 self.assertEqual(args.mode, 'by_pdh')
111 self.mnt = arvados_fuse.command.Mount(args)
112 e = self.check_ent_type(arvados_fuse.MagicDirectory)
113 self.assertEqual(e.pdh_only, True)
114 self.assertEqual(False, self.mnt.listen_for_events)
117 def test_by_tag(self):
118 args = arvados_fuse.command.ArgumentParser().parse_args([
120 '--foreground', self.mntdir])
121 self.assertEqual(args.mode, 'by_tag')
122 self.mnt = arvados_fuse.command.Mount(args)
123 e = self.check_ent_type(arvados_fuse.TagsDirectory)
124 self.assertEqual(True, self.mnt.listen_for_events)
127 def test_collection(self, id_type='uuid'):
128 c = run_test_server.fixture('collections')['public_text_file']
130 args = arvados_fuse.command.ArgumentParser().parse_args([
132 '--foreground', self.mntdir])
133 self.mnt = arvados_fuse.command.Mount(args)
134 e = self.check_ent_type(arvados_fuse.CollectionDirectory)
135 self.assertEqual(e.collection_locator, cid)
136 self.assertEqual(id_type == 'uuid', self.mnt.listen_for_events)
138 def test_collection_pdh(self):
139 self.test_collection('portable_data_hash')
143 args = arvados_fuse.command.ArgumentParser().parse_args([
145 '--foreground', self.mntdir])
146 self.assertEqual(args.mode, 'home')
147 self.mnt = arvados_fuse.command.Mount(args)
148 e = self.check_ent_type(arvados_fuse.ProjectDirectory)
149 self.assertEqual(e.project_object['uuid'],
150 run_test_server.fixture('users')['active']['uuid'])
151 self.assertEqual(True, self.mnt.listen_for_events)
153 def test_mutually_exclusive_args(self):
154 cid = run_test_server.fixture('collections')['public_text_file']['uuid']
155 gid = run_test_server.fixture('groups')['aproject']['uuid']
157 ['--mount-tmp', 'foo', '--collection', cid],
158 ['--mount-tmp', 'foo', '--project', gid],
159 ['--collection', cid, '--project', gid],
160 ['--by-id', '--project', gid],
161 ['--mount-tmp', 'foo', '--by-id'],
164 with self.assertRaises(SystemExit):
165 args = arvados_fuse.command.ArgumentParser().parse_args(
166 badargs + ['--foreground', self.mntdir])
167 arvados_fuse.command.Mount(args)
169 def test_project(self):
170 uuid = run_test_server.fixture('groups')['aproject']['uuid']
171 args = arvados_fuse.command.ArgumentParser().parse_args([
173 '--foreground', self.mntdir])
174 self.mnt = arvados_fuse.command.Mount(args)
175 e = self.check_ent_type(arvados_fuse.ProjectDirectory)
176 self.assertEqual(e.project_object['uuid'], uuid)
179 def test_shared(self):
180 args = arvados_fuse.command.ArgumentParser().parse_args([
182 '--foreground', self.mntdir])
183 self.assertEqual(args.mode, 'shared')
184 self.mnt = arvados_fuse.command.Mount(args)
185 e = self.check_ent_type(arvados_fuse.SharedDirectory)
186 self.assertEqual(e.current_user['uuid'],
187 run_test_server.fixture('users')['active']['uuid'])
188 self.assertEqual(True, self.mnt.listen_for_events)
190 def test_version_argument(self):
191 # The argparse version action prints to stderr in Python 2 and stdout
192 # in Python 3.4 and up. Write both to the same stream so the test can pass
196 sys.stderr = io.StringIO()
197 sys.stdout = sys.stderr
199 with self.assertRaises(SystemExit):
200 args = arvados_fuse.command.ArgumentParser().parse_args(['--version'])
201 assertRegex(self, sys.stdout.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
207 @mock.patch('arvados.events.subscribe')
208 def test_disable_event_listening(self, mock_subscribe):
209 args = arvados_fuse.command.ArgumentParser().parse_args([
210 '--disable-event-listening',
212 '--foreground', self.mntdir])
213 self.mnt = arvados_fuse.command.Mount(args)
214 self.assertEqual(True, self.mnt.listen_for_events)
215 self.assertEqual(True, self.mnt.args.disable_event_listening)
218 self.assertEqual(0, mock_subscribe.call_count)
221 @mock.patch('arvados.events.subscribe')
222 def test_custom(self, mock_subscribe):
223 args = arvados_fuse.command.ArgumentParser().parse_args([
224 '--mount-tmp', 'foo',
225 '--mount-tmp', 'bar',
226 '--mount-home', 'my_home',
227 '--foreground', self.mntdir])
228 self.assertEqual(args.mode, None)
229 self.mnt = arvados_fuse.command.Mount(args)
230 self.check_ent_type(arvados_fuse.Directory)
231 self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'foo')
232 self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'bar')
233 e = self.check_ent_type(arvados_fuse.ProjectDirectory, 'my_home')
234 self.assertEqual(e.project_object['uuid'],
235 run_test_server.fixture('users')['active']['uuid'])
236 self.assertEqual(True, self.mnt.listen_for_events)
239 self.assertEqual(1, mock_subscribe.call_count)
242 @mock.patch('arvados.events.subscribe')
243 def test_custom_no_listen(self, mock_subscribe):
244 args = arvados_fuse.command.ArgumentParser().parse_args([
245 '--mount-by-pdh', 'pdh',
246 '--mount-tmp', 'foo',
247 '--mount-tmp', 'bar',
248 '--foreground', self.mntdir])
249 self.mnt = arvados_fuse.command.Mount(args)
250 self.assertEqual(False, self.mnt.listen_for_events)
253 self.assertEqual(0, mock_subscribe.call_count)
255 def test_custom_unsupported_layouts(self):
256 for name in ['.', '..', '', 'foo/bar', '/foo']:
258 with self.assertRaises(SystemExit):
259 args = arvados_fuse.command.ArgumentParser().parse_args([
261 '--foreground', self.mntdir])
262 arvados_fuse.command.Mount(args)
264 class MountErrorTest(unittest.TestCase):
266 self.mntdir = tempfile.mkdtemp()
267 run_test_server.run()
268 run_test_server.authorize_with("active")
269 self.logger = logging.getLogger("null")
270 self.logger.setLevel(logging.CRITICAL+1)
273 if os.path.exists(self.mntdir):
274 # If the directory was not unmounted, this will raise an exception.
275 os.rmdir(self.mntdir)
276 run_test_server.reset()
278 def test_no_token(self):
279 del arvados.config._settings["ARVADOS_API_TOKEN"]
280 arvados.config._settings = {}
281 with self.assertRaises(SystemExit) as ex:
282 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
283 arvados_fuse.command.Mount(args, logger=self.logger).run()
284 self.assertEqual(1, ex.exception.code)
286 def test_no_host(self):
287 del arvados.config._settings["ARVADOS_API_HOST"]
288 with self.assertRaises(SystemExit) as ex:
289 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
290 arvados_fuse.command.Mount(args, logger=self.logger).run()
291 self.assertEqual(1, ex.exception.code)
293 def test_bogus_host(self):
294 arvados.config._settings["ARVADOS_API_HOST"] = "100::"
295 with self.assertRaises(SystemExit) as ex, mock.patch('time.sleep'):
296 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
297 arvados_fuse.command.Mount(args, logger=self.logger).run()
298 self.assertEqual(1, ex.exception.code)
300 def test_bogus_token(self):
301 arvados.config._settings["ARVADOS_API_TOKEN"] = "zzzzzzzzzzzzz"
302 with self.assertRaises(SystemExit) as ex:
303 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
304 arvados_fuse.command.Mount(args, logger=self.logger).run()
305 self.assertEqual(1, ex.exception.code)
307 def test_bogus_mount_dir(self):
308 # All FUSE errors in llfuse.init() are raised as RuntimeError
309 # An easy error to trigger is to supply a nonexistent mount point,
312 # Other possible errors that also raise RuntimeError (but are much
313 # harder to test automatically because they depend on operating
314 # system configuration):
316 # The user doesn't have permission to use FUSE
317 # The user specified --allow-other but user_allow_other is not set
319 os.rmdir(self.mntdir)
320 with self.assertRaises(SystemExit) as ex:
321 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
322 arvados_fuse.command.Mount(args, logger=self.logger).run()
323 self.assertEqual(1, ex.exception.code)
325 def test_unreadable_collection(self):
326 with self.assertRaises(SystemExit) as ex:
327 args = arvados_fuse.command.ArgumentParser().parse_args([
328 "--collection", "zzzzz-4zz18-zzzzzzzzzzzzzzz", self.mntdir])
329 arvados_fuse.command.Mount(args, logger=self.logger).run()
330 self.assertEqual(1, ex.exception.code)
332 def test_unreadable_project(self):
333 with self.assertRaises(SystemExit) as ex:
334 args = arvados_fuse.command.ArgumentParser().parse_args([
335 "--project", "zzzzz-j7d0g-zzzzzzzzzzzzzzz", self.mntdir])
336 arvados_fuse.command.Mount(args, logger=self.logger).run()
337 self.assertEqual(1, ex.exception.code)