dirnode.py: fix a bug in the no-write change for Adder, and improve test coverage. Add a 'metadata' argument to create_subdirectory, with documentation. Also update some comments in test_dirnode.py made stale by the ctime/mtime change.

This commit is contained in:
david-sarah 2010-06-01 20:26:41 -07:00
parent db394671e9
commit 29a06457d2
4 changed files with 54 additions and 16 deletions

View File

@ -415,9 +415,11 @@ POST /uri?t=mkdir-with-children
it is acceptable to set "rw_uri" to that cap and omit "ro_uri". The
client must not put a write cap into a "ro_uri" field.
A file may have a "no-write" metadata field that affects how writes to
it are handled via the SFTP frontend; see docs/frontends/FTP-and-SFTP.txt
for details.
The metadata may have a "no-write" field. If this is set to true in the
metadata of a link, it will not be possible to open that link for writing
via the SFTP frontend; see docs/frontends/FTP-and-SFTP.txt for details.
Also, if the "no-write" field is set to true in the metadata of a link to
a mutable child, it will cause the link to be diminished to read-only.
Note that the webapi-using client application must not provide the
"Content-Type: multipart/form-data" header that usually accompanies HTML
@ -669,7 +671,9 @@ GET /uri/$DIRCAP/[SUBDIRS../]FILENAME?t=json
In Tahoe earlier than v1.4.0, only the 'mtime'/'ctime' keys were populated.
Starting in Tahoe v1.4.0, the 'linkmotime'/'linkcrtime' keys in the 'tahoe'
sub-dict are also populated.
sub-dict are also populated. However, prior to v1.7.0, a bug caused the
'tahoe' sub-dict to be deleted by webapi requests in which new metadata
is specified, and not to be added to existing child links that lack it.
The reason we added the new values in Tahoe v1.4.0 is that there is a
"set_children" API (described below) which you can use to overwrite the

View File

@ -122,9 +122,10 @@ class MetadataSetter:
raise NoSuchChildError(name)
now = time.time()
metadata = update_metadata(children[name][1].copy(), self.metadata, now)
child = children[name][0]
if self.create_readonly_node and metadata and metadata.get('no-write', False):
metadata = update_metadata(children[name][1].copy(), self.metadata, now)
if self.create_readonly_node and metadata.get('no-write', False):
child = self.create_readonly_node(child, name)
children[name] = (child, metadata)
@ -167,10 +168,11 @@ class Adder:
raise ExistingChildError("child '%s' already exists" % name)
metadata = children[name][1].copy()
if self.create_readonly_node and metadata and metadata.get('no-write', False):
metadata = update_metadata(metadata, new_metadata, now)
if self.create_readonly_node and metadata.get('no-write', False):
child = self.create_readonly_node(child, name)
children[name] = (child, update_metadata(metadata, new_metadata, now))
children[name] = (child, metadata)
new_contents = self.node._pack_contents(children)
return new_contents
@ -591,7 +593,7 @@ class DirectoryNode:
return d
def create_subdirectory(self, name, initial_children={}, overwrite=True,
mutable=True):
mutable=True, metadata=None):
assert isinstance(name, unicode)
if self.is_readonly():
return defer.fail(NotWriteableError())
@ -600,7 +602,7 @@ class DirectoryNode:
else:
d = self._nodemaker.create_immutable_directory(initial_children)
def _created(child):
entries = {name: (child, None)}
entries = {name: (child, metadata)}
a = Adder(self, entries, overwrite=overwrite,
create_readonly_node=self._create_readonly_node)
d = self._node.modify(a.modify)

View File

@ -985,7 +985,7 @@ class IDirectoryNode(IFilesystemNode):
is a file, or if must_be_file is True and the child is a directory,
I raise ChildOfWrongTypeError."""
def create_subdirectory(name, initial_children={}, overwrite=True):
def create_subdirectory(name, initial_children={}, overwrite=True, metadata=None):
"""I create and attach a directory at the given name. The new
directory can be empty, or it can be populated with children
according to 'initial_children', which takes a dictionary in the same

View File

@ -775,8 +775,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
d.addCallback(lambda metadata:
self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"])))
# or we can add specific metadata at set_uri() time, which
# overrides the timestamps
# we can also add specific metadata at set_uri() time
d.addCallback(lambda res: n.set_uri(u"c4",
fake_file_uri, fake_file_uri,
{"key": "value"}))
@ -790,7 +789,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
d.addCallback(lambda res: n.delete(u"c4"))
# set_node + metadata
# it should be possible to add a child without any metadata
# it should be possible to add a child without any metadata except for timestamps
d.addCallback(lambda res: n.set_node(u"d2", n, {}))
d.addCallback(lambda res: c.create_dirnode())
d.addCallback(lambda n2:
@ -808,8 +807,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
d.addCallback(lambda metadata:
self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"])))
# or we can add specific metadata at set_node() time, which
# overrides the timestamps
# we can also add specific metadata at set_node() time
d.addCallback(lambda res: n.set_node(u"d4", n,
{"key": "value"}))
d.addCallback(lambda res: n.get_metadata_for(u"d4"))
@ -899,6 +897,11 @@ class Dirnode(GridTestMixin, unittest.TestCase,
metadata["tags"] == ["web2.0-compatible"] and
"bad" not in metadata["tahoe"], metadata))
d.addCallback(lambda res:
self.shouldFail(NoSuchChildError, "set_metadata_for-nosuch", "",
n.set_metadata_for, u"nosuch", {}))
def _start(res):
self._start_timestamp = time.time()
d.addCallback(_start)
@ -1047,6 +1050,35 @@ class Dirnode(GridTestMixin, unittest.TestCase,
self.failUnlessEqual(child.get_uri(),
other_file_uri))
# Setting the no-write field should diminish a mutable cap to read-only
# (for both files and directories).
d.addCallback(lambda ign: n.set_uri(u"mutable", other_file_uri, other_file_uri))
d.addCallback(lambda ign: n.get(u"mutable"))
d.addCallback(lambda mutable: self.failIf(mutable.is_readonly(), mutable))
d.addCallback(lambda ign: n.set_metadata_for(u"mutable", {"no-write": True}))
d.addCallback(lambda ign: n.get(u"mutable"))
d.addCallback(lambda mutable: self.failUnless(mutable.is_readonly(), mutable))
d.addCallback(lambda ign: n.set_metadata_for(u"mutable", {"no-write": True}))
d.addCallback(lambda ign: n.get(u"mutable"))
d.addCallback(lambda mutable: self.failUnless(mutable.is_readonly(), mutable))
d.addCallback(lambda ign: n.get(u"subdir2"))
d.addCallback(lambda subdir2: self.failIf(subdir2.is_readonly()))
d.addCallback(lambda ign: n.set_metadata_for(u"subdir2", {"no-write": True}))
d.addCallback(lambda ign: n.get(u"subdir2"))
d.addCallback(lambda subdir2: self.failUnless(subdir2.is_readonly(), subdir2))
d.addCallback(lambda ign: n.set_uri(u"mutable_ro", other_file_uri, other_file_uri,
metadata={"no-write": True}))
d.addCallback(lambda ign: n.get(u"mutable_ro"))
d.addCallback(lambda mutable_ro: self.failUnless(mutable_ro.is_readonly(), mutable_ro))
d.addCallback(lambda ign: n.create_subdirectory(u"subdir_ro", metadata={"no-write": True}))
d.addCallback(lambda ign: n.get(u"subdir_ro"))
d.addCallback(lambda subdir_ro: self.failUnless(subdir_ro.is_readonly(), subdir_ro))
return d
d.addCallback(_then)