1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
7 import arvados_fuse.command
16 import run_test_server
22 """If argparse or arvados_fuse tries to exit, fail the test instead"""
23 class SystemExitCaught(StandardError):
25 @functools.wraps(func)
26 def wrapper(*args, **kwargs):
28 return func(*args, **kwargs)
30 raise SystemExitCaught
33 @contextlib.contextmanager
35 orig, sys.stderr = sys.stderr, open(os.devnull, 'w')
42 class MountArgsTest(unittest.TestCase):
44 self.mntdir = tempfile.mkdtemp()
45 run_test_server.authorize_with('active')
50 def lookup(self, mnt, *path):
51 ent = mnt.operations.inodes[llfuse.ROOT_INODE]
56 @contextlib.contextmanager
57 def stderrMatches(self, stderr):
58 orig, sys.stderr = sys.stderr, stderr
64 def check_ent_type(self, cls, *path):
65 ent = self.lookup(self.mnt, *path)
66 self.assertEqual(ent.__class__, cls)
70 def test_default_all(self):
71 args = arvados_fuse.command.ArgumentParser().parse_args([
72 '--foreground', self.mntdir])
73 self.assertEqual(args.mode, None)
74 self.mnt = arvados_fuse.command.Mount(args)
75 e = self.check_ent_type(arvados_fuse.ProjectDirectory, 'home')
76 self.assertEqual(e.project_object['uuid'],
77 run_test_server.fixture('users')['active']['uuid'])
78 e = self.check_ent_type(arvados_fuse.MagicDirectory, 'by_id')
80 e = self.check_ent_type(arvados_fuse.StringFile, 'README')
81 readme = e.readfrom(0, -1)
82 self.assertRegexpMatches(readme, r'active-user@arvados\.local')
83 self.assertRegexpMatches(readme, r'\n$')
85 e = self.check_ent_type(arvados_fuse.StringFile, 'by_id', 'README')
86 txt = e.readfrom(0, -1)
87 self.assertRegexpMatches(txt, r'portable data hash')
88 self.assertRegexpMatches(txt, r'\n$')
92 args = arvados_fuse.command.ArgumentParser().parse_args([
94 '--foreground', self.mntdir])
95 self.assertEqual(args.mode, 'by_id')
96 self.mnt = arvados_fuse.command.Mount(args)
97 e = self.check_ent_type(arvados_fuse.MagicDirectory)
98 self.assertEqual(e.pdh_only, False)
99 self.assertEqual(True, self.mnt.listen_for_events)
102 def test_by_pdh(self):
103 args = arvados_fuse.command.ArgumentParser().parse_args([
105 '--foreground', self.mntdir])
106 self.assertEqual(args.mode, 'by_pdh')
107 self.mnt = arvados_fuse.command.Mount(args)
108 e = self.check_ent_type(arvados_fuse.MagicDirectory)
109 self.assertEqual(e.pdh_only, True)
110 self.assertEqual(False, self.mnt.listen_for_events)
113 def test_by_tag(self):
114 args = arvados_fuse.command.ArgumentParser().parse_args([
116 '--foreground', self.mntdir])
117 self.assertEqual(args.mode, 'by_tag')
118 self.mnt = arvados_fuse.command.Mount(args)
119 e = self.check_ent_type(arvados_fuse.TagsDirectory)
120 self.assertEqual(True, self.mnt.listen_for_events)
123 def test_collection(self, id_type='uuid'):
124 c = run_test_server.fixture('collections')['public_text_file']
126 args = arvados_fuse.command.ArgumentParser().parse_args([
128 '--foreground', self.mntdir])
129 self.mnt = arvados_fuse.command.Mount(args)
130 e = self.check_ent_type(arvados_fuse.CollectionDirectory)
131 self.assertEqual(e.collection_locator, cid)
132 self.assertEqual(id_type == 'uuid', self.mnt.listen_for_events)
134 def test_collection_pdh(self):
135 self.test_collection('portable_data_hash')
139 args = arvados_fuse.command.ArgumentParser().parse_args([
141 '--foreground', self.mntdir])
142 self.assertEqual(args.mode, 'home')
143 self.mnt = arvados_fuse.command.Mount(args)
144 e = self.check_ent_type(arvados_fuse.ProjectDirectory)
145 self.assertEqual(e.project_object['uuid'],
146 run_test_server.fixture('users')['active']['uuid'])
147 self.assertEqual(True, self.mnt.listen_for_events)
149 def test_mutually_exclusive_args(self):
150 cid = run_test_server.fixture('collections')['public_text_file']['uuid']
151 gid = run_test_server.fixture('groups')['aproject']['uuid']
153 ['--mount-tmp', 'foo', '--collection', cid],
154 ['--mount-tmp', 'foo', '--project', gid],
155 ['--collection', cid, '--project', gid],
156 ['--by-id', '--project', gid],
157 ['--mount-tmp', 'foo', '--by-id'],
160 with self.assertRaises(SystemExit):
161 args = arvados_fuse.command.ArgumentParser().parse_args(
162 badargs + ['--foreground', self.mntdir])
163 arvados_fuse.command.Mount(args)
165 def test_project(self):
166 uuid = run_test_server.fixture('groups')['aproject']['uuid']
167 args = arvados_fuse.command.ArgumentParser().parse_args([
169 '--foreground', self.mntdir])
170 self.mnt = arvados_fuse.command.Mount(args)
171 e = self.check_ent_type(arvados_fuse.ProjectDirectory)
172 self.assertEqual(e.project_object['uuid'], uuid)
175 def test_shared(self):
176 args = arvados_fuse.command.ArgumentParser().parse_args([
178 '--foreground', self.mntdir])
179 self.assertEqual(args.mode, 'shared')
180 self.mnt = arvados_fuse.command.Mount(args)
181 e = self.check_ent_type(arvados_fuse.SharedDirectory)
182 self.assertEqual(e.current_user['uuid'],
183 run_test_server.fixture('users')['active']['uuid'])
184 self.assertEqual(True, self.mnt.listen_for_events)
186 def test_version_argument(self):
187 orig, sys.stderr = sys.stderr, io.BytesIO()
188 with self.assertRaises(SystemExit):
189 args = arvados_fuse.command.ArgumentParser().parse_args(['--version'])
190 self.assertRegexpMatches(sys.stderr.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
194 @mock.patch('arvados.events.subscribe')
195 def test_disable_event_listening(self, mock_subscribe):
196 args = arvados_fuse.command.ArgumentParser().parse_args([
197 '--disable-event-listening',
199 '--foreground', self.mntdir])
200 self.mnt = arvados_fuse.command.Mount(args)
201 self.assertEqual(True, self.mnt.listen_for_events)
202 self.assertEqual(True, self.mnt.args.disable_event_listening)
205 self.assertEqual(0, mock_subscribe.call_count)
208 @mock.patch('arvados.events.subscribe')
209 def test_custom(self, mock_subscribe):
210 args = arvados_fuse.command.ArgumentParser().parse_args([
211 '--mount-tmp', 'foo',
212 '--mount-tmp', 'bar',
213 '--mount-home', 'my_home',
214 '--foreground', self.mntdir])
215 self.assertEqual(args.mode, None)
216 self.mnt = arvados_fuse.command.Mount(args)
217 self.check_ent_type(arvados_fuse.Directory)
218 self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'foo')
219 self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'bar')
220 e = self.check_ent_type(arvados_fuse.ProjectDirectory, 'my_home')
221 self.assertEqual(e.project_object['uuid'],
222 run_test_server.fixture('users')['active']['uuid'])
223 self.assertEqual(True, self.mnt.listen_for_events)
226 self.assertEqual(1, mock_subscribe.call_count)
229 @mock.patch('arvados.events.subscribe')
230 def test_custom_no_listen(self, mock_subscribe):
231 args = arvados_fuse.command.ArgumentParser().parse_args([
232 '--mount-by-pdh', 'pdh',
233 '--mount-tmp', 'foo',
234 '--mount-tmp', 'bar',
235 '--foreground', self.mntdir])
236 self.mnt = arvados_fuse.command.Mount(args)
237 self.assertEqual(False, self.mnt.listen_for_events)
240 self.assertEqual(0, mock_subscribe.call_count)
242 def test_custom_unsupported_layouts(self):
243 for name in ['.', '..', '', 'foo/bar', '/foo']:
245 with self.assertRaises(SystemExit):
246 args = arvados_fuse.command.ArgumentParser().parse_args([
248 '--foreground', self.mntdir])
249 arvados_fuse.command.Mount(args)
251 class MountErrorTest(unittest.TestCase):
253 self.mntdir = tempfile.mkdtemp()
254 run_test_server.run()
255 run_test_server.authorize_with("active")
256 self.logger = logging.getLogger("null")
257 self.logger.setLevel(logging.CRITICAL+1)
260 if os.path.exists(self.mntdir):
261 # If the directory was not unmounted, this will raise an exception.
262 os.rmdir(self.mntdir)
263 run_test_server.reset()
265 def test_no_token(self):
266 del arvados.config._settings["ARVADOS_API_TOKEN"]
267 arvados.config._settings = {}
268 with self.assertRaises(SystemExit) as ex:
269 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
270 arvados_fuse.command.Mount(args, logger=self.logger).run()
271 self.assertEqual(1, ex.exception.code)
273 def test_no_host(self):
274 del arvados.config._settings["ARVADOS_API_HOST"]
275 with self.assertRaises(SystemExit) as ex:
276 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
277 arvados_fuse.command.Mount(args, logger=self.logger).run()
278 self.assertEqual(1, ex.exception.code)
280 def test_bogus_host(self):
281 arvados.config._settings["ARVADOS_API_HOST"] = "100::"
282 with self.assertRaises(SystemExit) as ex:
283 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
284 arvados_fuse.command.Mount(args, logger=self.logger).run()
285 self.assertEqual(1, ex.exception.code)
287 def test_bogus_token(self):
288 arvados.config._settings["ARVADOS_API_TOKEN"] = "zzzzzzzzzzzzz"
289 with self.assertRaises(SystemExit) as ex:
290 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
291 arvados_fuse.command.Mount(args, logger=self.logger).run()
292 self.assertEqual(1, ex.exception.code)
294 def test_bogus_mount_dir(self):
295 # All FUSE errors in llfuse.init() are raised as RuntimeError
296 # An easy error to trigger is to supply a nonexistent mount point,
299 # Other possible errors that also raise RuntimeError (but are much
300 # harder to test automatically because they depend on operating
301 # system configuration):
303 # The user doesn't have permission to use FUSE
304 # The user specified --allow-other but user_allow_other is not set
306 os.rmdir(self.mntdir)
307 with self.assertRaises(SystemExit) as ex:
308 args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
309 arvados_fuse.command.Mount(args, logger=self.logger).run()
310 self.assertEqual(1, ex.exception.code)
312 def test_unreadable_collection(self):
313 with self.assertRaises(SystemExit) as ex:
314 args = arvados_fuse.command.ArgumentParser().parse_args([
315 "--collection", "zzzzz-4zz18-zzzzzzzzzzzzzzz", self.mntdir])
316 arvados_fuse.command.Mount(args, logger=self.logger).run()
317 self.assertEqual(1, ex.exception.code)
319 def test_unreadable_project(self):
320 with self.assertRaises(SystemExit) as ex:
321 args = arvados_fuse.command.ArgumentParser().parse_args([
322 "--project", "zzzzz-j7d0g-zzzzzzzzzzzzzzz", self.mntdir])
323 arvados_fuse.command.Mount(args, logger=self.logger).run()
324 self.assertEqual(1, ex.exception.code)