dirnode.set_uri/set_children: change signature to take writecap+readcap

instead of a single cap. The webapi t=set_children call benefits too.
This commit is contained in:
Brian Warner
2009-10-12 16:51:26 -07:00
parent 3ee740628a
commit e2ffc3dc03
7 changed files with 87 additions and 66 deletions

View File

@ -380,19 +380,11 @@ class DirectoryNode:
d = self.get_child_and_metadata(childname) d = self.get_child_and_metadata(childname)
return d return d
def set_uri(self, name, child_uri, metadata=None, overwrite=True): def set_uri(self, name, writecap, readcap, metadata=None, overwrite=True):
"""I add a child (by URI) at the specific name. I return a Deferred
that fires with the child node when the operation finishes. I will
replace any existing child of the same name.
The child_uri could be for a file, or for a directory (either
read-write or read-only, using a URI that came from get_uri() ).
If this directory node is read-only, the Deferred will errback with a
NotMutableError."""
precondition(isinstance(name, unicode), name) precondition(isinstance(name, unicode), name)
precondition(isinstance(child_uri, str), child_uri) precondition(isinstance(writecap, (str,type(None))), writecap)
child_node = self._create_node(child_uri, None) precondition(isinstance(readcap, (str,type(None))), readcap)
child_node = self._create_node(writecap, readcap)
if isinstance(child_node, UnknownNode): if isinstance(child_node, UnknownNode):
# don't be willing to pack unknown nodes: we might accidentally # don't be willing to pack unknown nodes: we might accidentally
# put some write-authority into the rocap slot because we don't # put some write-authority into the rocap slot because we don't
@ -409,15 +401,16 @@ class DirectoryNode:
a = Adder(self, overwrite=overwrite) a = Adder(self, overwrite=overwrite)
node_entries = [] node_entries = []
for e in entries: for e in entries:
if len(e) == 2: if len(e) == 3:
name, child_uri = e name, writecap, readcap = e
metadata = None metadata = None
else: else:
assert len(e) == 3 assert len(e) == 4
name, child_uri, metadata = e name, writecap, readcap, metadata = e
assert isinstance(name, unicode) assert isinstance(name, unicode)
assert isinstance(child_uri, str) precondition(isinstance(writecap, (str,type(None))), writecap)
child_node = self._create_node(child_uri, None) precondition(isinstance(readcap, (str,type(None))), readcap)
child_node = self._create_node(writecap, readcap)
if isinstance(child_node, UnknownNode): if isinstance(child_node, UnknownNode):
msg = "cannot pack unknown node as child %s" % str(name) msg = "cannot pack unknown node as child %s" % str(name)
raise CannotPackUnknownNodeError(msg) raise CannotPackUnknownNodeError(msg)

View File

@ -873,15 +873,19 @@ class IDirectoryNode(IMutableFilesystemNode):
is empty, the metadata will be an empty dictionary. is empty, the metadata will be an empty dictionary.
""" """
def set_uri(name, child_uri, metadata=None, overwrite=True): def set_uri(name, writecap, readcap=None, metadata=None, overwrite=True):
"""I add a child (by URI) at the specific name. I return a Deferred """I add a child (by writecap+readcap) at the specific name. I return
that fires when the operation finishes. If overwrite= is True, I will a Deferred that fires when the operation finishes. If overwrite= is
replace any existing child of the same name, otherwise an existing True, I will replace any existing child of the same name, otherwise
child will cause me to return ExistingChildError. The child name must an existing child will cause me to return ExistingChildError. The
be a unicode string. child name must be a unicode string.
The child_uri could be for a file, or for a directory (either The child caps could be for a file, or for a directory. If the new
read-write or read-only, using a URI that came from get_uri() ). child is read/write, you will provide both writecap and readcap. If
the child is read-only, you will provide the readcap write (i.e. the
writecap= and readcap= arguments will both be the child's readcap).
The filecaps are typically obtained from an IFilesystemNode with
get_uri() and get_readonly_uri().
If metadata= is provided, I will use it as the metadata for the named If metadata= is provided, I will use it as the metadata for the named
edge. This will replace any existing metadata. If metadata= is left edge. This will replace any existing metadata. If metadata= is left
@ -894,11 +898,11 @@ class IDirectoryNode(IMutableFilesystemNode):
NotMutableError.""" NotMutableError."""
def set_children(entries, overwrite=True): def set_children(entries, overwrite=True):
"""Add multiple (name, child_uri) pairs (or (name, child_uri, """Add multiple (name, writecap, readcap) triples (or (name,
metadata) triples) to a directory node. Returns a Deferred that fires writecap, readcap, metadata) 4-tuples) to a directory node. Returns a
(with None) when the operation finishes. This is equivalent to Deferred that fires (with None) when the operation finishes. This is
calling set_uri() multiple times, but is much more efficient. All equivalent to calling set_uri() multiple times, but is much more
child names must be unicode strings. efficient. All child names must be unicode strings.
""" """
def set_node(name, child, metadata=None, overwrite=True): def set_node(name, child, metadata=None, overwrite=True):

View File

@ -1186,8 +1186,8 @@ class Large(DeepCheckBase, unittest.TestCase):
self.subdir_node = subdir_node self.subdir_node = subdir_node
kids = [] kids = []
for i in range(1, COUNT): for i in range(1, COUNT):
litnode = LiteralFileURI("%03d-data" % i).to_string() litcap = LiteralFileURI("%03d-data" % i).to_string()
kids.append( (u"%03d-small" % i, litnode) ) kids.append( (u"%03d-small" % i, litcap, litcap) )
return subdir_node.set_children(kids) return subdir_node.set_children(kids)
d.addCallback(_add_children) d.addCallback(_add_children)
up = upload.Data("large enough for CHK" * 100, "") up = upload.Data("large enough for CHK" * 100, "")

View File

@ -67,6 +67,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
d.addCallback(lambda res: c.create_dirnode()) d.addCallback(lambda res: c.create_dirnode())
d.addCallback(lambda dn: d.addCallback(lambda dn:
self._rootnode.set_uri(u"rodir", self._rootnode.set_uri(u"rodir",
dn.get_uri(),
dn.get_readonly_uri())) dn.get_readonly_uri()))
return d return d
d.addCallback(_created_subdir) d.addCallback(_created_subdir)
@ -159,7 +160,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
d = c.create_dirnode() d = c.create_dirnode()
def _created(rw_dn): def _created(rw_dn):
d2 = rw_dn.set_uri(u"child", filecap) d2 = rw_dn.set_uri(u"child", filecap, filecap)
d2.addCallback(lambda res: rw_dn) d2.addCallback(lambda res: rw_dn)
return d2 return d2
d.addCallback(_created) d.addCallback(_created)
@ -171,7 +172,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
self.failUnless(ro_dn.is_mutable()) self.failUnless(ro_dn.is_mutable())
self.shouldFail(dirnode.NotMutableError, "set_uri ro", None, self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
ro_dn.set_uri, u"newchild", filecap) ro_dn.set_uri, u"newchild", filecap, filecap)
self.shouldFail(dirnode.NotMutableError, "set_uri ro", None, self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
ro_dn.set_node, u"newchild", filenode) ro_dn.set_node, u"newchild", filenode)
self.shouldFail(dirnode.NotMutableError, "set_nodes ro", None, self.shouldFail(dirnode.NotMutableError, "set_nodes ro", None,
@ -243,11 +244,13 @@ class Dirnode(GridTestMixin, unittest.TestCase,
self.expected_manifest.append( ((u"child",) , m.get_uri()) ) self.expected_manifest.append( ((u"child",) , m.get_uri()) )
self.expected_verifycaps.add(ffu_v) self.expected_verifycaps.add(ffu_v)
self.expected_storage_indexes.add(base32.b2a(m.get_storage_index())) self.expected_storage_indexes.add(base32.b2a(m.get_storage_index()))
d.addCallback(lambda res: n.set_uri(u"child", fake_file_uri)) d.addCallback(lambda res: n.set_uri(u"child",
fake_file_uri, fake_file_uri))
d.addCallback(lambda res: d.addCallback(lambda res:
self.shouldFail(ExistingChildError, "set_uri-no", self.shouldFail(ExistingChildError, "set_uri-no",
"child 'child' already exists", "child 'child' already exists",
n.set_uri, u"child", other_file_uri, n.set_uri, u"child",
other_file_uri, other_file_uri,
overwrite=False)) overwrite=False))
# / # /
# /child = mutable # /child = mutable
@ -373,12 +376,16 @@ class Dirnode(GridTestMixin, unittest.TestCase,
# set_uri + metadata # set_uri + metadata
# it should be possible to add a child without any metadata # it should be possible to add a child without any metadata
d.addCallback(lambda res: n.set_uri(u"c2", fake_file_uri, {})) d.addCallback(lambda res: n.set_uri(u"c2",
fake_file_uri, fake_file_uri,
{}))
d.addCallback(lambda res: n.get_metadata_for(u"c2")) d.addCallback(lambda res: n.get_metadata_for(u"c2"))
d.addCallback(lambda metadata: self.failUnlessEqual(metadata.keys(), ['tahoe'])) d.addCallback(lambda metadata: self.failUnlessEqual(metadata.keys(), ['tahoe']))
# You can't override the link timestamps. # You can't override the link timestamps.
d.addCallback(lambda res: n.set_uri(u"c2", fake_file_uri, { 'tahoe': {'linkcrtime': "bogus"}})) d.addCallback(lambda res: n.set_uri(u"c2",
fake_file_uri, fake_file_uri,
{ 'tahoe': {'linkcrtime': "bogus"}}))
d.addCallback(lambda res: n.get_metadata_for(u"c2")) d.addCallback(lambda res: n.get_metadata_for(u"c2"))
def _has_good_linkcrtime(metadata): def _has_good_linkcrtime(metadata):
self.failUnless(metadata.has_key('tahoe')) self.failUnless(metadata.has_key('tahoe'))
@ -387,7 +394,8 @@ class Dirnode(GridTestMixin, unittest.TestCase,
d.addCallback(_has_good_linkcrtime) d.addCallback(_has_good_linkcrtime)
# if we don't set any defaults, the child should get timestamps # if we don't set any defaults, the child should get timestamps
d.addCallback(lambda res: n.set_uri(u"c3", fake_file_uri)) d.addCallback(lambda res: n.set_uri(u"c3",
fake_file_uri, fake_file_uri))
d.addCallback(lambda res: n.get_metadata_for(u"c3")) d.addCallback(lambda res: n.get_metadata_for(u"c3"))
d.addCallback(lambda metadata: d.addCallback(lambda metadata:
self.failUnlessEqual(set(metadata.keys()), self.failUnlessEqual(set(metadata.keys()),
@ -395,7 +403,8 @@ class Dirnode(GridTestMixin, unittest.TestCase,
# or we can add specific metadata at set_uri() time, which # or we can add specific metadata at set_uri() time, which
# overrides the timestamps # overrides the timestamps
d.addCallback(lambda res: n.set_uri(u"c4", fake_file_uri, d.addCallback(lambda res: n.set_uri(u"c4",
fake_file_uri, fake_file_uri,
{"key": "value"})) {"key": "value"}))
d.addCallback(lambda res: n.get_metadata_for(u"c4")) d.addCallback(lambda res: n.get_metadata_for(u"c4"))
d.addCallback(lambda metadata: d.addCallback(lambda metadata:
@ -439,17 +448,19 @@ class Dirnode(GridTestMixin, unittest.TestCase,
d.addCallback(lambda res: n.delete(u"d4")) d.addCallback(lambda res: n.delete(u"d4"))
# metadata through set_children() # metadata through set_children()
d.addCallback(lambda res: n.set_children([ (u"e1", fake_file_uri), d.addCallback(lambda res:
(u"e2", fake_file_uri, {}), n.set_children([
(u"e3", fake_file_uri, (u"e1", fake_file_uri, fake_file_uri),
{"key": "value"}), (u"e2", fake_file_uri, fake_file_uri, {}),
])) (u"e3", fake_file_uri, fake_file_uri,
{"key": "value"}),
]))
d.addCallback(lambda res: d.addCallback(lambda res:
self.shouldFail(ExistingChildError, "set_children-no", self.shouldFail(ExistingChildError, "set_children-no",
"child 'e1' already exists", "child 'e1' already exists",
n.set_children, n.set_children,
[ (u"e1", other_file_uri), [ (u"e1", other_file_uri, other_file_uri),
(u"new", other_file_uri), ], (u"new", other_file_uri, other_file_uri), ],
overwrite=False)) overwrite=False))
# and 'new' should not have been created # and 'new' should not have been created
d.addCallback(lambda res: n.list()) d.addCallback(lambda res: n.list())
@ -645,7 +656,8 @@ class Dirnode(GridTestMixin, unittest.TestCase,
# now make sure that we honor overwrite=False # now make sure that we honor overwrite=False
d.addCallback(lambda res: d.addCallback(lambda res:
self.subdir2.set_uri(u"newchild", other_file_uri)) self.subdir2.set_uri(u"newchild",
other_file_uri, other_file_uri))
d.addCallback(lambda res: d.addCallback(lambda res:
self.shouldFail(ExistingChildError, "move_child_to-no", self.shouldFail(ExistingChildError, "move_child_to-no",
@ -804,7 +816,8 @@ class Dirnode2(unittest.TestCase, testutil.ShouldFailMixin):
self.shouldFail(CannotPackUnknownNodeError, self.shouldFail(CannotPackUnknownNodeError,
"copy unknown", "copy unknown",
"cannot pack unknown node as child add", "cannot pack unknown node as child add",
self._node.set_uri, u"add", future_writecap)) self._node.set_uri, u"add",
future_writecap, future_readcap))
d.addCallback(lambda ign: self._node.list()) d.addCallback(lambda ign: self._node.list())
def _check(children): def _check(children):
self.failUnlessEqual(len(children), 1) self.failUnlessEqual(len(children), 1)

View File

@ -862,8 +862,12 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
d1.addCallback(self.log, "made P/personal/sekrit data") d1.addCallback(self.log, "made P/personal/sekrit data")
d1.addCallback(lambda res: rootnode.get_child_at_path([u"subdir1", u"subdir2"])) d1.addCallback(lambda res: rootnode.get_child_at_path([u"subdir1", u"subdir2"]))
def _got_s2(s2node): def _got_s2(s2node):
d2 = privnode.set_uri(u"s2-rw", s2node.get_uri()) d2 = privnode.set_uri(u"s2-rw", s2node.get_uri(),
d2.addCallback(lambda node: privnode.set_uri(u"s2-ro", s2node.get_readonly_uri())) s2node.get_readonly_uri())
d2.addCallback(lambda node:
privnode.set_uri(u"s2-ro",
s2node.get_readonly_uri(),
s2node.get_readonly_uri()))
return d2 return d2
d1.addCallback(_got_s2) d1.addCallback(_got_s2)
d1.addCallback(lambda res: privnode) d1.addCallback(lambda res: privnode)
@ -944,7 +948,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
d1.addCallback(self.log, "doing delete(ro)") d1.addCallback(self.log, "doing delete(ro)")
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "delete(nope)", None, dirnode.delete, u"mydata992")) d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "delete(nope)", None, dirnode.delete, u"mydata992"))
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "set_uri(nope)", None, dirnode.set_uri, u"hopeless", self.uri)) d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "set_uri(nope)", None, dirnode.set_uri, u"hopeless", self.uri, self.uri))
d1.addCallback(lambda res: self.shouldFail2(NoSuchChildError, "get(missing)", "missing", dirnode.get, u"missing")) d1.addCallback(lambda res: self.shouldFail2(NoSuchChildError, "get(missing)", "missing", dirnode.get, u"missing"))

View File

@ -136,37 +136,40 @@ class WebMixin(object):
self._foo_verifycap = foo.get_verify_cap().to_string() self._foo_verifycap = foo.get_verify_cap().to_string()
# NOTE: we ignore the deferred on all set_uri() calls, because we # NOTE: we ignore the deferred on all set_uri() calls, because we
# know the fake nodes do these synchronously # know the fake nodes do these synchronously
self.public_root.set_uri(u"foo", foo.get_uri()) self.public_root.set_uri(u"foo", foo.get_uri(),
foo.get_readonly_uri())
self.BAR_CONTENTS, n, self._bar_txt_uri = self.makefile(0) self.BAR_CONTENTS, n, self._bar_txt_uri = self.makefile(0)
foo.set_uri(u"bar.txt", self._bar_txt_uri) foo.set_uri(u"bar.txt", self._bar_txt_uri, self._bar_txt_uri)
self._bar_txt_verifycap = n.get_verify_cap().to_string() self._bar_txt_verifycap = n.get_verify_cap().to_string()
foo.set_uri(u"empty", res[3][1].get_uri()) foo.set_uri(u"empty", res[3][1].get_uri(),
res[3][1].get_readonly_uri())
sub_uri = res[4][1].get_uri() sub_uri = res[4][1].get_uri()
self._sub_uri = sub_uri self._sub_uri = sub_uri
foo.set_uri(u"sub", sub_uri) foo.set_uri(u"sub", sub_uri, sub_uri)
sub = self.s.create_node_from_uri(sub_uri) sub = self.s.create_node_from_uri(sub_uri)
_ign, n, blocking_uri = self.makefile(1) _ign, n, blocking_uri = self.makefile(1)
foo.set_uri(u"blockingfile", blocking_uri) foo.set_uri(u"blockingfile", blocking_uri, blocking_uri)
unicode_filename = u"n\u00fc.txt" # n u-umlaut . t x t unicode_filename = u"n\u00fc.txt" # n u-umlaut . t x t
# ok, unicode calls it LATIN SMALL LETTER U WITH DIAERESIS but I # ok, unicode calls it LATIN SMALL LETTER U WITH DIAERESIS but I
# still think of it as an umlaut # still think of it as an umlaut
foo.set_uri(unicode_filename, self._bar_txt_uri) foo.set_uri(unicode_filename, self._bar_txt_uri, self._bar_txt_uri)
_ign, n, baz_file = self.makefile(2) _ign, n, baz_file = self.makefile(2)
self._baz_file_uri = baz_file self._baz_file_uri = baz_file
sub.set_uri(u"baz.txt", baz_file) sub.set_uri(u"baz.txt", baz_file, baz_file)
_ign, n, self._bad_file_uri = self.makefile(3) _ign, n, self._bad_file_uri = self.makefile(3)
# this uri should not be downloadable # this uri should not be downloadable
del FakeCHKFileNode.all_contents[self._bad_file_uri] del FakeCHKFileNode.all_contents[self._bad_file_uri]
rodir = res[5][1] rodir = res[5][1]
self.public_root.set_uri(u"reedownlee", rodir.get_readonly_uri()) self.public_root.set_uri(u"reedownlee", rodir.get_readonly_uri(),
rodir.set_uri(u"nor", baz_file) rodir.get_readonly_uri())
rodir.set_uri(u"nor", baz_file, baz_file)
# public/ # public/
# public/foo/ # public/foo/

View File

@ -204,7 +204,6 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
elif t == "stream-manifest": elif t == "stream-manifest":
d = self._POST_stream_manifest(ctx) d = self._POST_stream_manifest(ctx)
elif t == "set_children": elif t == "set_children":
# TODO: docs
d = self._POST_set_children(req) d = self._POST_set_children(req)
else: else:
raise WebError("POST to a directory with bad t=%s" % t) raise WebError("POST to a directory with bad t=%s" % t)
@ -303,7 +302,7 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
charset = get_arg(req, "_charset", "utf-8") charset = get_arg(req, "_charset", "utf-8")
name = name.decode(charset) name = name.decode(charset)
replace = boolean_of_arg(get_arg(req, "replace", "true")) replace = boolean_of_arg(get_arg(req, "replace", "true"))
d = self.node.set_uri(name, childcap, overwrite=replace) d = self.node.set_uri(name, childcap, childcap, overwrite=replace)
d.addCallback(lambda res: childcap) d.addCallback(lambda res: childcap)
return d return d
@ -471,8 +470,13 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
cs = [] cs = []
for name, (file_or_dir, mddict) in children.iteritems(): for name, (file_or_dir, mddict) in children.iteritems():
name = unicode(name) # simplejson-2.0.1 returns str *or* unicode name = unicode(name) # simplejson-2.0.1 returns str *or* unicode
cap = str(mddict.get('rw_uri') or mddict.get('ro_uri')) writecap = mddict.get('rw_uri')
cs.append((name, cap, mddict.get('metadata'))) if writecap is not None:
writecap = str(writecap)
readcap = mddict.get('ro_uri')
if readcap is not None:
readcap = str(readcap)
cs.append((name, writecap, readcap, mddict.get('metadata')))
d = self.node.set_children(cs, replace) d = self.node.set_children(cs, replace)
d.addCallback(lambda res: "Okay so I did it.") d.addCallback(lambda res: "Okay so I did it.")
# TODO: results # TODO: results