client.create_mutable_file(contents=) now accepts a callable, which is
invoked with the new MutableFileNode and is supposed to return the initial contents. This can be used by e.g. a new dirnode which needs the filenode's writekey to encrypt its initial children. create_mutable_file() still accepts a bytestring too, or None for an empty file.
This commit is contained in:
parent
b30041c5ec
commit
c2520e4ec7
|
@ -463,7 +463,7 @@ class Client(node.Node, pollmixin.PollMixin):
|
|||
d.addCallback(lambda n: n.set_children(initial_children))
|
||||
return d
|
||||
|
||||
def create_mutable_file(self, contents="", keysize=None):
|
||||
def create_mutable_file(self, contents=None, keysize=None):
|
||||
return self.nodemaker.create_mutable_file(contents, keysize)
|
||||
|
||||
def upload(self, uploadable):
|
||||
|
|
|
@ -2006,8 +2006,20 @@ class IClient(Interface):
|
|||
"""
|
||||
|
||||
def create_mutable_file(contents=""):
|
||||
"""Create a new mutable file with contents, get back the URI string.
|
||||
@param contents: the initial contents to place in the file.
|
||||
"""Create a new mutable file (with initial) contents, get back the
|
||||
URI string.
|
||||
|
||||
@param contents: (bytestring, callable, or None): this provides the
|
||||
initial contents of the mutable file. If 'contents' is a bytestring,
|
||||
it will be used as-is. If 'contents' is a callable, it will be
|
||||
invoked with the new MutableFileNode instance and is expected to
|
||||
return a bytestring with the initial contents of the file (the
|
||||
callable can use node.get_writekey() to decide how to encrypt the
|
||||
initial contents, e.g. for a brand new dirnode with initial
|
||||
children). contents=None is equivalent to an empty string. Using
|
||||
content_maker= is more efficient than creating a mutable file and
|
||||
setting its contents in two separate operations.
|
||||
|
||||
@return: a Deferred that fires with tne (string) SSK URI for the new
|
||||
file.
|
||||
"""
|
||||
|
|
|
@ -102,10 +102,11 @@ class MutableFileNode:
|
|||
self._encprivkey = None
|
||||
return self
|
||||
|
||||
def create_with_keys(self, (pubkey, privkey), initial_contents):
|
||||
def create_with_keys(self, (pubkey, privkey), contents):
|
||||
"""Call this to create a brand-new mutable file. It will create the
|
||||
shares, find homes for them, and upload the initial contents. Returns
|
||||
a Deferred that fires (with the MutableFileNode instance you should
|
||||
shares, find homes for them, and upload the initial contents (created
|
||||
with the same rules as IClient.create_mutable_file() ). Returns a
|
||||
Deferred that fires (with the MutableFileNode instance you should
|
||||
use) when it completes.
|
||||
"""
|
||||
self._pubkey, self._privkey = pubkey, privkey
|
||||
|
@ -117,8 +118,18 @@ class MutableFileNode:
|
|||
self._uri = WriteableSSKFileURI(self._writekey, self._fingerprint)
|
||||
self._readkey = self._uri.readkey
|
||||
self._storage_index = self._uri.storage_index
|
||||
initial_contents = self._get_initial_contents(contents)
|
||||
return self._upload(initial_contents, None)
|
||||
|
||||
def _get_initial_contents(self, contents):
|
||||
if isinstance(contents, str):
|
||||
return contents
|
||||
if contents is None:
|
||||
return ""
|
||||
assert callable(contents), "%s should be callable, not %s" % \
|
||||
(contents, type(contents))
|
||||
return contents(self)
|
||||
|
||||
def _encrypt_privkey(self, writekey, privkey):
|
||||
enc = AES(writekey)
|
||||
crypttext = enc.process(privkey)
|
||||
|
|
|
@ -80,7 +80,7 @@ class NodeMaker:
|
|||
return node
|
||||
|
||||
|
||||
def create_mutable_file(self, contents="", keysize=None):
|
||||
def create_mutable_file(self, contents=None, keysize=None):
|
||||
n = MutableFileNode(self.storage_broker, self.secret_holder,
|
||||
self.default_encoding_parameters, self.history)
|
||||
d = self.key_generator.generate(keysize)
|
||||
|
|
|
@ -299,6 +299,21 @@ class Filenode(unittest.TestCase, testutil.ShouldFailMixin):
|
|||
d.addCallback(_created)
|
||||
return d
|
||||
|
||||
def test_create_with_initial_contents_function(self):
|
||||
data = "initial contents"
|
||||
def _make_contents(n):
|
||||
self.failUnless(isinstance(n, MutableFileNode))
|
||||
key = n.get_writekey()
|
||||
self.failUnless(isinstance(key, str), key)
|
||||
self.failUnlessEqual(len(key), 16) # AES key size
|
||||
return data
|
||||
d = self.nodemaker.create_mutable_file(_make_contents)
|
||||
def _created(n):
|
||||
return n.download_best_version()
|
||||
d.addCallback(_created)
|
||||
d.addCallback(lambda data2: self.failUnlessEqual(data2, data))
|
||||
return d
|
||||
|
||||
def test_create_with_too_large_contents(self):
|
||||
BIG = "a" * (self.OLD_MAX_SEGMENT_SIZE + 1)
|
||||
d = self.nodemaker.create_mutable_file(BIG)
|
||||
|
|
Loading…
Reference in New Issue