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 with open(os.devnull, 'w') as dn:
38 orig, sys.stderr = sys.stderr, dn
45 class MountArgsTest(unittest.TestCase):
47 self.mntdir = tempfile.mkdtemp()
48 run_test_server.authorize_with('active')
53 def lookup(self, mnt, *path):
54 ent = mnt.operations.inodes[llfuse.ROOT_INODE]
59 @contextlib.contextmanager
60 def stderrMatches(self, stderr):
61 orig, sys.stderr = sys.stderr, stderr
67 def check_ent_type(self, cls, *path):
68 ent = self.lookup(self.mnt, *path)
69 self.assertEqual(ent.__class__, cls)
73 def test_default_all(self):
74 args = arvados_fuse.command.ArgumentParser().parse_args([
75 '--foreground', self.mntdir])
76 self.assertEqual(args.mode, None)
77 self.mnt = arvados_fuse.command.Mount(args)
78 e = self.check_ent_type(arvados_fuse.ProjectDirectory, 'home')
79 self.assertEqual(e.project_object['uuid'],
80 run_test_server.fixture('users')['active']['uuid'])
81 e = self.check_ent_type(arvados_fuse.MagicDirectory, 'by_id')
83 e = self.check_ent_type(arvados_fuse.StringFile, 'README')
84 readme = e.readfrom(0, -1).decode()
85 self.assertRegexpMatches(readme, r'active-user@arvados\.local')
86 self.assertRegexpMatches(readme, r'\n$')
88 e = self.check_ent_type(arvados_fuse.StringFile, 'by_id', 'README')
89 txt = e.readfrom(0, -1).decode()
90 self.assertRegexpMatches(txt, r'portable data hash')
91 self.assertRegexpMatches(txt, r'\n$')
95 args = arvados_fuse.command.ArgumentParser().parse_args([
97 '--foreground', self.mntdir])
98 self.assertEqual(args.mode, 'by_id')
99 self.mnt = arvados_fuse.command.Mount(args)
100 e = self.check_ent_type(arvados_fuse.MagicDirectory)
101 self.assertEqual(e.pdh_only, False)
102 self.assertEqual(True, self.mnt.listen_for_events)
105 def test_by_pdh(self):
106 args = arvados_fuse.command.ArgumentParser().parse_args([
108 '--foreground', self.mntdir])
109 self.assertEqual(args.mode, 'by_pdh')
110 self.mnt = arvados_fuse.command.Mount(args)
111 e = self.check_ent_type(arvados_fuse.MagicDirectory)
112 self.assertEqual(e.pdh_only, True)
113 self.assertEqual(False, self.mnt.listen_for_events)
116 def test_by_tag(self):
117 args = arvados_fuse.command.ArgumentParser().parse_args([
119 '--foreground', self.mntdir])
120 self.assertEqual(args.mode, 'by_tag')
121 self.mnt = arvados_fuse.command.Mount(args)
122 e = self.check_ent_type(arvados_fuse.TagsDirectory)
123 self.assertEqual(True, self.mnt.listen_for_events)
126 def test_collection(self, id_type='uuid'):
127 c = run_test_server.fixture('collections')['public_text_file']
129 args = arvados_fuse.command.ArgumentParser().parse_args([
131 '--foreground', self.mntdir])
132 self.mnt = arvados_fuse.command.Mount(args)
133 e = self.check_ent_type(arvados_fuse.CollectionDirectory)
134 self.assertEqual(e.collection_locator, cid)
135 self.assertEqual(id_type == 'uuid', self.mnt.listen_for_events)
137 def test_collection_pdh(self):
138 self.test_collection('portable_data_hash')
142 args = arvados_fuse.command.ArgumentParser().parse_args([
144 '--foreground', self.mntdir])
145 self.assertEqual(args.mode, 'home')
146 self.mnt = arvados_fuse.command.Mount(args)
147 e = self.check_ent_type(arvados_fuse.ProjectDirectory)
148 self.assertEqual(e.project_object['uuid'],
149 run_test_server.fixture('users')['active']['uuid'])
150 self.assertEqual(True, self.mnt.listen_for_events)
152 def test_mutually_exclusive_args(self):
153 cid = run_test_server.fixture('collections')['public_text_file']['uuid']
154 gid = run_test_server.fixture('groups')['aproject']['uuid']
156 ['--mount-tmp', 'foo', '--collection', cid],
157 ['--mount-tmp', 'foo', '--project', gid],
158 ['--collection', cid, '--project', gid],
159 ['--by-id', '--project', gid],
160 ['--mount-tmp', 'foo', '--by-id'],
163 with self.assertRaises(SystemExit):
164 args = arvados_fuse.command.ArgumentParser().parse_args(
165 badargs + ['--foreground', self.mntdir])
166 arvados_fuse.command.Mount(args)
168 def test_project(self):
169 uuid = run_test_server.fixture('groups')['aproject']['uuid']
170 args = arvados_fuse.command.ArgumentParser().parse_args([
172 '--foreground', self.mntdir])
173 self.mnt = arvados_fuse.command.Mount(args)
174 e = self.check_ent_type(arvados_fuse.ProjectDirectory)
175 self.assertEqual(e.project_object['uuid'], uuid)
178 def test_shared(self):
179 args = arvados_fuse.command.ArgumentParser().parse_args([
181 '--foreground', self.mntdir])
182 self.assertEqual(args.mode, 'shared')
183 self.mnt = arvados_fuse.command.Mount(args)
184 e = self.check_ent_type(arvados_fuse.SharedDirectory)
185 self.assertEqual(e.current_user['uuid'],
186 run_test_server.fixture('users')['active']['uuid'])
187 self.assertEqual(True, self.mnt.listen_for_events)
189 def test_version_argument(self):
190 # The argparse version action prints to stderr in Python 2 and stdout
191 # in Python 3.4 and up. Write both to the same stream so the test can pass
195 sys.stderr = io.StringIO()
196 sys.stdout = sys.stderr
198 with self.assertRaises(SystemExit):
199 args = arvados_fuse.command.ArgumentParser().parse_args(['--version'])
200 self.assertRegexpMatches(sys.stdout.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
206 @mock.patch('arvados.events.subscribe')
207 def test_disable_event_listening(self, mock_subscribe):
208 args = arvados_fuse.command.ArgumentParser().parse_args([
209 '--disable-event-listening',
211 '--foreground', self.mntdir])
212 self.mnt = arvados_fuse.command.Mount(args)
213 self.assertEqual(True, self.mnt.listen_for_events)
214 self.assertEqual(True, self.mnt.args.disable_event_listening)
217 self.assertEqual(0, mock_subscribe.call_count)
220 @mock.patch('arvados.events.subscribe')
221 def test_custom(self, mock_subscribe):
222 args = arvados_fuse.command.ArgumentParser().parse_args([
223 '--mount-tmp', 'foo',
224 '--mount-tmp', 'bar',
225 '--mount-home', 'my_home',
226 '--foreground', self.mntdir])
227 self.assertEqual(args.mode, None)
228 self.mnt = arvados_fuse.command.Mount(args)
229 self.check_ent_type(arvados_fuse.Directory)
230 self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'foo')
231 self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'bar')
232 e = self.check_ent_type(arvados_fuse.ProjectDirectory, 'my_home')
233 self.assertEqual(e.project_object['uuid'],
234 run_test_server.fixture('users')['active']['uuid'])
235 self.assertEqual(True, self.mnt.listen_for_events)
238 self.assertEqual(1, mock_subscribe.call_count)
241 @mock.patch('arvados.events.subscribe')
242 def test_custom_no_listen(self, mock_subscribe):
243 args = arvados_fuse.command.ArgumentParser().parse_args([
244 '--mount-by-pdh', 'pdh',
245 '--mount-tmp', 'foo',
246 '--mount-tmp', 'bar',
247 '--foreground', self.mntdir])
248 self.mnt = arvados_fuse.command.Mount(args)
249 self.assertEqual(False, self.mnt.listen_for_events)
252 self.assertEqual(0, mock_subscribe.call_count)
254 def test_custom_unsupported_layouts(self):
255 for name in ['.', '..', '', 'foo/bar', '/foo']:
257 with self.assertRaises(SystemExit):
258 args = arvados_fuse.command.ArgumentParser().parse_args([
260 '--foreground', self.mntdir])
261 arvados_fuse.command.Mount(args)
263 class MountErrorTest(unittest.TestCase):
265 self.mntdir = tempfile.mkdtemp()
266 run_test_server.run()
267 run_test_server.authorize_with("active")
268 self.logger = logging.getLogger("null")
269 self.logger.setLevel(logging.CRITICAL+1)
272 if os.path.exists(self.mntdir):
273 # If the directory was not unmounted, this will raise an exception.
274 os.rmdir(self.mntdir)
275 run_test_server.reset()
277 def test_no_token(self):
278 del arvados.config._settings["ARVADOS_API_TOKEN"]
279 arvados.config._settings = {}
280 with self.assertRaises(SystemExit) as ex:
281 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
282 arvados_fuse.command.Mount(args, logger=self.logger).run()
283 self.assertEqual(1, ex.exception.code)
285 def test_no_host(self):
286 del arvados.config._settings["ARVADOS_API_HOST"]
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)
292 def test_bogus_host(self):
293 arvados.config._settings["ARVADOS_API_HOST"] = "100::"
294 with self.assertRaises(SystemExit) as ex:
295 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
296 arvados_fuse.command.Mount(args, logger=self.logger).run()
297 self.assertEqual(1, ex.exception.code)
299 def test_bogus_token(self):
300 arvados.config._settings["ARVADOS_API_TOKEN"] = "zzzzzzzzzzzzz"
301 with self.assertRaises(SystemExit) as ex:
302 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
303 arvados_fuse.command.Mount(args, logger=self.logger).run()
304 self.assertEqual(1, ex.exception.code)
306 def test_bogus_mount_dir(self):
307 # All FUSE errors in llfuse.init() are raised as RuntimeError
308 # An easy error to trigger is to supply a nonexistent mount point,
311 # Other possible errors that also raise RuntimeError (but are much
312 # harder to test automatically because they depend on operating
313 # system configuration):
315 # The user doesn't have permission to use FUSE
316 # The user specified --allow-other but user_allow_other is not set
318 os.rmdir(self.mntdir)
319 with self.assertRaises(SystemExit) as ex:
320 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
321 arvados_fuse.command.Mount(args, logger=self.logger).run()
322 self.assertEqual(1, ex.exception.code)
324 def test_unreadable_collection(self):
325 with self.assertRaises(SystemExit) as ex:
326 args = arvados_fuse.command.ArgumentParser().parse_args([
327 "--collection", "zzzzz-4zz18-zzzzzzzzzzzzzzz", self.mntdir])
328 arvados_fuse.command.Mount(args, logger=self.logger).run()
329 self.assertEqual(1, ex.exception.code)
331 def test_unreadable_project(self):
332 with self.assertRaises(SystemExit) as ex:
333 args = arvados_fuse.command.ArgumentParser().parse_args([
334 "--project", "zzzzz-j7d0g-zzzzzzzzzzzzzzz", self.mntdir])
335 arvados_fuse.command.Mount(args, logger=self.logger).run()
336 self.assertEqual(1, ex.exception.code)