1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
16 from pathlib import Path
17 from unittest import mock
20 import arvados_fuse as fuse
23 from arvados_fuse import fusedir
25 from . import run_test_server
26 from .integration_test import IntegrationTest
27 from .mount_test_base import MountTestBase
28 from .test_tmp_collection import storage_classes_desired
30 logger = logging.getLogger('arvados.arv-mount')
32 class AssertWithTimeout(object):
33 """Allow some time for an assertion to pass."""
35 def __init__(self, timeout=0):
36 self.timeout = timeout
39 self.deadline = time.time() + self.timeout
48 def attempt(self, fn, *args, **kwargs):
51 except AssertionError:
52 if time.time() > self.deadline:
58 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
59 class FuseMountTest(MountTestBase):
61 super(FuseMountTest, self).setUp()
63 cw = arvados.collection.Collection()
64 with cw.open('thing1.txt', 'w') as f:
66 with cw.open('thing2.txt', 'w') as f:
69 with cw.open('dir1/thing3.txt', 'w') as f:
71 with cw.open('dir1/thing4.txt', 'w') as f:
74 with cw.open('dir2/thing5.txt', 'w') as f:
76 with cw.open('dir2/thing6.txt', 'w') as f:
79 with cw.open('dir2/dir3/thing7.txt', 'w') as f:
81 with cw.open('dir2/dir3/thing8.txt', 'w') as f:
84 for fnm in ":/.../-/*/ ".split("/"):
85 with cw.open('edgecases/'+fnm, 'w') as f:
88 for fnm in ":/.../-/*/ ".split("/"):
89 with cw.open('edgecases/dirs/'+fnm+'/x/x', 'w') as f:
92 self.testcollection = cw.portable_data_hash()
93 self.test_manifest = cw.manifest_text()
94 self.api.collections().create(body={"manifest_text": self.test_manifest}).execute()
97 self.make_mount(fuse.CollectionDirectory, collection_record=self.testcollection)
99 self.assertDirContents(None, ['thing1.txt', 'thing2.txt',
100 'edgecases', 'dir1', 'dir2'])
101 self.assertDirContents('dir1', ['thing3.txt', 'thing4.txt'])
102 self.assertDirContents('dir2', ['thing5.txt', 'thing6.txt', 'dir3'])
103 self.assertDirContents('dir2/dir3', ['thing7.txt', 'thing8.txt'])
104 self.assertDirContents('edgecases',
105 "dirs/:/.../-/*/ ".split("/"))
106 self.assertDirContents('edgecases/dirs',
107 ":/.../-/*/ ".split("/"))
109 files = {'thing1.txt': 'data 1',
110 'thing2.txt': 'data 2',
111 'dir1/thing3.txt': 'data 3',
112 'dir1/thing4.txt': 'data 4',
113 'dir2/thing5.txt': 'data 5',
114 'dir2/thing6.txt': 'data 6',
115 'dir2/dir3/thing7.txt': 'data 7',
116 'dir2/dir3/thing8.txt': 'data 8'}
118 for k, v in files.items():
119 with open(os.path.join(self.mounttmp, k), 'rb') as f:
120 self.assertEqual(v, f.read().decode())
123 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
124 class FuseMagicTest(MountTestBase):
125 def setUp(self, api=None):
126 super(FuseMagicTest, self).setUp(api=api)
128 self.test_project = run_test_server.fixture('groups')['aproject']['uuid']
129 self.non_project_group = run_test_server.fixture('groups')['public_role']['uuid']
130 self.filter_group = run_test_server.fixture('groups')['afiltergroup']['uuid']
131 self.collection_in_test_project = run_test_server.fixture('collections')['foo_collection_in_aproject']['name']
132 self.collection_in_filter_group = run_test_server.fixture('collections')['baz_file']['name']
134 cw = arvados.collection.Collection()
135 with cw.open('thing1.txt', 'w') as f:
138 self.testcollection = cw.portable_data_hash()
139 self.test_manifest = cw.manifest_text()
140 coll = self.api.collections().create(body={"manifest_text":self.test_manifest}).execute()
141 self.test_manifest_pdh = coll['portable_data_hash']
144 self.make_mount(fuse.MagicDirectory)
146 mount_ls = llfuse.listdir(self.mounttmp)
147 self.assertIn('README', mount_ls)
148 self.assertFalse(any(arvados.util.keep_locator_pattern.match(fn) or
149 arvados.util.uuid_pattern.match(fn)
151 "new FUSE MagicDirectory has no collections or projects")
152 self.assertDirContents(self.testcollection, ['thing1.txt'])
153 self.assertDirContents(os.path.join('by_id', self.testcollection),
155 self.assertIn(self.collection_in_test_project,
156 llfuse.listdir(os.path.join(self.mounttmp, self.test_project)))
157 self.assertIn(self.collection_in_test_project,
158 llfuse.listdir(os.path.join(self.mounttmp, 'by_id', self.test_project)))
159 self.assertIn(self.collection_in_filter_group,
160 llfuse.listdir(os.path.join(self.mounttmp, self.filter_group)))
161 self.assertIn(self.collection_in_filter_group,
162 llfuse.listdir(os.path.join(self.mounttmp, 'by_id', self.filter_group)))
165 mount_ls = llfuse.listdir(self.mounttmp)
166 self.assertIn('README', mount_ls)
167 self.assertIn(self.testcollection, mount_ls)
168 self.assertIn(self.testcollection,
169 llfuse.listdir(os.path.join(self.mounttmp, 'by_id')))
170 self.assertIn(self.test_project, mount_ls)
171 self.assertIn(self.test_project,
172 llfuse.listdir(os.path.join(self.mounttmp, 'by_id')))
173 self.assertIn(self.filter_group,
174 llfuse.listdir(os.path.join(self.mounttmp, 'by_id')))
176 with self.assertRaises(OSError):
177 llfuse.listdir(os.path.join(self.mounttmp, 'by_id', self.non_project_group))
180 files[os.path.join(self.mounttmp, self.testcollection, 'thing1.txt')] = 'data 1'
182 for k, v in files.items():
183 with open(os.path.join(self.mounttmp, k), 'rb') as f:
184 self.assertEqual(v, f.read().decode())
187 class FuseTagsTest(MountTestBase):
189 self.make_mount(fuse.TagsDirectory)
191 d1 = llfuse.listdir(self.mounttmp)
193 self.assertEqual(['foo_tag'], d1)
195 d2 = llfuse.listdir(os.path.join(self.mounttmp, 'foo_tag'))
197 self.assertEqual(['zzzzz-4zz18-fy296fx3hot09f7'], d2)
199 d3 = llfuse.listdir(os.path.join(self.mounttmp, 'foo_tag', 'zzzzz-4zz18-fy296fx3hot09f7'))
201 self.assertEqual(['foo'], d3)
204 class FuseTagsUpdateTest(MountTestBase):
205 def tag_collection(self, coll_uuid, tag_name):
206 return self.api.links().create(
207 body={'link': {'head_uuid': coll_uuid,
213 self.make_mount(fuse.TagsDirectory, poll_time=1)
215 self.assertIn('foo_tag', llfuse.listdir(self.mounttmp))
217 bar_uuid = run_test_server.fixture('collections')['bar_file']['uuid']
218 self.tag_collection(bar_uuid, 'fuse_test_tag')
219 for attempt in AssertWithTimeout(10):
220 attempt(self.assertIn, 'fuse_test_tag', llfuse.listdir(self.mounttmp))
221 self.assertDirContents('fuse_test_tag', [bar_uuid])
223 baz_uuid = run_test_server.fixture('collections')['baz_file']['uuid']
224 l = self.tag_collection(baz_uuid, 'fuse_test_tag')
225 for attempt in AssertWithTimeout(10):
226 attempt(self.assertDirContents, 'fuse_test_tag', [bar_uuid, baz_uuid])
228 self.api.links().delete(uuid=l['uuid']).execute()
229 for attempt in AssertWithTimeout(10):
230 attempt(self.assertDirContents, 'fuse_test_tag', [bar_uuid])
233 def fuseSharedTestHelper(mounttmp):
234 class Test(unittest.TestCase):
236 # Double check that we can open and read objects in this folder as a file,
237 # and that its contents are what we expect.
238 baz_path = os.path.join(
242 'collection in FUSE project',
244 with open(baz_path) as f:
245 self.assertEqual("baz", f.read())
247 # check mtime on collection
248 st = os.stat(baz_path)
250 mtime = st.st_mtime_ns // 1000000000
251 except AttributeError:
253 self.assertEqual(mtime, 1391448174)
255 # shared_dirs is a list of the directories exposed
256 # by fuse.SharedDirectory (i.e. any object visible
257 # to the current user)
258 shared_dirs = llfuse.listdir(mounttmp)
260 self.assertIn('FUSE User', shared_dirs)
262 # fuse_user_objs is a list of the objects owned by the FUSE
263 # test user (which present as files in the 'FUSE User'
265 fuse_user_objs = llfuse.listdir(os.path.join(mounttmp, 'FUSE User'))
266 fuse_user_objs.sort()
267 self.assertEqual(['FUSE Test Project', # project owned by user
268 'collection #1 owned by FUSE', # collection owned by user
269 'collection #2 owned by FUSE' # collection owned by user
272 # test_proj_files is a list of the files in the FUSE Test Project.
273 test_proj_files = llfuse.listdir(os.path.join(mounttmp, 'FUSE User', 'FUSE Test Project'))
274 test_proj_files.sort()
275 self.assertEqual(['collection in FUSE project'
281 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
282 class FuseSharedTest(MountTestBase):
284 self.make_mount(fuse.SharedDirectory,
285 exclude=self.api.users().current().execute()['uuid'])
286 keep = arvados.keep.KeepClient()
287 keep.put("baz".encode())
289 self.pool.apply(fuseSharedTestHelper, (self.mounttmp,))
292 class FuseHomeTest(MountTestBase):
294 self.make_mount(fuse.ProjectDirectory,
295 project_object=self.api.users().current().execute())
297 d1 = llfuse.listdir(self.mounttmp)
298 self.assertIn('Unrestricted public data', d1)
300 d2 = llfuse.listdir(os.path.join(self.mounttmp, 'Unrestricted public data'))
301 public_project = run_test_server.fixture('groups')[
302 'anonymously_accessible_project']
305 for name, item in run_test_server.fixture('collections').items():
306 if 'name' not in item:
308 elif item['owner_uuid'] == public_project['uuid']:
309 self.assertIn(item['name'], d2)
312 # Artificial assumption here: there is no public
313 # collection fixture with the same name as a
314 # non-public collection.
315 self.assertNotIn(item['name'], d2)
317 self.assertNotEqual(0, found_in)
318 self.assertNotEqual(0, found_not_in)
320 d3 = llfuse.listdir(os.path.join(self.mounttmp, 'Unrestricted public data', 'GNU General Public License, version 3'))
321 self.assertEqual(["GNU_General_Public_License,_version_3.pdf"], d3)
324 def fuseModifyFileTestHelperReadStartContents(mounttmp):
325 class Test(unittest.TestCase):
327 d1 = llfuse.listdir(mounttmp)
328 self.assertEqual(["file1.txt"], d1)
329 with open(os.path.join(mounttmp, "file1.txt")) as f:
330 self.assertEqual("blub", f.read())
333 def fuseModifyFileTestHelperReadEndContents(mounttmp):
334 class Test(unittest.TestCase):
336 d1 = llfuse.listdir(mounttmp)
337 self.assertEqual(["file1.txt"], d1)
338 with open(os.path.join(mounttmp, "file1.txt")) as f:
339 self.assertEqual("plnp", f.read())
342 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
343 class FuseModifyFileTest(MountTestBase):
345 collection = arvados.collection.Collection(api_client=self.api)
346 with collection.open("file1.txt", "w") as f:
349 collection.save_new()
351 m = self.make_mount(fuse.CollectionDirectory)
353 m.new_collection(collection.api_response(), collection)
355 self.pool.apply(fuseModifyFileTestHelperReadStartContents, (self.mounttmp,))
357 with collection.open("file1.txt", "w") as f:
360 self.pool.apply(fuseModifyFileTestHelperReadEndContents, (self.mounttmp,))
363 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
364 class FuseAddFileToCollectionTest(MountTestBase):
366 collection = arvados.collection.Collection(api_client=self.api)
367 with collection.open("file1.txt", "w") as f:
370 collection.save_new()
372 m = self.make_mount(fuse.CollectionDirectory)
374 m.new_collection(collection.api_response(), collection)
376 d1 = llfuse.listdir(self.mounttmp)
377 self.assertEqual(["file1.txt"], d1)
379 with collection.open("file2.txt", "w") as f:
382 d1 = llfuse.listdir(self.mounttmp)
383 self.assertEqual(["file1.txt", "file2.txt"], sorted(d1))
386 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
387 class FuseRemoveFileFromCollectionTest(MountTestBase):
389 collection = arvados.collection.Collection(api_client=self.api)
390 with collection.open("file1.txt", "w") as f:
393 with collection.open("file2.txt", "w") as f:
396 collection.save_new()
398 m = self.make_mount(fuse.CollectionDirectory)
400 m.new_collection(collection.api_response(), collection)
402 d1 = llfuse.listdir(self.mounttmp)
403 self.assertEqual(["file1.txt", "file2.txt"], sorted(d1))
405 collection.remove("file2.txt")
407 d1 = llfuse.listdir(self.mounttmp)
408 self.assertEqual(["file1.txt"], d1)
411 def fuseCreateFileTestHelper(mounttmp):
412 class Test(unittest.TestCase):
414 with open(os.path.join(mounttmp, "file1.txt"), "w") as f:
418 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
419 class FuseCreateFileTest(MountTestBase):
421 collection = arvados.collection.Collection(api_client=self.api)
422 collection.save_new()
424 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
425 self.assertEqual(collection2["manifest_text"], "")
427 collection.save_new()
429 m = self.make_mount(fuse.CollectionDirectory)
431 m.new_collection(collection.api_response(), collection)
432 self.assertTrue(m.writable())
434 self.assertNotIn("file1.txt", collection)
436 self.pool.apply(fuseCreateFileTestHelper, (self.mounttmp,))
438 self.assertIn("file1.txt", collection)
440 d1 = llfuse.listdir(self.mounttmp)
441 self.assertEqual(["file1.txt"], d1)
443 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
444 self.assertRegex(collection2["manifest_text"],
445 r'\. d41d8cd98f00b204e9800998ecf8427e\+0\+A\S+ 0:0:file1\.txt$')
448 def fuseWriteFileTestHelperWriteFile(mounttmp):
449 class Test(unittest.TestCase):
451 with open(os.path.join(mounttmp, "file1.txt"), "w") as f:
452 f.write("Hello world!")
455 def fuseWriteFileTestHelperReadFile(mounttmp):
456 class Test(unittest.TestCase):
458 with open(os.path.join(mounttmp, "file1.txt"), "r") as f:
459 self.assertEqual(f.read(), "Hello world!")
462 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
463 class FuseWriteFileTest(MountTestBase):
465 collection = arvados.collection.Collection(api_client=self.api)
466 collection.save_new()
468 m = self.make_mount(fuse.CollectionDirectory)
470 m.new_collection(collection.api_response(), collection)
471 self.assertTrue(m.writable())
473 self.assertNotIn("file1.txt", collection)
475 self.assertEqual(0, self.operations.write_counter.get())
476 self.pool.apply(fuseWriteFileTestHelperWriteFile, (self.mounttmp,))
477 self.assertEqual(12, self.operations.write_counter.get())
479 with collection.open("file1.txt") as f:
480 self.assertEqual(f.read(), "Hello world!")
482 self.assertEqual(0, self.operations.read_counter.get())
483 self.pool.apply(fuseWriteFileTestHelperReadFile, (self.mounttmp,))
484 self.assertEqual(12, self.operations.read_counter.get())
486 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
487 self.assertRegex(collection2["manifest_text"],
488 r'\. 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
490 def fuseMknodTestHelperReadFile(mounttmp):
491 class Test(unittest.TestCase):
493 with open(os.path.join(mounttmp, "file1.txt"), "r") as f:
494 self.assertEqual(f.read(), "")
497 class FuseMknodTest(MountTestBase):
499 # Check that os.mknod() can be used to create normal files.
500 collection = arvados.collection.Collection(api_client=self.api)
501 collection.save_new()
503 m = self.make_mount(fuse.CollectionDirectory)
505 m.new_collection(collection.api_response(), collection)
506 self.assertTrue(m.writable())
508 self.assertNotIn("file1.txt", collection)
510 self.assertEqual(0, self.operations.write_counter.get())
511 self.pool.apply(os.mknod, (os.path.join(self.mounttmp, "file1.txt"),))
513 with collection.open("file1.txt") as f:
514 self.assertEqual(f.read(), "")
516 self.pool.apply(fuseMknodTestHelperReadFile, (self.mounttmp,))
518 # Fail trying to create a FIFO
519 with self.assertRaises(OSError) as exc_check:
520 self.pool.apply(os.mknod, (os.path.join(self.mounttmp, "file2.txt"), stat.S_IFIFO))
522 class FuseMknodReadOnlyTest(MountTestBase):
524 collection = arvados.collection.Collection(api_client=self.api)
525 collection.save_new()
527 m = self.make_mount(fuse.CollectionDirectory, enable_write=False)
529 m.new_collection(collection.api_response(), collection)
530 self.assertTrue(m.writable() is False)
531 with self.assertRaises(OSError) as exc_check:
532 self.pool.apply(os.mknod, (os.path.join(self.mounttmp, "file1.txt"),))
534 class FuseMknodProjectTest(MountTestBase):
536 self.make_mount(fuse.ProjectDirectory,
537 project_object=self.api.users().current().execute())
538 with self.assertRaises(OSError) as exc_check:
539 self.pool.apply(os.mknod, (os.path.join(self.mounttmp, "file1.txt"),))
542 def fuseUpdateFileTestHelper(mounttmp):
543 class Test(unittest.TestCase):
545 with open(os.path.join(mounttmp, "file1.txt"), "w") as f:
546 f.write("Hello world!")
548 with open(os.path.join(mounttmp, "file1.txt"), "r+") as f:
550 self.assertEqual(fr, "Hello world!")
552 f.write("Hola mundo!")
555 self.assertEqual(fr, "Hola mundo!!")
557 with open(os.path.join(mounttmp, "file1.txt"), "r") as f:
558 self.assertEqual(f.read(), "Hola mundo!!")
562 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
563 class FuseUpdateFileTest(MountTestBase):
565 collection = arvados.collection.Collection(api_client=self.api)
566 collection.save_new()
568 m = self.make_mount(fuse.CollectionDirectory)
570 m.new_collection(collection.api_response(), collection)
571 self.assertTrue(m.writable())
573 # See note in MountTestBase.setUp
574 self.pool.apply(fuseUpdateFileTestHelper, (self.mounttmp,))
576 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
577 self.assertRegex(collection2["manifest_text"],
578 r'\. daaef200ebb921e011e3ae922dd3266b\+11\+A\S+ 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:11:file1\.txt 22:1:file1\.txt$')
581 def fuseMkdirTestHelper(mounttmp):
582 class Test(unittest.TestCase):
584 with self.assertRaises(IOError):
585 with open(os.path.join(mounttmp, "testdir", "file1.txt"), "w") as f:
586 f.write("Hello world!")
588 os.mkdir(os.path.join(mounttmp, "testdir"))
590 with self.assertRaises(OSError):
591 os.mkdir(os.path.join(mounttmp, "testdir"))
593 d1 = llfuse.listdir(mounttmp)
594 self.assertEqual(["testdir"], d1)
596 with open(os.path.join(mounttmp, "testdir", "file1.txt"), "w") as f:
597 f.write("Hello world!")
599 d1 = llfuse.listdir(os.path.join(mounttmp, "testdir"))
600 self.assertEqual(["file1.txt"], d1)
604 class FuseMkdirTest(MountTestBase):
606 collection = arvados.collection.Collection(api_client=self.api)
607 collection.save_new()
609 m = self.make_mount(fuse.CollectionDirectory)
611 m.new_collection(collection.api_response(), collection)
612 self.assertTrue(m.writable())
614 self.pool.apply(fuseMkdirTestHelper, (self.mounttmp,))
616 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
617 self.assertRegex(collection2["manifest_text"],
618 r'\./testdir 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
621 def fuseRmTestHelperWriteFile(mounttmp):
622 class Test(unittest.TestCase):
624 os.mkdir(os.path.join(mounttmp, "testdir"))
626 with open(os.path.join(mounttmp, "testdir", "file1.txt"), "w") as f:
627 f.write("Hello world!")
631 def fuseRmTestHelperDeleteFile(mounttmp):
632 class Test(unittest.TestCase):
634 # Can't delete because it's not empty
635 with self.assertRaises(OSError):
636 os.rmdir(os.path.join(mounttmp, "testdir"))
638 d1 = llfuse.listdir(os.path.join(mounttmp, "testdir"))
639 self.assertEqual(["file1.txt"], d1)
642 os.remove(os.path.join(mounttmp, "testdir", "file1.txt"))
644 # Make sure it's empty
645 d1 = llfuse.listdir(os.path.join(mounttmp, "testdir"))
646 self.assertEqual([], d1)
648 # Try to delete it again
649 with self.assertRaises(OSError):
650 os.remove(os.path.join(mounttmp, "testdir", "file1.txt"))
654 def fuseRmTestHelperRmdir(mounttmp):
655 class Test(unittest.TestCase):
657 # Should be able to delete now that it is empty
658 os.rmdir(os.path.join(mounttmp, "testdir"))
660 # Make sure it's empty
661 d1 = llfuse.listdir(os.path.join(mounttmp))
662 self.assertEqual([], d1)
664 # Try to delete it again
665 with self.assertRaises(OSError):
666 os.rmdir(os.path.join(mounttmp, "testdir"))
670 class FuseRmTest(MountTestBase):
672 collection = arvados.collection.Collection(api_client=self.api)
673 collection.save_new()
675 m = self.make_mount(fuse.CollectionDirectory)
677 m.new_collection(collection.api_response(), collection)
678 self.assertTrue(m.writable())
680 self.pool.apply(fuseRmTestHelperWriteFile, (self.mounttmp,))
683 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
684 self.assertRegex(collection2["manifest_text"],
685 r'\./testdir 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
686 self.pool.apply(fuseRmTestHelperDeleteFile, (self.mounttmp,))
688 # Empty directories are represented by an empty file named "."
689 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
690 self.assertRegex(collection2["manifest_text"],
691 r'./testdir d41d8cd98f00b204e9800998ecf8427e\+0\+A\S+ 0:0:\\056\n')
693 self.pool.apply(fuseRmTestHelperRmdir, (self.mounttmp,))
695 # manifest should be empty now.
696 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
697 self.assertEqual(collection2["manifest_text"], "")
700 def fuseMvFileTestHelperWriteFile(mounttmp):
701 class Test(unittest.TestCase):
703 os.mkdir(os.path.join(mounttmp, "testdir"))
705 with open(os.path.join(mounttmp, "testdir", "file1.txt"), "w") as f:
706 f.write("Hello world!")
710 def fuseMvFileTestHelperMoveFile(mounttmp):
711 class Test(unittest.TestCase):
713 d1 = llfuse.listdir(os.path.join(mounttmp))
714 self.assertEqual(["testdir"], d1)
715 d1 = llfuse.listdir(os.path.join(mounttmp, "testdir"))
716 self.assertEqual(["file1.txt"], d1)
718 os.rename(os.path.join(mounttmp, "testdir", "file1.txt"), os.path.join(mounttmp, "file1.txt"))
720 d1 = llfuse.listdir(os.path.join(mounttmp))
721 self.assertEqual(["file1.txt", "testdir"], sorted(d1))
722 d1 = llfuse.listdir(os.path.join(mounttmp, "testdir"))
723 self.assertEqual([], d1)
727 class FuseMvFileTest(MountTestBase):
729 collection = arvados.collection.Collection(api_client=self.api)
730 collection.save_new()
732 m = self.make_mount(fuse.CollectionDirectory)
734 m.new_collection(collection.api_response(), collection)
735 self.assertTrue(m.writable())
737 self.pool.apply(fuseMvFileTestHelperWriteFile, (self.mounttmp,))
740 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
741 self.assertRegex(collection2["manifest_text"],
742 r'\./testdir 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
744 self.pool.apply(fuseMvFileTestHelperMoveFile, (self.mounttmp,))
746 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
747 self.assertRegex(collection2["manifest_text"],
748 r'\. 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt\n\./testdir d41d8cd98f00b204e9800998ecf8427e\+0\+A\S+ 0:0:\\056\n')
751 def fuseRenameTestHelper(mounttmp):
752 class Test(unittest.TestCase):
754 os.mkdir(os.path.join(mounttmp, "testdir"))
756 with open(os.path.join(mounttmp, "testdir", "file1.txt"), "w") as f:
757 f.write("Hello world!")
761 class FuseRenameTest(MountTestBase):
763 collection = arvados.collection.Collection(api_client=self.api)
764 collection.save_new()
766 m = self.make_mount(fuse.CollectionDirectory)
768 m.new_collection(collection.api_response(), collection)
769 self.assertTrue(m.writable())
771 self.pool.apply(fuseRenameTestHelper, (self.mounttmp,))
774 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
775 self.assertRegex(collection2["manifest_text"],
776 r'\./testdir 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
778 d1 = llfuse.listdir(os.path.join(self.mounttmp))
779 self.assertEqual(["testdir"], d1)
780 d1 = llfuse.listdir(os.path.join(self.mounttmp, "testdir"))
781 self.assertEqual(["file1.txt"], d1)
783 os.rename(os.path.join(self.mounttmp, "testdir"), os.path.join(self.mounttmp, "testdir2"))
785 d1 = llfuse.listdir(os.path.join(self.mounttmp))
786 self.assertEqual(["testdir2"], sorted(d1))
787 d1 = llfuse.listdir(os.path.join(self.mounttmp, "testdir2"))
788 self.assertEqual(["file1.txt"], d1)
790 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
791 self.assertRegex(collection2["manifest_text"],
792 r'\./testdir2 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
795 class FuseUpdateFromEventTest(MountTestBase):
797 collection = arvados.collection.Collection(api_client=self.api)
798 collection.save_new()
800 m = self.make_mount(fuse.CollectionDirectory)
802 m.new_collection(collection.api_response(), collection)
804 self.operations.listen_for_events()
806 d1 = llfuse.listdir(os.path.join(self.mounttmp))
807 self.assertEqual([], sorted(d1))
809 with arvados.collection.Collection(collection.manifest_locator(), api_client=self.api) as collection2:
810 with collection2.open("file1.txt", "w") as f:
813 for attempt in AssertWithTimeout(10):
814 attempt(self.assertEqual, ["file1.txt"], llfuse.listdir(os.path.join(self.mounttmp)))
817 class FuseDeleteProjectEventTest(MountTestBase):
820 aproject = self.api.groups().create(body={
822 "group_class": "project"
825 bproject = self.api.groups().create(body={
827 "group_class": "project",
828 "owner_uuid": aproject["uuid"]
831 self.make_mount(fuse.ProjectDirectory,
832 project_object=self.api.users().current().execute())
834 self.operations.listen_for_events()
836 d1 = llfuse.listdir(os.path.join(self.mounttmp, "aproject"))
837 self.assertEqual(["bproject"], sorted(d1))
839 self.api.groups().delete(uuid=bproject["uuid"]).execute()
841 for attempt in AssertWithTimeout(10):
842 attempt(self.assertEqual, [], llfuse.listdir(os.path.join(self.mounttmp, "aproject")))
845 def fuseFileConflictTestHelper(mounttmp, uuid, keeptmp, settings):
846 class Test(unittest.TestCase):
848 os.environ['KEEP_LOCAL_STORE'] = keeptmp
850 with open(os.path.join(mounttmp, "file1.txt"), "w") as f:
851 with arvados.collection.Collection(uuid, api_client=arvados.api_from_config('v1', apiconfig=settings)) as collection2:
852 with collection2.open("file1.txt", "w") as f2:
856 d1 = sorted(llfuse.listdir(os.path.join(mounttmp)))
857 self.assertEqual(len(d1), 2)
859 with open(os.path.join(mounttmp, "file1.txt"), "r") as f:
860 self.assertEqual(f.read(), "bar")
862 self.assertRegex(d1[1],
863 r'file1\.txt~\d\d\d\d\d\d\d\d-\d\d\d\d\d\d~conflict~')
865 with open(os.path.join(mounttmp, d1[1]), "r") as f:
866 self.assertEqual(f.read(), "foo")
870 class FuseFileConflictTest(MountTestBase):
872 collection = arvados.collection.Collection(api_client=self.api)
873 collection.save_new()
875 m = self.make_mount(fuse.CollectionDirectory)
877 m.new_collection(collection.api_response(), collection)
879 d1 = llfuse.listdir(os.path.join(self.mounttmp))
880 self.assertEqual([], sorted(d1))
882 # See note in MountTestBase.setUp
883 self.pool.apply(fuseFileConflictTestHelper, (self.mounttmp, collection.manifest_locator(), self.keeptmp, arvados.config.settings()))
886 def fuseUnlinkOpenFileTest(mounttmp):
887 class Test(unittest.TestCase):
889 with open(os.path.join(mounttmp, "file1.txt"), "w+") as f:
892 d1 = llfuse.listdir(os.path.join(mounttmp))
893 self.assertEqual(["file1.txt"], sorted(d1))
895 os.remove(os.path.join(mounttmp, "file1.txt"))
897 d1 = llfuse.listdir(os.path.join(mounttmp))
898 self.assertEqual([], sorted(d1))
901 self.assertEqual(f.read(), "foo")
905 self.assertEqual(f.read(), "foobar")
909 class FuseUnlinkOpenFileTest(MountTestBase):
911 collection = arvados.collection.Collection(api_client=self.api)
912 collection.save_new()
914 m = self.make_mount(fuse.CollectionDirectory)
916 m.new_collection(collection.api_response(), collection)
918 # See note in MountTestBase.setUp
919 self.pool.apply(fuseUnlinkOpenFileTest, (self.mounttmp,))
921 self.assertEqual(collection.manifest_text(), "")
924 def fuseMvFileBetweenCollectionsTest1(mounttmp, uuid1, uuid2):
925 class Test(unittest.TestCase):
927 with open(os.path.join(mounttmp, uuid1, "file1.txt"), "w") as f:
928 f.write("Hello world!")
930 d1 = os.listdir(os.path.join(mounttmp, uuid1))
931 self.assertEqual(["file1.txt"], sorted(d1))
932 d1 = os.listdir(os.path.join(mounttmp, uuid2))
933 self.assertEqual([], sorted(d1))
937 def fuseMvFileBetweenCollectionsTest2(mounttmp, uuid1, uuid2):
938 class Test(unittest.TestCase):
940 os.rename(os.path.join(mounttmp, uuid1, "file1.txt"), os.path.join(mounttmp, uuid2, "file2.txt"))
942 d1 = os.listdir(os.path.join(mounttmp, uuid1))
943 self.assertEqual([], sorted(d1))
944 d1 = os.listdir(os.path.join(mounttmp, uuid2))
945 self.assertEqual(["file2.txt"], sorted(d1))
949 class FuseMvFileBetweenCollectionsTest(MountTestBase):
951 collection1 = arvados.collection.Collection(api_client=self.api)
952 collection1.save_new()
954 collection2 = arvados.collection.Collection(api_client=self.api)
955 collection2.save_new()
957 m = self.make_mount(fuse.MagicDirectory)
959 # See note in MountTestBase.setUp
960 self.pool.apply(fuseMvFileBetweenCollectionsTest1, (self.mounttmp,
961 collection1.manifest_locator(),
962 collection2.manifest_locator()))
967 self.assertRegex(collection1.manifest_text(), r"\. 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$")
968 self.assertEqual(collection2.manifest_text(), "")
970 self.pool.apply(fuseMvFileBetweenCollectionsTest2, (self.mounttmp,
971 collection1.manifest_locator(),
972 collection2.manifest_locator()))
977 self.assertEqual(collection1.manifest_text(), "")
978 self.assertRegex(collection2.manifest_text(), r"\. 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file2\.txt$")
980 collection1.stop_threads()
981 collection2.stop_threads()
984 def fuseMvDirBetweenCollectionsTest1(mounttmp, uuid1, uuid2):
985 class Test(unittest.TestCase):
987 os.mkdir(os.path.join(mounttmp, uuid1, "testdir"))
988 with open(os.path.join(mounttmp, uuid1, "testdir", "file1.txt"), "w") as f:
989 f.write("Hello world!")
991 d1 = os.listdir(os.path.join(mounttmp, uuid1))
992 self.assertEqual(["testdir"], sorted(d1))
993 d1 = os.listdir(os.path.join(mounttmp, uuid1, "testdir"))
994 self.assertEqual(["file1.txt"], sorted(d1))
996 d1 = os.listdir(os.path.join(mounttmp, uuid2))
997 self.assertEqual([], sorted(d1))
1002 def fuseMvDirBetweenCollectionsTest2(mounttmp, uuid1, uuid2):
1003 class Test(unittest.TestCase):
1005 os.rename(os.path.join(mounttmp, uuid1, "testdir"), os.path.join(mounttmp, uuid2, "testdir2"))
1007 d1 = os.listdir(os.path.join(mounttmp, uuid1))
1008 self.assertEqual([], sorted(d1))
1010 d1 = os.listdir(os.path.join(mounttmp, uuid2))
1011 self.assertEqual(["testdir2"], sorted(d1))
1012 d1 = os.listdir(os.path.join(mounttmp, uuid2, "testdir2"))
1013 self.assertEqual(["file1.txt"], sorted(d1))
1015 with open(os.path.join(mounttmp, uuid2, "testdir2", "file1.txt"), "r") as f:
1016 self.assertEqual(f.read(), "Hello world!")
1020 class FuseMvDirBetweenCollectionsTest(MountTestBase):
1022 collection1 = arvados.collection.Collection(api_client=self.api)
1023 collection1.save_new()
1025 collection2 = arvados.collection.Collection(api_client=self.api)
1026 collection2.save_new()
1028 m = self.make_mount(fuse.MagicDirectory)
1030 # See note in MountTestBase.setUp
1031 self.pool.apply(fuseMvDirBetweenCollectionsTest1, (self.mounttmp,
1032 collection1.manifest_locator(),
1033 collection2.manifest_locator()))
1035 collection1.update()
1036 collection2.update()
1038 self.assertRegex(collection1.manifest_text(), r"\./testdir 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$")
1039 self.assertEqual(collection2.manifest_text(), "")
1041 self.pool.apply(fuseMvDirBetweenCollectionsTest2, (self.mounttmp,
1042 collection1.manifest_locator(),
1043 collection2.manifest_locator()))
1045 collection1.update()
1046 collection2.update()
1048 self.assertEqual(collection1.manifest_text(), "")
1049 self.assertRegex(collection2.manifest_text(), r"\./testdir2 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$")
1051 collection1.stop_threads()
1052 collection2.stop_threads()
1054 def fuseProjectMkdirTestHelper1(mounttmp):
1055 class Test(unittest.TestCase):
1057 os.mkdir(os.path.join(mounttmp, "testcollection"))
1058 with self.assertRaises(OSError):
1059 os.mkdir(os.path.join(mounttmp, "testcollection"))
1062 def fuseProjectMkdirTestHelper2(mounttmp):
1063 class Test(unittest.TestCase):
1065 with open(os.path.join(mounttmp, "testcollection", "file1.txt"), "w") as f:
1066 f.write("Hello world!")
1067 with self.assertRaises(OSError):
1068 os.rmdir(os.path.join(mounttmp, "testcollection"))
1069 os.remove(os.path.join(mounttmp, "testcollection", "file1.txt"))
1070 with self.assertRaises(OSError):
1071 os.remove(os.path.join(mounttmp, "testcollection"))
1072 os.rmdir(os.path.join(mounttmp, "testcollection"))
1075 class FuseProjectMkdirRmdirTest(MountTestBase):
1077 self.make_mount(fuse.ProjectDirectory,
1078 project_object=self.api.users().current().execute())
1080 d1 = llfuse.listdir(self.mounttmp)
1081 self.assertNotIn('testcollection', d1)
1083 self.pool.apply(fuseProjectMkdirTestHelper1, (self.mounttmp,))
1085 d1 = llfuse.listdir(self.mounttmp)
1086 self.assertIn('testcollection', d1)
1088 self.pool.apply(fuseProjectMkdirTestHelper2, (self.mounttmp,))
1090 d1 = llfuse.listdir(self.mounttmp)
1091 self.assertNotIn('testcollection', d1)
1094 def fuseProjectMvTestHelper1(mounttmp):
1095 class Test(unittest.TestCase):
1097 d1 = llfuse.listdir(mounttmp)
1098 self.assertNotIn('testcollection', d1)
1100 os.mkdir(os.path.join(mounttmp, "testcollection"))
1102 d1 = llfuse.listdir(mounttmp)
1103 self.assertIn('testcollection', d1)
1105 with self.assertRaises(OSError):
1106 os.rename(os.path.join(mounttmp, "testcollection"), os.path.join(mounttmp, 'Unrestricted public data'))
1108 os.rename(os.path.join(mounttmp, "testcollection"), os.path.join(mounttmp, 'Unrestricted public data', 'testcollection'))
1110 d1 = llfuse.listdir(mounttmp)
1111 self.assertNotIn('testcollection', d1)
1113 d1 = llfuse.listdir(os.path.join(mounttmp, 'Unrestricted public data'))
1114 self.assertIn('testcollection', d1)
1118 class FuseProjectMvTest(MountTestBase):
1120 self.make_mount(fuse.ProjectDirectory,
1121 project_object=self.api.users().current().execute())
1123 self.pool.apply(fuseProjectMvTestHelper1, (self.mounttmp,))
1126 def fuseFsyncTestHelper(mounttmp, k):
1127 class Test(unittest.TestCase):
1129 fd = os.open(os.path.join(mounttmp, k), os.O_RDONLY)
1135 class FuseFsyncTest(FuseMagicTest):
1137 self.make_mount(fuse.MagicDirectory)
1138 self.pool.apply(fuseFsyncTestHelper, (self.mounttmp, self.testcollection))
1141 class MagicDirApiError(FuseMagicTest):
1143 api = mock.MagicMock()
1144 api.keep.block_cache = mock.MagicMock(cache_max=1)
1145 super(MagicDirApiError, self).setUp(api=api)
1146 api.collections().get().execute.side_effect = iter([
1147 Exception('API fail'),
1149 "manifest_text": self.test_manifest,
1150 "portable_data_hash": self.test_manifest_pdh,
1153 api.keep.get.side_effect = Exception('Keep fail')
1156 with mock.patch('arvados_fuse.fresh.FreshBase._poll_time', new_callable=mock.PropertyMock, return_value=60) as mock_poll_time:
1157 self.make_mount(fuse.MagicDirectory)
1159 self.operations.inodes.inode_cache.cap = 1
1160 self.operations.inodes.inode_cache.min_entries = 2
1162 with self.assertRaises(OSError):
1163 llfuse.listdir(os.path.join(self.mounttmp, self.testcollection))
1165 llfuse.listdir(os.path.join(self.mounttmp, self.testcollection))
1168 class SanitizeFilenameTest(MountTestBase):
1169 def test_sanitize_filename(self):
1170 pdir = fuse.ProjectDirectory(
1171 1, fuse.Inodes(None), self.api, 0, False, None,
1172 project_object=self.api.users().current().execute(),
1192 for f in acceptable:
1193 self.assertEqual(f, pdir.sanitize_filename(f))
1194 for f in unacceptable:
1195 self.assertNotEqual(f, pdir.sanitize_filename(f))
1196 # The sanitized filename should be the same length, though.
1197 self.assertEqual(len(f), len(pdir.sanitize_filename(f)))
1199 self.assertEqual("_", pdir.sanitize_filename(""))
1200 self.assertEqual("_", pdir.sanitize_filename("."))
1201 self.assertEqual("__", pdir.sanitize_filename(".."))
1204 class FuseMagicTestPDHOnly(MountTestBase):
1205 def setUp(self, api=None):
1206 super(FuseMagicTestPDHOnly, self).setUp(api=api)
1208 cw = arvados.collection.Collection()
1209 with cw.open('thing1.txt', 'w') as f:
1212 self.testcollection = cw.portable_data_hash()
1213 self.test_manifest = cw.manifest_text()
1214 created = self.api.collections().create(body={"manifest_text":self.test_manifest}).execute()
1215 self.testcollectionuuid = str(created['uuid'])
1217 def verify_pdh_only(self, pdh_only=False, skip_pdh_only=False):
1218 if skip_pdh_only is True:
1219 self.make_mount(fuse.MagicDirectory) # in this case, the default by_id applies
1221 self.make_mount(fuse.MagicDirectory, pdh_only=pdh_only)
1223 mount_ls = llfuse.listdir(self.mounttmp)
1224 self.assertIn('README', mount_ls)
1225 self.assertFalse(any(arvados.util.keep_locator_pattern.match(fn) or
1226 arvados.util.uuid_pattern.match(fn)
1227 for fn in mount_ls),
1228 "new FUSE MagicDirectory lists Collection")
1230 # look up using pdh should succeed in all cases
1231 self.assertDirContents(self.testcollection, ['thing1.txt'])
1232 self.assertDirContents(os.path.join('by_id', self.testcollection),
1234 mount_ls = llfuse.listdir(self.mounttmp)
1235 self.assertIn('README', mount_ls)
1236 self.assertIn(self.testcollection, mount_ls)
1237 self.assertIn(self.testcollection,
1238 llfuse.listdir(os.path.join(self.mounttmp, 'by_id')))
1241 files[os.path.join(self.mounttmp, self.testcollection, 'thing1.txt')] = 'data 1'
1243 for k, v in files.items():
1244 with open(os.path.join(self.mounttmp, k), 'rb') as f:
1245 self.assertEqual(v, f.read().decode())
1247 # look up using uuid should fail when pdh_only is set
1248 if pdh_only is True:
1249 with self.assertRaises(OSError):
1250 self.assertDirContents(os.path.join('by_id', self.testcollectionuuid),
1253 self.assertDirContents(os.path.join('by_id', self.testcollectionuuid),
1256 def test_with_pdh_only_true(self):
1257 self.verify_pdh_only(pdh_only=True)
1259 def test_with_pdh_only_false(self):
1260 self.verify_pdh_only(pdh_only=False)
1262 def test_with_default_by_id(self):
1263 self.verify_pdh_only(skip_pdh_only=True)
1266 class SlashSubstitutionTest(IntegrationTest):
1269 '--mount-home', 'zzz',
1274 super(SlashSubstitutionTest, self).setUp()
1276 self.api = arvados.safeapi.ThreadSafeApiCache(
1277 arvados.config.settings(),
1280 self.testcoll = self.api.collections().create(body={"name": "foo/bar/baz"}).execute()
1281 self.testcolleasy = self.api.collections().create(body={"name": "foo-bar-baz"}).execute()
1282 self.fusename = 'foo[SLASH]bar[SLASH]baz'
1284 @IntegrationTest.mount(argv=mnt_args)
1285 def test_slash_substitution_before_listing(self):
1286 self.pool_test(os.path.join(self.mnt, 'zzz'), self.fusename)
1287 self.checkContents()
1289 def _test_slash_substitution_before_listing(self, tmpdir, fusename):
1290 with open(os.path.join(tmpdir, 'foo-bar-baz', 'waz'), 'w') as f:
1292 with open(os.path.join(tmpdir, fusename, 'waz'), 'w') as f:
1295 @IntegrationTest.mount(argv=mnt_args)
1296 @mock.patch('arvados.util.get_config_once')
1297 def test_slash_substitution_after_listing(self, get_config_once):
1298 get_config_once.return_value = {"Collections": {"ForwardSlashNameSubstitution": "[SLASH]"}}
1299 self.pool_test(os.path.join(self.mnt, 'zzz'), self.fusename)
1300 self.checkContents()
1302 def _test_slash_substitution_after_listing(self, tmpdir, fusename):
1303 with open(os.path.join(tmpdir, 'foo-bar-baz', 'waz'), 'w') as f:
1306 with open(os.path.join(tmpdir, fusename, 'waz'), 'w') as f:
1309 def checkContents(self):
1310 self.assertRegex(self.api.collections().get(uuid=self.testcoll['uuid']).execute()['manifest_text'], r' acbd18db') # md5(foo)
1311 self.assertRegex(self.api.collections().get(uuid=self.testcolleasy['uuid']).execute()['manifest_text'], r' f561aaf6') # md5(xxx)
1313 @IntegrationTest.mount(argv=mnt_args)
1314 @mock.patch('arvados.util.get_config_once')
1315 def test_slash_substitution_conflict(self, get_config_once):
1316 self.testcollconflict = self.api.collections().create(body={"name": self.fusename}).execute()
1317 get_config_once.return_value = {"Collections": {"ForwardSlashNameSubstitution": "[SLASH]"}}
1318 self.pool_test(os.path.join(self.mnt, 'zzz'), self.fusename)
1319 self.assertRegex(self.api.collections().get(uuid=self.testcollconflict['uuid']).execute()['manifest_text'], r' acbd18db') # md5(foo)
1320 # foo/bar/baz collection unchanged, because it is masked by foo[SLASH]bar[SLASH]baz
1321 self.assertEqual(self.api.collections().get(uuid=self.testcoll['uuid']).execute()['manifest_text'], '')
1323 def _test_slash_substitution_conflict(self, tmpdir, fusename):
1324 with open(os.path.join(tmpdir, fusename, 'waz'), 'w') as f:
1327 class StorageClassesTest(IntegrationTest):
1330 '--mount-home', 'homedir',
1334 super(StorageClassesTest, self).setUp()
1335 self.api = arvados.safeapi.ThreadSafeApiCache(
1336 arvados.config.settings(),
1340 @IntegrationTest.mount(argv=mnt_args)
1341 def test_collection_default_storage_classes(self):
1342 coll_path = os.path.join(self.mnt, 'homedir', 'a_collection')
1343 self.api.collections().create(body={'name':'a_collection'}).execute()
1344 self.pool_test(coll_path)
1346 def _test_collection_default_storage_classes(self, coll):
1347 self.assertEqual(storage_classes_desired(coll), ['default'])
1349 @IntegrationTest.mount(argv=mnt_args+['--storage-classes', 'foo'])
1350 def test_collection_custom_storage_classes(self):
1351 coll_path = os.path.join(self.mnt, 'homedir', 'new_coll')
1353 self.pool_test(coll_path)
1355 def _test_collection_custom_storage_classes(self, coll):
1356 self.assertEqual(storage_classes_desired(coll), ['foo'])
1358 def _readonlyCollectionTestHelper(mounttmp):
1359 f = open(os.path.join(mounttmp, 'thing1.txt'), 'rt')
1360 # Testing that close() doesn't raise an error.
1363 class ReadonlyCollectionTest(MountTestBase):
1365 super(ReadonlyCollectionTest, self).setUp()
1366 cw = arvados.collection.Collection()
1367 with cw.open('thing1.txt', 'wt') as f:
1369 cw.save_new(owner_uuid=run_test_server.fixture("groups")["aproject"]["uuid"])
1370 self.testcollection = cw.api_response()
1373 settings = arvados.config.settings().copy()
1374 settings["ARVADOS_API_TOKEN"] = run_test_server.fixture("api_client_authorizations")["project_viewer"]["api_token"]
1375 self.api = arvados.safeapi.ThreadSafeApiCache(settings, version='v1')
1376 self.make_mount(fuse.CollectionDirectory, collection_record=self.testcollection, enable_write=False)
1378 self.pool.apply(_readonlyCollectionTestHelper, (self.mounttmp,))
1381 @parameterized.parameterized_class([
1382 {'root_class': fusedir.ProjectDirectory, 'root_kwargs': {
1383 'project_object': run_test_server.fixture('users')['admin'],
1385 {'root_class': fusedir.ProjectDirectory, 'root_kwargs': {
1386 'project_object': run_test_server.fixture('groups')['public'],
1389 class UnsupportedCreateTest(MountTestBase):
1395 if 'prefs' in self.root_kwargs.get('project_object', ()):
1396 self.root_kwargs['project_object']['prefs'] = {}
1397 self.make_mount(self.root_class, **self.root_kwargs)
1398 # Make sure the directory knows about its top-level ents.
1399 os.listdir(self.mounttmp)
1401 def test_create(self):
1402 test_path = Path(self.mounttmp, 'test_create')
1403 with self.assertRaises(OSError) as exc_check:
1404 with test_path.open('w'):
1406 self.assertEqual(exc_check.exception.errno, errno.ENOTSUP)
1409 # FIXME: IMO, for consistency with the "create inside a project" case,
1410 # these operations should also return ENOTSUP instead of EPERM.
1411 # Right now they're returning EPERM because the clasess' writable() method
1412 # usually returns False, and the Operations class transforms that accordingly.
1413 # However, for cases where the mount will never be writable, I think ENOTSUP
1414 # is a clearer error: it lets the user know they can't fix the problem by
1415 # adding permissions in Arvados, etc.
1416 @parameterized.parameterized_class([
1417 {'root_class': fusedir.MagicDirectory,
1418 'preset_dir': 'by_id',
1419 'preset_file': 'README',
1422 {'root_class': fusedir.SharedDirectory,
1424 'exclude': run_test_server.fixture('users')['admin']['uuid'],
1426 'preset_dir': 'Active User',
1429 {'root_class': fusedir.TagDirectory,
1431 'tag': run_test_server.fixture('links')['foo_collection_tag']['name'],
1433 'preset_dir': run_test_server.fixture('collections')['foo_collection_in_aproject']['uuid'],
1436 {'root_class': fusedir.TagsDirectory,
1437 'preset_dir': run_test_server.fixture('links')['foo_collection_tag']['name'],
1440 class UnsupportedOperationsTest(UnsupportedCreateTest):
1444 def test_create(self):
1445 test_path = Path(self.mounttmp, 'test_create')
1446 with self.assertRaises(OSError) as exc_check:
1447 with test_path.open('w'):
1449 self.assertEqual(exc_check.exception.errno, errno.EPERM)
1451 def test_mkdir(self):
1452 test_path = Path(self.mounttmp, 'test_mkdir')
1453 with self.assertRaises(OSError) as exc_check:
1455 self.assertEqual(exc_check.exception.errno, errno.EPERM)
1457 def test_rename(self):
1458 src_name = self.preset_dir or self.preset_file
1459 if src_name is None:
1461 test_src = Path(self.mounttmp, src_name)
1462 test_dst = test_src.with_name('test_dst')
1463 with self.assertRaises(OSError) as exc_check:
1464 test_src.rename(test_dst)
1465 self.assertEqual(exc_check.exception.errno, errno.EPERM)
1467 def test_rmdir(self):
1468 if self.preset_dir is None:
1470 test_path = Path(self.mounttmp, self.preset_dir)
1471 with self.assertRaises(OSError) as exc_check:
1473 self.assertEqual(exc_check.exception.errno, errno.EPERM)
1475 def test_unlink(self):
1476 if self.preset_file is None:
1478 test_path = Path(self.mounttmp, self.preset_file)
1479 with self.assertRaises(OSError) as exc_check:
1481 self.assertEqual(exc_check.exception.errno, errno.EPERM)