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
9 import arvados_fuse.command
18 from . import run_test_server
24 """If argparse or arvados_fuse tries to exit, fail the test instead"""
25 class SystemExitCaught(Exception):
27 @functools.wraps(func)
28 def wrapper(*args, **kwargs):
30 return func(*args, **kwargs)
32 raise SystemExitCaught
35 @contextlib.contextmanager
37 orig, sys.stderr = sys.stderr, open(os.devnull, 'w')
44 class MountArgsTest(unittest.TestCase):
46 self.mntdir = tempfile.mkdtemp()
47 run_test_server.authorize_with('active')
52 def lookup(self, mnt, *path):
53 ent = mnt.operations.inodes[llfuse.ROOT_INODE]
58 @contextlib.contextmanager
59 def stderrMatches(self, stderr):
60 orig, sys.stderr = sys.stderr, stderr
66 def check_ent_type(self, cls, *path):
67 ent = self.lookup(self.mnt, *path)
68 self.assertEqual(ent.__class__, cls)
72 def test_default_all(self):
73 args = arvados_fuse.command.ArgumentParser().parse_args([
74 '--foreground', self.mntdir])
75 self.assertEqual(args.mode, None)
76 self.mnt = arvados_fuse.command.Mount(args)
77 e = self.check_ent_type(arvados_fuse.ProjectDirectory, 'home')
78 self.assertEqual(e.project_object['uuid'],
79 run_test_server.fixture('users')['active']['uuid'])
80 e = self.check_ent_type(arvados_fuse.MagicDirectory, 'by_id')
82 e = self.check_ent_type(arvados_fuse.StringFile, 'README')
83 readme = e.readfrom(0, -1).decode()
84 self.assertRegexpMatches(readme, r'active-user@arvados\.local')
85 self.assertRegexpMatches(readme, r'\n$')
87 e = self.check_ent_type(arvados_fuse.StringFile, 'by_id', 'README')
88 txt = e.readfrom(0, -1).decode()
89 self.assertRegexpMatches(txt, r'portable data hash')
90 self.assertRegexpMatches(txt, r'\n$')
94 args = arvados_fuse.command.ArgumentParser().parse_args([
96 '--foreground', self.mntdir])
97 self.assertEqual(args.mode, 'by_id')
98 self.mnt = arvados_fuse.command.Mount(args)
99 e = self.check_ent_type(arvados_fuse.MagicDirectory)
100 self.assertEqual(e.pdh_only, False)
101 self.assertEqual(True, self.mnt.listen_for_events)
104 def test_by_pdh(self):
105 args = arvados_fuse.command.ArgumentParser().parse_args([
107 '--foreground', self.mntdir])
108 self.assertEqual(args.mode, 'by_pdh')
109 self.mnt = arvados_fuse.command.Mount(args)
110 e = self.check_ent_type(arvados_fuse.MagicDirectory)
111 self.assertEqual(e.pdh_only, True)
112 self.assertEqual(False, self.mnt.listen_for_events)
115 def test_by_tag(self):
116 args = arvados_fuse.command.ArgumentParser().parse_args([
118 '--foreground', self.mntdir])
119 self.assertEqual(args.mode, 'by_tag')
120 self.mnt = arvados_fuse.command.Mount(args)
121 e = self.check_ent_type(arvados_fuse.TagsDirectory)
122 self.assertEqual(True, self.mnt.listen_for_events)
125 def test_collection(self, id_type='uuid'):
126 c = run_test_server.fixture('collections')['public_text_file']
128 args = arvados_fuse.command.ArgumentParser().parse_args([
130 '--foreground', self.mntdir])
131 self.mnt = arvados_fuse.command.Mount(args)
132 e = self.check_ent_type(arvados_fuse.CollectionDirectory)
133 self.assertEqual(e.collection_locator, cid)
134 self.assertEqual(id_type == 'uuid', self.mnt.listen_for_events)
136 def test_collection_pdh(self):
137 self.test_collection('portable_data_hash')
141 args = arvados_fuse.command.ArgumentParser().parse_args([
143 '--foreground', self.mntdir])
144 self.assertEqual(args.mode, 'home')
145 self.mnt = arvados_fuse.command.Mount(args)
146 e = self.check_ent_type(arvados_fuse.ProjectDirectory)
147 self.assertEqual(e.project_object['uuid'],
148 run_test_server.fixture('users')['active']['uuid'])
149 self.assertEqual(True, self.mnt.listen_for_events)
151 def test_mutually_exclusive_args(self):
152 cid = run_test_server.fixture('collections')['public_text_file']['uuid']
153 gid = run_test_server.fixture('groups')['aproject']['uuid']
155 ['--mount-tmp', 'foo', '--collection', cid],
156 ['--mount-tmp', 'foo', '--project', gid],
157 ['--collection', cid, '--project', gid],
158 ['--by-id', '--project', gid],
159 ['--mount-tmp', 'foo', '--by-id'],
162 with self.assertRaises(SystemExit):
163 args = arvados_fuse.command.ArgumentParser().parse_args(
164 badargs + ['--foreground', self.mntdir])
165 arvados_fuse.command.Mount(args)
167 def test_project(self):
168 uuid = run_test_server.fixture('groups')['aproject']['uuid']
169 args = arvados_fuse.command.ArgumentParser().parse_args([
171 '--foreground', self.mntdir])
172 self.mnt = arvados_fuse.command.Mount(args)
173 e = self.check_ent_type(arvados_fuse.ProjectDirectory)
174 self.assertEqual(e.project_object['uuid'], uuid)
177 def test_shared(self):
178 args = arvados_fuse.command.ArgumentParser().parse_args([
180 '--foreground', self.mntdir])
181 self.assertEqual(args.mode, 'shared')
182 self.mnt = arvados_fuse.command.Mount(args)
183 e = self.check_ent_type(arvados_fuse.SharedDirectory)
184 self.assertEqual(e.current_user['uuid'],
185 run_test_server.fixture('users')['active']['uuid'])
186 self.assertEqual(True, self.mnt.listen_for_events)
188 def test_version_argument(self):
189 # The argparse version action prints to stderr in Python 2 and stdout
190 # in Python 3.4 and up. Write both to the same stream so the test can pass
194 sys.stderr = io.StringIO()
195 sys.stdout = sys.stderr
197 with self.assertRaises(SystemExit):
198 args = arvados_fuse.command.ArgumentParser().parse_args(['--version'])
199 self.assertRegexpMatches(sys.stdout.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
205 @mock.patch('arvados.events.subscribe')
206 def test_disable_event_listening(self, mock_subscribe):
207 args = arvados_fuse.command.ArgumentParser().parse_args([
208 '--disable-event-listening',
210 '--foreground', self.mntdir])
211 self.mnt = arvados_fuse.command.Mount(args)
212 self.assertEqual(True, self.mnt.listen_for_events)
213 self.assertEqual(True, self.mnt.args.disable_event_listening)
216 self.assertEqual(0, mock_subscribe.call_count)
219 @mock.patch('arvados.events.subscribe')
220 def test_custom(self, mock_subscribe):
221 args = arvados_fuse.command.ArgumentParser().parse_args([
222 '--mount-tmp', 'foo',
223 '--mount-tmp', 'bar',
224 '--mount-home', 'my_home',
225 '--foreground', self.mntdir])
226 self.assertEqual(args.mode, None)
227 self.mnt = arvados_fuse.command.Mount(args)
228 self.check_ent_type(arvados_fuse.Directory)
229 self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'foo')
230 self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'bar')
231 e = self.check_ent_type(arvados_fuse.ProjectDirectory, 'my_home')
232 self.assertEqual(e.project_object['uuid'],
233 run_test_server.fixture('users')['active']['uuid'])
234 self.assertEqual(True, self.mnt.listen_for_events)
237 self.assertEqual(1, mock_subscribe.call_count)
240 @mock.patch('arvados.events.subscribe')
241 def test_custom_no_listen(self, mock_subscribe):
242 args = arvados_fuse.command.ArgumentParser().parse_args([
243 '--mount-by-pdh', 'pdh',
244 '--mount-tmp', 'foo',
245 '--mount-tmp', 'bar',
246 '--foreground', self.mntdir])
247 self.mnt = arvados_fuse.command.Mount(args)
248 self.assertEqual(False, self.mnt.listen_for_events)
251 self.assertEqual(0, mock_subscribe.call_count)
253 def test_custom_unsupported_layouts(self):
254 for name in ['.', '..', '', 'foo/bar', '/foo']:
256 with self.assertRaises(SystemExit):
257 args = arvados_fuse.command.ArgumentParser().parse_args([
259 '--foreground', self.mntdir])
260 arvados_fuse.command.Mount(args)
262 class MountErrorTest(unittest.TestCase):
264 self.mntdir = tempfile.mkdtemp()
265 run_test_server.run()
266 run_test_server.authorize_with("active")
267 self.logger = logging.getLogger("null")
268 self.logger.setLevel(logging.CRITICAL+1)
271 if os.path.exists(self.mntdir):
272 # If the directory was not unmounted, this will raise an exception.
273 os.rmdir(self.mntdir)
274 run_test_server.reset()
276 def test_no_token(self):
277 del arvados.config._settings["ARVADOS_API_TOKEN"]
278 arvados.config._settings = {}
279 with self.assertRaises(SystemExit) as ex:
280 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
281 arvados_fuse.command.Mount(args, logger=self.logger).run()
282 self.assertEqual(1, ex.exception.code)
284 def test_no_host(self):
285 del arvados.config._settings["ARVADOS_API_HOST"]
286 with self.assertRaises(SystemExit) as ex:
287 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
288 arvados_fuse.command.Mount(args, logger=self.logger).run()
289 self.assertEqual(1, ex.exception.code)
291 def test_bogus_host(self):
292 arvados.config._settings["ARVADOS_API_HOST"] = "100::"
293 with self.assertRaises(SystemExit) as ex:
294 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
295 arvados_fuse.command.Mount(args, logger=self.logger).run()
296 self.assertEqual(1, ex.exception.code)
298 def test_bogus_token(self):
299 arvados.config._settings["ARVADOS_API_TOKEN"] = "zzzzzzzzzzzzz"
300 with self.assertRaises(SystemExit) as ex:
301 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
302 arvados_fuse.command.Mount(args, logger=self.logger).run()
303 self.assertEqual(1, ex.exception.code)
305 def test_bogus_mount_dir(self):
306 # All FUSE errors in llfuse.init() are raised as RuntimeError
307 # An easy error to trigger is to supply a nonexistent mount point,
310 # Other possible errors that also raise RuntimeError (but are much
311 # harder to test automatically because they depend on operating
312 # system configuration):
314 # The user doesn't have permission to use FUSE
315 # The user specified --allow-other but user_allow_other is not set
317 os.rmdir(self.mntdir)
318 with self.assertRaises(SystemExit) as ex:
319 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
320 arvados_fuse.command.Mount(args, logger=self.logger).run()
321 self.assertEqual(1, ex.exception.code)
323 def test_unreadable_collection(self):
324 with self.assertRaises(SystemExit) as ex:
325 args = arvados_fuse.command.ArgumentParser().parse_args([
326 "--collection", "zzzzz-4zz18-zzzzzzzzzzzzzzz", self.mntdir])
327 arvados_fuse.command.Mount(args, logger=self.logger).run()
328 self.assertEqual(1, ex.exception.code)
330 def test_unreadable_project(self):
331 with self.assertRaises(SystemExit) as ex:
332 args = arvados_fuse.command.ArgumentParser().parse_args([
333 "--project", "zzzzz-j7d0g-zzzzzzzzzzzzzzz", self.mntdir])
334 arvados_fuse.command.Mount(args, logger=self.logger).run()
335 self.assertEqual(1, ex.exception.code)