Merge branch '8284-fix-slurm-queue-timestamp-check' closes #8284
[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_custom(self, mock_subscribe):
176         args = arvados_fuse.command.ArgumentParser().parse_args([
177             '--mount-tmp', 'foo',
178             '--mount-tmp', 'bar',
179             '--mount-home', 'my_home',
180             '--foreground', self.mntdir])
181         self.assertEqual(args.mode, None)
182         self.mnt = arvados_fuse.command.Mount(args)
183         self.check_ent_type(arvados_fuse.Directory)
184         self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'foo')
185         self.check_ent_type(arvados_fuse.TmpCollectionDirectory, 'bar')
186         e = self.check_ent_type(arvados_fuse.ProjectDirectory, 'my_home')
187         self.assertEqual(e.project_object['uuid'],
188                          run_test_server.fixture('users')['active']['uuid'])
189         self.assertEqual(True, self.mnt.listen_for_events)
190         with self.mnt:
191             pass
192         self.assertEqual(1, mock_subscribe.call_count)
193
194     @noexit
195     @mock.patch('arvados.events.subscribe')
196     def test_custom_no_listen(self, mock_subscribe):
197         args = arvados_fuse.command.ArgumentParser().parse_args([
198             '--mount-by-pdh', 'pdh',
199             '--mount-tmp', 'foo',
200             '--mount-tmp', 'bar',
201             '--foreground', self.mntdir])
202         self.mnt = arvados_fuse.command.Mount(args)
203         self.assertEqual(False, self.mnt.listen_for_events)
204         with self.mnt:
205             pass
206         self.assertEqual(0, mock_subscribe.call_count)
207
208     def test_custom_unsupported_layouts(self):
209         for name in ['.', '..', '', 'foo/bar', '/foo']:
210             with nostderr():
211                 with self.assertRaises(SystemExit):
212                     args = arvados_fuse.command.ArgumentParser().parse_args([
213                         '--mount-tmp', name,
214                         '--foreground', self.mntdir])
215                     arvados_fuse.command.Mount(args)
216
217 class MountErrorTest(unittest.TestCase):
218     def setUp(self):
219         self.mntdir = tempfile.mkdtemp()
220         run_test_server.run()
221         run_test_server.authorize_with("active")
222         self.logger = logging.getLogger("null")
223         self.logger.setLevel(logging.CRITICAL+1)
224
225     def tearDown(self):
226         if os.path.exists(self.mntdir):
227             # If the directory was not unmounted, this will raise an exception.
228             os.rmdir(self.mntdir)
229         run_test_server.reset()
230
231     def test_no_token(self):
232         del arvados.config._settings["ARVADOS_API_TOKEN"]
233         arvados.config._settings = {}
234         with self.assertRaises(SystemExit) as ex:
235             args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
236             arvados_fuse.command.Mount(args, logger=self.logger).run()
237         self.assertEqual(1, ex.exception.code)
238
239     def test_no_host(self):
240         del arvados.config._settings["ARVADOS_API_HOST"]
241         with self.assertRaises(SystemExit) as ex:
242             args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
243             arvados_fuse.command.Mount(args, logger=self.logger).run()
244         self.assertEqual(1, ex.exception.code)
245
246     def test_bogus_host(self):
247         arvados.config._settings["ARVADOS_API_HOST"] = "100::"
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_bogus_token(self):
254         arvados.config._settings["ARVADOS_API_TOKEN"] = "zzzzzzzzzzzzz"
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_mount_dir(self):
261         # All FUSE errors in llfuse.init() are raised as RuntimeError
262         # An easy error to trigger is to supply a nonexistent mount point,
263         # so test that one.
264         #
265         # Other possible errors that also raise RuntimeError (but are much
266         # harder to test automatically because they depend on operating
267         # system configuration):
268         #
269         # The user doesn't have permission to use FUSE
270         # The user specified --allow-other but user_allow_other is not set
271         # in /etc/fuse.conf
272         os.rmdir(self.mntdir)
273         with self.assertRaises(SystemExit) as ex:
274             args = arvados_fuse.command.ArgumentParser().parse_args([self.mntdir])
275             arvados_fuse.command.Mount(args, logger=self.logger).run()
276         self.assertEqual(1, ex.exception.code)
277
278     def test_unreadable_collection(self):
279         with self.assertRaises(SystemExit) as ex:
280             args = arvados_fuse.command.ArgumentParser().parse_args([
281                 "--collection", "zzzzz-4zz18-zzzzzzzzzzzzzzz", self.mntdir])
282             arvados_fuse.command.Mount(args, logger=self.logger).run()
283         self.assertEqual(1, ex.exception.code)
284
285     def test_unreadable_project(self):
286         with self.assertRaises(SystemExit) as ex:
287             args = arvados_fuse.command.ArgumentParser().parse_args([
288                 "--project", "zzzzz-j7d0g-zzzzzzzzzzzzzzz", self.mntdir])
289             arvados_fuse.command.Mount(args, logger=self.logger).run()
290         self.assertEqual(1, ex.exception.code)