1 | diff -rN -u old-tahoe/src/allmydata/client.py new-tahoe/src/allmydata/client.py |
---|
2 | --- old-tahoe/src/allmydata/client.py 2010-01-23 12:59:08.664000000 +0000 |
---|
3 | +++ new-tahoe/src/allmydata/client.py 2010-01-23 12:59:11.145000000 +0000 |
---|
4 | @@ -471,13 +471,16 @@ |
---|
5 | # dirnodes. The first takes a URI and produces a filenode or (new-style) |
---|
6 | # dirnode. The other three create brand-new filenodes/dirnodes. |
---|
7 | |
---|
8 | - def create_node_from_uri(self, writecap, readcap=None): |
---|
9 | - # this returns synchronously. |
---|
10 | - return self.nodemaker.create_from_cap(writecap, readcap) |
---|
11 | + def create_node_from_uri(self, write_uri, read_uri=None, deep_immutable=False, name="<unknown name>"): |
---|
12 | + # This returns synchronously. |
---|
13 | + # Note that it does *not* validate the write_uri and read_uri; instead we |
---|
14 | + # may get an opaque node if there were any problems. |
---|
15 | + return self.nodemaker.create_from_cap(write_uri, read_uri, deep_immutable=deep_immutable, name=name) |
---|
16 | |
---|
17 | def create_dirnode(self, initial_children={}): |
---|
18 | d = self.nodemaker.create_new_mutable_directory(initial_children) |
---|
19 | return d |
---|
20 | + |
---|
21 | def create_immutable_dirnode(self, children, convergence=None): |
---|
22 | return self.nodemaker.create_immutable_directory(children, convergence) |
---|
23 | |
---|
24 | diff -rN -u old-tahoe/src/allmydata/control.py new-tahoe/src/allmydata/control.py |
---|
25 | --- old-tahoe/src/allmydata/control.py 2010-01-23 12:59:08.685000000 +0000 |
---|
26 | +++ new-tahoe/src/allmydata/control.py 2010-01-23 12:59:11.158000000 +0000 |
---|
27 | @@ -5,7 +5,7 @@ |
---|
28 | from twisted.internet import defer |
---|
29 | from twisted.internet.interfaces import IConsumer |
---|
30 | from foolscap.api import Referenceable |
---|
31 | -from allmydata.interfaces import RIControlClient |
---|
32 | +from allmydata.interfaces import RIControlClient, IFileNode |
---|
33 | from allmydata.util import fileutil, mathutil |
---|
34 | from allmydata.immutable import upload |
---|
35 | from twisted.python import log |
---|
36 | @@ -67,7 +67,9 @@ |
---|
37 | return d |
---|
38 | |
---|
39 | def remote_download_from_uri_to_file(self, uri, filename): |
---|
40 | - filenode = self.parent.create_node_from_uri(uri) |
---|
41 | + filenode = self.parent.create_node_from_uri(uri, name=filename) |
---|
42 | + if not IFileNode.providedBy(filenode): |
---|
43 | + raise AssertionError("The URI does not reference a file.") |
---|
44 | c = FileWritingConsumer(filename) |
---|
45 | d = filenode.read(c) |
---|
46 | d.addCallback(lambda res: filename) |
---|
47 | @@ -199,6 +201,8 @@ |
---|
48 | if i >= self.count: |
---|
49 | return |
---|
50 | n = self.parent.create_node_from_uri(self.uris[i]) |
---|
51 | + if not IFileNode.providedBy(n): |
---|
52 | + raise AssertionError("The URI does not reference a file.") |
---|
53 | if n.is_mutable(): |
---|
54 | d1 = n.download_best_version() |
---|
55 | else: |
---|
56 | diff -rN -u old-tahoe/src/allmydata/dirnode.py new-tahoe/src/allmydata/dirnode.py |
---|
57 | --- old-tahoe/src/allmydata/dirnode.py 2010-01-23 12:59:08.694000000 +0000 |
---|
58 | +++ new-tahoe/src/allmydata/dirnode.py 2010-01-23 12:59:11.166000000 +0000 |
---|
59 | @@ -5,13 +5,13 @@ |
---|
60 | from twisted.internet import defer |
---|
61 | from foolscap.api import fireEventually |
---|
62 | import simplejson |
---|
63 | -from allmydata.mutable.common import NotMutableError |
---|
64 | +from allmydata.mutable.common import NotWriteableError |
---|
65 | from allmydata.mutable.filenode import MutableFileNode |
---|
66 | -from allmydata.unknown import UnknownNode |
---|
67 | +from allmydata.unknown import UnknownNode, strip_prefix_for_ro |
---|
68 | from allmydata.interfaces import IFilesystemNode, IDirectoryNode, IFileNode, \ |
---|
69 | IImmutableFileNode, IMutableFileNode, \ |
---|
70 | ExistingChildError, NoSuchChildError, ICheckable, IDeepCheckable, \ |
---|
71 | - CannotPackUnknownNodeError |
---|
72 | + MustBeDeepImmutableError, CapConstraintError |
---|
73 | from allmydata.check_results import DeepCheckResults, \ |
---|
74 | DeepCheckAndRepairResults |
---|
75 | from allmydata.monitor import Monitor |
---|
76 | @@ -23,6 +23,11 @@ |
---|
77 | from pycryptopp.cipher.aes import AES |
---|
78 | from allmydata.util.dictutil import AuxValueDict |
---|
79 | |
---|
80 | + |
---|
81 | +# TODO: {Deleter,MetadataSetter,Adder}.modify all start by unpacking the |
---|
82 | +# contents and end by repacking them. It might be better to apply them to |
---|
83 | +# the unpacked contents. |
---|
84 | + |
---|
85 | class Deleter: |
---|
86 | def __init__(self, node, name, must_exist=True): |
---|
87 | self.node = node |
---|
88 | @@ -40,6 +45,7 @@ |
---|
89 | new_contents = self.node._pack_contents(children) |
---|
90 | return new_contents |
---|
91 | |
---|
92 | + |
---|
93 | class MetadataSetter: |
---|
94 | def __init__(self, node, name, metadata): |
---|
95 | self.node = node |
---|
96 | @@ -75,6 +81,11 @@ |
---|
97 | for (name, (child, new_metadata)) in self.entries.iteritems(): |
---|
98 | precondition(isinstance(name, unicode), name) |
---|
99 | precondition(IFilesystemNode.providedBy(child), child) |
---|
100 | + |
---|
101 | + # Strictly speaking this is redundant because we would raise the |
---|
102 | + # error again in pack_children. |
---|
103 | + child.raise_error() |
---|
104 | + |
---|
105 | if name in children: |
---|
106 | if not self.overwrite: |
---|
107 | raise ExistingChildError("child '%s' already exists" % name) |
---|
108 | @@ -123,25 +134,21 @@ |
---|
109 | new_contents = self.node._pack_contents(children) |
---|
110 | return new_contents |
---|
111 | |
---|
112 | -def _encrypt_rwcap(filenode, rwcap): |
---|
113 | - assert isinstance(rwcap, str) |
---|
114 | +def _encrypt_rw_uri(filenode, rw_uri): |
---|
115 | + assert isinstance(rw_uri, str) |
---|
116 | writekey = filenode.get_writekey() |
---|
117 | if not writekey: |
---|
118 | return "" |
---|
119 | - salt = hashutil.mutable_rwcap_salt_hash(rwcap) |
---|
120 | + salt = hashutil.mutable_rwcap_salt_hash(rw_uri) |
---|
121 | key = hashutil.mutable_rwcap_key_hash(salt, writekey) |
---|
122 | cryptor = AES(key) |
---|
123 | - crypttext = cryptor.process(rwcap) |
---|
124 | + crypttext = cryptor.process(rw_uri) |
---|
125 | mac = hashutil.hmac(key, salt + crypttext) |
---|
126 | assert len(mac) == 32 |
---|
127 | return salt + crypttext + mac |
---|
128 | # The MAC is not checked by readers in Tahoe >= 1.3.0, but we still |
---|
129 | # produce it for the sake of older readers. |
---|
130 | |
---|
131 | -class MustBeDeepImmutable(Exception): |
---|
132 | - """You tried to add a non-deep-immutable node to a deep-immutable |
---|
133 | - directory.""" |
---|
134 | - |
---|
135 | def pack_children(filenode, children, deep_immutable=False): |
---|
136 | """Take a dict that maps: |
---|
137 | children[unicode_name] = (IFileSystemNode, metadata_dict) |
---|
138 | @@ -152,7 +159,7 @@ |
---|
139 | time. |
---|
140 | |
---|
141 | If deep_immutable is True, I will require that all my children are deeply |
---|
142 | - immutable, and will raise a MustBeDeepImmutable exception if not. |
---|
143 | + immutable, and will raise a MustBeDeepImmutableError if not. |
---|
144 | """ |
---|
145 | |
---|
146 | has_aux = isinstance(children, AuxValueDict) |
---|
147 | @@ -161,25 +168,25 @@ |
---|
148 | assert isinstance(name, unicode) |
---|
149 | entry = None |
---|
150 | (child, metadata) = children[name] |
---|
151 | - if deep_immutable and child.is_mutable(): |
---|
152 | - # TODO: consider adding IFileSystemNode.is_deep_immutable() |
---|
153 | - raise MustBeDeepImmutable("child '%s' is mutable" % (name,)) |
---|
154 | + child.raise_error() |
---|
155 | + if deep_immutable and not child.is_allowed_in_immutable_directory(): |
---|
156 | + raise MustBeDeepImmutableError("child '%s' is not allowed in an immutable directory" % (name,), name) |
---|
157 | if has_aux: |
---|
158 | entry = children.get_aux(name) |
---|
159 | if not entry: |
---|
160 | assert IFilesystemNode.providedBy(child), (name,child) |
---|
161 | assert isinstance(metadata, dict) |
---|
162 | - rwcap = child.get_uri() # might be RO if the child is not writeable |
---|
163 | - if rwcap is None: |
---|
164 | - rwcap = "" |
---|
165 | - assert isinstance(rwcap, str), rwcap |
---|
166 | - rocap = child.get_readonly_uri() |
---|
167 | - if rocap is None: |
---|
168 | - rocap = "" |
---|
169 | - assert isinstance(rocap, str), rocap |
---|
170 | + rw_uri = child.get_write_uri() |
---|
171 | + if rw_uri is None: |
---|
172 | + rw_uri = "" |
---|
173 | + assert isinstance(rw_uri, str), rw_uri |
---|
174 | + ro_uri = child.get_readonly_uri() |
---|
175 | + if ro_uri is None: |
---|
176 | + ro_uri = "" |
---|
177 | + assert isinstance(ro_uri, str), ro_uri |
---|
178 | entry = "".join([netstring(name.encode("utf-8")), |
---|
179 | - netstring(rocap), |
---|
180 | - netstring(_encrypt_rwcap(filenode, rwcap)), |
---|
181 | + netstring(strip_prefix_for_ro(ro_uri, deep_immutable)), |
---|
182 | + netstring(_encrypt_rw_uri(filenode, rw_uri)), |
---|
183 | netstring(simplejson.dumps(metadata))]) |
---|
184 | entries.append(netstring(entry)) |
---|
185 | return "".join(entries) |
---|
186 | @@ -230,38 +237,66 @@ |
---|
187 | plaintext = cryptor.process(crypttext) |
---|
188 | return plaintext |
---|
189 | |
---|
190 | - def _create_node(self, rwcap, rocap): |
---|
191 | - return self._nodemaker.create_from_cap(rwcap, rocap) |
---|
192 | + def _create_and_validate_node(self, rw_uri, ro_uri, name): |
---|
193 | + #print "mutable? %r\n" % self.is_mutable() |
---|
194 | + #print "_create_and_validate_node(rw_uri=%r, ro_uri=%r, name=%r)\n" % (rw_uri, ro_uri, name) |
---|
195 | + node = self._nodemaker.create_from_cap(rw_uri, ro_uri, |
---|
196 | + deep_immutable=not self.is_mutable(), |
---|
197 | + name=name) |
---|
198 | + node.raise_error() |
---|
199 | + return node |
---|
200 | |
---|
201 | def _unpack_contents(self, data): |
---|
202 | # the directory is serialized as a list of netstrings, one per child. |
---|
203 | - # Each child is serialized as a list of four netstrings: (name, |
---|
204 | - # rocap, rwcap, metadata), in which the name,rocap,metadata are in |
---|
205 | - # cleartext. The 'name' is UTF-8 encoded. The rwcap is formatted as: |
---|
206 | - # pack("16ss32s", iv, AES(H(writekey+iv), plaintextrwcap), mac) |
---|
207 | + # Each child is serialized as a list of four netstrings: (name, ro_uri, |
---|
208 | + # rwcapdata, metadata), in which the name, ro_uri, metadata are in |
---|
209 | + # cleartext. The 'name' is UTF-8 encoded. The rwcapdata is formatted as: |
---|
210 | + # pack("16ss32s", iv, AES(H(writekey+iv), plaintext_rw_uri), mac) |
---|
211 | assert isinstance(data, str), (repr(data), type(data)) |
---|
212 | # an empty directory is serialized as an empty string |
---|
213 | if data == "": |
---|
214 | return AuxValueDict() |
---|
215 | writeable = not self.is_readonly() |
---|
216 | + mutable = self.is_mutable() |
---|
217 | children = AuxValueDict() |
---|
218 | position = 0 |
---|
219 | while position < len(data): |
---|
220 | entries, position = split_netstring(data, 1, position) |
---|
221 | entry = entries[0] |
---|
222 | - (name, rocap, rwcapdata, metadata_s), subpos = split_netstring(entry, 4) |
---|
223 | + (name, ro_uri, rwcapdata, metadata_s), subpos = split_netstring(entry, 4) |
---|
224 | name = name.decode("utf-8") |
---|
225 | - rwcap = None |
---|
226 | + rw_uri = "" |
---|
227 | if writeable: |
---|
228 | - rwcap = self._decrypt_rwcapdata(rwcapdata) |
---|
229 | - if not rwcap: |
---|
230 | - rwcap = None # rwcap is None or a non-empty string |
---|
231 | - if not rocap: |
---|
232 | - rocap = None # rocap is None or a non-empty string |
---|
233 | - child = self._create_node(rwcap, rocap) |
---|
234 | - metadata = simplejson.loads(metadata_s) |
---|
235 | - assert isinstance(metadata, dict) |
---|
236 | - children.set_with_aux(name, (child, metadata), auxilliary=entry) |
---|
237 | + rw_uri = self._decrypt_rwcapdata(rwcapdata) |
---|
238 | + #print "mutable=%r, writeable=%r, rw_uri=%r, ro_uri=%r, name=%r" % (mutable, writeable, rw_uri, ro_uri, name) |
---|
239 | + |
---|
240 | + # Since the encryption uses CTR mode, it currently leaks the length of the |
---|
241 | + # plaintext rw_uri -- and therefore whether it is present, i.e. whether the |
---|
242 | + # dirnode is writable (ticket #925). By stripping spaces in Tahoe >= 1.6.0, |
---|
243 | + # we may make it easier for future versions to plug this leak. |
---|
244 | + rw_uri = rw_uri.strip() |
---|
245 | + if not rw_uri: |
---|
246 | + rw_uri = None # rw_uri is None or a non-empty string |
---|
247 | + |
---|
248 | + # Treat ro_uri in the same way for consistency. |
---|
249 | + ro_uri = ro_uri.strip() |
---|
250 | + if not ro_uri: |
---|
251 | + ro_uri = None # ro_uri is None or a non-empty string |
---|
252 | + |
---|
253 | + try: |
---|
254 | + child = self._create_and_validate_node(rw_uri, ro_uri, name) |
---|
255 | + #print "%r.is_allowed_in_immutable_directory() = %r" % (child, child.is_allowed_in_immutable_directory()) |
---|
256 | + if mutable or child.is_allowed_in_immutable_directory(): |
---|
257 | + metadata = simplejson.loads(metadata_s) |
---|
258 | + assert isinstance(metadata, dict) |
---|
259 | + children[name] = (child, metadata) |
---|
260 | + children.set_with_aux(name, (child, metadata), auxilliary=entry) |
---|
261 | + except CapConstraintError, e: |
---|
262 | + #print "unmet constraint: (%s, %s)" % (e.args[0], e.args[1].encode("utf-8")) |
---|
263 | + log.msg(format="unmet constraint on cap for child '%(name)s' unpacked from a directory:\n" |
---|
264 | + "%(message)s", message=e.args[0], name=e.args[1].encode("utf-8"), |
---|
265 | + facility="tahoe.webish", level=log.UNUSUAL) |
---|
266 | + |
---|
267 | return children |
---|
268 | |
---|
269 | def _pack_contents(self, children): |
---|
270 | @@ -270,21 +305,39 @@ |
---|
271 | |
---|
272 | def is_readonly(self): |
---|
273 | return self._node.is_readonly() |
---|
274 | + |
---|
275 | def is_mutable(self): |
---|
276 | return self._node.is_mutable() |
---|
277 | |
---|
278 | + def is_unknown(self): |
---|
279 | + return False |
---|
280 | + |
---|
281 | + def is_allowed_in_immutable_directory(self): |
---|
282 | + return not self._node.is_mutable() |
---|
283 | + |
---|
284 | + def raise_error(self): |
---|
285 | + pass |
---|
286 | + |
---|
287 | def get_uri(self): |
---|
288 | return self._uri.to_string() |
---|
289 | |
---|
290 | + def get_write_uri(self): |
---|
291 | + if self.is_readonly(): |
---|
292 | + return None |
---|
293 | + return self._uri.to_string() |
---|
294 | + |
---|
295 | def get_readonly_uri(self): |
---|
296 | return self._uri.get_readonly().to_string() |
---|
297 | |
---|
298 | def get_cap(self): |
---|
299 | return self._uri |
---|
300 | + |
---|
301 | def get_readcap(self): |
---|
302 | return self._uri.get_readonly() |
---|
303 | + |
---|
304 | def get_verify_cap(self): |
---|
305 | return self._uri.get_verify_cap() |
---|
306 | + |
---|
307 | def get_repair_cap(self): |
---|
308 | if self._node.is_readonly(): |
---|
309 | return None # readonly (mutable) dirnodes are not yet repairable |
---|
310 | @@ -350,7 +403,7 @@ |
---|
311 | def set_metadata_for(self, name, metadata): |
---|
312 | assert isinstance(name, unicode) |
---|
313 | if self.is_readonly(): |
---|
314 | - return defer.fail(NotMutableError()) |
---|
315 | + return defer.fail(NotWriteableError()) |
---|
316 | assert isinstance(metadata, dict) |
---|
317 | s = MetadataSetter(self, name, metadata) |
---|
318 | d = self._node.modify(s.modify) |
---|
319 | @@ -398,14 +451,10 @@ |
---|
320 | precondition(isinstance(name, unicode), name) |
---|
321 | precondition(isinstance(writecap, (str,type(None))), writecap) |
---|
322 | precondition(isinstance(readcap, (str,type(None))), readcap) |
---|
323 | - child_node = self._create_node(writecap, readcap) |
---|
324 | - if isinstance(child_node, UnknownNode): |
---|
325 | - # don't be willing to pack unknown nodes: we might accidentally |
---|
326 | - # put some write-authority into the rocap slot because we don't |
---|
327 | - # know how to diminish the URI they gave us. We don't even know |
---|
328 | - # if they gave us a readcap or a writecap. |
---|
329 | - msg = "cannot pack unknown node as child %s" % str(name) |
---|
330 | - raise CannotPackUnknownNodeError(msg) |
---|
331 | + |
---|
332 | + # We now allow packing unknown nodes, provided they are valid |
---|
333 | + # for this type of directory. |
---|
334 | + child_node = self._create_and_validate_node(writecap, readcap, name) |
---|
335 | d = self.set_node(name, child_node, metadata, overwrite) |
---|
336 | d.addCallback(lambda res: child_node) |
---|
337 | return d |
---|
338 | @@ -423,10 +472,10 @@ |
---|
339 | writecap, readcap, metadata = e |
---|
340 | precondition(isinstance(writecap, (str,type(None))), writecap) |
---|
341 | precondition(isinstance(readcap, (str,type(None))), readcap) |
---|
342 | - child_node = self._create_node(writecap, readcap) |
---|
343 | - if isinstance(child_node, UnknownNode): |
---|
344 | - msg = "cannot pack unknown node as child %s" % str(name) |
---|
345 | - raise CannotPackUnknownNodeError(msg) |
---|
346 | + |
---|
347 | + # We now allow packing unknown nodes, provided they are valid |
---|
348 | + # for this type of directory. |
---|
349 | + child_node = self._create_and_validate_node(writecap, readcap, name) |
---|
350 | a.set_node(name, child_node, metadata) |
---|
351 | d = self._node.modify(a.modify) |
---|
352 | d.addCallback(lambda ign: self) |
---|
353 | @@ -439,12 +488,12 @@ |
---|
354 | same name. |
---|
355 | |
---|
356 | If this directory node is read-only, the Deferred will errback with a |
---|
357 | - NotMutableError.""" |
---|
358 | + NotWriteableError.""" |
---|
359 | |
---|
360 | precondition(IFilesystemNode.providedBy(child), child) |
---|
361 | |
---|
362 | if self.is_readonly(): |
---|
363 | - return defer.fail(NotMutableError()) |
---|
364 | + return defer.fail(NotWriteableError()) |
---|
365 | assert isinstance(name, unicode) |
---|
366 | assert IFilesystemNode.providedBy(child), child |
---|
367 | a = Adder(self, overwrite=overwrite) |
---|
368 | @@ -456,7 +505,7 @@ |
---|
369 | def set_nodes(self, entries, overwrite=True): |
---|
370 | precondition(isinstance(entries, dict), entries) |
---|
371 | if self.is_readonly(): |
---|
372 | - return defer.fail(NotMutableError()) |
---|
373 | + return defer.fail(NotWriteableError()) |
---|
374 | a = Adder(self, entries, overwrite=overwrite) |
---|
375 | d = self._node.modify(a.modify) |
---|
376 | d.addCallback(lambda res: self) |
---|
377 | @@ -470,10 +519,10 @@ |
---|
378 | the operation completes.""" |
---|
379 | assert isinstance(name, unicode) |
---|
380 | if self.is_readonly(): |
---|
381 | - return defer.fail(NotMutableError()) |
---|
382 | + return defer.fail(NotWriteableError()) |
---|
383 | d = self._uploader.upload(uploadable) |
---|
384 | - d.addCallback(lambda results: results.uri) |
---|
385 | - d.addCallback(self._nodemaker.create_from_cap) |
---|
386 | + d.addCallback(lambda results: |
---|
387 | + self._create_and_validate_node(results.uri, None, name)) |
---|
388 | d.addCallback(lambda node: |
---|
389 | self.set_node(name, node, metadata, overwrite)) |
---|
390 | return d |
---|
391 | @@ -483,7 +532,7 @@ |
---|
392 | fires (with the node just removed) when the operation finishes.""" |
---|
393 | assert isinstance(name, unicode) |
---|
394 | if self.is_readonly(): |
---|
395 | - return defer.fail(NotMutableError()) |
---|
396 | + return defer.fail(NotWriteableError()) |
---|
397 | deleter = Deleter(self, name) |
---|
398 | d = self._node.modify(deleter.modify) |
---|
399 | d.addCallback(lambda res: deleter.old_child) |
---|
400 | @@ -493,7 +542,7 @@ |
---|
401 | mutable=True): |
---|
402 | assert isinstance(name, unicode) |
---|
403 | if self.is_readonly(): |
---|
404 | - return defer.fail(NotMutableError()) |
---|
405 | + return defer.fail(NotWriteableError()) |
---|
406 | if mutable: |
---|
407 | d = self._nodemaker.create_new_mutable_directory(initial_children) |
---|
408 | else: |
---|
409 | @@ -515,7 +564,7 @@ |
---|
410 | Deferred that fires when the operation finishes.""" |
---|
411 | assert isinstance(current_child_name, unicode) |
---|
412 | if self.is_readonly() or new_parent.is_readonly(): |
---|
413 | - return defer.fail(NotMutableError()) |
---|
414 | + return defer.fail(NotWriteableError()) |
---|
415 | if new_child_name is None: |
---|
416 | new_child_name = current_child_name |
---|
417 | assert isinstance(new_child_name, unicode) |
---|
418 | diff -rN -u old-tahoe/src/allmydata/immutable/filenode.py new-tahoe/src/allmydata/immutable/filenode.py |
---|
419 | --- old-tahoe/src/allmydata/immutable/filenode.py 2010-01-23 12:59:08.893000000 +0000 |
---|
420 | +++ new-tahoe/src/allmydata/immutable/filenode.py 2010-01-23 12:59:11.317000000 +0000 |
---|
421 | @@ -17,6 +17,9 @@ |
---|
422 | class _ImmutableFileNodeBase(object): |
---|
423 | implements(IImmutableFileNode, ICheckable) |
---|
424 | |
---|
425 | + def get_write_uri(self): |
---|
426 | + return None |
---|
427 | + |
---|
428 | def get_readonly_uri(self): |
---|
429 | return self.get_uri() |
---|
430 | |
---|
431 | @@ -26,6 +29,15 @@ |
---|
432 | def is_readonly(self): |
---|
433 | return True |
---|
434 | |
---|
435 | + def is_unknown(self): |
---|
436 | + return False |
---|
437 | + |
---|
438 | + def is_allowed_in_immutable_directory(self): |
---|
439 | + return True |
---|
440 | + |
---|
441 | + def raise_error(self): |
---|
442 | + pass |
---|
443 | + |
---|
444 | def __hash__(self): |
---|
445 | return self.u.__hash__() |
---|
446 | def __eq__(self, other): |
---|
447 | diff -rN -u old-tahoe/src/allmydata/interfaces.py new-tahoe/src/allmydata/interfaces.py |
---|
448 | --- old-tahoe/src/allmydata/interfaces.py 2010-01-23 12:59:08.923000000 +0000 |
---|
449 | +++ new-tahoe/src/allmydata/interfaces.py 2010-01-23 12:59:11.366000000 +0000 |
---|
450 | @@ -426,6 +426,7 @@ |
---|
451 | """Return True if the data can be modified by *somebody* (perhaps |
---|
452 | someone who has a more powerful URI than this one).""" |
---|
453 | |
---|
454 | + # TODO: rename to get_read_cap() |
---|
455 | def get_readonly(): |
---|
456 | """Return another IURI instance, which represents a read-only form of |
---|
457 | this one. If is_readonly() is True, this returns self.""" |
---|
458 | @@ -456,7 +457,6 @@ |
---|
459 | class IDirnodeURI(Interface): |
---|
460 | """I am a URI which represents a dirnode.""" |
---|
461 | |
---|
462 | - |
---|
463 | class IFileURI(Interface): |
---|
464 | """I am a URI which represents a filenode.""" |
---|
465 | def get_size(): |
---|
466 | @@ -467,21 +467,28 @@ |
---|
467 | |
---|
468 | class IMutableFileURI(Interface): |
---|
469 | """I am a URI which represents a mutable filenode.""" |
---|
470 | + |
---|
471 | class IDirectoryURI(Interface): |
---|
472 | pass |
---|
473 | + |
---|
474 | class IReadonlyDirectoryURI(Interface): |
---|
475 | pass |
---|
476 | |
---|
477 | -class CannotPackUnknownNodeError(Exception): |
---|
478 | - """UnknownNodes (using filecaps from the future that we don't understand) |
---|
479 | - cannot yet be copied safely, so I refuse to copy them.""" |
---|
480 | - |
---|
481 | -class UnhandledCapTypeError(Exception): |
---|
482 | - """I recognize the cap/URI, but I cannot create an IFilesystemNode for |
---|
483 | - it.""" |
---|
484 | +class CapConstraintError(Exception): |
---|
485 | + """A constraint on a cap was violated.""" |
---|
486 | |
---|
487 | -class NotDeepImmutableError(Exception): |
---|
488 | - """Deep-immutable directories can only contain deep-immutable children""" |
---|
489 | +class MustBeDeepImmutableError(CapConstraintError): |
---|
490 | + """Mutable children cannot be added to an immutable directory. |
---|
491 | + Also, caps obtained from an immutable directory can trigger this error |
---|
492 | + if they are later found to refer to a mutable object and then used.""" |
---|
493 | + |
---|
494 | +class MustBeReadonlyError(CapConstraintError): |
---|
495 | + """Known write caps cannot be specified in a ro_uri field. Also, |
---|
496 | + caps obtained from a ro_uri field can trigger this error if they |
---|
497 | + are later found to be write caps and then used.""" |
---|
498 | + |
---|
499 | +class MustNotBeUnknownRWError(CapConstraintError): |
---|
500 | + """Cannot add an unknown child cap specified in a rw_uri field.""" |
---|
501 | |
---|
502 | # The hierarchy looks like this: |
---|
503 | # IFilesystemNode |
---|
504 | @@ -518,9 +525,8 @@ |
---|
505 | """ |
---|
506 | |
---|
507 | def get_uri(): |
---|
508 | - """ |
---|
509 | - Return the URI string that can be used by others to get access to |
---|
510 | - this node. If this node is read-only, the URI will only offer |
---|
511 | + """Return the URI string corresponding to the strongest cap associated |
---|
512 | + with this node. If this node is read-only, the URI will only offer |
---|
513 | read-only access. If this node is read-write, the URI will offer |
---|
514 | read-write access. |
---|
515 | |
---|
516 | @@ -528,6 +534,11 @@ |
---|
517 | read-only access with others, use get_readonly_uri(). |
---|
518 | """ |
---|
519 | |
---|
520 | + def get_write_uri(n): |
---|
521 | + """Return the URI string that can be used by others to get write |
---|
522 | + access to this node, if it is writeable. If this is a read-only node, |
---|
523 | + return None.""" |
---|
524 | + |
---|
525 | def get_readonly_uri(): |
---|
526 | """Return the URI string that can be used by others to get read-only |
---|
527 | access to this node. The result is a read-only URI, regardless of |
---|
528 | @@ -557,6 +568,18 @@ |
---|
529 | file. |
---|
530 | """ |
---|
531 | |
---|
532 | + def is_unknown(): |
---|
533 | + """Return True if this is an unknown node.""" |
---|
534 | + |
---|
535 | + def is_allowed_in_immutable_directory(): |
---|
536 | + """Return True if this node is allowed as a child of a deep-immutable |
---|
537 | + directory. This is true if either the node is of a known-immutable type, |
---|
538 | + or it is unknown and read-only. |
---|
539 | + """ |
---|
540 | + |
---|
541 | + def raise_error(): |
---|
542 | + """Raise any error associated with this node.""" |
---|
543 | + |
---|
544 | def get_size(): |
---|
545 | """Return the length (in bytes) of the data this node represents. For |
---|
546 | directory nodes, I return the size of the backing store. I return |
---|
547 | @@ -902,7 +925,7 @@ |
---|
548 | ctime/mtime semantics of traditional filesystems. |
---|
549 | |
---|
550 | If this directory node is read-only, the Deferred will errback with a |
---|
551 | - NotMutableError.""" |
---|
552 | + NotWriteableError.""" |
---|
553 | |
---|
554 | def set_children(entries, overwrite=True): |
---|
555 | """Add multiple children (by writecap+readcap) to a directory node. |
---|
556 | @@ -928,7 +951,7 @@ |
---|
557 | ctime/mtime semantics of traditional filesystems. |
---|
558 | |
---|
559 | If this directory node is read-only, the Deferred will errback with a |
---|
560 | - NotMutableError.""" |
---|
561 | + NotWriteableError.""" |
---|
562 | |
---|
563 | def set_nodes(entries, overwrite=True): |
---|
564 | """Add multiple children to a directory node. Takes a dict mapping |
---|
565 | @@ -2074,7 +2097,7 @@ |
---|
566 | Tahoe process will typically have a single NodeMaker, but unit tests may |
---|
567 | create simplified/mocked forms for testing purposes. |
---|
568 | """ |
---|
569 | - def create_from_cap(writecap, readcap=None): |
---|
570 | + def create_from_cap(writecap, readcap=None, **kwargs): |
---|
571 | """I create an IFilesystemNode from the given writecap/readcap. I can |
---|
572 | only provide nodes for existing file/directory objects: use my other |
---|
573 | methods to create new objects. I return synchronously.""" |
---|
574 | diff -rN -u old-tahoe/src/allmydata/mutable/common.py new-tahoe/src/allmydata/mutable/common.py |
---|
575 | --- old-tahoe/src/allmydata/mutable/common.py 2010-01-23 12:59:08.999000000 +0000 |
---|
576 | +++ new-tahoe/src/allmydata/mutable/common.py 2010-01-23 12:59:11.412000000 +0000 |
---|
577 | @@ -8,7 +8,7 @@ |
---|
578 | # creation |
---|
579 | MODE_READ = "MODE_READ" |
---|
580 | |
---|
581 | -class NotMutableError(Exception): |
---|
582 | +class NotWriteableError(Exception): |
---|
583 | pass |
---|
584 | |
---|
585 | class NeedMoreDataError(Exception): |
---|
586 | diff -rN -u old-tahoe/src/allmydata/mutable/filenode.py new-tahoe/src/allmydata/mutable/filenode.py |
---|
587 | --- old-tahoe/src/allmydata/mutable/filenode.py 2010-01-23 12:59:09.004000000 +0000 |
---|
588 | +++ new-tahoe/src/allmydata/mutable/filenode.py 2010-01-23 12:59:11.416000000 +0000 |
---|
589 | @@ -214,6 +214,12 @@ |
---|
590 | |
---|
591 | def get_uri(self): |
---|
592 | return self._uri.to_string() |
---|
593 | + |
---|
594 | + def get_write_uri(self): |
---|
595 | + if self.is_readonly(): |
---|
596 | + return None |
---|
597 | + return self._uri.to_string() |
---|
598 | + |
---|
599 | def get_readonly_uri(self): |
---|
600 | return self._uri.get_readonly().to_string() |
---|
601 | |
---|
602 | @@ -227,9 +233,19 @@ |
---|
603 | |
---|
604 | def is_mutable(self): |
---|
605 | return self._uri.is_mutable() |
---|
606 | + |
---|
607 | def is_readonly(self): |
---|
608 | return self._uri.is_readonly() |
---|
609 | |
---|
610 | + def is_unknown(self): |
---|
611 | + return False |
---|
612 | + |
---|
613 | + def is_allowed_in_immutable_directory(self): |
---|
614 | + return not self._uri.is_mutable() |
---|
615 | + |
---|
616 | + def raise_error(self): |
---|
617 | + pass |
---|
618 | + |
---|
619 | def __hash__(self): |
---|
620 | return hash((self.__class__, self._uri)) |
---|
621 | def __cmp__(self, them): |
---|
622 | diff -rN -u old-tahoe/src/allmydata/nodemaker.py new-tahoe/src/allmydata/nodemaker.py |
---|
623 | --- old-tahoe/src/allmydata/nodemaker.py 2010-01-23 12:59:09.045000000 +0000 |
---|
624 | +++ new-tahoe/src/allmydata/nodemaker.py 2010-01-23 12:59:11.445000000 +0000 |
---|
625 | @@ -1,7 +1,7 @@ |
---|
626 | import weakref |
---|
627 | from zope.interface import implements |
---|
628 | from allmydata.util.assertutil import precondition |
---|
629 | -from allmydata.interfaces import INodeMaker, NotDeepImmutableError |
---|
630 | +from allmydata.interfaces import INodeMaker, MustBeDeepImmutableError |
---|
631 | from allmydata.immutable.filenode import ImmutableFileNode, LiteralFileNode |
---|
632 | from allmydata.immutable.upload import Data |
---|
633 | from allmydata.mutable.filenode import MutableFileNode |
---|
634 | @@ -44,28 +44,36 @@ |
---|
635 | def _create_dirnode(self, filenode): |
---|
636 | return DirectoryNode(filenode, self, self.uploader) |
---|
637 | |
---|
638 | - def create_from_cap(self, writecap, readcap=None): |
---|
639 | + def create_from_cap(self, writecap, readcap=None, deep_immutable=False, name=u"<unknown name>"): |
---|
640 | # this returns synchronously. It starts with a "cap string". |
---|
641 | assert isinstance(writecap, (str, type(None))), type(writecap) |
---|
642 | assert isinstance(readcap, (str, type(None))), type(readcap) |
---|
643 | + #import traceback |
---|
644 | + #traceback.print_stack() |
---|
645 | + #print '%r.create_from_cap(%r, %r, %r)' % (self, writecap, readcap, kwargs) |
---|
646 | + |
---|
647 | bigcap = writecap or readcap |
---|
648 | if not bigcap: |
---|
649 | # maybe the writecap was hidden because we're in a readonly |
---|
650 | # directory, and the future cap format doesn't have a readcap, or |
---|
651 | # something. |
---|
652 | - return UnknownNode(writecap, readcap) |
---|
653 | - if bigcap in self._node_cache: |
---|
654 | - return self._node_cache[bigcap] |
---|
655 | - cap = uri.from_string(bigcap) |
---|
656 | - node = self._create_from_cap(cap) |
---|
657 | + return UnknownNode(None, None) # deep_immutable and name not needed |
---|
658 | + |
---|
659 | + # The name doesn't matter for caching since it's only used in the error |
---|
660 | + # attribute of an UnknownNode, and we don't cache those. |
---|
661 | + memokey = ("I" if deep_immutable else "M") + bigcap |
---|
662 | + if memokey in self._node_cache: |
---|
663 | + return self._node_cache[memokey] |
---|
664 | + cap = uri.from_string(bigcap, deep_immutable=deep_immutable, name=name) |
---|
665 | + node = self._create_from_single_cap(cap) |
---|
666 | if node: |
---|
667 | - self._node_cache[bigcap] = node # note: WeakValueDictionary |
---|
668 | + self._node_cache[memokey] = node # note: WeakValueDictionary |
---|
669 | else: |
---|
670 | - node = UnknownNode(writecap, readcap) # don't cache UnknownNode |
---|
671 | + # don't cache UnknownNode |
---|
672 | + node = UnknownNode(writecap, readcap, deep_immutable=deep_immutable, name=name) |
---|
673 | return node |
---|
674 | |
---|
675 | - def _create_from_cap(self, cap): |
---|
676 | - # This starts with a "cap instance" |
---|
677 | + def _create_from_single_cap(self, cap): |
---|
678 | if isinstance(cap, uri.LiteralFileURI): |
---|
679 | return self._create_lit(cap) |
---|
680 | if isinstance(cap, uri.CHKFileURI): |
---|
681 | @@ -76,7 +84,7 @@ |
---|
682 | uri.ReadonlyDirectoryURI, |
---|
683 | uri.ImmutableDirectoryURI, |
---|
684 | uri.LiteralDirectoryURI)): |
---|
685 | - filenode = self._create_from_cap(cap.get_filenode_cap()) |
---|
686 | + filenode = self._create_from_single_cap(cap.get_filenode_cap()) |
---|
687 | return self._create_dirnode(filenode) |
---|
688 | return None |
---|
689 | |
---|
690 | @@ -89,13 +97,11 @@ |
---|
691 | return d |
---|
692 | |
---|
693 | def create_new_mutable_directory(self, initial_children={}): |
---|
694 | - # initial_children must have metadata (i.e. {} instead of None), and |
---|
695 | - # should not contain UnknownNodes |
---|
696 | + # initial_children must have metadata (i.e. {} instead of None) |
---|
697 | for (name, (node, metadata)) in initial_children.iteritems(): |
---|
698 | - precondition(not isinstance(node, UnknownNode), |
---|
699 | - "create_new_mutable_directory does not accept UnknownNode", node) |
---|
700 | precondition(isinstance(metadata, dict), |
---|
701 | "create_new_mutable_directory requires metadata to be a dict, not None", metadata) |
---|
702 | + node.raise_error() |
---|
703 | d = self.create_mutable_file(lambda n: |
---|
704 | pack_children(n, initial_children)) |
---|
705 | d.addCallback(self._create_dirnode) |
---|
706 | @@ -105,19 +111,15 @@ |
---|
707 | if convergence is None: |
---|
708 | convergence = self.secret_holder.get_convergence_secret() |
---|
709 | for (name, (node, metadata)) in children.iteritems(): |
---|
710 | - precondition(not isinstance(node, UnknownNode), |
---|
711 | - "create_immutable_directory does not accept UnknownNode", node) |
---|
712 | precondition(isinstance(metadata, dict), |
---|
713 | "create_immutable_directory requires metadata to be a dict, not None", metadata) |
---|
714 | - if node.is_mutable(): |
---|
715 | - raise NotDeepImmutableError("%s is not immutable" % (node,)) |
---|
716 | + node.raise_error() |
---|
717 | + if not node.is_allowed_in_immutable_directory(): |
---|
718 | + raise MustBeDeepImmutableError("%s is not immutable" % (node,), name) |
---|
719 | n = DummyImmutableFileNode() # writekey=None |
---|
720 | packed = pack_children(n, children) |
---|
721 | uploadable = Data(packed, convergence) |
---|
722 | d = self.uploader.upload(uploadable, history=self.history) |
---|
723 | - def _uploaded(results): |
---|
724 | - filecap = self.create_from_cap(results.uri) |
---|
725 | - return filecap |
---|
726 | - d.addCallback(_uploaded) |
---|
727 | + d.addCallback(lambda results: self.create_from_cap(None, results.uri)) |
---|
728 | d.addCallback(self._create_dirnode) |
---|
729 | return d |
---|
730 | diff -rN -u old-tahoe/src/allmydata/scripts/common.py new-tahoe/src/allmydata/scripts/common.py |
---|
731 | --- old-tahoe/src/allmydata/scripts/common.py 2010-01-23 12:59:09.089000000 +0000 |
---|
732 | +++ new-tahoe/src/allmydata/scripts/common.py 2010-01-23 12:59:11.483000000 +0000 |
---|
733 | @@ -128,12 +128,14 @@ |
---|
734 | pass |
---|
735 | |
---|
736 | def get_alias(aliases, path, default): |
---|
737 | + from allmydata import uri |
---|
738 | # transform "work:path/filename" into (aliases["work"], "path/filename"). |
---|
739 | # If default=None, then an empty alias is indicated by returning |
---|
740 | - # DefaultAliasMarker. We special-case "URI:" to make it easy to access |
---|
741 | - # specific files/directories by their read-cap. |
---|
742 | + # DefaultAliasMarker. We special-case strings with a recognized cap URI |
---|
743 | + # prefix, to make it easy to access specific files/directories by their |
---|
744 | + # caps. |
---|
745 | path = path.strip() |
---|
746 | - if path.startswith("URI:"): |
---|
747 | + if uri.has_uri_prefix(path): |
---|
748 | # The only way to get a sub-path is to use URI:blah:./foo, and we |
---|
749 | # strip out the :./ sequence. |
---|
750 | sep = path.find(":./") |
---|
751 | diff -rN -u old-tahoe/src/allmydata/scripts/tahoe_cp.py new-tahoe/src/allmydata/scripts/tahoe_cp.py |
---|
752 | --- old-tahoe/src/allmydata/scripts/tahoe_cp.py 2010-01-23 12:59:09.170000000 +0000 |
---|
753 | +++ new-tahoe/src/allmydata/scripts/tahoe_cp.py 2010-01-23 12:59:11.536000000 +0000 |
---|
754 | @@ -258,8 +258,7 @@ |
---|
755 | readcap = ascii_or_none(data[1].get("ro_uri")) |
---|
756 | self.children[name] = TahoeFileSource(self.nodeurl, mutable, |
---|
757 | writecap, readcap) |
---|
758 | - else: |
---|
759 | - assert data[0] == "dirnode" |
---|
760 | + elif data[0] == "dirnode": |
---|
761 | writecap = ascii_or_none(data[1].get("rw_uri")) |
---|
762 | readcap = ascii_or_none(data[1].get("ro_uri")) |
---|
763 | if writecap and writecap in self.cache: |
---|
764 | @@ -277,6 +276,11 @@ |
---|
765 | if recurse: |
---|
766 | child.populate(True) |
---|
767 | self.children[name] = child |
---|
768 | + else: |
---|
769 | + # TODO: there should be an option to skip unknown nodes. |
---|
770 | + raise TahoeError("Cannot copy unknown nodes (ticket #839). " |
---|
771 | + "You probably need to use a later version of " |
---|
772 | + "Tahoe-LAFS to copy this directory.") |
---|
773 | |
---|
774 | class TahoeMissingTarget: |
---|
775 | def __init__(self, url): |
---|
776 | @@ -353,8 +357,7 @@ |
---|
777 | urllib.quote(name.encode('utf-8'))]) |
---|
778 | self.children[name] = TahoeFileTarget(self.nodeurl, mutable, |
---|
779 | writecap, readcap, url) |
---|
780 | - else: |
---|
781 | - assert data[0] == "dirnode" |
---|
782 | + elif data[0] == "dirnode": |
---|
783 | writecap = ascii_or_none(data[1].get("rw_uri")) |
---|
784 | readcap = ascii_or_none(data[1].get("ro_uri")) |
---|
785 | if writecap and writecap in self.cache: |
---|
786 | @@ -372,6 +375,11 @@ |
---|
787 | if recurse: |
---|
788 | child.populate(True) |
---|
789 | self.children[name] = child |
---|
790 | + else: |
---|
791 | + # TODO: there should be an option to skip unknown nodes. |
---|
792 | + raise TahoeError("Cannot copy unknown nodes (ticket #839). " |
---|
793 | + "You probably need to use a later version of " |
---|
794 | + "Tahoe-LAFS to copy this directory.") |
---|
795 | |
---|
796 | def get_child_target(self, name): |
---|
797 | # return a new target for a named subdirectory of this dir |
---|
798 | @@ -407,9 +415,11 @@ |
---|
799 | set_data = {} |
---|
800 | for (name, filecap) in self.new_children.items(): |
---|
801 | # it just so happens that ?t=set_children will accept both file |
---|
802 | - # read-caps and write-caps as ['rw_uri'], and will handle eithe |
---|
803 | + # read-caps and write-caps as ['rw_uri'], and will handle either |
---|
804 | # correctly. So don't bother trying to figure out whether the one |
---|
805 | # we have is read-only or read-write. |
---|
806 | + # TODO: think about how this affects forward-compatibility for |
---|
807 | + # unknown caps |
---|
808 | set_data[name] = ["filenode", {"rw_uri": filecap}] |
---|
809 | body = simplejson.dumps(set_data) |
---|
810 | POST(url, body) |
---|
811 | @@ -770,6 +780,7 @@ |
---|
812 | # local-file-in-the-way |
---|
813 | # touch proposed |
---|
814 | # tahoe cp -r my:docs/proposed/denver.txt proposed/denver.txt |
---|
815 | +# handling of unknown nodes |
---|
816 | |
---|
817 | # things that maybe should be errors but aren't |
---|
818 | # local-dir-in-the-way |
---|
819 | diff -rN -u old-tahoe/src/allmydata/scripts/tahoe_put.py new-tahoe/src/allmydata/scripts/tahoe_put.py |
---|
820 | --- old-tahoe/src/allmydata/scripts/tahoe_put.py 2010-01-23 12:59:09.198000000 +0000 |
---|
821 | +++ new-tahoe/src/allmydata/scripts/tahoe_put.py 2010-01-23 12:59:11.557000000 +0000 |
---|
822 | @@ -40,6 +40,7 @@ |
---|
823 | # DIRCAP:./subdir/foo : DIRCAP/subdir/foo |
---|
824 | # MUTABLE-FILE-WRITECAP : filecap |
---|
825 | |
---|
826 | + # FIXME: this shouldn't rely on a particular prefix. |
---|
827 | if to_file.startswith("URI:SSK:"): |
---|
828 | url = nodeurl + "uri/%s" % urllib.quote(to_file) |
---|
829 | else: |
---|
830 | diff -rN -u old-tahoe/src/allmydata/test/common.py new-tahoe/src/allmydata/test/common.py |
---|
831 | --- old-tahoe/src/allmydata/test/common.py 2010-01-23 12:59:09.443000000 +0000 |
---|
832 | +++ new-tahoe/src/allmydata/test/common.py 2010-01-23 12:59:11.729000000 +0000 |
---|
833 | @@ -51,6 +51,8 @@ |
---|
834 | |
---|
835 | def get_uri(self): |
---|
836 | return self.my_uri.to_string() |
---|
837 | + def get_write_uri(self): |
---|
838 | + return None |
---|
839 | def get_readonly_uri(self): |
---|
840 | return self.my_uri.to_string() |
---|
841 | def get_cap(self): |
---|
842 | @@ -103,6 +105,12 @@ |
---|
843 | return False |
---|
844 | def is_readonly(self): |
---|
845 | return True |
---|
846 | + def is_unknown(self): |
---|
847 | + return False |
---|
848 | + def is_allowed_in_immutable_directory(self): |
---|
849 | + return True |
---|
850 | + def raise_error(self): |
---|
851 | + pass |
---|
852 | |
---|
853 | def get_size(self): |
---|
854 | try: |
---|
855 | @@ -190,6 +198,10 @@ |
---|
856 | return self.my_uri.get_readonly() |
---|
857 | def get_uri(self): |
---|
858 | return self.my_uri.to_string() |
---|
859 | + def get_write_uri(self): |
---|
860 | + if self.is_readonly(): |
---|
861 | + return None |
---|
862 | + return self.my_uri.to_string() |
---|
863 | def get_readonly(self): |
---|
864 | return self.my_uri.get_readonly() |
---|
865 | def get_readonly_uri(self): |
---|
866 | @@ -200,6 +212,12 @@ |
---|
867 | return self.my_uri.is_readonly() |
---|
868 | def is_mutable(self): |
---|
869 | return self.my_uri.is_mutable() |
---|
870 | + def is_unknown(self): |
---|
871 | + return False |
---|
872 | + def is_allowed_in_immutable_directory(self): |
---|
873 | + return not self.my_uri.is_mutable() |
---|
874 | + def raise_error(self): |
---|
875 | + pass |
---|
876 | def get_writekey(self): |
---|
877 | return "\x00"*16 |
---|
878 | def get_size(self): |
---|
879 | diff -rN -u old-tahoe/src/allmydata/test/test_client.py new-tahoe/src/allmydata/test/test_client.py |
---|
880 | --- old-tahoe/src/allmydata/test/test_client.py 2010-01-23 12:59:09.713000000 +0000 |
---|
881 | +++ new-tahoe/src/allmydata/test/test_client.py 2010-01-23 12:59:11.853000000 +0000 |
---|
882 | @@ -288,11 +288,14 @@ |
---|
883 | self.failUnless(n.is_readonly()) |
---|
884 | self.failUnless(n.is_mutable()) |
---|
885 | |
---|
886 | - future = "x-tahoe-crazy://future_cap_format." |
---|
887 | - n = c.create_node_from_uri(future) |
---|
888 | + unknown_rw = "lafs://from_the_future" |
---|
889 | + unknown_ro = "lafs://readonly_from_the_future" |
---|
890 | + n = c.create_node_from_uri(unknown_rw, unknown_ro) |
---|
891 | self.failUnless(IFilesystemNode.providedBy(n)) |
---|
892 | self.failIf(IFileNode.providedBy(n)) |
---|
893 | self.failIf(IImmutableFileNode.providedBy(n)) |
---|
894 | self.failIf(IMutableFileNode.providedBy(n)) |
---|
895 | self.failIf(IDirectoryNode.providedBy(n)) |
---|
896 | - self.failUnlessEqual(n.get_uri(), future) |
---|
897 | + self.failUnless(n.is_unknown()) |
---|
898 | + self.failUnlessEqual(n.get_uri(), unknown_rw) |
---|
899 | + self.failUnlessEqual(n.get_readonly_uri(), "ro." + unknown_ro) |
---|
900 | diff -rN -u old-tahoe/src/allmydata/test/test_dirnode.py new-tahoe/src/allmydata/test/test_dirnode.py |
---|
901 | --- old-tahoe/src/allmydata/test/test_dirnode.py 2010-01-23 12:59:09.774000000 +0000 |
---|
902 | +++ new-tahoe/src/allmydata/test/test_dirnode.py 2010-01-23 12:59:11.898000000 +0000 |
---|
903 | @@ -7,8 +7,8 @@ |
---|
904 | from allmydata.client import Client |
---|
905 | from allmydata.immutable import upload |
---|
906 | from allmydata.interfaces import IImmutableFileNode, IMutableFileNode, \ |
---|
907 | - ExistingChildError, NoSuchChildError, NotDeepImmutableError, \ |
---|
908 | - IDeepCheckResults, IDeepCheckAndRepairResults, CannotPackUnknownNodeError |
---|
909 | + ExistingChildError, NoSuchChildError, MustBeDeepImmutableError, \ |
---|
910 | + IDeepCheckResults, IDeepCheckAndRepairResults, MustNotBeUnknownRWError |
---|
911 | from allmydata.mutable.filenode import MutableFileNode |
---|
912 | from allmydata.mutable.common import UncoordinatedWriteError |
---|
913 | from allmydata.util import hashutil, base32 |
---|
914 | @@ -32,6 +32,11 @@ |
---|
915 | d = c.create_dirnode() |
---|
916 | def _done(res): |
---|
917 | self.failUnless(isinstance(res, dirnode.DirectoryNode)) |
---|
918 | + self.failUnless(res.is_mutable()) |
---|
919 | + self.failIf(res.is_readonly()) |
---|
920 | + self.failIf(res.is_unknown()) |
---|
921 | + self.failIf(res.is_allowed_in_immutable_directory()) |
---|
922 | + res.raise_error() |
---|
923 | rep = str(res) |
---|
924 | self.failUnless("RW-MUT" in rep) |
---|
925 | d.addCallback(_done) |
---|
926 | @@ -44,36 +49,74 @@ |
---|
927 | nm = c.nodemaker |
---|
928 | setup_py_uri = "URI:CHK:n7r3m6wmomelk4sep3kw5cvduq:os7ijw5c3maek7pg65e5254k2fzjflavtpejjyhshpsxuqzhcwwq:3:20:14861" |
---|
929 | one_uri = "URI:LIT:n5xgk" # LIT for "one" |
---|
930 | + mut_write_uri = "URI:SSK:vfvcbdfbszyrsaxchgevhmmlii:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq" |
---|
931 | + mut_read_uri = "URI:SSK-RO:jf6wkflosyvntwxqcdo7a54jvm:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq" |
---|
932 | + future_write_uri = "x-tahoe-crazy://I_am_from_the_future." |
---|
933 | + future_read_uri = "x-tahoe-crazy-readonly://I_am_from_the_future." |
---|
934 | kids = {u"one": (nm.create_from_cap(one_uri), {}), |
---|
935 | u"two": (nm.create_from_cap(setup_py_uri), |
---|
936 | {"metakey": "metavalue"}), |
---|
937 | + u"mut": (nm.create_from_cap(mut_write_uri, mut_read_uri), {}), |
---|
938 | + u"fut": (nm.create_from_cap(future_write_uri, future_read_uri), {}), |
---|
939 | + u"fro": (nm.create_from_cap(None, future_read_uri), {}), |
---|
940 | } |
---|
941 | d = c.create_dirnode(kids) |
---|
942 | + |
---|
943 | def _created(dn): |
---|
944 | self.failUnless(isinstance(dn, dirnode.DirectoryNode)) |
---|
945 | + self.failUnless(dn.is_mutable()) |
---|
946 | + self.failIf(dn.is_readonly()) |
---|
947 | + self.failIf(dn.is_unknown()) |
---|
948 | + self.failIf(dn.is_allowed_in_immutable_directory()) |
---|
949 | + dn.raise_error() |
---|
950 | rep = str(dn) |
---|
951 | self.failUnless("RW-MUT" in rep) |
---|
952 | return dn.list() |
---|
953 | d.addCallback(_created) |
---|
954 | + |
---|
955 | def _check_kids(children): |
---|
956 | - self.failUnlessEqual(sorted(children.keys()), [u"one", u"two"]) |
---|
957 | + self.failUnlessEqual(sorted(children.keys()), |
---|
958 | + [u"fro", u"fut", u"mut", u"one", u"two"]) |
---|
959 | one_node, one_metadata = children[u"one"] |
---|
960 | two_node, two_metadata = children[u"two"] |
---|
961 | + mut_node, mut_metadata = children[u"mut"] |
---|
962 | + fut_node, fut_metadata = children[u"fut"] |
---|
963 | + fro_node, fro_metadata = children[u"fro"] |
---|
964 | + |
---|
965 | self.failUnlessEqual(one_node.get_size(), 3) |
---|
966 | - self.failUnlessEqual(two_node.get_size(), 14861) |
---|
967 | + self.failUnlessEqual(one_node.get_uri(), one_uri) |
---|
968 | + self.failUnlessEqual(one_node.get_readonly_uri(), one_uri) |
---|
969 | self.failUnless(isinstance(one_metadata, dict), one_metadata) |
---|
970 | + |
---|
971 | + self.failUnlessEqual(two_node.get_size(), 14861) |
---|
972 | + self.failUnlessEqual(two_node.get_uri(), setup_py_uri) |
---|
973 | + self.failUnlessEqual(two_node.get_readonly_uri(), setup_py_uri) |
---|
974 | self.failUnlessEqual(two_metadata["metakey"], "metavalue") |
---|
975 | + |
---|
976 | + self.failUnlessEqual(mut_node.get_uri(), mut_write_uri) |
---|
977 | + self.failUnlessEqual(mut_node.get_readonly_uri(), mut_read_uri) |
---|
978 | + self.failUnless(isinstance(mut_metadata, dict), mut_metadata) |
---|
979 | + |
---|
980 | + self.failUnless(fut_node.is_unknown()) |
---|
981 | + self.failUnlessEqual(fut_node.get_uri(), future_write_uri) |
---|
982 | + self.failUnlessEqual(fut_node.get_readonly_uri(), "ro." + future_read_uri) |
---|
983 | + self.failUnless(isinstance(fut_metadata, dict), fut_metadata) |
---|
984 | + |
---|
985 | + self.failUnless(fro_node.is_unknown()) |
---|
986 | + self.failUnlessEqual(fro_node.get_uri(), "ro." + future_read_uri) |
---|
987 | + self.failUnlessEqual(fut_node.get_readonly_uri(), "ro." + future_read_uri) |
---|
988 | + self.failUnless(isinstance(fro_metadata, dict), fro_metadata) |
---|
989 | d.addCallback(_check_kids) |
---|
990 | + |
---|
991 | d.addCallback(lambda ign: nm.create_new_mutable_directory(kids)) |
---|
992 | d.addCallback(lambda dn: dn.list()) |
---|
993 | d.addCallback(_check_kids) |
---|
994 | - future_writecap = "x-tahoe-crazy://I_am_from_the_future." |
---|
995 | - future_readcap = "x-tahoe-crazy-readonly://I_am_from_the_future." |
---|
996 | - future_node = UnknownNode(future_writecap, future_readcap) |
---|
997 | - bad_kids1 = {u"one": (future_node, {})} |
---|
998 | + |
---|
999 | + bad_future_node = UnknownNode(future_write_uri, None) |
---|
1000 | + bad_kids1 = {u"one": (bad_future_node, {})} |
---|
1001 | d.addCallback(lambda ign: |
---|
1002 | - self.shouldFail(AssertionError, "bad_kids1", |
---|
1003 | - "does not accept UnknownNode", |
---|
1004 | + self.shouldFail(MustNotBeUnknownRWError, "bad_kids1", |
---|
1005 | + "cannot attach unknown", |
---|
1006 | nm.create_new_mutable_directory, |
---|
1007 | bad_kids1)) |
---|
1008 | bad_kids2 = {u"one": (nm.create_from_cap(one_uri), None)} |
---|
1009 | @@ -91,17 +134,24 @@ |
---|
1010 | nm = c.nodemaker |
---|
1011 | setup_py_uri = "URI:CHK:n7r3m6wmomelk4sep3kw5cvduq:os7ijw5c3maek7pg65e5254k2fzjflavtpejjyhshpsxuqzhcwwq:3:20:14861" |
---|
1012 | one_uri = "URI:LIT:n5xgk" # LIT for "one" |
---|
1013 | - mut_readcap = "URI:SSK-RO:e3mdrzfwhoq42hy5ubcz6rp3o4:ybyibhnp3vvwuq2vaw2ckjmesgkklfs6ghxleztqidihjyofgw7q" |
---|
1014 | - mut_writecap = "URI:SSK:vfvcbdfbszyrsaxchgevhmmlii:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq" |
---|
1015 | + mut_write_uri = "URI:SSK:vfvcbdfbszyrsaxchgevhmmlii:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq" |
---|
1016 | + mut_read_uri = "URI:SSK-RO:e3mdrzfwhoq42hy5ubcz6rp3o4:ybyibhnp3vvwuq2vaw2ckjmesgkklfs6ghxleztqidihjyofgw7q" |
---|
1017 | + future_write_uri = "x-tahoe-crazy://I_am_from_the_future." |
---|
1018 | + future_read_uri = "x-tahoe-crazy-readonly://I_am_from_the_future." |
---|
1019 | kids = {u"one": (nm.create_from_cap(one_uri), {}), |
---|
1020 | u"two": (nm.create_from_cap(setup_py_uri), |
---|
1021 | {"metakey": "metavalue"}), |
---|
1022 | + u"fut": (nm.create_from_cap(None, future_read_uri), {}), |
---|
1023 | } |
---|
1024 | d = c.create_immutable_dirnode(kids) |
---|
1025 | + |
---|
1026 | def _created(dn): |
---|
1027 | self.failUnless(isinstance(dn, dirnode.DirectoryNode)) |
---|
1028 | self.failIf(dn.is_mutable()) |
---|
1029 | self.failUnless(dn.is_readonly()) |
---|
1030 | + self.failIf(dn.is_unknown()) |
---|
1031 | + self.failUnless(dn.is_allowed_in_immutable_directory()) |
---|
1032 | + dn.raise_error() |
---|
1033 | rep = str(dn) |
---|
1034 | self.failUnless("RO-IMM" in rep) |
---|
1035 | cap = dn.get_cap() |
---|
1036 | @@ -109,50 +159,73 @@ |
---|
1037 | self.cap = cap |
---|
1038 | return dn.list() |
---|
1039 | d.addCallback(_created) |
---|
1040 | + |
---|
1041 | def _check_kids(children): |
---|
1042 | - self.failUnlessEqual(sorted(children.keys()), [u"one", u"two"]) |
---|
1043 | + self.failUnlessEqual(sorted(children.keys()), [u"fut", u"one", u"two"]) |
---|
1044 | one_node, one_metadata = children[u"one"] |
---|
1045 | two_node, two_metadata = children[u"two"] |
---|
1046 | + fut_node, fut_metadata = children[u"fut"] |
---|
1047 | + |
---|
1048 | self.failUnlessEqual(one_node.get_size(), 3) |
---|
1049 | - self.failUnlessEqual(two_node.get_size(), 14861) |
---|
1050 | + self.failUnlessEqual(one_node.get_uri(), one_uri) |
---|
1051 | + self.failUnlessEqual(one_node.get_readonly_uri(), one_uri) |
---|
1052 | self.failUnless(isinstance(one_metadata, dict), one_metadata) |
---|
1053 | + |
---|
1054 | + self.failUnlessEqual(two_node.get_size(), 14861) |
---|
1055 | + self.failUnlessEqual(two_node.get_uri(), setup_py_uri) |
---|
1056 | + self.failUnlessEqual(two_node.get_readonly_uri(), setup_py_uri) |
---|
1057 | self.failUnlessEqual(two_metadata["metakey"], "metavalue") |
---|
1058 | + |
---|
1059 | + self.failUnless(fut_node.is_unknown()) |
---|
1060 | + self.failUnlessEqual(fut_node.get_uri(), "imm." + future_read_uri) |
---|
1061 | + self.failUnlessEqual(fut_node.get_readonly_uri(), "imm." + future_read_uri) |
---|
1062 | + self.failUnless(isinstance(fut_metadata, dict), fut_metadata) |
---|
1063 | d.addCallback(_check_kids) |
---|
1064 | + |
---|
1065 | d.addCallback(lambda ign: nm.create_from_cap(self.cap.to_string())) |
---|
1066 | d.addCallback(lambda dn: dn.list()) |
---|
1067 | d.addCallback(_check_kids) |
---|
1068 | - future_writecap = "x-tahoe-crazy://I_am_from_the_future." |
---|
1069 | - future_readcap = "x-tahoe-crazy-readonly://I_am_from_the_future." |
---|
1070 | - future_node = UnknownNode(future_writecap, future_readcap) |
---|
1071 | - bad_kids1 = {u"one": (future_node, {})} |
---|
1072 | + |
---|
1073 | + bad_future_node1 = UnknownNode(future_write_uri, None) |
---|
1074 | + bad_kids1 = {u"one": (bad_future_node1, {})} |
---|
1075 | d.addCallback(lambda ign: |
---|
1076 | - self.shouldFail(AssertionError, "bad_kids1", |
---|
1077 | - "does not accept UnknownNode", |
---|
1078 | + self.shouldFail(MustNotBeUnknownRWError, "bad_kids1", |
---|
1079 | + "cannot attach unknown", |
---|
1080 | c.create_immutable_dirnode, |
---|
1081 | bad_kids1)) |
---|
1082 | - bad_kids2 = {u"one": (nm.create_from_cap(one_uri), None)} |
---|
1083 | + bad_future_node2 = UnknownNode(future_write_uri, future_read_uri) |
---|
1084 | + bad_kids2 = {u"one": (bad_future_node2, {})} |
---|
1085 | d.addCallback(lambda ign: |
---|
1086 | - self.shouldFail(AssertionError, "bad_kids2", |
---|
1087 | - "requires metadata to be a dict", |
---|
1088 | + self.shouldFail(MustBeDeepImmutableError, "bad_kids2", |
---|
1089 | + "is not immutable", |
---|
1090 | c.create_immutable_dirnode, |
---|
1091 | bad_kids2)) |
---|
1092 | - bad_kids3 = {u"one": (nm.create_from_cap(mut_writecap), {})} |
---|
1093 | + bad_kids3 = {u"one": (nm.create_from_cap(one_uri), None)} |
---|
1094 | d.addCallback(lambda ign: |
---|
1095 | - self.shouldFail(NotDeepImmutableError, "bad_kids3", |
---|
1096 | - "is not immutable", |
---|
1097 | + self.shouldFail(AssertionError, "bad_kids3", |
---|
1098 | + "requires metadata to be a dict", |
---|
1099 | c.create_immutable_dirnode, |
---|
1100 | bad_kids3)) |
---|
1101 | - bad_kids4 = {u"one": (nm.create_from_cap(mut_readcap), {})} |
---|
1102 | + bad_kids4 = {u"one": (nm.create_from_cap(mut_write_uri), {})} |
---|
1103 | d.addCallback(lambda ign: |
---|
1104 | - self.shouldFail(NotDeepImmutableError, "bad_kids4", |
---|
1105 | + self.shouldFail(MustBeDeepImmutableError, "bad_kids4", |
---|
1106 | "is not immutable", |
---|
1107 | c.create_immutable_dirnode, |
---|
1108 | bad_kids4)) |
---|
1109 | + bad_kids5 = {u"one": (nm.create_from_cap(mut_read_uri), {})} |
---|
1110 | + d.addCallback(lambda ign: |
---|
1111 | + self.shouldFail(MustBeDeepImmutableError, "bad_kids5", |
---|
1112 | + "is not immutable", |
---|
1113 | + c.create_immutable_dirnode, |
---|
1114 | + bad_kids5)) |
---|
1115 | d.addCallback(lambda ign: c.create_immutable_dirnode({})) |
---|
1116 | def _created_empty(dn): |
---|
1117 | self.failUnless(isinstance(dn, dirnode.DirectoryNode)) |
---|
1118 | self.failIf(dn.is_mutable()) |
---|
1119 | self.failUnless(dn.is_readonly()) |
---|
1120 | + self.failIf(dn.is_unknown()) |
---|
1121 | + self.failUnless(dn.is_allowed_in_immutable_directory()) |
---|
1122 | + dn.raise_error() |
---|
1123 | rep = str(dn) |
---|
1124 | self.failUnless("RO-IMM" in rep) |
---|
1125 | cap = dn.get_cap() |
---|
1126 | @@ -168,6 +241,9 @@ |
---|
1127 | self.failUnless(isinstance(dn, dirnode.DirectoryNode)) |
---|
1128 | self.failIf(dn.is_mutable()) |
---|
1129 | self.failUnless(dn.is_readonly()) |
---|
1130 | + self.failIf(dn.is_unknown()) |
---|
1131 | + self.failUnless(dn.is_allowed_in_immutable_directory()) |
---|
1132 | + dn.raise_error() |
---|
1133 | rep = str(dn) |
---|
1134 | self.failUnless("RO-IMM" in rep) |
---|
1135 | cap = dn.get_cap() |
---|
1136 | @@ -193,9 +269,9 @@ |
---|
1137 | d.addCallback(_check_kids) |
---|
1138 | d.addCallback(lambda ign: n.get(u"subdir")) |
---|
1139 | d.addCallback(lambda sd: self.failIf(sd.is_mutable())) |
---|
1140 | - bad_kids = {u"one": (nm.create_from_cap(mut_writecap), {})} |
---|
1141 | + bad_kids = {u"one": (nm.create_from_cap(mut_write_uri), {})} |
---|
1142 | d.addCallback(lambda ign: |
---|
1143 | - self.shouldFail(NotDeepImmutableError, "YZ", |
---|
1144 | + self.shouldFail(MustBeDeepImmutableError, "YZ", |
---|
1145 | "is not immutable", |
---|
1146 | n.create_subdirectory, |
---|
1147 | u"sub2", bad_kids, mutable=False)) |
---|
1148 | @@ -203,7 +279,6 @@ |
---|
1149 | d.addCallback(_made_parent) |
---|
1150 | return d |
---|
1151 | |
---|
1152 | - |
---|
1153 | def test_check(self): |
---|
1154 | self.basedir = "dirnode/Dirnode/test_check" |
---|
1155 | self.set_up_grid() |
---|
1156 | @@ -337,24 +412,27 @@ |
---|
1157 | ro_dn = c.create_node_from_uri(ro_uri) |
---|
1158 | self.failUnless(ro_dn.is_readonly()) |
---|
1159 | self.failUnless(ro_dn.is_mutable()) |
---|
1160 | + self.failIf(ro_dn.is_unknown()) |
---|
1161 | + self.failIf(ro_dn.is_allowed_in_immutable_directory()) |
---|
1162 | + ro_dn.raise_error() |
---|
1163 | |
---|
1164 | - self.shouldFail(dirnode.NotMutableError, "set_uri ro", None, |
---|
1165 | + self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None, |
---|
1166 | ro_dn.set_uri, u"newchild", filecap, filecap) |
---|
1167 | - self.shouldFail(dirnode.NotMutableError, "set_uri ro", None, |
---|
1168 | + self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None, |
---|
1169 | ro_dn.set_node, u"newchild", filenode) |
---|
1170 | - self.shouldFail(dirnode.NotMutableError, "set_nodes ro", None, |
---|
1171 | + self.shouldFail(dirnode.NotWriteableError, "set_nodes ro", None, |
---|
1172 | ro_dn.set_nodes, { u"newchild": (filenode, None) }) |
---|
1173 | - self.shouldFail(dirnode.NotMutableError, "set_uri ro", None, |
---|
1174 | + self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None, |
---|
1175 | ro_dn.add_file, u"newchild", uploadable) |
---|
1176 | - self.shouldFail(dirnode.NotMutableError, "set_uri ro", None, |
---|
1177 | + self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None, |
---|
1178 | ro_dn.delete, u"child") |
---|
1179 | - self.shouldFail(dirnode.NotMutableError, "set_uri ro", None, |
---|
1180 | + self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None, |
---|
1181 | ro_dn.create_subdirectory, u"newchild") |
---|
1182 | - self.shouldFail(dirnode.NotMutableError, "set_metadata_for ro", None, |
---|
1183 | + self.shouldFail(dirnode.NotWriteableError, "set_metadata_for ro", None, |
---|
1184 | ro_dn.set_metadata_for, u"child", {}) |
---|
1185 | - self.shouldFail(dirnode.NotMutableError, "set_uri ro", None, |
---|
1186 | + self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None, |
---|
1187 | ro_dn.move_child_to, u"child", rw_dn) |
---|
1188 | - self.shouldFail(dirnode.NotMutableError, "set_uri ro", None, |
---|
1189 | + self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None, |
---|
1190 | rw_dn.move_child_to, u"child", ro_dn) |
---|
1191 | return ro_dn.list() |
---|
1192 | d.addCallback(_ready) |
---|
1193 | @@ -901,8 +979,8 @@ |
---|
1194 | nodemaker = NodeMaker(None, None, None, |
---|
1195 | None, None, None, |
---|
1196 | {"k": 3, "n": 10}, None) |
---|
1197 | - writecap = "URI:SSK-RO:e3mdrzfwhoq42hy5ubcz6rp3o4:ybyibhnp3vvwuq2vaw2ckjmesgkklfs6ghxleztqidihjyofgw7q" |
---|
1198 | - filenode = nodemaker.create_from_cap(writecap) |
---|
1199 | + write_uri = "URI:SSK-RO:e3mdrzfwhoq42hy5ubcz6rp3o4:ybyibhnp3vvwuq2vaw2ckjmesgkklfs6ghxleztqidihjyofgw7q" |
---|
1200 | + filenode = nodemaker.create_from_cap(write_uri) |
---|
1201 | node = dirnode.DirectoryNode(filenode, nodemaker, None) |
---|
1202 | children = node._unpack_contents(known_tree) |
---|
1203 | self._check_children(children) |
---|
1204 | @@ -975,23 +1053,23 @@ |
---|
1205 | self.failUnlessIn("lit", packed) |
---|
1206 | |
---|
1207 | kids = self._make_kids(nm, ["imm", "lit", "write"]) |
---|
1208 | - self.failUnlessRaises(dirnode.MustBeDeepImmutable, |
---|
1209 | + self.failUnlessRaises(dirnode.MustBeDeepImmutableError, |
---|
1210 | dirnode.pack_children, |
---|
1211 | fn, kids, deep_immutable=True) |
---|
1212 | |
---|
1213 | # read-only is not enough: all children must be immutable |
---|
1214 | kids = self._make_kids(nm, ["imm", "lit", "read"]) |
---|
1215 | - self.failUnlessRaises(dirnode.MustBeDeepImmutable, |
---|
1216 | + self.failUnlessRaises(dirnode.MustBeDeepImmutableError, |
---|
1217 | dirnode.pack_children, |
---|
1218 | fn, kids, deep_immutable=True) |
---|
1219 | |
---|
1220 | kids = self._make_kids(nm, ["imm", "lit", "dirwrite"]) |
---|
1221 | - self.failUnlessRaises(dirnode.MustBeDeepImmutable, |
---|
1222 | + self.failUnlessRaises(dirnode.MustBeDeepImmutableError, |
---|
1223 | dirnode.pack_children, |
---|
1224 | fn, kids, deep_immutable=True) |
---|
1225 | |
---|
1226 | kids = self._make_kids(nm, ["imm", "lit", "dirread"]) |
---|
1227 | - self.failUnlessRaises(dirnode.MustBeDeepImmutable, |
---|
1228 | + self.failUnlessRaises(dirnode.MustBeDeepImmutableError, |
---|
1229 | dirnode.pack_children, |
---|
1230 | fn, kids, deep_immutable=True) |
---|
1231 | |
---|
1232 | @@ -1017,16 +1095,31 @@ |
---|
1233 | |
---|
1234 | def get_cap(self): |
---|
1235 | return self.uri |
---|
1236 | + |
---|
1237 | def get_uri(self): |
---|
1238 | return self.uri.to_string() |
---|
1239 | + |
---|
1240 | + def get_write_uri(self): |
---|
1241 | + return self.uri.to_string() |
---|
1242 | + |
---|
1243 | def download_best_version(self): |
---|
1244 | return defer.succeed(self.data) |
---|
1245 | + |
---|
1246 | def get_writekey(self): |
---|
1247 | return "writekey" |
---|
1248 | + |
---|
1249 | def is_readonly(self): |
---|
1250 | return False |
---|
1251 | + |
---|
1252 | def is_mutable(self): |
---|
1253 | return True |
---|
1254 | + |
---|
1255 | + def is_unknown(self): |
---|
1256 | + return False |
---|
1257 | + |
---|
1258 | + def is_allowed_in_immutable_directory(self): |
---|
1259 | + return False |
---|
1260 | + |
---|
1261 | def modify(self, modifier): |
---|
1262 | self.data = modifier(self.data, None, True) |
---|
1263 | return defer.succeed(None) |
---|
1264 | @@ -1050,47 +1143,59 @@ |
---|
1265 | |
---|
1266 | def test_from_future(self): |
---|
1267 | # create a dirnode that contains unknown URI types, and make sure we |
---|
1268 | - # tolerate them properly. Since dirnodes aren't allowed to add |
---|
1269 | - # unknown node types, we have to be tricky. |
---|
1270 | + # tolerate them properly. |
---|
1271 | d = self.nodemaker.create_new_mutable_directory() |
---|
1272 | - future_writecap = "x-tahoe-crazy://I_am_from_the_future." |
---|
1273 | - future_readcap = "x-tahoe-crazy-readonly://I_am_from_the_future." |
---|
1274 | - future_node = UnknownNode(future_writecap, future_readcap) |
---|
1275 | + future_write_uri = "x-tahoe-crazy://I_am_from_the_future." |
---|
1276 | + future_read_uri = "x-tahoe-crazy-readonly://I_am_from_the_future." |
---|
1277 | + future_node = UnknownNode(future_write_uri, future_read_uri) |
---|
1278 | def _then(n): |
---|
1279 | self._node = n |
---|
1280 | return n.set_node(u"future", future_node) |
---|
1281 | d.addCallback(_then) |
---|
1282 | |
---|
1283 | - # we should be prohibited from adding an unknown URI to a directory, |
---|
1284 | - # since we don't know how to diminish the cap to a readcap (for the |
---|
1285 | - # dirnode's rocap slot), and we don't want to accidentally grant |
---|
1286 | - # write access to a holder of the dirnode's readcap. |
---|
1287 | + # We should be prohibited from adding an unknown URI to a directory |
---|
1288 | + # just in the rw_uri slot, since we don't know how to diminish the cap |
---|
1289 | + # to a readcap (for the ro_uri slot). |
---|
1290 | d.addCallback(lambda ign: |
---|
1291 | - self.shouldFail(CannotPackUnknownNodeError, |
---|
1292 | + self.shouldFail(MustNotBeUnknownRWError, |
---|
1293 | "copy unknown", |
---|
1294 | - "cannot pack unknown node as child add", |
---|
1295 | + "cannot attach unknown rw cap as child", |
---|
1296 | self._node.set_uri, u"add", |
---|
1297 | - future_writecap, future_readcap)) |
---|
1298 | + future_write_uri, None)) |
---|
1299 | + |
---|
1300 | + # However, we should be able to add both rw_uri and ro_uri as a pair of |
---|
1301 | + # unknown URIs. |
---|
1302 | + d.addCallback(lambda ign: self._node.set_uri(u"add-pair", |
---|
1303 | + future_write_uri, future_read_uri)) |
---|
1304 | + |
---|
1305 | d.addCallback(lambda ign: self._node.list()) |
---|
1306 | def _check(children): |
---|
1307 | - self.failUnlessEqual(len(children), 1) |
---|
1308 | + self.failUnlessEqual(len(children), 2) |
---|
1309 | (fn, metadata) = children[u"future"] |
---|
1310 | self.failUnless(isinstance(fn, UnknownNode), fn) |
---|
1311 | - self.failUnlessEqual(fn.get_uri(), future_writecap) |
---|
1312 | - self.failUnlessEqual(fn.get_readonly_uri(), future_readcap) |
---|
1313 | - # but we *should* be allowed to copy this node, because the |
---|
1314 | + self.failUnlessEqual(fn.get_uri(), future_write_uri) |
---|
1315 | + self.failUnlessEqual(fn.get_readonly_uri(), "ro." + future_read_uri) |
---|
1316 | + |
---|
1317 | + (fn2, metadata2) = children[u"add-pair"] |
---|
1318 | + self.failUnless(isinstance(fn2, UnknownNode), fn2) |
---|
1319 | + self.failUnlessEqual(fn2.get_uri(), future_write_uri) |
---|
1320 | + self.failUnlessEqual(fn2.get_readonly_uri(), "ro." + future_read_uri) |
---|
1321 | + |
---|
1322 | + # we should also be allowed to copy this node, because the |
---|
1323 | # UnknownNode contains all the information that was in the |
---|
1324 | # original directory (readcap and writecap), so we're preserving |
---|
1325 | # everything. |
---|
1326 | return self._node.set_node(u"copy", fn) |
---|
1327 | d.addCallback(_check) |
---|
1328 | + |
---|
1329 | d.addCallback(lambda ign: self._node.list()) |
---|
1330 | def _check2(children): |
---|
1331 | - self.failUnlessEqual(len(children), 2) |
---|
1332 | + self.failUnlessEqual(len(children), 3) |
---|
1333 | (fn, metadata) = children[u"copy"] |
---|
1334 | self.failUnless(isinstance(fn, UnknownNode), fn) |
---|
1335 | - self.failUnlessEqual(fn.get_uri(), future_writecap) |
---|
1336 | - self.failUnlessEqual(fn.get_readonly_uri(), future_readcap) |
---|
1337 | + self.failUnlessEqual(fn.get_uri(), future_write_uri) |
---|
1338 | + self.failUnlessEqual(fn.get_readonly_uri(), "ro." + future_read_uri) |
---|
1339 | + d.addCallback(_check2) |
---|
1340 | return d |
---|
1341 | |
---|
1342 | class DeepStats(unittest.TestCase): |
---|
1343 | diff -rN -u old-tahoe/src/allmydata/test/test_filenode.py new-tahoe/src/allmydata/test/test_filenode.py |
---|
1344 | --- old-tahoe/src/allmydata/test/test_filenode.py 2010-01-23 12:59:09.796000000 +0000 |
---|
1345 | +++ new-tahoe/src/allmydata/test/test_filenode.py 2010-01-23 12:59:11.912000000 +0000 |
---|
1346 | @@ -41,14 +41,21 @@ |
---|
1347 | self.failUnlessEqual(fn1.get_readcap(), u) |
---|
1348 | self.failUnlessEqual(fn1.is_readonly(), True) |
---|
1349 | self.failUnlessEqual(fn1.is_mutable(), False) |
---|
1350 | + self.failUnlessEqual(fn1.is_unknown(), False) |
---|
1351 | + self.failUnlessEqual(fn1.is_allowed_in_immutable_directory(), True) |
---|
1352 | + self.failUnlessEqual(fn1.get_write_uri(), None) |
---|
1353 | self.failUnlessEqual(fn1.get_readonly_uri(), u.to_string()) |
---|
1354 | self.failUnlessEqual(fn1.get_size(), 1000) |
---|
1355 | self.failUnlessEqual(fn1.get_storage_index(), u.storage_index) |
---|
1356 | + fn1.raise_error() |
---|
1357 | + fn2.raise_error() |
---|
1358 | d = {} |
---|
1359 | d[fn1] = 1 # exercise __hash__ |
---|
1360 | v = fn1.get_verify_cap() |
---|
1361 | self.failUnless(isinstance(v, uri.CHKFileVerifierURI)) |
---|
1362 | self.failUnlessEqual(fn1.get_repair_cap(), v) |
---|
1363 | + self.failUnlessEqual(v.is_readonly(), True) |
---|
1364 | + self.failUnlessEqual(v.is_mutable(), False) |
---|
1365 | |
---|
1366 | |
---|
1367 | def test_literal_filenode(self): |
---|
1368 | @@ -64,9 +71,14 @@ |
---|
1369 | self.failUnlessEqual(fn1.get_readcap(), u) |
---|
1370 | self.failUnlessEqual(fn1.is_readonly(), True) |
---|
1371 | self.failUnlessEqual(fn1.is_mutable(), False) |
---|
1372 | + self.failUnlessEqual(fn1.is_unknown(), False) |
---|
1373 | + self.failUnlessEqual(fn1.is_allowed_in_immutable_directory(), True) |
---|
1374 | + self.failUnlessEqual(fn1.get_write_uri(), None) |
---|
1375 | self.failUnlessEqual(fn1.get_readonly_uri(), u.to_string()) |
---|
1376 | self.failUnlessEqual(fn1.get_size(), len(DATA)) |
---|
1377 | self.failUnlessEqual(fn1.get_storage_index(), None) |
---|
1378 | + fn1.raise_error() |
---|
1379 | + fn2.raise_error() |
---|
1380 | d = {} |
---|
1381 | d[fn1] = 1 # exercise __hash__ |
---|
1382 | |
---|
1383 | @@ -99,24 +111,29 @@ |
---|
1384 | self.failUnlessEqual(n.get_writekey(), wk) |
---|
1385 | self.failUnlessEqual(n.get_readkey(), rk) |
---|
1386 | self.failUnlessEqual(n.get_storage_index(), si) |
---|
1387 | - # these itmes are populated on first read (or create), so until that |
---|
1388 | + # these items are populated on first read (or create), so until that |
---|
1389 | # happens they'll be None |
---|
1390 | self.failUnlessEqual(n.get_privkey(), None) |
---|
1391 | self.failUnlessEqual(n.get_encprivkey(), None) |
---|
1392 | self.failUnlessEqual(n.get_pubkey(), None) |
---|
1393 | |
---|
1394 | self.failUnlessEqual(n.get_uri(), u.to_string()) |
---|
1395 | + self.failUnlessEqual(n.get_write_uri(), u.to_string()) |
---|
1396 | self.failUnlessEqual(n.get_readonly_uri(), u.get_readonly().to_string()) |
---|
1397 | self.failUnlessEqual(n.get_cap(), u) |
---|
1398 | self.failUnlessEqual(n.get_readcap(), u.get_readonly()) |
---|
1399 | self.failUnlessEqual(n.is_mutable(), True) |
---|
1400 | self.failUnlessEqual(n.is_readonly(), False) |
---|
1401 | + self.failUnlessEqual(n.is_unknown(), False) |
---|
1402 | + self.failUnlessEqual(n.is_allowed_in_immutable_directory(), False) |
---|
1403 | + n.raise_error() |
---|
1404 | |
---|
1405 | n2 = MutableFileNode(None, None, client.get_encoding_parameters(), |
---|
1406 | None).init_from_cap(u) |
---|
1407 | self.failUnlessEqual(n, n2) |
---|
1408 | self.failIfEqual(n, "not even the right type") |
---|
1409 | self.failIfEqual(n, u) # not the right class |
---|
1410 | + n.raise_error() |
---|
1411 | d = {n: "can these be used as dictionary keys?"} |
---|
1412 | d[n2] = "replace the old one" |
---|
1413 | self.failUnlessEqual(len(d), 1) |
---|
1414 | @@ -127,12 +144,16 @@ |
---|
1415 | self.failUnlessEqual(nro.get_readonly(), nro) |
---|
1416 | self.failUnlessEqual(nro.get_cap(), u.get_readonly()) |
---|
1417 | self.failUnlessEqual(nro.get_readcap(), u.get_readonly()) |
---|
1418 | + self.failUnlessEqual(nro.is_mutable(), True) |
---|
1419 | + self.failUnlessEqual(nro.is_readonly(), True) |
---|
1420 | + self.failUnlessEqual(nro.is_unknown(), False) |
---|
1421 | + self.failUnlessEqual(nro.is_allowed_in_immutable_directory(), False) |
---|
1422 | nro_u = nro.get_uri() |
---|
1423 | self.failUnlessEqual(nro_u, nro.get_readonly_uri()) |
---|
1424 | self.failUnlessEqual(nro_u, u.get_readonly().to_string()) |
---|
1425 | - self.failUnlessEqual(nro.is_mutable(), True) |
---|
1426 | - self.failUnlessEqual(nro.is_readonly(), True) |
---|
1427 | + self.failUnlessEqual(nro.get_write_uri(), None) |
---|
1428 | self.failUnlessEqual(nro.get_repair_cap(), None) # RSAmut needs writecap |
---|
1429 | + nro.raise_error() |
---|
1430 | |
---|
1431 | v = n.get_verify_cap() |
---|
1432 | self.failUnless(isinstance(v, uri.SSKVerifierURI)) |
---|
1433 | diff -rN -u old-tahoe/src/allmydata/test/test_system.py new-tahoe/src/allmydata/test/test_system.py |
---|
1434 | --- old-tahoe/src/allmydata/test/test_system.py 2010-01-23 12:59:10.091000000 +0000 |
---|
1435 | +++ new-tahoe/src/allmydata/test/test_system.py 2010-01-23 12:59:12.085000000 +0000 |
---|
1436 | @@ -17,7 +17,7 @@ |
---|
1437 | from allmydata.interfaces import IDirectoryNode, IFileNode, \ |
---|
1438 | NoSuchChildError, NoSharesError |
---|
1439 | from allmydata.monitor import Monitor |
---|
1440 | -from allmydata.mutable.common import NotMutableError |
---|
1441 | +from allmydata.mutable.common import NotWriteableError |
---|
1442 | from allmydata.mutable import layout as mutable_layout |
---|
1443 | from foolscap.api import DeadReferenceError |
---|
1444 | from twisted.python.failure import Failure |
---|
1445 | @@ -890,11 +890,11 @@ |
---|
1446 | d1.addCallback(lambda res: dirnode.list()) |
---|
1447 | d1.addCallback(self.log, "dirnode.list") |
---|
1448 | |
---|
1449 | - d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mkdir(nope)", None, dirnode.create_subdirectory, u"nope")) |
---|
1450 | + d1.addCallback(lambda res: self.shouldFail2(NotWriteableError, "mkdir(nope)", None, dirnode.create_subdirectory, u"nope")) |
---|
1451 | |
---|
1452 | d1.addCallback(self.log, "doing add_file(ro)") |
---|
1453 | ut = upload.Data("I will disappear, unrecorded and unobserved. The tragedy of my demise is made more poignant by its silence, but this beauty is not for you to ever know.", convergence="99i-p1x4-xd4-18yc-ywt-87uu-msu-zo -- completely and totally unguessable string (unless you read this)") |
---|
1454 | - d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "add_file(nope)", None, dirnode.add_file, u"hope", ut)) |
---|
1455 | + d1.addCallback(lambda res: self.shouldFail2(NotWriteableError, "add_file(nope)", None, dirnode.add_file, u"hope", ut)) |
---|
1456 | |
---|
1457 | d1.addCallback(self.log, "doing get(ro)") |
---|
1458 | d1.addCallback(lambda res: dirnode.get(u"mydata992")) |
---|
1459 | @@ -902,17 +902,17 @@ |
---|
1460 | self.failUnless(IFileNode.providedBy(filenode))) |
---|
1461 | |
---|
1462 | d1.addCallback(self.log, "doing delete(ro)") |
---|
1463 | - d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "delete(nope)", None, dirnode.delete, u"mydata992")) |
---|
1464 | + d1.addCallback(lambda res: self.shouldFail2(NotWriteableError, "delete(nope)", None, dirnode.delete, u"mydata992")) |
---|
1465 | |
---|
1466 | - d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "set_uri(nope)", None, dirnode.set_uri, u"hopeless", self.uri, self.uri)) |
---|
1467 | + d1.addCallback(lambda res: self.shouldFail2(NotWriteableError, "set_uri(nope)", None, dirnode.set_uri, u"hopeless", self.uri, self.uri)) |
---|
1468 | |
---|
1469 | d1.addCallback(lambda res: self.shouldFail2(NoSuchChildError, "get(missing)", "missing", dirnode.get, u"missing")) |
---|
1470 | |
---|
1471 | personal = self._personal_node |
---|
1472 | - d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mv from readonly", None, dirnode.move_child_to, u"mydata992", personal, u"nope")) |
---|
1473 | + d1.addCallback(lambda res: self.shouldFail2(NotWriteableError, "mv from readonly", None, dirnode.move_child_to, u"mydata992", personal, u"nope")) |
---|
1474 | |
---|
1475 | d1.addCallback(self.log, "doing move_child_to(ro)2") |
---|
1476 | - d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mv to readonly", None, personal.move_child_to, u"sekrit data", dirnode, u"nope")) |
---|
1477 | + d1.addCallback(lambda res: self.shouldFail2(NotWriteableError, "mv to readonly", None, personal.move_child_to, u"sekrit data", dirnode, u"nope")) |
---|
1478 | |
---|
1479 | d1.addCallback(self.log, "finished with _got_s2ro") |
---|
1480 | return d1 |
---|
1481 | diff -rN -u old-tahoe/src/allmydata/test/test_uri.py new-tahoe/src/allmydata/test/test_uri.py |
---|
1482 | --- old-tahoe/src/allmydata/test/test_uri.py 2010-01-23 12:59:10.134000000 +0000 |
---|
1483 | +++ new-tahoe/src/allmydata/test/test_uri.py 2010-01-23 12:59:12.094000000 +0000 |
---|
1484 | @@ -3,7 +3,7 @@ |
---|
1485 | from allmydata import uri |
---|
1486 | from allmydata.util import hashutil, base32 |
---|
1487 | from allmydata.interfaces import IURI, IFileURI, IDirnodeURI, IMutableFileURI, \ |
---|
1488 | - IVerifierURI |
---|
1489 | + IVerifierURI, CapConstraintError |
---|
1490 | |
---|
1491 | class Literal(unittest.TestCase): |
---|
1492 | def _help_test(self, data): |
---|
1493 | @@ -22,8 +22,16 @@ |
---|
1494 | self.failIf(IDirnodeURI.providedBy(u2)) |
---|
1495 | self.failUnlessEqual(u2.data, data) |
---|
1496 | self.failUnlessEqual(u2.get_size(), len(data)) |
---|
1497 | - self.failUnless(u.is_readonly()) |
---|
1498 | - self.failIf(u.is_mutable()) |
---|
1499 | + self.failUnless(u2.is_readonly()) |
---|
1500 | + self.failIf(u2.is_mutable()) |
---|
1501 | + |
---|
1502 | + u2i = uri.from_string(u.to_string(), deep_immutable=True) |
---|
1503 | + self.failUnless(IFileURI.providedBy(u2i)) |
---|
1504 | + self.failIf(IDirnodeURI.providedBy(u2i)) |
---|
1505 | + self.failUnlessEqual(u2i.data, data) |
---|
1506 | + self.failUnlessEqual(u2i.get_size(), len(data)) |
---|
1507 | + self.failUnless(u2i.is_readonly()) |
---|
1508 | + self.failIf(u2i.is_mutable()) |
---|
1509 | |
---|
1510 | u3 = u.get_readonly() |
---|
1511 | self.failUnlessIdentical(u, u3) |
---|
1512 | @@ -51,18 +59,36 @@ |
---|
1513 | fileURI = 'URI:CHK:f5ahxa25t4qkktywz6teyfvcx4:opuioq7tj2y6idzfp6cazehtmgs5fdcebcz3cygrxyydvcozrmeq:3:10:345834' |
---|
1514 | chk1 = uri.CHKFileURI.init_from_string(fileURI) |
---|
1515 | chk2 = uri.CHKFileURI.init_from_string(fileURI) |
---|
1516 | + unk = uri.UnknownURI("lafs://from_the_future") |
---|
1517 | self.failIfEqual(lit1, chk1) |
---|
1518 | self.failUnlessEqual(chk1, chk2) |
---|
1519 | self.failIfEqual(chk1, "not actually a URI") |
---|
1520 | # these should be hashable too |
---|
1521 | - s = set([lit1, chk1, chk2]) |
---|
1522 | - self.failUnlessEqual(len(s), 2) # since chk1==chk2 |
---|
1523 | + s = set([lit1, chk1, chk2, unk]) |
---|
1524 | + self.failUnlessEqual(len(s), 3) # since chk1==chk2 |
---|
1525 | |
---|
1526 | def test_is_uri(self): |
---|
1527 | lit1 = uri.LiteralFileURI("some data").to_string() |
---|
1528 | self.failUnless(uri.is_uri(lit1)) |
---|
1529 | self.failIf(uri.is_uri(None)) |
---|
1530 | |
---|
1531 | + def test_is_literal_file_uri(self): |
---|
1532 | + lit1 = uri.LiteralFileURI("some data").to_string() |
---|
1533 | + self.failUnless(uri.is_literal_file_uri(lit1)) |
---|
1534 | + self.failIf(uri.is_literal_file_uri(None)) |
---|
1535 | + self.failIf(uri.is_literal_file_uri("foo")) |
---|
1536 | + self.failIf(uri.is_literal_file_uri("ro.foo")) |
---|
1537 | + self.failIf(uri.is_literal_file_uri("URI:LITfoo")) |
---|
1538 | + self.failUnless(uri.is_literal_file_uri("ro.URI:LIT:foo")) |
---|
1539 | + self.failUnless(uri.is_literal_file_uri("imm.URI:LIT:foo")) |
---|
1540 | + |
---|
1541 | + def test_has_uri_prefix(self): |
---|
1542 | + self.failUnless(uri.has_uri_prefix("URI:foo")) |
---|
1543 | + self.failUnless(uri.has_uri_prefix("ro.URI:foo")) |
---|
1544 | + self.failUnless(uri.has_uri_prefix("imm.URI:foo")) |
---|
1545 | + self.failIf(uri.has_uri_prefix(None)) |
---|
1546 | + self.failIf(uri.has_uri_prefix("foo")) |
---|
1547 | + |
---|
1548 | class CHKFile(unittest.TestCase): |
---|
1549 | def test_pack(self): |
---|
1550 | key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" |
---|
1551 | @@ -88,8 +114,7 @@ |
---|
1552 | self.failUnless(IFileURI.providedBy(u)) |
---|
1553 | self.failIf(IDirnodeURI.providedBy(u)) |
---|
1554 | self.failUnlessEqual(u.get_size(), 1234) |
---|
1555 | - self.failUnless(u.is_readonly()) |
---|
1556 | - self.failIf(u.is_mutable()) |
---|
1557 | + |
---|
1558 | u_ro = u.get_readonly() |
---|
1559 | self.failUnlessIdentical(u, u_ro) |
---|
1560 | he = u.to_human_encoding() |
---|
1561 | @@ -109,11 +134,19 @@ |
---|
1562 | self.failUnless(IFileURI.providedBy(u2)) |
---|
1563 | self.failIf(IDirnodeURI.providedBy(u2)) |
---|
1564 | self.failUnlessEqual(u2.get_size(), 1234) |
---|
1565 | - self.failUnless(u2.is_readonly()) |
---|
1566 | - self.failIf(u2.is_mutable()) |
---|
1567 | + |
---|
1568 | + u2i = uri.from_string(u.to_string(), deep_immutable=True) |
---|
1569 | + self.failUnlessEqual(u.to_string(), u2i.to_string()) |
---|
1570 | + u2ro = uri.from_string(uri.ALLEGED_READONLY_PREFIX + u.to_string()) |
---|
1571 | + self.failUnlessEqual(u.to_string(), u2ro.to_string()) |
---|
1572 | + u2imm = uri.from_string(uri.ALLEGED_IMMUTABLE_PREFIX + u.to_string()) |
---|
1573 | + self.failUnlessEqual(u.to_string(), u2imm.to_string()) |
---|
1574 | |
---|
1575 | v = u.get_verify_cap() |
---|
1576 | self.failUnless(isinstance(v.to_string(), str)) |
---|
1577 | + self.failUnless(v.is_readonly()) |
---|
1578 | + self.failIf(v.is_mutable()) |
---|
1579 | + |
---|
1580 | v2 = uri.from_string(v.to_string()) |
---|
1581 | self.failUnlessEqual(v, v2) |
---|
1582 | he = v.to_human_encoding() |
---|
1583 | @@ -126,6 +159,8 @@ |
---|
1584 | total_shares=10, |
---|
1585 | size=1234) |
---|
1586 | self.failUnless(isinstance(v3.to_string(), str)) |
---|
1587 | + self.failUnless(v3.is_readonly()) |
---|
1588 | + self.failIf(v3.is_mutable()) |
---|
1589 | |
---|
1590 | def test_pack_badly(self): |
---|
1591 | key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" |
---|
1592 | @@ -179,13 +214,20 @@ |
---|
1593 | self.failUnlessEqual(readable["UEB_hash"], |
---|
1594 | base32.b2a(hashutil.uri_extension_hash(ext))) |
---|
1595 | |
---|
1596 | -class Invalid(unittest.TestCase): |
---|
1597 | +class Unknown(unittest.TestCase): |
---|
1598 | def test_from_future(self): |
---|
1599 | # any URI type that we don't recognize should be treated as unknown |
---|
1600 | future_uri = "I am a URI from the future. Whatever you do, don't " |
---|
1601 | u = uri.from_string(future_uri) |
---|
1602 | self.failUnless(isinstance(u, uri.UnknownURI)) |
---|
1603 | self.failUnlessEqual(u.to_string(), future_uri) |
---|
1604 | + self.failUnless(u.get_readonly() is None) |
---|
1605 | + self.failUnless(u.get_error() is None) |
---|
1606 | + |
---|
1607 | + u2 = uri.UnknownURI(future_uri, error=CapConstraintError("...")) |
---|
1608 | + self.failUnlessEqual(u.to_string(), future_uri) |
---|
1609 | + self.failUnless(u2.get_readonly() is None) |
---|
1610 | + self.failUnless(isinstance(u2.get_error(), CapConstraintError)) |
---|
1611 | |
---|
1612 | class Constraint(unittest.TestCase): |
---|
1613 | def test_constraint(self): |
---|
1614 | @@ -226,6 +268,13 @@ |
---|
1615 | self.failUnless(IMutableFileURI.providedBy(u2)) |
---|
1616 | self.failIf(IDirnodeURI.providedBy(u2)) |
---|
1617 | |
---|
1618 | + u2i = uri.from_string(u.to_string(), deep_immutable=True) |
---|
1619 | + self.failUnless(isinstance(u2i, uri.UnknownURI), u2i) |
---|
1620 | + u2ro = uri.from_string(uri.ALLEGED_READONLY_PREFIX + u.to_string()) |
---|
1621 | + self.failUnless(isinstance(u2ro, uri.UnknownURI), u2ro) |
---|
1622 | + u2imm = uri.from_string(uri.ALLEGED_IMMUTABLE_PREFIX + u.to_string()) |
---|
1623 | + self.failUnless(isinstance(u2imm, uri.UnknownURI), u2imm) |
---|
1624 | + |
---|
1625 | u3 = u2.get_readonly() |
---|
1626 | readkey = hashutil.ssk_readkey_hash(writekey) |
---|
1627 | self.failUnlessEqual(u3.fingerprint, fingerprint) |
---|
1628 | @@ -236,6 +285,13 @@ |
---|
1629 | self.failUnless(IMutableFileURI.providedBy(u3)) |
---|
1630 | self.failIf(IDirnodeURI.providedBy(u3)) |
---|
1631 | |
---|
1632 | + u3i = uri.from_string(u3.to_string(), deep_immutable=True) |
---|
1633 | + self.failUnless(isinstance(u3i, uri.UnknownURI), u3i) |
---|
1634 | + u3ro = uri.from_string(uri.ALLEGED_READONLY_PREFIX + u3.to_string()) |
---|
1635 | + self.failUnlessEqual(u3.to_string(), u3ro.to_string()) |
---|
1636 | + u3imm = uri.from_string(uri.ALLEGED_IMMUTABLE_PREFIX + u3.to_string()) |
---|
1637 | + self.failUnless(isinstance(u3imm, uri.UnknownURI), u3imm) |
---|
1638 | + |
---|
1639 | he = u3.to_human_encoding() |
---|
1640 | u3_h = uri.ReadonlySSKFileURI.init_from_human_encoding(he) |
---|
1641 | self.failUnlessEqual(u3, u3_h) |
---|
1642 | @@ -249,6 +305,13 @@ |
---|
1643 | self.failUnless(IMutableFileURI.providedBy(u4)) |
---|
1644 | self.failIf(IDirnodeURI.providedBy(u4)) |
---|
1645 | |
---|
1646 | + u4i = uri.from_string(u4.to_string(), deep_immutable=True) |
---|
1647 | + self.failUnless(isinstance(u4i, uri.UnknownURI), u4i) |
---|
1648 | + u4ro = uri.from_string(uri.ALLEGED_READONLY_PREFIX + u4.to_string()) |
---|
1649 | + self.failUnlessEqual(u4.to_string(), u4ro.to_string()) |
---|
1650 | + u4imm = uri.from_string(uri.ALLEGED_IMMUTABLE_PREFIX + u4.to_string()) |
---|
1651 | + self.failUnless(isinstance(u4imm, uri.UnknownURI), u4imm) |
---|
1652 | + |
---|
1653 | u4a = uri.from_string(u4.to_string()) |
---|
1654 | self.failUnlessEqual(u4a, u4) |
---|
1655 | self.failUnless("ReadonlySSKFileURI" in str(u4a)) |
---|
1656 | @@ -291,12 +354,19 @@ |
---|
1657 | self.failIf(IFileURI.providedBy(u2)) |
---|
1658 | self.failUnless(IDirnodeURI.providedBy(u2)) |
---|
1659 | |
---|
1660 | + u2i = uri.from_string(u1.to_string(), deep_immutable=True) |
---|
1661 | + self.failUnless(isinstance(u2i, uri.UnknownURI)) |
---|
1662 | + |
---|
1663 | u3 = u2.get_readonly() |
---|
1664 | self.failUnless(u3.is_readonly()) |
---|
1665 | self.failUnless(u3.is_mutable()) |
---|
1666 | self.failUnless(IURI.providedBy(u3)) |
---|
1667 | self.failIf(IFileURI.providedBy(u3)) |
---|
1668 | self.failUnless(IDirnodeURI.providedBy(u3)) |
---|
1669 | + |
---|
1670 | + u3i = uri.from_string(u2.to_string(), deep_immutable=True) |
---|
1671 | + self.failUnless(isinstance(u3i, uri.UnknownURI)) |
---|
1672 | + |
---|
1673 | u3n = u3._filenode_uri |
---|
1674 | self.failUnless(u3n.is_readonly()) |
---|
1675 | self.failUnless(u3n.is_mutable()) |
---|
1676 | @@ -363,10 +433,16 @@ |
---|
1677 | self.failIf(IFileURI.providedBy(u2)) |
---|
1678 | self.failUnless(IDirnodeURI.providedBy(u2)) |
---|
1679 | |
---|
1680 | + u2i = uri.from_string(u1.to_string(), deep_immutable=True) |
---|
1681 | + self.failUnlessEqual(u1.to_string(), u2i.to_string()) |
---|
1682 | + |
---|
1683 | u3 = u2.get_readonly() |
---|
1684 | self.failUnlessEqual(u3.to_string(), u2.to_string()) |
---|
1685 | self.failUnless(str(u3)) |
---|
1686 | |
---|
1687 | + u3i = uri.from_string(u2.to_string(), deep_immutable=True) |
---|
1688 | + self.failUnlessEqual(u2.to_string(), u3i.to_string()) |
---|
1689 | + |
---|
1690 | u2_verifier = u2.get_verify_cap() |
---|
1691 | self.failUnless(isinstance(u2_verifier, |
---|
1692 | uri.ImmutableDirectoryURIVerifier), |
---|
1693 | diff -rN -u old-tahoe/src/allmydata/test/test_web.py new-tahoe/src/allmydata/test/test_web.py |
---|
1694 | --- old-tahoe/src/allmydata/test/test_web.py 2010-01-23 12:59:10.149000000 +0000 |
---|
1695 | +++ new-tahoe/src/allmydata/test/test_web.py 2010-01-23 12:59:12.131000000 +0000 |
---|
1696 | @@ -7,7 +7,7 @@ |
---|
1697 | from twisted.web import client, error, http |
---|
1698 | from twisted.python import failure, log |
---|
1699 | from nevow import rend |
---|
1700 | -from allmydata import interfaces, uri, webish |
---|
1701 | +from allmydata import interfaces, uri, webish, dirnode |
---|
1702 | from allmydata.storage.shares import get_share_file |
---|
1703 | from allmydata.storage_client import StorageFarmBroker |
---|
1704 | from allmydata.immutable import upload, download |
---|
1705 | @@ -18,6 +18,7 @@ |
---|
1706 | from allmydata.scripts.debug import CorruptShareOptions, corrupt_share |
---|
1707 | from allmydata.util import fileutil, base32 |
---|
1708 | from allmydata.util.consumer import download_to_data |
---|
1709 | +from allmydata.util.netstring import split_netstring |
---|
1710 | from allmydata.test.common import FakeCHKFileNode, FakeMutableFileNode, \ |
---|
1711 | create_chk_filenode, WebErrorMixin, ShouldFailMixin, make_mutable_file_uri |
---|
1712 | from allmydata.interfaces import IMutableFileNode |
---|
1713 | @@ -366,25 +367,101 @@ |
---|
1714 | self.fail("%s was supposed to Error(404), not get '%s'" % |
---|
1715 | (which, res)) |
---|
1716 | |
---|
1717 | + def _dump_res(self, res): |
---|
1718 | + import traceback |
---|
1719 | + s = "%r\n" % (res,) |
---|
1720 | + if hasattr(res, 'tb_frame'): |
---|
1721 | + s += "Traceback:\n%s\n" % (traceback.format_tb(res),) |
---|
1722 | + if hasattr(res, 'value'): |
---|
1723 | + s += "%r\n" % (res.value,) |
---|
1724 | + if hasattr(res.value, 'tb_frame'): |
---|
1725 | + s += "Traceback:\n%s\n" % (res, res.value, traceback.format_tb(res)) |
---|
1726 | + if hasattr(res.value, 'response'): |
---|
1727 | + s += "Response body:\n%s\n" % (res.value.response,) |
---|
1728 | + return s |
---|
1729 | + |
---|
1730 | + def shouldSucceedGET(self, urlpath, followRedirect=False, |
---|
1731 | + expected_statuscode=http.OK, return_response=False, **kwargs): |
---|
1732 | + d = self.GET(urlpath, followRedirect=followRedirect, return_response=True, **kwargs) |
---|
1733 | + def done((res, statuscode, headers)): |
---|
1734 | + if isinstance(res, failure.Failure): |
---|
1735 | + self.fail(("'GET %s' with kwargs %r was supposed to succeed with statuscode %s, " |
---|
1736 | + "but it failed with statuscode %s instead.\n" |
---|
1737 | + "%s\nThe response headers were:\n%s") % ( |
---|
1738 | + urlpath, kwargs, expected_statuscode, statuscode, |
---|
1739 | + self._dump_res(res), headers)) |
---|
1740 | + if str(statuscode) != str(expected_statuscode): |
---|
1741 | + self.fail(("'GET %s' with kwargs %r was supposed to succeed with statuscode %s, " |
---|
1742 | + "but it succeeded with statuscode %s instead.\n" |
---|
1743 | + "The response headers were:\n%s\n\n" |
---|
1744 | + "The response body was:\n%s") % ( |
---|
1745 | + urlpath, kwargs, expected_statuscode, statuscode, headers, res)) |
---|
1746 | + if return_response: |
---|
1747 | + return (res, statuscode, headers) |
---|
1748 | + else: |
---|
1749 | + return res |
---|
1750 | + d.addBoth(done) |
---|
1751 | + return d |
---|
1752 | + |
---|
1753 | + def shouldSucceedHEAD(self, urlpath, expected_statuscode=http.OK, |
---|
1754 | + return_response=False, **kwargs): |
---|
1755 | + d = self.HEAD(urlpath, return_response=True, **kwargs) |
---|
1756 | + def done((res, statuscode, headers)): |
---|
1757 | + if isinstance(res, failure.Failure): |
---|
1758 | + self.fail(("'HEAD %s' with kwargs %r was supposed to succeed with statuscode %s, " |
---|
1759 | + "but it failed with statuscode %s instead.\n" |
---|
1760 | + "%s\nThe response headers were:\n%s") % ( |
---|
1761 | + urlpath, kwargs, expected_statuscode, statuscode, |
---|
1762 | + self._dump_res(res), headers)) |
---|
1763 | + if str(statuscode) != str(expected_statuscode): |
---|
1764 | + self.fail(("'HEAD %s' with kwargs %r was supposed to succeed with statuscode %s, " |
---|
1765 | + "but it succeeded with statuscode %s instead.\n" |
---|
1766 | + "The response headers were:\n%s\n\n" |
---|
1767 | + "The response body was:\n%s") % ( |
---|
1768 | + urlpath, kwargs, expected_statuscode, statuscode, headers, res)) |
---|
1769 | + if return_response: |
---|
1770 | + return (res, statuscode, headers) |
---|
1771 | + else: |
---|
1772 | + return res |
---|
1773 | + d.addBoth(done) |
---|
1774 | + return d |
---|
1775 | + |
---|
1776 | + def shouldSucceed(self, which, expected_statuscode, callable, *args, **kwargs): |
---|
1777 | + d = defer.maybeDeferred(callable, *args, **kwargs) |
---|
1778 | + def done(res): |
---|
1779 | + if isinstance(res, failure.Failure): |
---|
1780 | + self.fail(("%s:\nAn HTTP op with args %r and kwargs %r was supposed to " |
---|
1781 | + "succeed with statuscode %s, but it failed:\n%s") % ( |
---|
1782 | + which, args, kwargs, expected_statuscode, |
---|
1783 | + self._dump_res(res))) |
---|
1784 | + #if str(statuscode) != str(expected_statuscode): |
---|
1785 | + # self.fail(("%s:\nAn HTTP op with args %r and kwargs %r was supposed to " |
---|
1786 | + # "succeed with statuscode %s, but it succeeded with statuscode %s instead.\n" |
---|
1787 | + # "The response body was:\n%s") % ( |
---|
1788 | + # which, args, kwargs, expected_statuscode, statuscode, res)) |
---|
1789 | + return res |
---|
1790 | + d.addBoth(done) |
---|
1791 | + return d |
---|
1792 | + |
---|
1793 | |
---|
1794 | class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase): |
---|
1795 | def test_create(self): |
---|
1796 | pass |
---|
1797 | |
---|
1798 | def test_welcome(self): |
---|
1799 | - d = self.GET("/") |
---|
1800 | + d = self.shouldSucceedGET("/") |
---|
1801 | def _check(res): |
---|
1802 | self.failUnless('Welcome To Tahoe-LAFS' in res, res) |
---|
1803 | |
---|
1804 | self.s.basedir = 'web/test_welcome' |
---|
1805 | fileutil.make_dirs("web/test_welcome") |
---|
1806 | fileutil.make_dirs("web/test_welcome/private") |
---|
1807 | - return self.GET("/") |
---|
1808 | + return self.shouldSucceedGET("/") |
---|
1809 | d.addCallback(_check) |
---|
1810 | return d |
---|
1811 | |
---|
1812 | def test_provisioning(self): |
---|
1813 | - d = self.GET("/provisioning/") |
---|
1814 | + d = self.shouldSucceedGET("/provisioning/") |
---|
1815 | def _check(res): |
---|
1816 | self.failUnless('Tahoe Provisioning Tool' in res) |
---|
1817 | fields = {'filled': True, |
---|
1818 | @@ -400,9 +477,10 @@ |
---|
1819 | "delete_rate": 10, |
---|
1820 | "lease_timer": 7, |
---|
1821 | } |
---|
1822 | - return self.POST("/provisioning/", **fields) |
---|
1823 | - |
---|
1824 | + return self.shouldSucceed("POST_provisioning-1", http.OK, self.POST, |
---|
1825 | + "/provisioning/", **fields) |
---|
1826 | d.addCallback(_check) |
---|
1827 | + |
---|
1828 | def _check2(res): |
---|
1829 | self.failUnless('Tahoe Provisioning Tool' in res) |
---|
1830 | self.failUnless("Share space consumed: 167.01TB" in res) |
---|
1831 | @@ -422,13 +500,17 @@ |
---|
1832 | "delete_rate": 100, |
---|
1833 | "lease_timer": 7, |
---|
1834 | } |
---|
1835 | - return self.POST("/provisioning/", **fields) |
---|
1836 | + return self.shouldSucceed("POST_provisioning-2", http.OK, self.POST, |
---|
1837 | + "/provisioning/", **fields) |
---|
1838 | d.addCallback(_check2) |
---|
1839 | + |
---|
1840 | def _check3(res): |
---|
1841 | self.failUnless("Share space consumed: huge!" in res) |
---|
1842 | fields = {'filled': True} |
---|
1843 | - return self.POST("/provisioning/", **fields) |
---|
1844 | + return self.shouldSucceed("POST_provisioning-3", http.OK, self.POST, |
---|
1845 | + "/provisioning/", **fields) |
---|
1846 | d.addCallback(_check3) |
---|
1847 | + |
---|
1848 | def _check4(res): |
---|
1849 | self.failUnless("Share space consumed:" in res) |
---|
1850 | d.addCallback(_check4) |
---|
1851 | @@ -442,7 +524,7 @@ |
---|
1852 | except: |
---|
1853 | raise unittest.SkipTest("reliability tool requires NumPy") |
---|
1854 | |
---|
1855 | - d = self.GET("/reliability/") |
---|
1856 | + d = self.shouldSucceedGET("/reliability/") |
---|
1857 | def _check(res): |
---|
1858 | self.failUnless('Tahoe Reliability Tool' in res) |
---|
1859 | fields = {'drive_lifetime': "8Y", |
---|
1860 | @@ -471,7 +553,7 @@ |
---|
1861 | mu_num = h.list_all_mapupdate_statuses()[0].get_counter() |
---|
1862 | pub_num = h.list_all_publish_statuses()[0].get_counter() |
---|
1863 | ret_num = h.list_all_retrieve_statuses()[0].get_counter() |
---|
1864 | - d = self.GET("/status", followRedirect=True) |
---|
1865 | + d = self.shouldSucceedGET("/status", followRedirect=True) |
---|
1866 | def _check(res): |
---|
1867 | self.failUnless('Upload and Download Status' in res, res) |
---|
1868 | self.failUnless('"down-%d"' % dl_num in res, res) |
---|
1869 | @@ -480,7 +562,7 @@ |
---|
1870 | self.failUnless('"publish-%d"' % pub_num in res, res) |
---|
1871 | self.failUnless('"retrieve-%d"' % ret_num in res, res) |
---|
1872 | d.addCallback(_check) |
---|
1873 | - d.addCallback(lambda res: self.GET("/status/?t=json")) |
---|
1874 | + d.addCallback(lambda res: self.shouldSucceedGET("/status/?t=json")) |
---|
1875 | def _check_json(res): |
---|
1876 | data = simplejson.loads(res) |
---|
1877 | self.failUnless(isinstance(data, dict)) |
---|
1878 | @@ -489,23 +571,23 @@ |
---|
1879 | # here. |
---|
1880 | d.addCallback(_check_json) |
---|
1881 | |
---|
1882 | - d.addCallback(lambda res: self.GET("/status/down-%d" % dl_num)) |
---|
1883 | + d.addCallback(lambda res: self.shouldSucceedGET("/status/down-%d" % dl_num)) |
---|
1884 | def _check_dl(res): |
---|
1885 | self.failUnless("File Download Status" in res, res) |
---|
1886 | d.addCallback(_check_dl) |
---|
1887 | - d.addCallback(lambda res: self.GET("/status/up-%d" % ul_num)) |
---|
1888 | + d.addCallback(lambda res: self.shouldSucceedGET("/status/up-%d" % ul_num)) |
---|
1889 | def _check_ul(res): |
---|
1890 | self.failUnless("File Upload Status" in res, res) |
---|
1891 | d.addCallback(_check_ul) |
---|
1892 | - d.addCallback(lambda res: self.GET("/status/mapupdate-%d" % mu_num)) |
---|
1893 | + d.addCallback(lambda res: self.shouldSucceedGET("/status/mapupdate-%d" % mu_num)) |
---|
1894 | def _check_mapupdate(res): |
---|
1895 | self.failUnless("Mutable File Servermap Update Status" in res, res) |
---|
1896 | d.addCallback(_check_mapupdate) |
---|
1897 | - d.addCallback(lambda res: self.GET("/status/publish-%d" % pub_num)) |
---|
1898 | + d.addCallback(lambda res: self.shouldSucceedGET("/status/publish-%d" % pub_num)) |
---|
1899 | def _check_publish(res): |
---|
1900 | self.failUnless("Mutable File Publish Status" in res, res) |
---|
1901 | d.addCallback(_check_publish) |
---|
1902 | - d.addCallback(lambda res: self.GET("/status/retrieve-%d" % ret_num)) |
---|
1903 | + d.addCallback(lambda res: self.shouldSucceedGET("/status/retrieve-%d" % ret_num)) |
---|
1904 | def _check_retrieve(res): |
---|
1905 | self.failUnless("Mutable File Retrieve Status" in res, res) |
---|
1906 | d.addCallback(_check_retrieve) |
---|
1907 | @@ -536,16 +618,15 @@ |
---|
1908 | self.failUnlessEqual(urrm.render_rate(None, 123), "123Bps") |
---|
1909 | |
---|
1910 | def test_GET_FILEURL(self): |
---|
1911 | - d = self.GET(self.public_url + "/foo/bar.txt") |
---|
1912 | + d = self.shouldSucceedGET(self.public_url + "/foo/bar.txt") |
---|
1913 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
1914 | return d |
---|
1915 | |
---|
1916 | def test_GET_FILEURL_range(self): |
---|
1917 | headers = {"range": "bytes=1-10"} |
---|
1918 | - d = self.GET(self.public_url + "/foo/bar.txt", headers=headers, |
---|
1919 | - return_response=True) |
---|
1920 | - def _got((res, status, headers)): |
---|
1921 | - self.failUnlessEqual(int(status), 206) |
---|
1922 | + d = self.shouldSucceedGET(self.public_url + "/foo/bar.txt", headers=headers, |
---|
1923 | + expected_statuscode=http.PARTIAL_CONTENT, return_response=True) |
---|
1924 | + def _got((res, statuscode, headers)): |
---|
1925 | self.failUnless(headers.has_key("content-range")) |
---|
1926 | self.failUnlessEqual(headers["content-range"][0], |
---|
1927 | "bytes 1-10/%d" % len(self.BAR_CONTENTS)) |
---|
1928 | @@ -556,10 +637,9 @@ |
---|
1929 | def test_GET_FILEURL_partial_range(self): |
---|
1930 | headers = {"range": "bytes=5-"} |
---|
1931 | length = len(self.BAR_CONTENTS) |
---|
1932 | - d = self.GET(self.public_url + "/foo/bar.txt", headers=headers, |
---|
1933 | - return_response=True) |
---|
1934 | - def _got((res, status, headers)): |
---|
1935 | - self.failUnlessEqual(int(status), 206) |
---|
1936 | + d = self.shouldSucceedGET(self.public_url + "/foo/bar.txt", headers=headers, |
---|
1937 | + expected_statuscode=http.PARTIAL_CONTENT, return_response=True) |
---|
1938 | + def _got((res, statuscode, headers)): |
---|
1939 | self.failUnless(headers.has_key("content-range")) |
---|
1940 | self.failUnlessEqual(headers["content-range"][0], |
---|
1941 | "bytes 5-%d/%d" % (length-1, length)) |
---|
1942 | @@ -569,11 +649,10 @@ |
---|
1943 | |
---|
1944 | def test_HEAD_FILEURL_range(self): |
---|
1945 | headers = {"range": "bytes=1-10"} |
---|
1946 | - d = self.HEAD(self.public_url + "/foo/bar.txt", headers=headers, |
---|
1947 | - return_response=True) |
---|
1948 | - def _got((res, status, headers)): |
---|
1949 | + d = self.shouldSucceedHEAD(self.public_url + "/foo/bar.txt", headers=headers, |
---|
1950 | + expected_statuscode=http.PARTIAL_CONTENT, return_response=True) |
---|
1951 | + def _got((res, statuscode, headers)): |
---|
1952 | self.failUnlessEqual(res, "") |
---|
1953 | - self.failUnlessEqual(int(status), 206) |
---|
1954 | self.failUnless(headers.has_key("content-range")) |
---|
1955 | self.failUnlessEqual(headers["content-range"][0], |
---|
1956 | "bytes 1-10/%d" % len(self.BAR_CONTENTS)) |
---|
1957 | @@ -583,10 +662,9 @@ |
---|
1958 | def test_HEAD_FILEURL_partial_range(self): |
---|
1959 | headers = {"range": "bytes=5-"} |
---|
1960 | length = len(self.BAR_CONTENTS) |
---|
1961 | - d = self.HEAD(self.public_url + "/foo/bar.txt", headers=headers, |
---|
1962 | - return_response=True) |
---|
1963 | - def _got((res, status, headers)): |
---|
1964 | - self.failUnlessEqual(int(status), 206) |
---|
1965 | + d = self.shouldSucceedHEAD(self.public_url + "/foo/bar.txt", headers=headers, |
---|
1966 | + expected_statuscode=http.PARTIAL_CONTENT, return_response=True) |
---|
1967 | + def _got((res, statuscode, headers)): |
---|
1968 | self.failUnless(headers.has_key("content-range")) |
---|
1969 | self.failUnlessEqual(headers["content-range"][0], |
---|
1970 | "bytes 5-%d/%d" % (length-1, length)) |
---|
1971 | @@ -595,7 +673,7 @@ |
---|
1972 | |
---|
1973 | def test_GET_FILEURL_range_bad(self): |
---|
1974 | headers = {"range": "BOGUS=fizbop-quarnak"} |
---|
1975 | - d = self.shouldFail2(error.Error, "test_GET_FILEURL_range_bad", |
---|
1976 | + d = self.shouldFail2(error.Error, "GET_FILEURL_range_bad", |
---|
1977 | "400 Bad Request", |
---|
1978 | "Syntactically invalid http range header", |
---|
1979 | self.GET, self.public_url + "/foo/bar.txt", |
---|
1980 | @@ -603,8 +681,9 @@ |
---|
1981 | return d |
---|
1982 | |
---|
1983 | def test_HEAD_FILEURL(self): |
---|
1984 | - d = self.HEAD(self.public_url + "/foo/bar.txt", return_response=True) |
---|
1985 | - def _got((res, status, headers)): |
---|
1986 | + d = self.shouldSucceedHEAD(self.public_url + "/foo/bar.txt", |
---|
1987 | + expected_statuscode=http.OK, return_response=True) |
---|
1988 | + def _got((res, statuscode, headers)): |
---|
1989 | self.failUnlessEqual(res, "") |
---|
1990 | self.failUnlessEqual(headers["content-length"][0], |
---|
1991 | str(len(self.BAR_CONTENTS))) |
---|
1992 | @@ -615,27 +694,27 @@ |
---|
1993 | def test_GET_FILEURL_named(self): |
---|
1994 | base = "/file/%s" % urllib.quote(self._bar_txt_uri) |
---|
1995 | base2 = "/named/%s" % urllib.quote(self._bar_txt_uri) |
---|
1996 | - d = self.GET(base + "/@@name=/blah.txt") |
---|
1997 | + d = self.shouldSucceedGET(base + "/@@name=/blah.txt") |
---|
1998 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
1999 | - d.addCallback(lambda res: self.GET(base + "/blah.txt")) |
---|
2000 | + d.addCallback(lambda res: self.shouldSucceedGET(base + "/blah.txt")) |
---|
2001 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
2002 | - d.addCallback(lambda res: self.GET(base + "/ignore/lots/blah.txt")) |
---|
2003 | + d.addCallback(lambda res: self.shouldSucceedGET(base + "/ignore/lots/blah.txt")) |
---|
2004 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
2005 | - d.addCallback(lambda res: self.GET(base2 + "/@@name=/blah.txt")) |
---|
2006 | + d.addCallback(lambda res: self.shouldSucceedGET(base2 + "/@@name=/blah.txt")) |
---|
2007 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
2008 | save_url = base + "?save=true&filename=blah.txt" |
---|
2009 | - d.addCallback(lambda res: self.GET(save_url)) |
---|
2010 | + d.addCallback(lambda res: self.shouldSucceedGET(save_url)) |
---|
2011 | d.addCallback(self.failUnlessIsBarDotTxt) # TODO: check headers |
---|
2012 | u_filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t |
---|
2013 | u_fn_e = urllib.quote(u_filename.encode("utf-8")) |
---|
2014 | u_url = base + "?save=true&filename=" + u_fn_e |
---|
2015 | - d.addCallback(lambda res: self.GET(u_url)) |
---|
2016 | + d.addCallback(lambda res: self.shouldSucceedGET(u_url)) |
---|
2017 | d.addCallback(self.failUnlessIsBarDotTxt) # TODO: check headers |
---|
2018 | return d |
---|
2019 | |
---|
2020 | def test_PUT_FILEURL_named_bad(self): |
---|
2021 | base = "/file/%s" % urllib.quote(self._bar_txt_uri) |
---|
2022 | - d = self.shouldFail2(error.Error, "test_PUT_FILEURL_named_bad", |
---|
2023 | + d = self.shouldFail2(error.Error, "PUT_FILEURL_named_bad", |
---|
2024 | "400 Bad Request", |
---|
2025 | "/file can only be used with GET or HEAD", |
---|
2026 | self.PUT, base + "/@@name=/blah.txt", "") |
---|
2027 | @@ -643,14 +722,14 @@ |
---|
2028 | |
---|
2029 | def test_GET_DIRURL_named_bad(self): |
---|
2030 | base = "/file/%s" % urllib.quote(self._foo_uri) |
---|
2031 | - d = self.shouldFail2(error.Error, "test_PUT_DIRURL_named_bad", |
---|
2032 | + d = self.shouldFail2(error.Error, "PUT_DIRURL_named_bad", |
---|
2033 | "400 Bad Request", |
---|
2034 | "is not a file-cap", |
---|
2035 | self.GET, base + "/@@name=/blah.txt") |
---|
2036 | return d |
---|
2037 | |
---|
2038 | def test_GET_slash_file_bad(self): |
---|
2039 | - d = self.shouldFail2(error.Error, "test_GET_slash_file_bad", |
---|
2040 | + d = self.shouldFail2(error.Error, "GET_slash_file_bad", |
---|
2041 | "404 Not Found", |
---|
2042 | "/file must be followed by a file-cap and a name", |
---|
2043 | self.GET, "/file") |
---|
2044 | @@ -671,7 +750,7 @@ |
---|
2045 | verifier_cap = n.get_verify_cap().to_string() |
---|
2046 | base = "/uri/%s" % urllib.quote(verifier_cap) |
---|
2047 | # client.create_node_from_uri() can't handle verify-caps |
---|
2048 | - d = self.shouldFail2(error.Error, "test_GET_unhandled_URI", |
---|
2049 | + d = self.shouldFail2(error.Error, "GET_unhandled_URI", |
---|
2050 | "400 Bad Request", |
---|
2051 | "GET unknown URI type: can only do t=info", |
---|
2052 | self.GET, base) |
---|
2053 | @@ -679,14 +758,14 @@ |
---|
2054 | |
---|
2055 | def test_GET_FILE_URI(self): |
---|
2056 | base = "/uri/%s" % urllib.quote(self._bar_txt_uri) |
---|
2057 | - d = self.GET(base) |
---|
2058 | + d = self.shouldSucceedGET(base) |
---|
2059 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
2060 | return d |
---|
2061 | |
---|
2062 | def test_GET_FILE_URI_badchild(self): |
---|
2063 | base = "/uri/%s/boguschild" % urllib.quote(self._bar_txt_uri) |
---|
2064 | errmsg = "Files have no children, certainly not named 'boguschild'" |
---|
2065 | - d = self.shouldFail2(error.Error, "test_GET_FILE_URI_badchild", |
---|
2066 | + d = self.shouldFail2(error.Error, "GET_FILE_URI_badchild", |
---|
2067 | "400 Bad Request", errmsg, |
---|
2068 | self.GET, base) |
---|
2069 | return d |
---|
2070 | @@ -694,35 +773,42 @@ |
---|
2071 | def test_PUT_FILE_URI_badchild(self): |
---|
2072 | base = "/uri/%s/boguschild" % urllib.quote(self._bar_txt_uri) |
---|
2073 | errmsg = "Cannot create directory 'boguschild', because its parent is a file, not a directory" |
---|
2074 | - d = self.shouldFail2(error.Error, "test_GET_FILE_URI_badchild", |
---|
2075 | + d = self.shouldFail2(error.Error, "GET_FILE_URI_badchild", |
---|
2076 | "400 Bad Request", errmsg, |
---|
2077 | self.PUT, base, "") |
---|
2078 | return d |
---|
2079 | |
---|
2080 | + # TODO: version of this with a Unicode filename |
---|
2081 | def test_GET_FILEURL_save(self): |
---|
2082 | - d = self.GET(self.public_url + "/foo/bar.txt?filename=bar.txt&save=true") |
---|
2083 | - # TODO: look at the headers, expect a Content-Disposition: attachment |
---|
2084 | - # header. |
---|
2085 | - d.addCallback(self.failUnlessIsBarDotTxt) |
---|
2086 | + d = self.shouldSucceedGET(self.public_url + "/foo/bar.txt?filename=bar.txt&save=true", |
---|
2087 | + return_response=True) |
---|
2088 | + def _got((res, statuscode, headers)): |
---|
2089 | + content_disposition = headers["content-disposition"][0] |
---|
2090 | + self.failUnless(content_disposition == 'attachment; filename="bar.txt"', content_disposition) |
---|
2091 | + self.failUnlessIsBarDotTxt(res) |
---|
2092 | + d.addCallback(_got) |
---|
2093 | return d |
---|
2094 | |
---|
2095 | def test_GET_FILEURL_missing(self): |
---|
2096 | d = self.GET(self.public_url + "/foo/missing") |
---|
2097 | - d.addBoth(self.should404, "test_GET_FILEURL_missing") |
---|
2098 | + d.addBoth(self.should404, "GET_FILEURL_missing") |
---|
2099 | return d |
---|
2100 | |
---|
2101 | def test_PUT_overwrite_only_files(self): |
---|
2102 | # create a directory, put a file in that directory. |
---|
2103 | contents, n, filecap = self.makefile(8) |
---|
2104 | - d = self.PUT(self.public_url + "/foo/dir?t=mkdir", "") |
---|
2105 | + d = self.shouldSucceed("PUT_overwrite_only_files_1", http.OK, self.PUT, |
---|
2106 | + self.public_url + "/foo/dir?t=mkdir", "") |
---|
2107 | d.addCallback(lambda res: |
---|
2108 | - self.PUT(self.public_url + "/foo/dir/file1.txt", |
---|
2109 | - self.NEWFILE_CONTENTS)) |
---|
2110 | + self.shouldSucceed("PUT_overwrite_only_files_2", http.OK, self.PUT, |
---|
2111 | + self.public_url + "/foo/dir/file1.txt", |
---|
2112 | + self.NEWFILE_CONTENTS)) |
---|
2113 | # try to overwrite the file with replace=only-files |
---|
2114 | # (this should work) |
---|
2115 | d.addCallback(lambda res: |
---|
2116 | - self.PUT(self.public_url + "/foo/dir/file1.txt?t=uri&replace=only-files", |
---|
2117 | - filecap)) |
---|
2118 | + self.shouldSucceed("PUT_overwrite_only_files_3", http.OK, self.PUT, |
---|
2119 | + self.public_url + "/foo/dir/file1.txt?t=uri&replace=only-files", |
---|
2120 | + filecap)) |
---|
2121 | d.addCallback(lambda res: |
---|
2122 | self.shouldFail2(error.Error, "PUT_bad_t", "409 Conflict", |
---|
2123 | "There was already a child by that name, and you asked me " |
---|
2124 | @@ -732,21 +818,19 @@ |
---|
2125 | return d |
---|
2126 | |
---|
2127 | def test_PUT_NEWFILEURL(self): |
---|
2128 | - d = self.PUT(self.public_url + "/foo/new.txt", self.NEWFILE_CONTENTS) |
---|
2129 | - # TODO: we lose the response code, so we can't check this |
---|
2130 | - #self.failUnlessEqual(responsecode, 201) |
---|
2131 | - d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt") |
---|
2132 | + d = self.shouldSucceed("PUT_NEWFILEURL", http.CREATED, self.PUT, |
---|
2133 | + self.public_url + "/foo/new.txt", self.NEWFILE_CONTENTS) |
---|
2134 | + d.addCallback(self.failUnlessURIMatchesROChild, self._foo_node, u"new.txt") |
---|
2135 | d.addCallback(lambda res: |
---|
2136 | self.failUnlessChildContentsAre(self._foo_node, u"new.txt", |
---|
2137 | self.NEWFILE_CONTENTS)) |
---|
2138 | return d |
---|
2139 | |
---|
2140 | def test_PUT_NEWFILEURL_not_mutable(self): |
---|
2141 | - d = self.PUT(self.public_url + "/foo/new.txt?mutable=false", |
---|
2142 | - self.NEWFILE_CONTENTS) |
---|
2143 | - # TODO: we lose the response code, so we can't check this |
---|
2144 | - #self.failUnlessEqual(responsecode, 201) |
---|
2145 | - d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt") |
---|
2146 | + d = self.shouldSucceed("PUT_NEWFILEURL_not_mutable", http.CREATED, self.PUT, |
---|
2147 | + self.public_url + "/foo/new.txt?mutable=false", |
---|
2148 | + self.NEWFILE_CONTENTS) |
---|
2149 | + d.addCallback(self.failUnlessURIMatchesROChild, self._foo_node, u"new.txt") |
---|
2150 | d.addCallback(lambda res: |
---|
2151 | self.failUnlessChildContentsAre(self._foo_node, u"new.txt", |
---|
2152 | self.NEWFILE_CONTENTS)) |
---|
2153 | @@ -755,7 +839,7 @@ |
---|
2154 | def test_PUT_NEWFILEURL_range_bad(self): |
---|
2155 | headers = {"content-range": "bytes 1-10/%d" % len(self.NEWFILE_CONTENTS)} |
---|
2156 | target = self.public_url + "/foo/new.txt" |
---|
2157 | - d = self.shouldFail2(error.Error, "test_PUT_NEWFILEURL_range_bad", |
---|
2158 | + d = self.shouldFail2(error.Error, "PUT_NEWFILEURL_range_bad", |
---|
2159 | "501 Not Implemented", |
---|
2160 | "Content-Range in PUT not yet supported", |
---|
2161 | # (and certainly not for immutable files) |
---|
2162 | @@ -766,17 +850,16 @@ |
---|
2163 | return d |
---|
2164 | |
---|
2165 | def test_PUT_NEWFILEURL_mutable(self): |
---|
2166 | - d = self.PUT(self.public_url + "/foo/new.txt?mutable=true", |
---|
2167 | - self.NEWFILE_CONTENTS) |
---|
2168 | - # TODO: we lose the response code, so we can't check this |
---|
2169 | - #self.failUnlessEqual(responsecode, 201) |
---|
2170 | + d = self.shouldSucceed("PUT_NEWFILEURL_mutable", http.CREATED, self.PUT, |
---|
2171 | + self.public_url + "/foo/new.txt?mutable=true", |
---|
2172 | + self.NEWFILE_CONTENTS) |
---|
2173 | def _check_uri(res): |
---|
2174 | u = uri.from_string_mutable_filenode(res) |
---|
2175 | self.failUnless(u.is_mutable()) |
---|
2176 | self.failIf(u.is_readonly()) |
---|
2177 | return res |
---|
2178 | d.addCallback(_check_uri) |
---|
2179 | - d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt") |
---|
2180 | + d.addCallback(self.failUnlessURIMatchesRWChild, self._foo_node, u"new.txt") |
---|
2181 | d.addCallback(lambda res: |
---|
2182 | self.failUnlessMutableChildContentsAre(self._foo_node, |
---|
2183 | u"new.txt", |
---|
2184 | @@ -784,7 +867,7 @@ |
---|
2185 | return d |
---|
2186 | |
---|
2187 | def test_PUT_NEWFILEURL_mutable_toobig(self): |
---|
2188 | - d = self.shouldFail2(error.Error, "test_PUT_NEWFILEURL_mutable_toobig", |
---|
2189 | + d = self.shouldFail2(error.Error, "PUT_NEWFILEURL_mutable_toobig", |
---|
2190 | "413 Request Entity Too Large", |
---|
2191 | "SDMF is limited to one segment, and 10001 > 10000", |
---|
2192 | self.PUT, |
---|
2193 | @@ -793,10 +876,9 @@ |
---|
2194 | return d |
---|
2195 | |
---|
2196 | def test_PUT_NEWFILEURL_replace(self): |
---|
2197 | - d = self.PUT(self.public_url + "/foo/bar.txt", self.NEWFILE_CONTENTS) |
---|
2198 | - # TODO: we lose the response code, so we can't check this |
---|
2199 | - #self.failUnlessEqual(responsecode, 200) |
---|
2200 | - d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt") |
---|
2201 | + d = self.shouldSucceed("PUT_NEWFILEURL_replace", http.OK, self.PUT, |
---|
2202 | + self.public_url + "/foo/bar.txt", self.NEWFILE_CONTENTS) |
---|
2203 | + d.addCallback(self.failUnlessURIMatchesROChild, self._foo_node, u"bar.txt") |
---|
2204 | d.addCallback(lambda res: |
---|
2205 | self.failUnlessChildContentsAre(self._foo_node, u"bar.txt", |
---|
2206 | self.NEWFILE_CONTENTS)) |
---|
2207 | @@ -819,9 +901,11 @@ |
---|
2208 | return d |
---|
2209 | |
---|
2210 | def test_PUT_NEWFILEURL_mkdirs(self): |
---|
2211 | - d = self.PUT(self.public_url + "/foo/newdir/new.txt", self.NEWFILE_CONTENTS) |
---|
2212 | + d = self.shouldSucceed("PUT_NEWFILEURL_mkdirs", http.OK, self.PUT, |
---|
2213 | + self.public_url + "/foo/newdir/new.txt", |
---|
2214 | + self.NEWFILE_CONTENTS) |
---|
2215 | fn = self._foo_node |
---|
2216 | - d.addCallback(self.failUnlessURIMatchesChild, fn, u"newdir/new.txt") |
---|
2217 | + d.addCallback(self.failUnlessURIMatchesROChild, fn, u"newdir/new.txt") |
---|
2218 | d.addCallback(lambda res: self.failIfNodeHasChild(fn, u"new.txt")) |
---|
2219 | d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, u"newdir")) |
---|
2220 | d.addCallback(lambda res: |
---|
2221 | @@ -839,26 +923,27 @@ |
---|
2222 | |
---|
2223 | def test_PUT_NEWFILEURL_emptyname(self): |
---|
2224 | # an empty pathname component (i.e. a double-slash) is disallowed |
---|
2225 | - d = self.shouldFail2(error.Error, "test_PUT_NEWFILEURL_emptyname", |
---|
2226 | + d = self.shouldFail2(error.Error, "PUT_NEWFILEURL_emptyname", |
---|
2227 | "400 Bad Request", |
---|
2228 | "The webapi does not allow empty pathname components", |
---|
2229 | self.PUT, self.public_url + "/foo//new.txt", "") |
---|
2230 | return d |
---|
2231 | |
---|
2232 | def test_DELETE_FILEURL(self): |
---|
2233 | - d = self.DELETE(self.public_url + "/foo/bar.txt") |
---|
2234 | + d = self.shouldSucceed("DELETE_FILEURL", http.OK, self.DELETE, |
---|
2235 | + self.public_url + "/foo/bar.txt") |
---|
2236 | d.addCallback(lambda res: |
---|
2237 | self.failIfNodeHasChild(self._foo_node, u"bar.txt")) |
---|
2238 | return d |
---|
2239 | |
---|
2240 | def test_DELETE_FILEURL_missing(self): |
---|
2241 | d = self.DELETE(self.public_url + "/foo/missing") |
---|
2242 | - d.addBoth(self.should404, "test_DELETE_FILEURL_missing") |
---|
2243 | + d.addBoth(self.should404, "DELETE_FILEURL_missing") |
---|
2244 | return d |
---|
2245 | |
---|
2246 | def test_DELETE_FILEURL_missing2(self): |
---|
2247 | d = self.DELETE(self.public_url + "/missing/missing") |
---|
2248 | - d.addBoth(self.should404, "test_DELETE_FILEURL_missing2") |
---|
2249 | + d.addBoth(self.should404, "DELETE_FILEURL_missing2") |
---|
2250 | return d |
---|
2251 | |
---|
2252 | def failUnlessHasBarDotTxtMetadata(self, res): |
---|
2253 | @@ -875,7 +960,7 @@ |
---|
2254 | # I can't do "GET /path?json", I have to do "GET /path/t=json" |
---|
2255 | # instead. This may make it tricky to emulate the S3 interface |
---|
2256 | # completely. |
---|
2257 | - d = self.GET(self.public_url + "/foo/bar.txt?t=json") |
---|
2258 | + d = self.shouldSucceedGET(self.public_url + "/foo/bar.txt?t=json") |
---|
2259 | def _check1(data): |
---|
2260 | self.failUnlessIsBarJSON(data) |
---|
2261 | self.failUnlessHasBarDotTxtMetadata(data) |
---|
2262 | @@ -885,16 +970,16 @@ |
---|
2263 | |
---|
2264 | def test_GET_FILEURL_json_missing(self): |
---|
2265 | d = self.GET(self.public_url + "/foo/missing?json") |
---|
2266 | - d.addBoth(self.should404, "test_GET_FILEURL_json_missing") |
---|
2267 | + d.addBoth(self.should404, "GET_FILEURL_json_missing") |
---|
2268 | return d |
---|
2269 | |
---|
2270 | def test_GET_FILEURL_uri(self): |
---|
2271 | - d = self.GET(self.public_url + "/foo/bar.txt?t=uri") |
---|
2272 | + d = self.shouldSucceedGET(self.public_url + "/foo/bar.txt?t=uri") |
---|
2273 | def _check(res): |
---|
2274 | self.failUnlessEqual(res, self._bar_txt_uri) |
---|
2275 | d.addCallback(_check) |
---|
2276 | d.addCallback(lambda res: |
---|
2277 | - self.GET(self.public_url + "/foo/bar.txt?t=readonly-uri")) |
---|
2278 | + self.shouldSucceedGET(self.public_url + "/foo/bar.txt?t=readonly-uri")) |
---|
2279 | def _check2(res): |
---|
2280 | # for now, for files, uris and readonly-uris are the same |
---|
2281 | self.failUnlessEqual(res, self._bar_txt_uri) |
---|
2282 | @@ -910,14 +995,14 @@ |
---|
2283 | |
---|
2284 | def test_GET_FILEURL_uri_missing(self): |
---|
2285 | d = self.GET(self.public_url + "/foo/missing?t=uri") |
---|
2286 | - d.addBoth(self.should404, "test_GET_FILEURL_uri_missing") |
---|
2287 | + d.addBoth(self.should404, "GET_FILEURL_uri_missing") |
---|
2288 | return d |
---|
2289 | |
---|
2290 | def test_GET_DIRURL(self): |
---|
2291 | # the addSlash means we get a redirect here |
---|
2292 | # from /uri/$URI/foo/ , we need ../../../ to get back to the root |
---|
2293 | ROOT = "../../.." |
---|
2294 | - d = self.GET(self.public_url + "/foo", followRedirect=True) |
---|
2295 | + d = self.shouldSucceedGET(self.public_url + "/foo", followRedirect=True) |
---|
2296 | def _check(res): |
---|
2297 | self.failUnless(('<a href="%s">Return to Welcome page' % ROOT) |
---|
2298 | in res, res) |
---|
2299 | @@ -954,9 +1039,9 @@ |
---|
2300 | self.failUnless(re.search(get_sub, res), res) |
---|
2301 | d.addCallback(_check) |
---|
2302 | |
---|
2303 | - # look at a directory which is readonly |
---|
2304 | + # look at a readonly directory |
---|
2305 | d.addCallback(lambda res: |
---|
2306 | - self.GET(self.public_url + "/reedownlee", followRedirect=True)) |
---|
2307 | + self.shouldSucceedGET(self.public_url + "/reedownlee", followRedirect=True)) |
---|
2308 | def _check2(res): |
---|
2309 | self.failUnless("(read-only)" in res, res) |
---|
2310 | self.failIf("Upload a file" in res, res) |
---|
2311 | @@ -964,14 +1049,14 @@ |
---|
2312 | |
---|
2313 | # and at a directory that contains a readonly directory |
---|
2314 | d.addCallback(lambda res: |
---|
2315 | - self.GET(self.public_url, followRedirect=True)) |
---|
2316 | + self.shouldSucceedGET(self.public_url, followRedirect=True)) |
---|
2317 | def _check3(res): |
---|
2318 | self.failUnless(re.search('<td>DIR-RO</td>' |
---|
2319 | r'\s+<td><a href="[\.\/]+/uri/URI%3ADIR2-RO%3A[^"]+">reedownlee</a></td>', res), res) |
---|
2320 | d.addCallback(_check3) |
---|
2321 | |
---|
2322 | # and an empty directory |
---|
2323 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty/")) |
---|
2324 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/empty/")) |
---|
2325 | def _check4(res): |
---|
2326 | self.failUnless("directory is empty" in res, res) |
---|
2327 | MKDIR_BUTTON_RE=re.compile('<input type="hidden" name="t" value="mkdir" />.*<legend class="freeform-form-label">Create a new directory in this directory</legend>.*<input type="submit" value="Create" />', re.I) |
---|
2328 | @@ -981,7 +1066,7 @@ |
---|
2329 | return d |
---|
2330 | |
---|
2331 | def test_GET_DIRURL_badtype(self): |
---|
2332 | - d = self.shouldHTTPError("test_GET_DIRURL_badtype", |
---|
2333 | + d = self.shouldHTTPError("GET_DIRURL_badtype", |
---|
2334 | 400, "Bad Request", |
---|
2335 | "bad t=bogus", |
---|
2336 | self.GET, |
---|
2337 | @@ -989,14 +1074,14 @@ |
---|
2338 | return d |
---|
2339 | |
---|
2340 | def test_GET_DIRURL_json(self): |
---|
2341 | - d = self.GET(self.public_url + "/foo?t=json") |
---|
2342 | + d = self.shouldSucceedGET(self.public_url + "/foo?t=json") |
---|
2343 | d.addCallback(self.failUnlessIsFooJSON) |
---|
2344 | return d |
---|
2345 | |
---|
2346 | |
---|
2347 | def test_POST_DIRURL_manifest_no_ophandle(self): |
---|
2348 | d = self.shouldFail2(error.Error, |
---|
2349 | - "test_POST_DIRURL_manifest_no_ophandle", |
---|
2350 | + "POST_DIRURL_manifest_no_ophandle", |
---|
2351 | "400 Bad Request", |
---|
2352 | "slow operation requires ophandle=", |
---|
2353 | self.POST, self.public_url, t="start-manifest") |
---|
2354 | @@ -1005,8 +1090,9 @@ |
---|
2355 | def test_POST_DIRURL_manifest(self): |
---|
2356 | d = defer.succeed(None) |
---|
2357 | def getman(ignored, output): |
---|
2358 | - d = self.POST(self.public_url + "/foo/?t=start-manifest&ophandle=125", |
---|
2359 | - followRedirect=True) |
---|
2360 | + d = self.shouldSucceed("POST_DIRURL_manifest", http.OK, self.POST, |
---|
2361 | + self.public_url + "/foo/?t=start-manifest&ophandle=125", |
---|
2362 | + followRedirect=True) |
---|
2363 | d.addCallback(self.wait_for_operation, "125") |
---|
2364 | d.addCallback(self.get_operation_results, "125", output) |
---|
2365 | return d |
---|
2366 | @@ -1019,7 +1105,7 @@ |
---|
2367 | d.addCallback(_got_html) |
---|
2368 | |
---|
2369 | # both t=status and unadorned GET should be identical |
---|
2370 | - d.addCallback(lambda res: self.GET("/operations/125")) |
---|
2371 | + d.addCallback(lambda res: self.shouldSucceedGET("/operations/125")) |
---|
2372 | d.addCallback(_got_html) |
---|
2373 | |
---|
2374 | d.addCallback(getman, "html") |
---|
2375 | @@ -1047,15 +1133,16 @@ |
---|
2376 | |
---|
2377 | def test_POST_DIRURL_deepsize_no_ophandle(self): |
---|
2378 | d = self.shouldFail2(error.Error, |
---|
2379 | - "test_POST_DIRURL_deepsize_no_ophandle", |
---|
2380 | + "POST_DIRURL_deepsize_no_ophandle", |
---|
2381 | "400 Bad Request", |
---|
2382 | "slow operation requires ophandle=", |
---|
2383 | self.POST, self.public_url, t="start-deep-size") |
---|
2384 | return d |
---|
2385 | |
---|
2386 | def test_POST_DIRURL_deepsize(self): |
---|
2387 | - d = self.POST(self.public_url + "/foo/?t=start-deep-size&ophandle=126", |
---|
2388 | - followRedirect=True) |
---|
2389 | + d = self.shouldSucceed("POST_DIRURL_deepsize", http.OK, self.POST, |
---|
2390 | + self.public_url + "/foo/?t=start-deep-size&ophandle=126", |
---|
2391 | + followRedirect=True) |
---|
2392 | d.addCallback(self.wait_for_operation, "126") |
---|
2393 | d.addCallback(self.get_operation_results, "126", "json") |
---|
2394 | def _got_json(data): |
---|
2395 | @@ -1075,15 +1162,16 @@ |
---|
2396 | |
---|
2397 | def test_POST_DIRURL_deepstats_no_ophandle(self): |
---|
2398 | d = self.shouldFail2(error.Error, |
---|
2399 | - "test_POST_DIRURL_deepstats_no_ophandle", |
---|
2400 | + "POST_DIRURL_deepstats_no_ophandle", |
---|
2401 | "400 Bad Request", |
---|
2402 | "slow operation requires ophandle=", |
---|
2403 | self.POST, self.public_url, t="start-deep-stats") |
---|
2404 | return d |
---|
2405 | |
---|
2406 | def test_POST_DIRURL_deepstats(self): |
---|
2407 | - d = self.POST(self.public_url + "/foo/?t=start-deep-stats&ophandle=127", |
---|
2408 | - followRedirect=True) |
---|
2409 | + d = self.shouldSucceed("POST_DIRURL_deepstats", http.OK, self.POST, |
---|
2410 | + self.public_url + "/foo/?t=start-deep-stats&ophandle=127", |
---|
2411 | + followRedirect=True) |
---|
2412 | d.addCallback(self.wait_for_operation, "127") |
---|
2413 | d.addCallback(self.get_operation_results, "127", "json") |
---|
2414 | def _got_json(stats): |
---|
2415 | @@ -1109,7 +1197,8 @@ |
---|
2416 | return d |
---|
2417 | |
---|
2418 | def test_POST_DIRURL_stream_manifest(self): |
---|
2419 | - d = self.POST(self.public_url + "/foo/?t=stream-manifest") |
---|
2420 | + d = self.shouldSucceed("POST_DIRURL_stream_manifest", http.OK, self.POST, |
---|
2421 | + self.public_url + "/foo/?t=stream-manifest") |
---|
2422 | def _check(res): |
---|
2423 | self.failUnless(res.endswith("\n")) |
---|
2424 | units = [simplejson.loads(t) for t in res[:-1].split("\n")] |
---|
2425 | @@ -1129,21 +1218,22 @@ |
---|
2426 | return d |
---|
2427 | |
---|
2428 | def test_GET_DIRURL_uri(self): |
---|
2429 | - d = self.GET(self.public_url + "/foo?t=uri") |
---|
2430 | + d = self.shouldSucceedGET(self.public_url + "/foo?t=uri") |
---|
2431 | def _check(res): |
---|
2432 | self.failUnlessEqual(res, self._foo_uri) |
---|
2433 | d.addCallback(_check) |
---|
2434 | return d |
---|
2435 | |
---|
2436 | def test_GET_DIRURL_readonly_uri(self): |
---|
2437 | - d = self.GET(self.public_url + "/foo?t=readonly-uri") |
---|
2438 | + d = self.shouldSucceedGET(self.public_url + "/foo?t=readonly-uri") |
---|
2439 | def _check(res): |
---|
2440 | self.failUnlessEqual(res, self._foo_readonly_uri) |
---|
2441 | d.addCallback(_check) |
---|
2442 | return d |
---|
2443 | |
---|
2444 | def test_PUT_NEWDIRURL(self): |
---|
2445 | - d = self.PUT(self.public_url + "/foo/newdir?t=mkdir", "") |
---|
2446 | + d = self.shouldSucceed("PUT_NEWDIRURL", http.OK, self.PUT, |
---|
2447 | + self.public_url + "/foo/newdir?t=mkdir", "") |
---|
2448 | d.addCallback(lambda res: |
---|
2449 | self.failUnlessNodeHasChild(self._foo_node, u"newdir")) |
---|
2450 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
2451 | @@ -1151,7 +1241,8 @@ |
---|
2452 | return d |
---|
2453 | |
---|
2454 | def test_POST_NEWDIRURL(self): |
---|
2455 | - d = self.POST2(self.public_url + "/foo/newdir?t=mkdir", "") |
---|
2456 | + d = self.shouldSucceed("POST_NEWDIRURL", http.OK, self.POST2, |
---|
2457 | + self.public_url + "/foo/newdir?t=mkdir", "") |
---|
2458 | d.addCallback(lambda res: |
---|
2459 | self.failUnlessNodeHasChild(self._foo_node, u"newdir")) |
---|
2460 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
2461 | @@ -1160,30 +1251,41 @@ |
---|
2462 | |
---|
2463 | def test_POST_NEWDIRURL_emptyname(self): |
---|
2464 | # an empty pathname component (i.e. a double-slash) is disallowed |
---|
2465 | - d = self.shouldFail2(error.Error, "test_POST_NEWDIRURL_emptyname", |
---|
2466 | + d = self.shouldFail2(error.Error, "POST_NEWDIRURL_emptyname", |
---|
2467 | "400 Bad Request", |
---|
2468 | "The webapi does not allow empty pathname components, i.e. a double slash", |
---|
2469 | self.POST, self.public_url + "//?t=mkdir") |
---|
2470 | return d |
---|
2471 | |
---|
2472 | def test_POST_NEWDIRURL_initial_children(self): |
---|
2473 | - (newkids, filecap1, filecap2, filecap3, |
---|
2474 | - dircap) = self._create_initial_children() |
---|
2475 | - d = self.POST2(self.public_url + "/foo/newdir?t=mkdir-with-children", |
---|
2476 | - simplejson.dumps(newkids)) |
---|
2477 | + (newkids, caps) = self._create_initial_children() |
---|
2478 | + d = self.shouldSucceed("POST_NEWDIRURL_initial_children", http.OK, self.POST2, |
---|
2479 | + self.public_url + "/foo/newdir?t=mkdir-with-children", |
---|
2480 | + simplejson.dumps(newkids)) |
---|
2481 | def _check(uri): |
---|
2482 | n = self.s.create_node_from_uri(uri.strip()) |
---|
2483 | d2 = self.failUnlessNodeKeysAre(n, newkids.keys()) |
---|
2484 | d2.addCallback(lambda ign: |
---|
2485 | - self.failUnlessChildURIIs(n, u"child-imm", filecap1)) |
---|
2486 | + self.failUnlessROChildURIIs(n, u"child-imm", |
---|
2487 | + caps['filecap1'])) |
---|
2488 | + d2.addCallback(lambda ign: |
---|
2489 | + self.failUnlessRWChildURIIs(n, u"child-mutable", |
---|
2490 | + caps['filecap2'])) |
---|
2491 | + d2.addCallback(lambda ign: |
---|
2492 | + self.failUnlessROChildURIIs(n, u"child-mutable-ro", |
---|
2493 | + caps['filecap3'])) |
---|
2494 | d2.addCallback(lambda ign: |
---|
2495 | - self.failUnlessChildURIIs(n, u"child-mutable", |
---|
2496 | - filecap2)) |
---|
2497 | + self.failUnlessROChildURIIs(n, u"unknownchild-ro", |
---|
2498 | + caps['unknown_rocap'])) |
---|
2499 | d2.addCallback(lambda ign: |
---|
2500 | - self.failUnlessChildURIIs(n, u"child-mutable-ro", |
---|
2501 | - filecap3)) |
---|
2502 | + self.failUnlessRWChildURIIs(n, u"unknownchild-rw", |
---|
2503 | + caps['unknown_rwcap'])) |
---|
2504 | d2.addCallback(lambda ign: |
---|
2505 | - self.failUnlessChildURIIs(n, u"dirchild", dircap)) |
---|
2506 | + self.failUnlessROChildURIIs(n, u"unknownchild-imm", |
---|
2507 | + caps['unknown_immcap'])) |
---|
2508 | + d2.addCallback(lambda ign: |
---|
2509 | + self.failUnlessRWChildURIIs(n, u"dirchild", |
---|
2510 | + caps['dircap'])) |
---|
2511 | return d2 |
---|
2512 | d.addCallback(_check) |
---|
2513 | d.addCallback(lambda res: |
---|
2514 | @@ -1191,21 +1293,26 @@ |
---|
2515 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
2516 | d.addCallback(self.failUnlessNodeKeysAre, newkids.keys()) |
---|
2517 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
2518 | - d.addCallback(self.failUnlessChildURIIs, u"child-imm", filecap1) |
---|
2519 | + d.addCallback(self.failUnlessROChildURIIs, u"child-imm", caps['filecap1']) |
---|
2520 | return d |
---|
2521 | |
---|
2522 | def test_POST_NEWDIRURL_immutable(self): |
---|
2523 | - (newkids, filecap1, immdircap) = self._create_immutable_children() |
---|
2524 | - d = self.POST2(self.public_url + "/foo/newdir?t=mkdir-immutable", |
---|
2525 | - simplejson.dumps(newkids)) |
---|
2526 | + (newkids, caps) = self._create_immutable_children() |
---|
2527 | + d = self.shouldSucceed("POST_NEWDIRURL_immutable", http.OK, self.POST2, |
---|
2528 | + self.public_url + "/foo/newdir?t=mkdir-immutable", |
---|
2529 | + simplejson.dumps(newkids)) |
---|
2530 | def _check(uri): |
---|
2531 | n = self.s.create_node_from_uri(uri.strip()) |
---|
2532 | d2 = self.failUnlessNodeKeysAre(n, newkids.keys()) |
---|
2533 | d2.addCallback(lambda ign: |
---|
2534 | - self.failUnlessChildURIIs(n, u"child-imm", filecap1)) |
---|
2535 | + self.failUnlessROChildURIIs(n, u"child-imm", |
---|
2536 | + caps['filecap1'])) |
---|
2537 | + d2.addCallback(lambda ign: |
---|
2538 | + self.failUnlessROChildURIIs(n, u"unknownchild-imm", |
---|
2539 | + caps['unknown_immcap'])) |
---|
2540 | d2.addCallback(lambda ign: |
---|
2541 | - self.failUnlessChildURIIs(n, u"dirchild-imm", |
---|
2542 | - immdircap)) |
---|
2543 | + self.failUnlessROChildURIIs(n, u"dirchild-imm", |
---|
2544 | + caps['immdircap'])) |
---|
2545 | return d2 |
---|
2546 | d.addCallback(_check) |
---|
2547 | d.addCallback(lambda res: |
---|
2548 | @@ -1213,25 +1320,27 @@ |
---|
2549 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
2550 | d.addCallback(self.failUnlessNodeKeysAre, newkids.keys()) |
---|
2551 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
2552 | - d.addCallback(self.failUnlessChildURIIs, u"child-imm", filecap1) |
---|
2553 | + d.addCallback(self.failUnlessROChildURIIs, u"child-imm", caps['filecap1']) |
---|
2554 | + d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
2555 | + d.addCallback(self.failUnlessROChildURIIs, u"unknownchild-imm", caps['unknown_immcap']) |
---|
2556 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
2557 | - d.addCallback(self.failUnlessChildURIIs, u"dirchild-imm", immdircap) |
---|
2558 | + d.addCallback(self.failUnlessROChildURIIs, u"dirchild-imm", caps['immdircap']) |
---|
2559 | d.addErrback(self.explain_web_error) |
---|
2560 | return d |
---|
2561 | |
---|
2562 | def test_POST_NEWDIRURL_immutable_bad(self): |
---|
2563 | - (newkids, filecap1, filecap2, filecap3, |
---|
2564 | - dircap) = self._create_initial_children() |
---|
2565 | - d = self.shouldFail2(error.Error, "test_POST_NEWDIRURL_immutable_bad", |
---|
2566 | + (newkids, caps) = self._create_initial_children() |
---|
2567 | + d = self.shouldFail2(error.Error, "POST_NEWDIRURL_immutable_bad", |
---|
2568 | "400 Bad Request", |
---|
2569 | - "a mkdir-immutable operation was given a child that was not itself immutable", |
---|
2570 | + "needed to be immutable but was not", |
---|
2571 | self.POST2, |
---|
2572 | self.public_url + "/foo/newdir?t=mkdir-immutable", |
---|
2573 | simplejson.dumps(newkids)) |
---|
2574 | return d |
---|
2575 | |
---|
2576 | def test_PUT_NEWDIRURL_exists(self): |
---|
2577 | - d = self.PUT(self.public_url + "/foo/sub?t=mkdir", "") |
---|
2578 | + d = self.shouldSucceed("PUT_NEWDIRURL_exists", http.OK, self.PUT, |
---|
2579 | + self.public_url + "/foo/sub?t=mkdir", "") |
---|
2580 | d.addCallback(lambda res: |
---|
2581 | self.failUnlessNodeHasChild(self._foo_node, u"sub")) |
---|
2582 | d.addCallback(lambda res: self._foo_node.get(u"sub")) |
---|
2583 | @@ -1249,18 +1358,21 @@ |
---|
2584 | d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"]) |
---|
2585 | return d |
---|
2586 | |
---|
2587 | - def test_PUT_NEWDIRURL_mkdir_p(self): |
---|
2588 | + def test_POST_NEWDIRURL_mkdir_p(self): |
---|
2589 | d = defer.succeed(None) |
---|
2590 | - d.addCallback(lambda res: self.POST(self.public_url + "/foo", t='mkdir', name='mkp')) |
---|
2591 | + d.addCallback(lambda res: self.shouldSucceed("POST_NEWDIRURL_mkdir_p-1", http.OK, self.POST, |
---|
2592 | + self.public_url + "/foo", t='mkdir', name='mkp')) |
---|
2593 | d.addCallback(lambda res: self.failUnlessNodeHasChild(self._foo_node, u"mkp")) |
---|
2594 | d.addCallback(lambda res: self._foo_node.get(u"mkp")) |
---|
2595 | def mkdir_p(mkpnode): |
---|
2596 | url = '/uri/%s?t=mkdir-p&path=/sub1/sub2' % urllib.quote(mkpnode.get_uri()) |
---|
2597 | - d = self.POST(url) |
---|
2598 | + d = self.shouldSucceed("POST_NEWDIRURL_mkdir_p-2", http.OK, self.POST, |
---|
2599 | + url) |
---|
2600 | def made_subsub(ssuri): |
---|
2601 | d = self._foo_node.get_child_at_path(u"mkp/sub1/sub2") |
---|
2602 | d.addCallback(lambda ssnode: self.failUnlessEqual(ssnode.get_uri(), ssuri)) |
---|
2603 | - d = self.POST(url) |
---|
2604 | + d = self.shouldSucceed("POST_NEWDIRURL_mkdir_p-3", http.OK, self.POST, |
---|
2605 | + url) |
---|
2606 | d.addCallback(lambda uri2: self.failUnlessEqual(uri2, ssuri)) |
---|
2607 | return d |
---|
2608 | d.addCallback(made_subsub) |
---|
2609 | @@ -1269,7 +1381,8 @@ |
---|
2610 | return d |
---|
2611 | |
---|
2612 | def test_PUT_NEWDIRURL_mkdirs(self): |
---|
2613 | - d = self.PUT(self.public_url + "/foo/subdir/newdir?t=mkdir", "") |
---|
2614 | + d = self.shouldSucceed("PUT_NEWDIRURL_mkdirs", http.OK, self.PUT, |
---|
2615 | + self.public_url + "/foo/subdir/newdir?t=mkdir", "") |
---|
2616 | d.addCallback(lambda res: |
---|
2617 | self.failIfNodeHasChild(self._foo_node, u"newdir")) |
---|
2618 | d.addCallback(lambda res: |
---|
2619 | @@ -1280,21 +1393,22 @@ |
---|
2620 | return d |
---|
2621 | |
---|
2622 | def test_DELETE_DIRURL(self): |
---|
2623 | - d = self.DELETE(self.public_url + "/foo") |
---|
2624 | + d = self.shouldSucceed("DELETE_DIRURL", http.OK, self.DELETE, |
---|
2625 | + self.public_url + "/foo") |
---|
2626 | d.addCallback(lambda res: |
---|
2627 | self.failIfNodeHasChild(self.public_root, u"foo")) |
---|
2628 | return d |
---|
2629 | |
---|
2630 | def test_DELETE_DIRURL_missing(self): |
---|
2631 | d = self.DELETE(self.public_url + "/foo/missing") |
---|
2632 | - d.addBoth(self.should404, "test_DELETE_DIRURL_missing") |
---|
2633 | + d.addBoth(self.should404, "DELETE_DIRURL_missing") |
---|
2634 | d.addCallback(lambda res: |
---|
2635 | self.failUnlessNodeHasChild(self.public_root, u"foo")) |
---|
2636 | return d |
---|
2637 | |
---|
2638 | def test_DELETE_DIRURL_missing2(self): |
---|
2639 | d = self.DELETE(self.public_url + "/missing") |
---|
2640 | - d.addBoth(self.should404, "test_DELETE_DIRURL_missing2") |
---|
2641 | + d.addBoth(self.should404, "DELETE_DIRURL_missing2") |
---|
2642 | return d |
---|
2643 | |
---|
2644 | def dump_root(self): |
---|
2645 | @@ -1346,18 +1460,44 @@ |
---|
2646 | d.addCallback(_check) |
---|
2647 | return d |
---|
2648 | |
---|
2649 | - def failUnlessChildURIIs(self, node, name, expected_uri): |
---|
2650 | + def failUnlessRWChildURIIs(self, node, name, expected_uri): |
---|
2651 | + assert isinstance(name, unicode) |
---|
2652 | + d = node.get_child_at_path(name) |
---|
2653 | + def _check(child): |
---|
2654 | + self.failUnless(child.is_unknown() or not child.is_readonly()) |
---|
2655 | + self.failUnlessEqual(child.get_uri(), expected_uri.strip()) |
---|
2656 | + expected_ro_uri = self._make_readonly(expected_uri) |
---|
2657 | + if expected_ro_uri: |
---|
2658 | + self.failUnlessEqual(child.get_readonly_uri(), expected_ro_uri.strip()) |
---|
2659 | + d.addCallback(_check) |
---|
2660 | + return d |
---|
2661 | + |
---|
2662 | + def failUnlessROChildURIIs(self, node, name, expected_uri): |
---|
2663 | assert isinstance(name, unicode) |
---|
2664 | d = node.get_child_at_path(name) |
---|
2665 | def _check(child): |
---|
2666 | + self.failUnless(child.is_unknown() or child.is_readonly()) |
---|
2667 | self.failUnlessEqual(child.get_uri(), expected_uri.strip()) |
---|
2668 | d.addCallback(_check) |
---|
2669 | return d |
---|
2670 | |
---|
2671 | - def failUnlessURIMatchesChild(self, got_uri, node, name): |
---|
2672 | + def failUnlessURIMatchesRWChild(self, got_uri, node, name): |
---|
2673 | + assert isinstance(name, unicode) |
---|
2674 | + d = node.get_child_at_path(name) |
---|
2675 | + def _check(child): |
---|
2676 | + self.failUnless(child.is_unknown() or not child.is_readonly()) |
---|
2677 | + self.failUnlessEqual(child.get_uri(), got_uri.strip()) |
---|
2678 | + expected_ro_uri = self._make_readonly(got_uri) |
---|
2679 | + if expected_ro_uri: |
---|
2680 | + self.failUnlessEqual(child.get_readonly_uri(), expected_ro_uri.strip()) |
---|
2681 | + d.addCallback(_check) |
---|
2682 | + return d |
---|
2683 | + |
---|
2684 | + def failUnlessURIMatchesROChild(self, got_uri, node, name): |
---|
2685 | assert isinstance(name, unicode) |
---|
2686 | d = node.get_child_at_path(name) |
---|
2687 | def _check(child): |
---|
2688 | + self.failUnless(child.is_unknown() or child.is_readonly()) |
---|
2689 | self.failUnlessEqual(got_uri.strip(), child.get_uri()) |
---|
2690 | d.addCallback(_check) |
---|
2691 | return d |
---|
2692 | @@ -1366,10 +1506,11 @@ |
---|
2693 | self.failUnless(FakeCHKFileNode.all_contents[got_uri] == contents) |
---|
2694 | |
---|
2695 | def test_POST_upload(self): |
---|
2696 | - d = self.POST(self.public_url + "/foo", t="upload", |
---|
2697 | - file=("new.txt", self.NEWFILE_CONTENTS)) |
---|
2698 | + d = self.shouldSucceed("POST_upload", http.OK, self.POST, |
---|
2699 | + self.public_url + "/foo", t="upload", |
---|
2700 | + file=("new.txt", self.NEWFILE_CONTENTS)) |
---|
2701 | fn = self._foo_node |
---|
2702 | - d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt") |
---|
2703 | + d.addCallback(self.failUnlessURIMatchesROChild, fn, u"new.txt") |
---|
2704 | d.addCallback(lambda res: |
---|
2705 | self.failUnlessChildContentsAre(fn, u"new.txt", |
---|
2706 | self.NEWFILE_CONTENTS)) |
---|
2707 | @@ -1377,15 +1518,16 @@ |
---|
2708 | |
---|
2709 | def test_POST_upload_unicode(self): |
---|
2710 | filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t |
---|
2711 | - d = self.POST(self.public_url + "/foo", t="upload", |
---|
2712 | - file=(filename, self.NEWFILE_CONTENTS)) |
---|
2713 | + d = self.shouldSucceed("POST_upload_unicode", http.OK, self.POST, |
---|
2714 | + self.public_url + "/foo", t="upload", |
---|
2715 | + file=(filename, self.NEWFILE_CONTENTS)) |
---|
2716 | fn = self._foo_node |
---|
2717 | - d.addCallback(self.failUnlessURIMatchesChild, fn, filename) |
---|
2718 | + d.addCallback(self.failUnlessURIMatchesROChild, fn, filename) |
---|
2719 | d.addCallback(lambda res: |
---|
2720 | self.failUnlessChildContentsAre(fn, filename, |
---|
2721 | self.NEWFILE_CONTENTS)) |
---|
2722 | target_url = self.public_url + "/foo/" + filename.encode("utf-8") |
---|
2723 | - d.addCallback(lambda res: self.GET(target_url)) |
---|
2724 | + d.addCallback(lambda res: self.shouldSucceedGET(target_url)) |
---|
2725 | d.addCallback(lambda contents: self.failUnlessEqual(contents, |
---|
2726 | self.NEWFILE_CONTENTS, |
---|
2727 | contents)) |
---|
2728 | @@ -1393,24 +1535,26 @@ |
---|
2729 | |
---|
2730 | def test_POST_upload_unicode_named(self): |
---|
2731 | filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t |
---|
2732 | - d = self.POST(self.public_url + "/foo", t="upload", |
---|
2733 | - name=filename, |
---|
2734 | - file=("overridden", self.NEWFILE_CONTENTS)) |
---|
2735 | + d = self.shouldSucceed("POST_upload_unicode_named", http.OK, self.POST, |
---|
2736 | + self.public_url + "/foo", t="upload", |
---|
2737 | + name=filename, |
---|
2738 | + file=("overridden", self.NEWFILE_CONTENTS)) |
---|
2739 | fn = self._foo_node |
---|
2740 | - d.addCallback(self.failUnlessURIMatchesChild, fn, filename) |
---|
2741 | + d.addCallback(self.failUnlessURIMatchesROChild, fn, filename) |
---|
2742 | d.addCallback(lambda res: |
---|
2743 | self.failUnlessChildContentsAre(fn, filename, |
---|
2744 | self.NEWFILE_CONTENTS)) |
---|
2745 | target_url = self.public_url + "/foo/" + filename.encode("utf-8") |
---|
2746 | - d.addCallback(lambda res: self.GET(target_url)) |
---|
2747 | + d.addCallback(lambda res: self.shouldSucceedGET(target_url)) |
---|
2748 | d.addCallback(lambda contents: self.failUnlessEqual(contents, |
---|
2749 | self.NEWFILE_CONTENTS, |
---|
2750 | contents)) |
---|
2751 | return d |
---|
2752 | |
---|
2753 | def test_POST_upload_no_link(self): |
---|
2754 | - d = self.POST("/uri", t="upload", |
---|
2755 | - file=("new.txt", self.NEWFILE_CONTENTS)) |
---|
2756 | + d = self.shouldSucceed("POST_upload_no_link", http.OK, self.POST, |
---|
2757 | + "/uri", t="upload", |
---|
2758 | + file=("new.txt", self.NEWFILE_CONTENTS)) |
---|
2759 | def _check_upload_results(page): |
---|
2760 | # this should be a page which describes the results of the upload |
---|
2761 | # that just finished. |
---|
2762 | @@ -1449,7 +1593,7 @@ |
---|
2763 | self.failUnlessEqual(statuscode, str(http.FOUND)) |
---|
2764 | self.failUnless(target.startswith(self.webish_url), target) |
---|
2765 | return client.getPage(target, method="GET") |
---|
2766 | - d = self.shouldRedirect2("test_POST_upload_no_link_whendone_results", |
---|
2767 | + d = self.shouldRedirect2("POST_upload_no_link_whendone_results", |
---|
2768 | check, |
---|
2769 | self.POST, "/uri", t="upload", |
---|
2770 | when_done="/uri/%(uri)s", |
---|
2771 | @@ -1459,8 +1603,9 @@ |
---|
2772 | return d |
---|
2773 | |
---|
2774 | def test_POST_upload_no_link_mutable(self): |
---|
2775 | - d = self.POST("/uri", t="upload", mutable="true", |
---|
2776 | - file=("new.txt", self.NEWFILE_CONTENTS)) |
---|
2777 | + d = self.shouldSucceed("POST_upload_no_link_mutable", http.OK, self.POST, |
---|
2778 | + "/uri", t="upload", mutable="true", |
---|
2779 | + file=("new.txt", self.NEWFILE_CONTENTS)) |
---|
2780 | def _check(filecap): |
---|
2781 | filecap = filecap.strip() |
---|
2782 | self.failUnless(filecap.startswith("URI:SSK:"), filecap) |
---|
2783 | @@ -1472,11 +1617,11 @@ |
---|
2784 | d.addCallback(_check) |
---|
2785 | def _check2(data): |
---|
2786 | self.failUnlessEqual(data, self.NEWFILE_CONTENTS) |
---|
2787 | - return self.GET("/uri/%s" % urllib.quote(self.filecap)) |
---|
2788 | + return self.shouldSucceedGET("/uri/%s" % urllib.quote(self.filecap)) |
---|
2789 | d.addCallback(_check2) |
---|
2790 | def _check3(data): |
---|
2791 | self.failUnlessEqual(data, self.NEWFILE_CONTENTS) |
---|
2792 | - return self.GET("/file/%s" % urllib.quote(self.filecap)) |
---|
2793 | + return self.shouldSucceedGET("/file/%s" % urllib.quote(self.filecap)) |
---|
2794 | d.addCallback(_check3) |
---|
2795 | def _check4(data): |
---|
2796 | self.failUnlessEqual(data, self.NEWFILE_CONTENTS) |
---|
2797 | @@ -1485,7 +1630,7 @@ |
---|
2798 | |
---|
2799 | def test_POST_upload_no_link_mutable_toobig(self): |
---|
2800 | d = self.shouldFail2(error.Error, |
---|
2801 | - "test_POST_upload_no_link_mutable_toobig", |
---|
2802 | + "POST_upload_no_link_mutable_toobig", |
---|
2803 | "413 Request Entity Too Large", |
---|
2804 | "SDMF is limited to one segment, and 10001 > 10000", |
---|
2805 | self.POST, |
---|
2806 | @@ -1496,10 +1641,11 @@ |
---|
2807 | |
---|
2808 | def test_POST_upload_mutable(self): |
---|
2809 | # this creates a mutable file |
---|
2810 | - d = self.POST(self.public_url + "/foo", t="upload", mutable="true", |
---|
2811 | - file=("new.txt", self.NEWFILE_CONTENTS)) |
---|
2812 | + d = self.shouldSucceed("POST_upload_mutable", http.OK, self.POST, |
---|
2813 | + self.public_url + "/foo", t="upload", mutable="true", |
---|
2814 | + file=("new.txt", self.NEWFILE_CONTENTS)) |
---|
2815 | fn = self._foo_node |
---|
2816 | - d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt") |
---|
2817 | + d.addCallback(self.failUnlessURIMatchesRWChild, fn, u"new.txt") |
---|
2818 | d.addCallback(lambda res: |
---|
2819 | self.failUnlessMutableChildContentsAre(fn, u"new.txt", |
---|
2820 | self.NEWFILE_CONTENTS)) |
---|
2821 | @@ -1515,10 +1661,11 @@ |
---|
2822 | # now upload it again and make sure that the URI doesn't change |
---|
2823 | NEWER_CONTENTS = self.NEWFILE_CONTENTS + "newer\n" |
---|
2824 | d.addCallback(lambda res: |
---|
2825 | - self.POST(self.public_url + "/foo", t="upload", |
---|
2826 | - mutable="true", |
---|
2827 | - file=("new.txt", NEWER_CONTENTS))) |
---|
2828 | - d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt") |
---|
2829 | + self.shouldSucceed("POST_upload_mutable-again", http.OK, self.POST, |
---|
2830 | + self.public_url + "/foo", t="upload", |
---|
2831 | + mutable="true", |
---|
2832 | + file=("new.txt", NEWER_CONTENTS))) |
---|
2833 | + d.addCallback(self.failUnlessURIMatchesRWChild, fn, u"new.txt") |
---|
2834 | d.addCallback(lambda res: |
---|
2835 | self.failUnlessMutableChildContentsAre(fn, u"new.txt", |
---|
2836 | NEWER_CONTENTS)) |
---|
2837 | @@ -1533,8 +1680,9 @@ |
---|
2838 | # upload a second time, using PUT instead of POST |
---|
2839 | NEW2_CONTENTS = NEWER_CONTENTS + "overwrite with PUT\n" |
---|
2840 | d.addCallback(lambda res: |
---|
2841 | - self.PUT(self.public_url + "/foo/new.txt", NEW2_CONTENTS)) |
---|
2842 | - d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt") |
---|
2843 | + self.shouldSucceed("POST_upload_mutable-again-with-PUT", http.OK, self.PUT, |
---|
2844 | + self.public_url + "/foo/new.txt", NEW2_CONTENTS)) |
---|
2845 | + d.addCallback(self.failUnlessURIMatchesRWChild, fn, u"new.txt") |
---|
2846 | d.addCallback(lambda res: |
---|
2847 | self.failUnlessMutableChildContentsAre(fn, u"new.txt", |
---|
2848 | NEW2_CONTENTS)) |
---|
2849 | @@ -1543,8 +1691,8 @@ |
---|
2850 | # slightly differently |
---|
2851 | |
---|
2852 | d.addCallback(lambda res: |
---|
2853 | - self.GET(self.public_url + "/foo/", |
---|
2854 | - followRedirect=True)) |
---|
2855 | + self.shouldSucceedGET(self.public_url + "/foo/", |
---|
2856 | + followRedirect=True)) |
---|
2857 | def _check_page(res): |
---|
2858 | # TODO: assert more about the contents |
---|
2859 | self.failUnless("SSK" in res) |
---|
2860 | @@ -1561,8 +1709,8 @@ |
---|
2861 | |
---|
2862 | # look at the JSON form of the enclosing directory |
---|
2863 | d.addCallback(lambda res: |
---|
2864 | - self.GET(self.public_url + "/foo/?t=json", |
---|
2865 | - followRedirect=True)) |
---|
2866 | + self.shouldSucceedGET(self.public_url + "/foo/?t=json", |
---|
2867 | + followRedirect=True)) |
---|
2868 | def _check_page_json(res): |
---|
2869 | parsed = simplejson.loads(res) |
---|
2870 | self.failUnlessEqual(parsed[0], "dirnode") |
---|
2871 | @@ -1580,7 +1728,7 @@ |
---|
2872 | |
---|
2873 | # and the JSON form of the file |
---|
2874 | d.addCallback(lambda res: |
---|
2875 | - self.GET(self.public_url + "/foo/new.txt?t=json")) |
---|
2876 | + self.shouldSucceedGET(self.public_url + "/foo/new.txt?t=json")) |
---|
2877 | def _check_file_json(res): |
---|
2878 | parsed = simplejson.loads(res) |
---|
2879 | self.failUnlessEqual(parsed[0], "filenode") |
---|
2880 | @@ -1592,10 +1740,10 @@ |
---|
2881 | |
---|
2882 | # and look at t=uri and t=readonly-uri |
---|
2883 | d.addCallback(lambda res: |
---|
2884 | - self.GET(self.public_url + "/foo/new.txt?t=uri")) |
---|
2885 | + self.shouldSucceedGET(self.public_url + "/foo/new.txt?t=uri")) |
---|
2886 | d.addCallback(lambda res: self.failUnlessEqual(res, self._mutable_uri)) |
---|
2887 | d.addCallback(lambda res: |
---|
2888 | - self.GET(self.public_url + "/foo/new.txt?t=readonly-uri")) |
---|
2889 | + self.shouldSucceedGET(self.public_url + "/foo/new.txt?t=readonly-uri")) |
---|
2890 | def _check_ro_uri(res): |
---|
2891 | ro_uri = unicode(self._mutable_node.get_readonly().to_string()) |
---|
2892 | self.failUnlessEqual(res, ro_uri) |
---|
2893 | @@ -1603,15 +1751,15 @@ |
---|
2894 | |
---|
2895 | # make sure we can get to it from /uri/URI |
---|
2896 | d.addCallback(lambda res: |
---|
2897 | - self.GET("/uri/%s" % urllib.quote(self._mutable_uri))) |
---|
2898 | + self.shouldSucceedGET("/uri/%s" % urllib.quote(self._mutable_uri))) |
---|
2899 | d.addCallback(lambda res: |
---|
2900 | self.failUnlessEqual(res, NEW2_CONTENTS)) |
---|
2901 | |
---|
2902 | # and that HEAD computes the size correctly |
---|
2903 | d.addCallback(lambda res: |
---|
2904 | - self.HEAD(self.public_url + "/foo/new.txt", |
---|
2905 | - return_response=True)) |
---|
2906 | - def _got_headers((res, status, headers)): |
---|
2907 | + self.shouldSucceedHEAD(self.public_url + "/foo/new.txt", |
---|
2908 | + return_response=True)) |
---|
2909 | + def _got_headers((res, statuscode, headers)): |
---|
2910 | self.failUnlessEqual(res, "") |
---|
2911 | self.failUnlessEqual(headers["content-length"][0], |
---|
2912 | str(len(NEW2_CONTENTS))) |
---|
2913 | @@ -1621,7 +1769,7 @@ |
---|
2914 | # make sure that size errors are displayed correctly for overwrite |
---|
2915 | d.addCallback(lambda res: |
---|
2916 | self.shouldFail2(error.Error, |
---|
2917 | - "test_POST_upload_mutable-toobig", |
---|
2918 | + "POST_upload_mutable-toobig", |
---|
2919 | "413 Request Entity Too Large", |
---|
2920 | "SDMF is limited to one segment, and 10001 > 10000", |
---|
2921 | self.POST, |
---|
2922 | @@ -1636,7 +1784,7 @@ |
---|
2923 | |
---|
2924 | def test_POST_upload_mutable_toobig(self): |
---|
2925 | d = self.shouldFail2(error.Error, |
---|
2926 | - "test_POST_upload_mutable_toobig", |
---|
2927 | + "POST_upload_mutable_toobig", |
---|
2928 | "413 Request Entity Too Large", |
---|
2929 | "SDMF is limited to one segment, and 10001 > 10000", |
---|
2930 | self.POST, |
---|
2931 | @@ -1660,19 +1808,21 @@ |
---|
2932 | return f |
---|
2933 | |
---|
2934 | def test_POST_upload_replace(self): |
---|
2935 | - d = self.POST(self.public_url + "/foo", t="upload", |
---|
2936 | - file=("bar.txt", self.NEWFILE_CONTENTS)) |
---|
2937 | + d = self.shouldSucceed("POST_upload_replace", http.OK, self.POST, |
---|
2938 | + self.public_url + "/foo", t="upload", |
---|
2939 | + file=("bar.txt", self.NEWFILE_CONTENTS)) |
---|
2940 | fn = self._foo_node |
---|
2941 | - d.addCallback(self.failUnlessURIMatchesChild, fn, u"bar.txt") |
---|
2942 | + d.addCallback(self.failUnlessURIMatchesROChild, fn, u"bar.txt") |
---|
2943 | d.addCallback(lambda res: |
---|
2944 | self.failUnlessChildContentsAre(fn, u"bar.txt", |
---|
2945 | self.NEWFILE_CONTENTS)) |
---|
2946 | return d |
---|
2947 | |
---|
2948 | def test_POST_upload_no_replace_ok(self): |
---|
2949 | - d = self.POST(self.public_url + "/foo?replace=false", t="upload", |
---|
2950 | - file=("new.txt", self.NEWFILE_CONTENTS)) |
---|
2951 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/new.txt")) |
---|
2952 | + d = self.shouldSucceed("POST_upload_no_replace_ok", http.OK, self.POST, |
---|
2953 | + self.public_url + "/foo?replace=false", t="upload", |
---|
2954 | + file=("new.txt", self.NEWFILE_CONTENTS)) |
---|
2955 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/new.txt")) |
---|
2956 | d.addCallback(lambda res: self.failUnlessEqual(res, |
---|
2957 | self.NEWFILE_CONTENTS)) |
---|
2958 | return d |
---|
2959 | @@ -1685,7 +1835,7 @@ |
---|
2960 | "409 Conflict", |
---|
2961 | "There was already a child by that name, and you asked me " |
---|
2962 | "to not replace it") |
---|
2963 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt")) |
---|
2964 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/bar.txt")) |
---|
2965 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
2966 | return d |
---|
2967 | |
---|
2968 | @@ -1696,7 +1846,7 @@ |
---|
2969 | "409 Conflict", |
---|
2970 | "There was already a child by that name, and you asked me " |
---|
2971 | "to not replace it") |
---|
2972 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt")) |
---|
2973 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/bar.txt")) |
---|
2974 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
2975 | return d |
---|
2976 | |
---|
2977 | @@ -1712,9 +1862,10 @@ |
---|
2978 | |
---|
2979 | def test_POST_upload_named(self): |
---|
2980 | fn = self._foo_node |
---|
2981 | - d = self.POST(self.public_url + "/foo", t="upload", |
---|
2982 | - name="new.txt", file=self.NEWFILE_CONTENTS) |
---|
2983 | - d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt") |
---|
2984 | + d = self.shouldSucceed("POST_upload_named", http.OK, self.POST, |
---|
2985 | + self.public_url + "/foo", t="upload", |
---|
2986 | + name="new.txt", file=self.NEWFILE_CONTENTS) |
---|
2987 | + d.addCallback(self.failUnlessURIMatchesROChild, fn, u"new.txt") |
---|
2988 | d.addCallback(lambda res: |
---|
2989 | self.failUnlessChildContentsAre(fn, u"new.txt", |
---|
2990 | self.NEWFILE_CONTENTS)) |
---|
2991 | @@ -1724,7 +1875,7 @@ |
---|
2992 | d = self.POST(self.public_url + "/foo", t="upload", |
---|
2993 | name="slashes/are/bad.txt", file=self.NEWFILE_CONTENTS) |
---|
2994 | d.addBoth(self.shouldFail, error.Error, |
---|
2995 | - "test_POST_upload_named_badfilename", |
---|
2996 | + "POST_upload_named_badfilename", |
---|
2997 | "400 Bad Request", |
---|
2998 | "name= may not contain a slash", |
---|
2999 | ) |
---|
3000 | @@ -1738,7 +1889,8 @@ |
---|
3001 | |
---|
3002 | def test_POST_FILEURL_check(self): |
---|
3003 | bar_url = self.public_url + "/foo/bar.txt" |
---|
3004 | - d = self.POST(bar_url, t="check") |
---|
3005 | + d = self.shouldSucceed("POST_FILEURL_check-1", http.OK, self.POST, |
---|
3006 | + bar_url, t="check") |
---|
3007 | def _check(res): |
---|
3008 | self.failUnless("Healthy :" in res) |
---|
3009 | d.addCallback(_check) |
---|
3010 | @@ -1747,13 +1899,14 @@ |
---|
3011 | self.failUnlessEqual(statuscode, str(http.FOUND)) |
---|
3012 | self.failUnlessEqual(target, redir_url) |
---|
3013 | d.addCallback(lambda res: |
---|
3014 | - self.shouldRedirect2("test_POST_FILEURL_check", |
---|
3015 | + self.shouldRedirect2("POST_FILEURL_check-2", |
---|
3016 | _check2, |
---|
3017 | self.POST, bar_url, |
---|
3018 | t="check", |
---|
3019 | when_done=redir_url)) |
---|
3020 | d.addCallback(lambda res: |
---|
3021 | - self.POST(bar_url, t="check", return_to=redir_url)) |
---|
3022 | + self.shouldSucceed("POST_FILEURL_check-3", http.OK, self.POST, |
---|
3023 | + bar_url, t="check", return_to=redir_url)) |
---|
3024 | def _check3(res): |
---|
3025 | self.failUnless("Healthy :" in res) |
---|
3026 | self.failUnless("Return to file" in res) |
---|
3027 | @@ -1761,7 +1914,8 @@ |
---|
3028 | d.addCallback(_check3) |
---|
3029 | |
---|
3030 | d.addCallback(lambda res: |
---|
3031 | - self.POST(bar_url, t="check", output="JSON")) |
---|
3032 | + self.shouldSucceed("POST_FILEURL_check-4", http.OK, self.POST, |
---|
3033 | + bar_url, t="check", output="JSON")) |
---|
3034 | def _check_json(res): |
---|
3035 | data = simplejson.loads(res) |
---|
3036 | self.failUnless("storage-index" in data) |
---|
3037 | @@ -1772,7 +1926,8 @@ |
---|
3038 | |
---|
3039 | def test_POST_FILEURL_check_and_repair(self): |
---|
3040 | bar_url = self.public_url + "/foo/bar.txt" |
---|
3041 | - d = self.POST(bar_url, t="check", repair="true") |
---|
3042 | + d = self.shouldSucceed("POST_FILEURL_check_and_repair-1", http.OK, self.POST, |
---|
3043 | + bar_url, t="check", repair="true") |
---|
3044 | def _check(res): |
---|
3045 | self.failUnless("Healthy :" in res) |
---|
3046 | d.addCallback(_check) |
---|
3047 | @@ -1781,13 +1936,14 @@ |
---|
3048 | self.failUnlessEqual(statuscode, str(http.FOUND)) |
---|
3049 | self.failUnlessEqual(target, redir_url) |
---|
3050 | d.addCallback(lambda res: |
---|
3051 | - self.shouldRedirect2("test_POST_FILEURL_check_and_repair", |
---|
3052 | + self.shouldRedirect2("POST_FILEURL_check_and_repair-2", |
---|
3053 | _check2, |
---|
3054 | self.POST, bar_url, |
---|
3055 | t="check", repair="true", |
---|
3056 | when_done=redir_url)) |
---|
3057 | d.addCallback(lambda res: |
---|
3058 | - self.POST(bar_url, t="check", return_to=redir_url)) |
---|
3059 | + self.shouldSucceed("POST_FILEURL_check_and_repair-3", http.OK, self.POST, |
---|
3060 | + bar_url, t="check", return_to=redir_url)) |
---|
3061 | def _check3(res): |
---|
3062 | self.failUnless("Healthy :" in res) |
---|
3063 | self.failUnless("Return to file" in res) |
---|
3064 | @@ -1797,7 +1953,8 @@ |
---|
3065 | |
---|
3066 | def test_POST_DIRURL_check(self): |
---|
3067 | foo_url = self.public_url + "/foo/" |
---|
3068 | - d = self.POST(foo_url, t="check") |
---|
3069 | + d = self.shouldSucceed("POST_DIRURL_check-1", http.OK, self.POST, |
---|
3070 | + foo_url, t="check") |
---|
3071 | def _check(res): |
---|
3072 | self.failUnless("Healthy :" in res, res) |
---|
3073 | d.addCallback(_check) |
---|
3074 | @@ -1806,13 +1963,14 @@ |
---|
3075 | self.failUnlessEqual(statuscode, str(http.FOUND)) |
---|
3076 | self.failUnlessEqual(target, redir_url) |
---|
3077 | d.addCallback(lambda res: |
---|
3078 | - self.shouldRedirect2("test_POST_DIRURL_check", |
---|
3079 | + self.shouldRedirect2("POST_DIRURL_check-2", |
---|
3080 | _check2, |
---|
3081 | self.POST, foo_url, |
---|
3082 | t="check", |
---|
3083 | when_done=redir_url)) |
---|
3084 | d.addCallback(lambda res: |
---|
3085 | - self.POST(foo_url, t="check", return_to=redir_url)) |
---|
3086 | + self.shouldSucceed("POST_DIRURL_check-3", http.OK, self.POST, |
---|
3087 | + foo_url, t="check", return_to=redir_url)) |
---|
3088 | def _check3(res): |
---|
3089 | self.failUnless("Healthy :" in res, res) |
---|
3090 | self.failUnless("Return to file/directory" in res) |
---|
3091 | @@ -1820,7 +1978,8 @@ |
---|
3092 | d.addCallback(_check3) |
---|
3093 | |
---|
3094 | d.addCallback(lambda res: |
---|
3095 | - self.POST(foo_url, t="check", output="JSON")) |
---|
3096 | + self.shouldSucceed("POST_DIRURL_check-4", http.OK, self.POST, |
---|
3097 | + foo_url, t="check", output="JSON")) |
---|
3098 | def _check_json(res): |
---|
3099 | data = simplejson.loads(res) |
---|
3100 | self.failUnless("storage-index" in data) |
---|
3101 | @@ -1831,7 +1990,8 @@ |
---|
3102 | |
---|
3103 | def test_POST_DIRURL_check_and_repair(self): |
---|
3104 | foo_url = self.public_url + "/foo/" |
---|
3105 | - d = self.POST(foo_url, t="check", repair="true") |
---|
3106 | + d = self.shouldSucceed("POST_DIRURL_check_and_repair-1", http.OK, self.POST, |
---|
3107 | + foo_url, t="check", repair="true") |
---|
3108 | def _check(res): |
---|
3109 | self.failUnless("Healthy :" in res, res) |
---|
3110 | d.addCallback(_check) |
---|
3111 | @@ -1840,13 +2000,14 @@ |
---|
3112 | self.failUnlessEqual(statuscode, str(http.FOUND)) |
---|
3113 | self.failUnlessEqual(target, redir_url) |
---|
3114 | d.addCallback(lambda res: |
---|
3115 | - self.shouldRedirect2("test_POST_DIRURL_check_and_repair", |
---|
3116 | + self.shouldRedirect2("POST_DIRURL_check_and_repair-2", |
---|
3117 | _check2, |
---|
3118 | self.POST, foo_url, |
---|
3119 | t="check", repair="true", |
---|
3120 | when_done=redir_url)) |
---|
3121 | d.addCallback(lambda res: |
---|
3122 | - self.POST(foo_url, t="check", return_to=redir_url)) |
---|
3123 | + self.shouldSucceed("POST_DIRURL_check_and_repair-3", http.OK, self.POST, |
---|
3124 | + foo_url, t="check", return_to=redir_url)) |
---|
3125 | def _check3(res): |
---|
3126 | self.failUnless("Healthy :" in res) |
---|
3127 | self.failUnless("Return to file/directory" in res) |
---|
3128 | @@ -1857,7 +2018,7 @@ |
---|
3129 | def wait_for_operation(self, ignored, ophandle): |
---|
3130 | url = "/operations/" + ophandle |
---|
3131 | url += "?t=status&output=JSON" |
---|
3132 | - d = self.GET(url) |
---|
3133 | + d = self.shouldSucceedGET(url) |
---|
3134 | def _got(res): |
---|
3135 | data = simplejson.loads(res) |
---|
3136 | if not data["finished"]: |
---|
3137 | @@ -1873,7 +2034,7 @@ |
---|
3138 | url += "?t=status" |
---|
3139 | if output: |
---|
3140 | url += "&output=" + output |
---|
3141 | - d = self.GET(url) |
---|
3142 | + d = self.shouldSucceedGET(url) |
---|
3143 | def _got(res): |
---|
3144 | if output and output.lower() == "json": |
---|
3145 | return simplejson.loads(res) |
---|
3146 | @@ -1883,7 +2044,7 @@ |
---|
3147 | |
---|
3148 | def test_POST_DIRURL_deepcheck_no_ophandle(self): |
---|
3149 | d = self.shouldFail2(error.Error, |
---|
3150 | - "test_POST_DIRURL_deepcheck_no_ophandle", |
---|
3151 | + "POST_DIRURL_deepcheck_no_ophandle", |
---|
3152 | "400 Bad Request", |
---|
3153 | "slow operation requires ophandle=", |
---|
3154 | self.POST, self.public_url, t="start-deep-check") |
---|
3155 | @@ -1893,7 +2054,7 @@ |
---|
3156 | def _check_redirect(statuscode, target): |
---|
3157 | self.failUnlessEqual(statuscode, str(http.FOUND)) |
---|
3158 | self.failUnless(target.endswith("/operations/123")) |
---|
3159 | - d = self.shouldRedirect2("test_POST_DIRURL_deepcheck", _check_redirect, |
---|
3160 | + d = self.shouldRedirect2("POST_DIRURL_deepcheck", _check_redirect, |
---|
3161 | self.POST, self.public_url, |
---|
3162 | t="start-deep-check", ophandle="123") |
---|
3163 | d.addCallback(self.wait_for_operation, "123") |
---|
3164 | @@ -1909,7 +2070,7 @@ |
---|
3165 | d.addCallback(_check_html) |
---|
3166 | |
---|
3167 | d.addCallback(lambda res: |
---|
3168 | - self.GET("/operations/123/")) |
---|
3169 | + self.shouldSucceedGET("/operations/123/")) |
---|
3170 | d.addCallback(_check_html) # should be the same as without the slash |
---|
3171 | |
---|
3172 | d.addCallback(lambda res: |
---|
3173 | @@ -1920,7 +2081,7 @@ |
---|
3174 | foo_si = self._foo_node.get_storage_index() |
---|
3175 | foo_si_s = base32.b2a(foo_si) |
---|
3176 | d.addCallback(lambda res: |
---|
3177 | - self.GET("/operations/123/%s?output=JSON" % foo_si_s)) |
---|
3178 | + self.shouldSucceedGET("/operations/123/%s?output=JSON" % foo_si_s)) |
---|
3179 | def _check_foo_json(res): |
---|
3180 | data = simplejson.loads(res) |
---|
3181 | self.failUnlessEqual(data["storage-index"], foo_si_s) |
---|
3182 | @@ -1929,8 +2090,9 @@ |
---|
3183 | return d |
---|
3184 | |
---|
3185 | def test_POST_DIRURL_deepcheck_and_repair(self): |
---|
3186 | - d = self.POST(self.public_url, t="start-deep-check", repair="true", |
---|
3187 | - ophandle="124", output="json", followRedirect=True) |
---|
3188 | + d = self.shouldSucceed("POST_DIRURL_deepcheck_and_repair", http.OK, self.POST, |
---|
3189 | + self.public_url, t="start-deep-check", repair="true", |
---|
3190 | + ophandle="124", output="json", followRedirect=True) |
---|
3191 | d.addCallback(self.wait_for_operation, "124") |
---|
3192 | def _check_json(data): |
---|
3193 | self.failUnlessEqual(data["finished"], True) |
---|
3194 | @@ -1971,45 +2133,47 @@ |
---|
3195 | return d |
---|
3196 | |
---|
3197 | def test_POST_mkdir(self): # return value? |
---|
3198 | - d = self.POST(self.public_url + "/foo", t="mkdir", name="newdir") |
---|
3199 | + d = self.shouldSucceed("POST_mkdir", http.OK, self.POST, |
---|
3200 | + self.public_url + "/foo", t="mkdir", name="newdir") |
---|
3201 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
3202 | d.addCallback(self.failUnlessNodeKeysAre, []) |
---|
3203 | return d |
---|
3204 | |
---|
3205 | def test_POST_mkdir_initial_children(self): |
---|
3206 | - newkids, filecap1, ign, ign, ign = self._create_initial_children() |
---|
3207 | - d = self.POST2(self.public_url + |
---|
3208 | - "/foo?t=mkdir-with-children&name=newdir", |
---|
3209 | - simplejson.dumps(newkids)) |
---|
3210 | + (newkids, caps) = self._create_initial_children() |
---|
3211 | + d = self.shouldSucceed("POST_mkdir_initial_children", http.OK, self.POST2, |
---|
3212 | + self.public_url + "/foo?t=mkdir-with-children&name=newdir", |
---|
3213 | + simplejson.dumps(newkids)) |
---|
3214 | d.addCallback(lambda res: |
---|
3215 | self.failUnlessNodeHasChild(self._foo_node, u"newdir")) |
---|
3216 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
3217 | d.addCallback(self.failUnlessNodeKeysAre, newkids.keys()) |
---|
3218 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
3219 | - d.addCallback(self.failUnlessChildURIIs, u"child-imm", filecap1) |
---|
3220 | + d.addCallback(self.failUnlessROChildURIIs, u"child-imm", caps['filecap1']) |
---|
3221 | return d |
---|
3222 | |
---|
3223 | def test_POST_mkdir_immutable(self): |
---|
3224 | - (newkids, filecap1, immdircap) = self._create_immutable_children() |
---|
3225 | - d = self.POST2(self.public_url + |
---|
3226 | - "/foo?t=mkdir-immutable&name=newdir", |
---|
3227 | - simplejson.dumps(newkids)) |
---|
3228 | + (newkids, caps) = self._create_immutable_children() |
---|
3229 | + d = self.shouldSucceed("POST_mkdir_immutable", http.OK, self.POST2, |
---|
3230 | + self.public_url + "/foo?t=mkdir-immutable&name=newdir", |
---|
3231 | + simplejson.dumps(newkids)) |
---|
3232 | d.addCallback(lambda res: |
---|
3233 | self.failUnlessNodeHasChild(self._foo_node, u"newdir")) |
---|
3234 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
3235 | d.addCallback(self.failUnlessNodeKeysAre, newkids.keys()) |
---|
3236 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
3237 | - d.addCallback(self.failUnlessChildURIIs, u"child-imm", filecap1) |
---|
3238 | + d.addCallback(self.failUnlessROChildURIIs, u"child-imm", caps['filecap1']) |
---|
3239 | + d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
3240 | + d.addCallback(self.failUnlessROChildURIIs, u"unknownchild-imm", caps['unknown_immcap']) |
---|
3241 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
3242 | - d.addCallback(self.failUnlessChildURIIs, u"dirchild-imm", immdircap) |
---|
3243 | + d.addCallback(self.failUnlessROChildURIIs, u"dirchild-imm", caps['immdircap']) |
---|
3244 | return d |
---|
3245 | |
---|
3246 | def test_POST_mkdir_immutable_bad(self): |
---|
3247 | - (newkids, filecap1, filecap2, filecap3, |
---|
3248 | - dircap) = self._create_initial_children() |
---|
3249 | - d = self.shouldFail2(error.Error, "test_POST_mkdir_immutable_bad", |
---|
3250 | + (newkids, caps) = self._create_initial_children() |
---|
3251 | + d = self.shouldFail2(error.Error, "POST_mkdir_immutable_bad", |
---|
3252 | "400 Bad Request", |
---|
3253 | - "a mkdir-immutable operation was given a child that was not itself immutable", |
---|
3254 | + "needed to be immutable but was not", |
---|
3255 | self.POST2, |
---|
3256 | self.public_url + |
---|
3257 | "/foo?t=mkdir-immutable&name=newdir", |
---|
3258 | @@ -2017,7 +2181,8 @@ |
---|
3259 | return d |
---|
3260 | |
---|
3261 | def test_POST_mkdir_2(self): |
---|
3262 | - d = self.POST(self.public_url + "/foo/newdir?t=mkdir", "") |
---|
3263 | + d = self.shouldSucceed("POST_mkdir_2", http.OK, self.POST, |
---|
3264 | + self.public_url + "/foo/newdir?t=mkdir", "") |
---|
3265 | d.addCallback(lambda res: |
---|
3266 | self.failUnlessNodeHasChild(self._foo_node, u"newdir")) |
---|
3267 | d.addCallback(lambda res: self._foo_node.get(u"newdir")) |
---|
3268 | @@ -2025,7 +2190,8 @@ |
---|
3269 | return d |
---|
3270 | |
---|
3271 | def test_POST_mkdirs_2(self): |
---|
3272 | - d = self.POST(self.public_url + "/foo/bardir/newdir?t=mkdir", "") |
---|
3273 | + d = self.shouldSucceed("POST_mkdirs_2", http.OK, self.POST, |
---|
3274 | + self.public_url + "/foo/bardir/newdir?t=mkdir", "") |
---|
3275 | d.addCallback(lambda res: |
---|
3276 | self.failUnlessNodeHasChild(self._foo_node, u"bardir")) |
---|
3277 | d.addCallback(lambda res: self._foo_node.get(u"bardir")) |
---|
3278 | @@ -2034,7 +2200,8 @@ |
---|
3279 | return d |
---|
3280 | |
---|
3281 | def test_POST_mkdir_no_parentdir_noredirect(self): |
---|
3282 | - d = self.POST("/uri?t=mkdir") |
---|
3283 | + d = self.shouldSucceed("POST_mkdir_no_parentdir_noredirect", http.OK, self.POST, |
---|
3284 | + "/uri?t=mkdir") |
---|
3285 | def _after_mkdir(res): |
---|
3286 | uri.DirectoryURI.init_from_string(res) |
---|
3287 | d.addCallback(_after_mkdir) |
---|
3288 | @@ -2049,21 +2216,43 @@ |
---|
3289 | d.addCallback(_check_target) |
---|
3290 | return d |
---|
3291 | |
---|
3292 | + def _make_readonly(self, u): |
---|
3293 | + ro_uri = uri.from_string(u).get_readonly() |
---|
3294 | + if ro_uri is None: |
---|
3295 | + return None |
---|
3296 | + return ro_uri.to_string() |
---|
3297 | + |
---|
3298 | def _create_initial_children(self): |
---|
3299 | contents, n, filecap1 = self.makefile(12) |
---|
3300 | md1 = {"metakey1": "metavalue1"} |
---|
3301 | filecap2 = make_mutable_file_uri() |
---|
3302 | node3 = self.s.create_node_from_uri(make_mutable_file_uri()) |
---|
3303 | filecap3 = node3.get_readonly_uri() |
---|
3304 | + unknown_rwcap = "lafs://from_the_future" |
---|
3305 | + unknown_rocap = "ro.lafs://readonly_from_the_future" |
---|
3306 | + unknown_immcap = "imm.lafs://immutable_from_the_future" |
---|
3307 | node4 = self.s.create_node_from_uri(make_mutable_file_uri()) |
---|
3308 | dircap = DirectoryNode(node4, None, None).get_uri() |
---|
3309 | - newkids = {u"child-imm": ["filenode", {"ro_uri": filecap1, |
---|
3310 | - "metadata": md1, }], |
---|
3311 | - u"child-mutable": ["filenode", {"rw_uri": filecap2}], |
---|
3312 | + newkids = {u"child-imm": ["filenode", {"rw_uri": filecap1, |
---|
3313 | + "ro_uri": self._make_readonly(filecap1), |
---|
3314 | + "metadata": md1, }], |
---|
3315 | + u"child-mutable": ["filenode", {"rw_uri": filecap2, |
---|
3316 | + "ro_uri": self._make_readonly(filecap2)}], |
---|
3317 | u"child-mutable-ro": ["filenode", {"ro_uri": filecap3}], |
---|
3318 | - u"dirchild": ["dirnode", {"rw_uri": dircap}], |
---|
3319 | + u"unknownchild-rw": ["unknown", {"rw_uri": unknown_rwcap, |
---|
3320 | + "ro_uri": unknown_rocap}], |
---|
3321 | + u"unknownchild-ro": ["unknown", {"ro_uri": unknown_rocap}], |
---|
3322 | + u"unknownchild-imm": ["unknown", {"ro_uri": unknown_immcap}], |
---|
3323 | + u"dirchild": ["dirnode", {"rw_uri": dircap, |
---|
3324 | + "ro_uri": self._make_readonly(dircap)}], |
---|
3325 | } |
---|
3326 | - return newkids, filecap1, filecap2, filecap3, dircap |
---|
3327 | + return newkids, {'filecap1': filecap1, |
---|
3328 | + 'filecap2': filecap2, |
---|
3329 | + 'filecap3': filecap3, |
---|
3330 | + 'unknown_rwcap': unknown_rwcap, |
---|
3331 | + 'unknown_rocap': unknown_rocap, |
---|
3332 | + 'unknown_immcap': unknown_immcap, |
---|
3333 | + 'dircap': dircap} |
---|
3334 | |
---|
3335 | def _create_immutable_children(self): |
---|
3336 | contents, n, filecap1 = self.makefile(12) |
---|
3337 | @@ -2071,31 +2260,46 @@ |
---|
3338 | tnode = create_chk_filenode("immutable directory contents\n"*10) |
---|
3339 | dnode = DirectoryNode(tnode, None, None) |
---|
3340 | assert not dnode.is_mutable() |
---|
3341 | + unknown_immcap = "imm.lafs://immutable_from_the_future" |
---|
3342 | immdircap = dnode.get_uri() |
---|
3343 | - newkids = {u"child-imm": ["filenode", {"ro_uri": filecap1, |
---|
3344 | - "metadata": md1, }], |
---|
3345 | - u"dirchild-imm": ["dirnode", {"ro_uri": immdircap}], |
---|
3346 | + newkids = {u"child-imm": ["filenode", {"ro_uri": filecap1, |
---|
3347 | + "metadata": md1, }], |
---|
3348 | + u"unknownchild-imm": ["unknown", {"ro_uri": unknown_immcap}], |
---|
3349 | + u"dirchild-imm": ["dirnode", {"ro_uri": immdircap}], |
---|
3350 | } |
---|
3351 | - return newkids, filecap1, immdircap |
---|
3352 | + return newkids, {'filecap1': filecap1, |
---|
3353 | + 'unknown_immcap': unknown_immcap, |
---|
3354 | + 'immdircap': immdircap} |
---|
3355 | |
---|
3356 | def test_POST_mkdir_no_parentdir_initial_children(self): |
---|
3357 | - (newkids, filecap1, filecap2, filecap3, |
---|
3358 | - dircap) = self._create_initial_children() |
---|
3359 | - d = self.POST2("/uri?t=mkdir-with-children", simplejson.dumps(newkids)) |
---|
3360 | + (newkids, caps) = self._create_initial_children() |
---|
3361 | + d = self.shouldSucceed("POST_mkdir_no_parentdir_initial_children", http.OK, self.POST2, |
---|
3362 | + "/uri?t=mkdir-with-children", simplejson.dumps(newkids)) |
---|
3363 | def _after_mkdir(res): |
---|
3364 | self.failUnless(res.startswith("URI:DIR"), res) |
---|
3365 | n = self.s.create_node_from_uri(res) |
---|
3366 | d2 = self.failUnlessNodeKeysAre(n, newkids.keys()) |
---|
3367 | d2.addCallback(lambda ign: |
---|
3368 | - self.failUnlessChildURIIs(n, u"child-imm", filecap1)) |
---|
3369 | + self.failUnlessROChildURIIs(n, u"child-imm", |
---|
3370 | + caps['filecap1'])) |
---|
3371 | + d2.addCallback(lambda ign: |
---|
3372 | + self.failUnlessRWChildURIIs(n, u"child-mutable", |
---|
3373 | + caps['filecap2'])) |
---|
3374 | + d2.addCallback(lambda ign: |
---|
3375 | + self.failUnlessROChildURIIs(n, u"child-mutable-ro", |
---|
3376 | + caps['filecap3'])) |
---|
3377 | d2.addCallback(lambda ign: |
---|
3378 | - self.failUnlessChildURIIs(n, u"child-mutable", |
---|
3379 | - filecap2)) |
---|
3380 | + self.failUnlessRWChildURIIs(n, u"unknownchild-rw", |
---|
3381 | + caps['unknown_rwcap'])) |
---|
3382 | d2.addCallback(lambda ign: |
---|
3383 | - self.failUnlessChildURIIs(n, u"child-mutable-ro", |
---|
3384 | - filecap3)) |
---|
3385 | + self.failUnlessROChildURIIs(n, u"unknownchild-ro", |
---|
3386 | + caps['unknown_rocap'])) |
---|
3387 | d2.addCallback(lambda ign: |
---|
3388 | - self.failUnlessChildURIIs(n, u"dirchild", dircap)) |
---|
3389 | + self.failUnlessROChildURIIs(n, u"unknownchild-imm", |
---|
3390 | + caps['unknown_immcap'])) |
---|
3391 | + d2.addCallback(lambda ign: |
---|
3392 | + self.failUnlessRWChildURIIs(n, u"dirchild", |
---|
3393 | + caps['dircap'])) |
---|
3394 | return d2 |
---|
3395 | d.addCallback(_after_mkdir) |
---|
3396 | return d |
---|
3397 | @@ -2103,8 +2307,7 @@ |
---|
3398 | def test_POST_mkdir_no_parentdir_unexpected_children(self): |
---|
3399 | # the regular /uri?t=mkdir operation is specified to ignore its body. |
---|
3400 | # Only t=mkdir-with-children pays attention to it. |
---|
3401 | - (newkids, filecap1, filecap2, filecap3, |
---|
3402 | - dircap) = self._create_initial_children() |
---|
3403 | + (newkids, caps) = self._create_initial_children() |
---|
3404 | d = self.shouldHTTPError("POST t=mkdir unexpected children", |
---|
3405 | 400, "Bad Request", |
---|
3406 | "t=mkdir does not accept children=, " |
---|
3407 | @@ -2121,28 +2324,32 @@ |
---|
3408 | return d |
---|
3409 | |
---|
3410 | def test_POST_mkdir_no_parentdir_immutable(self): |
---|
3411 | - (newkids, filecap1, immdircap) = self._create_immutable_children() |
---|
3412 | - d = self.POST2("/uri?t=mkdir-immutable", simplejson.dumps(newkids)) |
---|
3413 | + (newkids, caps) = self._create_immutable_children() |
---|
3414 | + d = self.shouldSucceed("POST_mkdir_no_parentdir_immutable", http.OK, self.POST2, |
---|
3415 | + "/uri?t=mkdir-immutable", simplejson.dumps(newkids)) |
---|
3416 | def _after_mkdir(res): |
---|
3417 | self.failUnless(res.startswith("URI:DIR"), res) |
---|
3418 | n = self.s.create_node_from_uri(res) |
---|
3419 | d2 = self.failUnlessNodeKeysAre(n, newkids.keys()) |
---|
3420 | d2.addCallback(lambda ign: |
---|
3421 | - self.failUnlessChildURIIs(n, u"child-imm", filecap1)) |
---|
3422 | + self.failUnlessROChildURIIs(n, u"child-imm", |
---|
3423 | + caps['filecap1'])) |
---|
3424 | + d2.addCallback(lambda ign: |
---|
3425 | + self.failUnlessROChildURIIs(n, u"unknownchild-imm", |
---|
3426 | + caps['unknown_immcap'])) |
---|
3427 | d2.addCallback(lambda ign: |
---|
3428 | - self.failUnlessChildURIIs(n, u"dirchild-imm", |
---|
3429 | - immdircap)) |
---|
3430 | + self.failUnlessROChildURIIs(n, u"dirchild-imm", |
---|
3431 | + caps['immdircap'])) |
---|
3432 | return d2 |
---|
3433 | d.addCallback(_after_mkdir) |
---|
3434 | return d |
---|
3435 | |
---|
3436 | def test_POST_mkdir_no_parentdir_immutable_bad(self): |
---|
3437 | - (newkids, filecap1, filecap2, filecap3, |
---|
3438 | - dircap) = self._create_initial_children() |
---|
3439 | + (newkids, caps) = self._create_initial_children() |
---|
3440 | d = self.shouldFail2(error.Error, |
---|
3441 | - "test_POST_mkdir_no_parentdir_immutable_bad", |
---|
3442 | + "POST_mkdir_no_parentdir_immutable_bad", |
---|
3443 | "400 Bad Request", |
---|
3444 | - "a mkdir-immutable operation was given a child that was not itself immutable", |
---|
3445 | + "needed to be immutable but was not", |
---|
3446 | self.POST2, |
---|
3447 | "/uri?t=mkdir-immutable", |
---|
3448 | simplejson.dumps(newkids)) |
---|
3449 | @@ -2150,9 +2357,14 @@ |
---|
3450 | |
---|
3451 | def test_welcome_page_mkdir_button(self): |
---|
3452 | # Fetch the welcome page. |
---|
3453 | - d = self.GET("/") |
---|
3454 | + d = self.shouldSucceedGET("/") |
---|
3455 | def _after_get_welcome_page(res): |
---|
3456 | - MKDIR_BUTTON_RE=re.compile('<form action="([^"]*)" method="post".*?<input type="hidden" name="t" value="([^"]*)" /><input type="hidden" name="([^"]*)" value="([^"]*)" /><input type="submit" value="Create a directory" />', re.I) |
---|
3457 | + MKDIR_BUTTON_RE = re.compile( |
---|
3458 | + '<form action="([^"]*)" method="post".*?' |
---|
3459 | + '<input type="hidden" name="t" value="([^"]*)" />' |
---|
3460 | + '<input type="hidden" name="([^"]*)" value="([^"]*)" />' |
---|
3461 | + '<input type="submit" value="Create a directory" />', |
---|
3462 | + re.I) |
---|
3463 | mo = MKDIR_BUTTON_RE.search(res) |
---|
3464 | formaction = mo.group(1) |
---|
3465 | formt = mo.group(2) |
---|
3466 | @@ -2168,7 +2380,8 @@ |
---|
3467 | return d |
---|
3468 | |
---|
3469 | def test_POST_mkdir_replace(self): # return value? |
---|
3470 | - d = self.POST(self.public_url + "/foo", t="mkdir", name="sub") |
---|
3471 | + d = self.shouldSucceed("POST_mkdir_replace", http.OK, self.POST, |
---|
3472 | + self.public_url + "/foo", t="mkdir", name="sub") |
---|
3473 | d.addCallback(lambda res: self._foo_node.get(u"sub")) |
---|
3474 | d.addCallback(self.failUnlessNodeKeysAre, []) |
---|
3475 | return d |
---|
3476 | @@ -2250,9 +2463,9 @@ |
---|
3477 | |
---|
3478 | d = client.getPage(url, method="POST", postdata=reqbody) |
---|
3479 | def _then(res): |
---|
3480 | - self.failUnlessURIMatchesChild(newuri9, self._foo_node, u"atomic_added_1") |
---|
3481 | - self.failUnlessURIMatchesChild(newuri10, self._foo_node, u"atomic_added_2") |
---|
3482 | - self.failUnlessURIMatchesChild(newuri11, self._foo_node, u"atomic_added_3") |
---|
3483 | + self.failUnlessURIMatchesROChild(newuri9, self._foo_node, u"atomic_added_1") |
---|
3484 | + self.failUnlessURIMatchesROChild(newuri10, self._foo_node, u"atomic_added_2") |
---|
3485 | + self.failUnlessURIMatchesROChild(newuri11, self._foo_node, u"atomic_added_3") |
---|
3486 | |
---|
3487 | d.addCallback(_then) |
---|
3488 | d.addErrback(self.dump_error) |
---|
3489 | @@ -2260,8 +2473,9 @@ |
---|
3490 | |
---|
3491 | def test_POST_put_uri(self): |
---|
3492 | contents, n, newuri = self.makefile(8) |
---|
3493 | - d = self.POST(self.public_url + "/foo", t="uri", name="new.txt", uri=newuri) |
---|
3494 | - d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt") |
---|
3495 | + d = self.shouldSucceed("POST_put_uri", http.OK, self.POST, |
---|
3496 | + self.public_url + "/foo", t="uri", name="new.txt", uri=newuri) |
---|
3497 | + d.addCallback(self.failUnlessURIMatchesROChild, self._foo_node, u"new.txt") |
---|
3498 | d.addCallback(lambda res: |
---|
3499 | self.failUnlessChildContentsAre(self._foo_node, u"new.txt", |
---|
3500 | contents)) |
---|
3501 | @@ -2269,8 +2483,9 @@ |
---|
3502 | |
---|
3503 | def test_POST_put_uri_replace(self): |
---|
3504 | contents, n, newuri = self.makefile(8) |
---|
3505 | - d = self.POST(self.public_url + "/foo", t="uri", name="bar.txt", uri=newuri) |
---|
3506 | - d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt") |
---|
3507 | + d = self.shouldSucceed("POST_put_uri_replace", http.OK, self.POST, |
---|
3508 | + self.public_url + "/foo", t="uri", name="bar.txt", uri=newuri) |
---|
3509 | + d.addCallback(self.failUnlessURIMatchesROChild, self._foo_node, u"bar.txt") |
---|
3510 | d.addCallback(lambda res: |
---|
3511 | self.failUnlessChildContentsAre(self._foo_node, u"bar.txt", |
---|
3512 | contents)) |
---|
3513 | @@ -2285,7 +2500,7 @@ |
---|
3514 | "409 Conflict", |
---|
3515 | "There was already a child by that name, and you asked me " |
---|
3516 | "to not replace it") |
---|
3517 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt")) |
---|
3518 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/bar.txt")) |
---|
3519 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
3520 | return d |
---|
3521 | |
---|
3522 | @@ -2298,12 +2513,13 @@ |
---|
3523 | "409 Conflict", |
---|
3524 | "There was already a child by that name, and you asked me " |
---|
3525 | "to not replace it") |
---|
3526 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt")) |
---|
3527 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/bar.txt")) |
---|
3528 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
3529 | return d |
---|
3530 | |
---|
3531 | def test_POST_delete(self): |
---|
3532 | - d = self.POST(self.public_url + "/foo", t="delete", name="bar.txt") |
---|
3533 | + d = self.shouldSucceed("POST_delete", http.OK, self.POST, |
---|
3534 | + self.public_url + "/foo", t="delete", name="bar.txt") |
---|
3535 | d.addCallback(lambda res: self._foo_node.list()) |
---|
3536 | def _check(children): |
---|
3537 | self.failIf(u"bar.txt" in children) |
---|
3538 | @@ -2311,40 +2527,43 @@ |
---|
3539 | return d |
---|
3540 | |
---|
3541 | def test_POST_rename_file(self): |
---|
3542 | - d = self.POST(self.public_url + "/foo", t="rename", |
---|
3543 | - from_name="bar.txt", to_name='wibble.txt') |
---|
3544 | + d = self.shouldSucceed("POST_rename_file", http.OK, self.POST, |
---|
3545 | + self.public_url + "/foo", t="rename", |
---|
3546 | + from_name="bar.txt", to_name='wibble.txt') |
---|
3547 | d.addCallback(lambda res: |
---|
3548 | self.failIfNodeHasChild(self._foo_node, u"bar.txt")) |
---|
3549 | d.addCallback(lambda res: |
---|
3550 | self.failUnlessNodeHasChild(self._foo_node, u"wibble.txt")) |
---|
3551 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt")) |
---|
3552 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/wibble.txt")) |
---|
3553 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
3554 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt?t=json")) |
---|
3555 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/wibble.txt?t=json")) |
---|
3556 | d.addCallback(self.failUnlessIsBarJSON) |
---|
3557 | return d |
---|
3558 | |
---|
3559 | def test_POST_rename_file_redundant(self): |
---|
3560 | - d = self.POST(self.public_url + "/foo", t="rename", |
---|
3561 | - from_name="bar.txt", to_name='bar.txt') |
---|
3562 | + d = self.shouldSucceed("POST_rename_file_redundant", http.OK, self.POST, |
---|
3563 | + self.public_url + "/foo", t="rename", |
---|
3564 | + from_name="bar.txt", to_name='bar.txt') |
---|
3565 | d.addCallback(lambda res: |
---|
3566 | self.failUnlessNodeHasChild(self._foo_node, u"bar.txt")) |
---|
3567 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt")) |
---|
3568 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/bar.txt")) |
---|
3569 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
3570 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt?t=json")) |
---|
3571 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/bar.txt?t=json")) |
---|
3572 | d.addCallback(self.failUnlessIsBarJSON) |
---|
3573 | return d |
---|
3574 | |
---|
3575 | def test_POST_rename_file_replace(self): |
---|
3576 | # rename a file and replace a directory with it |
---|
3577 | - d = self.POST(self.public_url + "/foo", t="rename", |
---|
3578 | - from_name="bar.txt", to_name='empty') |
---|
3579 | + d = self.shouldSucceed("POST_rename_file_replace", http.OK, self.POST, |
---|
3580 | + self.public_url + "/foo", t="rename", |
---|
3581 | + from_name="bar.txt", to_name='empty') |
---|
3582 | d.addCallback(lambda res: |
---|
3583 | self.failIfNodeHasChild(self._foo_node, u"bar.txt")) |
---|
3584 | d.addCallback(lambda res: |
---|
3585 | self.failUnlessNodeHasChild(self._foo_node, u"empty")) |
---|
3586 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty")) |
---|
3587 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/empty")) |
---|
3588 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
3589 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json")) |
---|
3590 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/empty?t=json")) |
---|
3591 | d.addCallback(self.failUnlessIsBarJSON) |
---|
3592 | return d |
---|
3593 | |
---|
3594 | @@ -2357,7 +2576,7 @@ |
---|
3595 | "409 Conflict", |
---|
3596 | "There was already a child by that name, and you asked me " |
---|
3597 | "to not replace it") |
---|
3598 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json")) |
---|
3599 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/empty?t=json")) |
---|
3600 | d.addCallback(self.failUnlessIsEmptyJSON) |
---|
3601 | return d |
---|
3602 | |
---|
3603 | @@ -2370,7 +2589,7 @@ |
---|
3604 | "409 Conflict", |
---|
3605 | "There was already a child by that name, and you asked me " |
---|
3606 | "to not replace it") |
---|
3607 | - d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json")) |
---|
3608 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/foo/empty?t=json")) |
---|
3609 | d.addCallback(self.failUnlessIsEmptyJSON) |
---|
3610 | return d |
---|
3611 | |
---|
3612 | @@ -2383,7 +2602,7 @@ |
---|
3613 | d = self.POST(self.public_url + "/foo", t="rename", |
---|
3614 | from_name="bar.txt", to_name='kirk/spock.txt') |
---|
3615 | d.addBoth(self.shouldFail, error.Error, |
---|
3616 | - "test_POST_rename_file_slash_fail", |
---|
3617 | + "POST_rename_file_slash_fail", |
---|
3618 | "400 Bad Request", |
---|
3619 | "to_name= may not contain a slash", |
---|
3620 | ) |
---|
3621 | @@ -2392,13 +2611,14 @@ |
---|
3622 | return d |
---|
3623 | |
---|
3624 | def test_POST_rename_dir(self): |
---|
3625 | - d = self.POST(self.public_url, t="rename", |
---|
3626 | - from_name="foo", to_name='plunk') |
---|
3627 | + d = self.shouldSucceed("POST_rename_dir", http.OK, self.POST, |
---|
3628 | + self.public_url, t="rename", |
---|
3629 | + from_name="foo", to_name='plunk') |
---|
3630 | d.addCallback(lambda res: |
---|
3631 | self.failIfNodeHasChild(self.public_root, u"foo")) |
---|
3632 | d.addCallback(lambda res: |
---|
3633 | self.failUnlessNodeHasChild(self.public_root, u"plunk")) |
---|
3634 | - d.addCallback(lambda res: self.GET(self.public_url + "/plunk?t=json")) |
---|
3635 | + d.addCallback(lambda res: self.shouldSucceedGET(self.public_url + "/plunk?t=json")) |
---|
3636 | d.addCallback(self.failUnlessIsFooJSON) |
---|
3637 | return d |
---|
3638 | |
---|
3639 | @@ -2433,24 +2653,24 @@ |
---|
3640 | d.addCallback(lambda res: self.GET(base+"&t=json")) |
---|
3641 | d.addBoth(self.shouldRedirect, targetbase+"?t=json") |
---|
3642 | d.addCallback(self.log, "about to get file by uri") |
---|
3643 | - d.addCallback(lambda res: self.GET(base, followRedirect=True)) |
---|
3644 | + d.addCallback(lambda res: self.shouldSucceedGET(base, followRedirect=True)) |
---|
3645 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
3646 | d.addCallback(self.log, "got file by uri, about to get dir by uri") |
---|
3647 | - d.addCallback(lambda res: self.GET("/uri?uri=%s&t=json" % self._foo_uri, |
---|
3648 | - followRedirect=True)) |
---|
3649 | + d.addCallback(lambda res: self.shouldSucceedGET("/uri?uri=%s&t=json" % self._foo_uri, |
---|
3650 | + followRedirect=True)) |
---|
3651 | d.addCallback(self.failUnlessIsFooJSON) |
---|
3652 | d.addCallback(self.log, "got dir by uri") |
---|
3653 | |
---|
3654 | return d |
---|
3655 | |
---|
3656 | def test_GET_URI_form_bad(self): |
---|
3657 | - d = self.shouldFail2(error.Error, "test_GET_URI_form_bad", |
---|
3658 | + d = self.shouldFail2(error.Error, "GET_URI_form_bad", |
---|
3659 | "400 Bad Request", "GET /uri requires uri=", |
---|
3660 | self.GET, "/uri") |
---|
3661 | return d |
---|
3662 | |
---|
3663 | def test_GET_rename_form(self): |
---|
3664 | - d = self.GET(self.public_url + "/foo?t=rename-form&name=bar.txt", |
---|
3665 | + d = self.shouldSucceedGET(self.public_url + "/foo?t=rename-form&name=bar.txt", |
---|
3666 | followRedirect=True) |
---|
3667 | def _check(res): |
---|
3668 | self.failUnless('name="when_done" value="."' in res, res) |
---|
3669 | @@ -2465,23 +2685,23 @@ |
---|
3670 | |
---|
3671 | def test_GET_URI_URL(self): |
---|
3672 | base = "/uri/%s" % self._bar_txt_uri |
---|
3673 | - d = self.GET(base) |
---|
3674 | + d = self.shouldSucceedGET(base) |
---|
3675 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
3676 | - d.addCallback(lambda res: self.GET(base+"?filename=bar.txt")) |
---|
3677 | + d.addCallback(lambda res: self.shouldSucceedGET(base+"?filename=bar.txt")) |
---|
3678 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
3679 | - d.addCallback(lambda res: self.GET(base+"?filename=bar.txt&save=true")) |
---|
3680 | + d.addCallback(lambda res: self.shouldSucceedGET(base+"?filename=bar.txt&save=true")) |
---|
3681 | d.addCallback(self.failUnlessIsBarDotTxt) |
---|
3682 | return d |
---|
3683 | |
---|
3684 | def test_GET_URI_URL_dir(self): |
---|
3685 | base = "/uri/%s?t=json" % self._foo_uri |
---|
3686 | - d = self.GET(base) |
---|
3687 | + d = self.shouldSucceedGET(base) |
---|
3688 | d.addCallback(self.failUnlessIsFooJSON) |
---|
3689 | return d |
---|
3690 | |
---|
3691 | def test_GET_URI_URL_missing(self): |
---|
3692 | base = "/uri/%s" % self._bad_file_uri |
---|
3693 | - d = self.shouldHTTPError("test_GET_URI_URL_missing", |
---|
3694 | + d = self.shouldHTTPError("GET_URI_URL_missing", |
---|
3695 | http.GONE, None, "NotEnoughSharesError", |
---|
3696 | self.GET, base) |
---|
3697 | # TODO: how can we exercise both sides of WebDownloadTarget.fail |
---|
3698 | @@ -2499,9 +2719,9 @@ |
---|
3699 | d.addCallback(lambda res: |
---|
3700 | self.failUnlessEqual(res.strip(), new_uri)) |
---|
3701 | d.addCallback(lambda res: |
---|
3702 | - self.failUnlessChildURIIs(self.public_root, |
---|
3703 | - u"foo", |
---|
3704 | - new_uri)) |
---|
3705 | + self.failUnlessRWChildURIIs(self.public_root, |
---|
3706 | + u"foo", |
---|
3707 | + new_uri)) |
---|
3708 | return d |
---|
3709 | d.addCallback(_made_dir) |
---|
3710 | return d |
---|
3711 | @@ -2512,32 +2732,33 @@ |
---|
3712 | new_uri = dn.get_uri() |
---|
3713 | # replace /foo with a new (empty) directory, but ask that |
---|
3714 | # replace=false, so it should fail |
---|
3715 | - d = self.shouldFail2(error.Error, "test_PUT_DIRURL_uri_noreplace", |
---|
3716 | + d = self.shouldFail2(error.Error, "PUT_DIRURL_uri_noreplace", |
---|
3717 | "409 Conflict", "There was already a child by that name, and you asked me to not replace it", |
---|
3718 | self.PUT, |
---|
3719 | self.public_url + "/foo?t=uri&replace=false", |
---|
3720 | new_uri) |
---|
3721 | d.addCallback(lambda res: |
---|
3722 | - self.failUnlessChildURIIs(self.public_root, |
---|
3723 | - u"foo", |
---|
3724 | - self._foo_uri)) |
---|
3725 | + self.failUnlessRWChildURIIs(self.public_root, |
---|
3726 | + u"foo", |
---|
3727 | + self._foo_uri)) |
---|
3728 | return d |
---|
3729 | d.addCallback(_made_dir) |
---|
3730 | return d |
---|
3731 | |
---|
3732 | def test_PUT_DIRURL_bad_t(self): |
---|
3733 | - d = self.shouldFail2(error.Error, "test_PUT_DIRURL_bad_t", |
---|
3734 | + d = self.shouldFail2(error.Error, "PUT_DIRURL_bad_t", |
---|
3735 | "400 Bad Request", "PUT to a directory", |
---|
3736 | self.PUT, self.public_url + "/foo?t=BOGUS", "") |
---|
3737 | d.addCallback(lambda res: |
---|
3738 | - self.failUnlessChildURIIs(self.public_root, |
---|
3739 | - u"foo", |
---|
3740 | - self._foo_uri)) |
---|
3741 | + self.failUnlessRWChildURIIs(self.public_root, |
---|
3742 | + u"foo", |
---|
3743 | + self._foo_uri)) |
---|
3744 | return d |
---|
3745 | |
---|
3746 | def test_PUT_NEWFILEURL_uri(self): |
---|
3747 | contents, n, new_uri = self.makefile(8) |
---|
3748 | - d = self.PUT(self.public_url + "/foo/new.txt?t=uri", new_uri) |
---|
3749 | + d = self.shouldSucceed("PUT_NEWFILEURL_uri", http.OK, self.PUT, |
---|
3750 | + self.public_url + "/foo/new.txt?t=uri", new_uri) |
---|
3751 | d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri)) |
---|
3752 | d.addCallback(lambda res: |
---|
3753 | self.failUnlessChildContentsAre(self._foo_node, u"new.txt", |
---|
3754 | @@ -2564,13 +2785,14 @@ |
---|
3755 | |
---|
3756 | def test_PUT_NEWFILE_URI(self): |
---|
3757 | file_contents = "New file contents here\n" |
---|
3758 | - d = self.PUT("/uri", file_contents) |
---|
3759 | + d = self.shouldSucceed("PUT_NEWFILE_URI", http.OK, self.PUT, |
---|
3760 | + "/uri", file_contents) |
---|
3761 | def _check(uri): |
---|
3762 | assert isinstance(uri, str), uri |
---|
3763 | self.failUnless(uri in FakeCHKFileNode.all_contents) |
---|
3764 | self.failUnlessEqual(FakeCHKFileNode.all_contents[uri], |
---|
3765 | file_contents) |
---|
3766 | - return self.GET("/uri/%s" % uri) |
---|
3767 | + return self.shouldSucceedGET("/uri/%s" % uri) |
---|
3768 | d.addCallback(_check) |
---|
3769 | def _check2(res): |
---|
3770 | self.failUnlessEqual(res, file_contents) |
---|
3771 | @@ -2579,13 +2801,14 @@ |
---|
3772 | |
---|
3773 | def test_PUT_NEWFILE_URI_not_mutable(self): |
---|
3774 | file_contents = "New file contents here\n" |
---|
3775 | - d = self.PUT("/uri?mutable=false", file_contents) |
---|
3776 | + d = self.shouldSucceed("PUT_NEWFILE_URI_not_mutable", http.OK, self.PUT, |
---|
3777 | + "/uri?mutable=false", file_contents) |
---|
3778 | def _check(uri): |
---|
3779 | assert isinstance(uri, str), uri |
---|
3780 | self.failUnless(uri in FakeCHKFileNode.all_contents) |
---|
3781 | self.failUnlessEqual(FakeCHKFileNode.all_contents[uri], |
---|
3782 | file_contents) |
---|
3783 | - return self.GET("/uri/%s" % uri) |
---|
3784 | + return self.shouldSucceedGET("/uri/%s" % uri) |
---|
3785 | d.addCallback(_check) |
---|
3786 | def _check2(res): |
---|
3787 | self.failUnlessEqual(res, file_contents) |
---|
3788 | @@ -2602,7 +2825,8 @@ |
---|
3789 | |
---|
3790 | def test_PUT_NEWFILE_URI_mutable(self): |
---|
3791 | file_contents = "New file contents here\n" |
---|
3792 | - d = self.PUT("/uri?mutable=true", file_contents) |
---|
3793 | + d = self.shouldSucceed("PUT_NEWFILE_URI_mutable", http.OK, self.PUT, |
---|
3794 | + "/uri?mutable=true", file_contents) |
---|
3795 | def _check1(filecap): |
---|
3796 | filecap = filecap.strip() |
---|
3797 | self.failUnless(filecap.startswith("URI:SSK:"), filecap) |
---|
3798 | @@ -2614,7 +2838,7 @@ |
---|
3799 | d.addCallback(_check1) |
---|
3800 | def _check2(data): |
---|
3801 | self.failUnlessEqual(data, file_contents) |
---|
3802 | - return self.GET("/uri/%s" % urllib.quote(self.filecap)) |
---|
3803 | + return self.shouldSucceedGET("/uri/%s" % urllib.quote(self.filecap)) |
---|
3804 | d.addCallback(_check2) |
---|
3805 | def _check3(res): |
---|
3806 | self.failUnlessEqual(res, file_contents) |
---|
3807 | @@ -2622,19 +2846,21 @@ |
---|
3808 | return d |
---|
3809 | |
---|
3810 | def test_PUT_mkdir(self): |
---|
3811 | - d = self.PUT("/uri?t=mkdir", "") |
---|
3812 | + d = self.shouldSucceed("PUT_mkdir", http.OK, self.PUT, |
---|
3813 | + "/uri?t=mkdir", "") |
---|
3814 | def _check(uri): |
---|
3815 | n = self.s.create_node_from_uri(uri.strip()) |
---|
3816 | d2 = self.failUnlessNodeKeysAre(n, []) |
---|
3817 | d2.addCallback(lambda res: |
---|
3818 | - self.GET("/uri/%s?t=json" % uri)) |
---|
3819 | + self.shouldSucceedGET("/uri/%s?t=json" % uri)) |
---|
3820 | return d2 |
---|
3821 | d.addCallback(_check) |
---|
3822 | d.addCallback(self.failUnlessIsEmptyJSON) |
---|
3823 | return d |
---|
3824 | |
---|
3825 | def test_POST_check(self): |
---|
3826 | - d = self.POST(self.public_url + "/foo", t="check", name="bar.txt") |
---|
3827 | + d = self.shouldSucceed("POST_check", http.OK, self.POST, |
---|
3828 | + self.public_url + "/foo", t="check", name="bar.txt") |
---|
3829 | def _done(res): |
---|
3830 | # this returns a string form of the results, which are probably |
---|
3831 | # None since we're using fake filenodes. |
---|
3832 | @@ -2647,7 +2873,7 @@ |
---|
3833 | |
---|
3834 | def test_bad_method(self): |
---|
3835 | url = self.webish_url + self.public_url + "/foo/bar.txt" |
---|
3836 | - d = self.shouldHTTPError("test_bad_method", |
---|
3837 | + d = self.shouldHTTPError("bad_method", |
---|
3838 | 501, "Not Implemented", |
---|
3839 | "I don't know how to treat a BOGUS request.", |
---|
3840 | client.getPage, url, method="BOGUS") |
---|
3841 | @@ -2655,28 +2881,30 @@ |
---|
3842 | |
---|
3843 | def test_short_url(self): |
---|
3844 | url = self.webish_url + "/uri" |
---|
3845 | - d = self.shouldHTTPError("test_short_url", 501, "Not Implemented", |
---|
3846 | + d = self.shouldHTTPError("short_url", 501, "Not Implemented", |
---|
3847 | "I don't know how to treat a DELETE request.", |
---|
3848 | client.getPage, url, method="DELETE") |
---|
3849 | return d |
---|
3850 | |
---|
3851 | def test_ophandle_bad(self): |
---|
3852 | url = self.webish_url + "/operations/bogus?t=status" |
---|
3853 | - d = self.shouldHTTPError("test_ophandle_bad", 404, "404 Not Found", |
---|
3854 | + d = self.shouldHTTPError("ophandle_bad", 404, "404 Not Found", |
---|
3855 | "unknown/expired handle 'bogus'", |
---|
3856 | client.getPage, url) |
---|
3857 | return d |
---|
3858 | |
---|
3859 | def test_ophandle_cancel(self): |
---|
3860 | - d = self.POST(self.public_url + "/foo/?t=start-manifest&ophandle=128", |
---|
3861 | - followRedirect=True) |
---|
3862 | + d = self.shouldSucceed("ophandle_cancel-1", http.OK, self.POST, |
---|
3863 | + self.public_url + "/foo/?t=start-manifest&ophandle=128", |
---|
3864 | + followRedirect=True) |
---|
3865 | d.addCallback(lambda ignored: |
---|
3866 | - self.GET("/operations/128?t=status&output=JSON")) |
---|
3867 | + self.shouldSucceedGET("/operations/128?t=status&output=JSON")) |
---|
3868 | def _check1(res): |
---|
3869 | data = simplejson.loads(res) |
---|
3870 | self.failUnless("finished" in data, res) |
---|
3871 | monitor = self.ws.root.child_operations.handles["128"][0] |
---|
3872 | - d = self.POST("/operations/128?t=cancel&output=JSON") |
---|
3873 | + d = self.shouldSucceed("ophandle_cancel-2", http.OK, self.POST, |
---|
3874 | + "/operations/128?t=cancel&output=JSON") |
---|
3875 | def _check2(res): |
---|
3876 | data = simplejson.loads(res) |
---|
3877 | self.failUnless("finished" in data, res) |
---|
3878 | @@ -2686,7 +2914,7 @@ |
---|
3879 | return d |
---|
3880 | d.addCallback(_check1) |
---|
3881 | d.addCallback(lambda ignored: |
---|
3882 | - self.shouldHTTPError("test_ophandle_cancel", |
---|
3883 | + self.shouldHTTPError("ophandle_cancel", |
---|
3884 | 404, "404 Not Found", |
---|
3885 | "unknown/expired handle '128'", |
---|
3886 | self.GET, |
---|
3887 | @@ -2697,7 +2925,7 @@ |
---|
3888 | d = self.POST(self.public_url + "/foo/?t=start-manifest&ophandle=129&retain-for=60", |
---|
3889 | followRedirect=True) |
---|
3890 | d.addCallback(lambda ignored: |
---|
3891 | - self.GET("/operations/129?t=status&output=JSON&retain-for=0")) |
---|
3892 | + self.shouldSucceedGET("/operations/129?t=status&output=JSON&retain-for=0")) |
---|
3893 | def _check1(res): |
---|
3894 | data = simplejson.loads(res) |
---|
3895 | self.failUnless("finished" in data, res) |
---|
3896 | @@ -2705,7 +2933,7 @@ |
---|
3897 | # the retain-for=0 will cause the handle to be expired very soon |
---|
3898 | d.addCallback(self.stall, 2.0) |
---|
3899 | d.addCallback(lambda ignored: |
---|
3900 | - self.shouldHTTPError("test_ophandle_retainfor", |
---|
3901 | + self.shouldHTTPError("ophandle_retainfor", |
---|
3902 | 404, "404 Not Found", |
---|
3903 | "unknown/expired handle '129'", |
---|
3904 | self.GET, |
---|
3905 | @@ -2713,14 +2941,15 @@ |
---|
3906 | return d |
---|
3907 | |
---|
3908 | def test_ophandle_release_after_complete(self): |
---|
3909 | - d = self.POST(self.public_url + "/foo/?t=start-manifest&ophandle=130", |
---|
3910 | - followRedirect=True) |
---|
3911 | + d = self.shouldSucceed("ophandle_release_after_complete", http.OK, self.POST, |
---|
3912 | + self.public_url + "/foo/?t=start-manifest&ophandle=130", |
---|
3913 | + followRedirect=True) |
---|
3914 | d.addCallback(self.wait_for_operation, "130") |
---|
3915 | d.addCallback(lambda ignored: |
---|
3916 | - self.GET("/operations/130?t=status&output=JSON&release-after-complete=true")) |
---|
3917 | + self.shouldSucceedGET("/operations/130?t=status&output=JSON&release-after-complete=true")) |
---|
3918 | # the release-after-complete=true will cause the handle to be expired |
---|
3919 | d.addCallback(lambda ignored: |
---|
3920 | - self.shouldHTTPError("test_ophandle_release_after_complete", |
---|
3921 | + self.shouldHTTPError("ophandle_release_after_complete", |
---|
3922 | 404, "404 Not Found", |
---|
3923 | "unknown/expired handle '130'", |
---|
3924 | self.GET, |
---|
3925 | @@ -2728,7 +2957,8 @@ |
---|
3926 | return d |
---|
3927 | |
---|
3928 | def test_incident(self): |
---|
3929 | - d = self.POST("/report_incident", details="eek") |
---|
3930 | + d = self.shouldSucceed("incident", http.OK, self.POST, |
---|
3931 | + "/report_incident", details="eek") |
---|
3932 | def _done(res): |
---|
3933 | self.failUnless("Thank you for your report!" in res, res) |
---|
3934 | d.addCallback(_done) |
---|
3935 | @@ -2741,7 +2971,7 @@ |
---|
3936 | f.write("hello") |
---|
3937 | f.close() |
---|
3938 | |
---|
3939 | - d = self.GET("/static/subdir/hello.txt") |
---|
3940 | + d = self.shouldSucceedGET("/static/subdir/hello.txt") |
---|
3941 | def _check(res): |
---|
3942 | self.failUnlessEqual(res, "hello") |
---|
3943 | d.addCallback(_check) |
---|
3944 | @@ -2754,7 +2984,7 @@ |
---|
3945 | self.failUnlessEqual(common.parse_replace_arg("false"), False) |
---|
3946 | self.failUnlessEqual(common.parse_replace_arg("only-files"), |
---|
3947 | "only-files") |
---|
3948 | - self.shouldFail(AssertionError, "test_parse_replace_arg", "", |
---|
3949 | + self.shouldFail(AssertionError, "parse_replace_arg", "", |
---|
3950 | common.parse_replace_arg, "only_fles") |
---|
3951 | |
---|
3952 | def test_abbreviate_time(self): |
---|
3953 | @@ -3059,71 +3289,225 @@ |
---|
3954 | d.addErrback(self.explain_web_error) |
---|
3955 | return d |
---|
3956 | |
---|
3957 | - def test_unknown(self): |
---|
3958 | + def test_unknown(self, immutable=False): |
---|
3959 | self.basedir = "web/Grid/unknown" |
---|
3960 | + if immutable: |
---|
3961 | + self.basedir = "web/Grid/unknown-immutable" |
---|
3962 | + |
---|
3963 | self.set_up_grid() |
---|
3964 | c0 = self.g.clients[0] |
---|
3965 | self.uris = {} |
---|
3966 | self.fileurls = {} |
---|
3967 | |
---|
3968 | - future_writecap = "x-tahoe-crazy://I_am_from_the_future." |
---|
3969 | - future_readcap = "x-tahoe-crazy-readonly://I_am_from_the_future." |
---|
3970 | + future_write_uri = "x-tahoe-crazy://I_am_from_the_future." |
---|
3971 | + future_read_uri = "x-tahoe-crazy-readonly://I_am_from_the_future." |
---|
3972 | # the future cap format may contain slashes, which must be tolerated |
---|
3973 | - expected_info_url = "uri/%s?t=info" % urllib.quote(future_writecap, |
---|
3974 | + expected_info_url = "uri/%s?t=info" % urllib.quote(future_write_uri, |
---|
3975 | safe="") |
---|
3976 | - future_node = UnknownNode(future_writecap, future_readcap) |
---|
3977 | |
---|
3978 | - d = c0.create_dirnode() |
---|
3979 | + if immutable: |
---|
3980 | + name = u"future-imm" |
---|
3981 | + future_node = UnknownNode(None, future_read_uri, deep_immutable=True) |
---|
3982 | + d = c0.create_immutable_dirnode({name: (future_node, {})}) |
---|
3983 | + else: |
---|
3984 | + name = u"future" |
---|
3985 | + future_node = UnknownNode(future_write_uri, future_read_uri) |
---|
3986 | + d = c0.create_dirnode() |
---|
3987 | + |
---|
3988 | def _stash_root_and_create_file(n): |
---|
3989 | self.rootnode = n |
---|
3990 | self.rooturl = "uri/" + urllib.quote(n.get_uri()) + "/" |
---|
3991 | self.rourl = "uri/" + urllib.quote(n.get_readonly_uri()) + "/" |
---|
3992 | - return self.rootnode.set_node(u"future", future_node) |
---|
3993 | + if not immutable: |
---|
3994 | + return self.rootnode.set_node(name, future_node) |
---|
3995 | d.addCallback(_stash_root_and_create_file) |
---|
3996 | + |
---|
3997 | # make sure directory listing tolerates unknown nodes |
---|
3998 | d.addCallback(lambda ign: self.GET(self.rooturl)) |
---|
3999 | def _check_html(res): |
---|
4000 | - self.failUnlessIn("<td>future</td>", res) |
---|
4001 | - # find the More Info link for "future", should be relative |
---|
4002 | + self.failUnlessIn("<td>%s</td>" % (str(name),), res) |
---|
4003 | + # find the More Info link for name, should be relative |
---|
4004 | mo = re.search(r'<a href="([^"]+)">More Info</a>', res) |
---|
4005 | info_url = mo.group(1) |
---|
4006 | - self.failUnlessEqual(info_url, "future?t=info") |
---|
4007 | + self.failUnlessEqual(info_url, "%s?t=info" % (str(name),)) |
---|
4008 | |
---|
4009 | d.addCallback(_check_html) |
---|
4010 | d.addCallback(lambda ign: self.GET(self.rooturl+"?t=json")) |
---|
4011 | - def _check_json(res, expect_writecap): |
---|
4012 | + def _check_json(res, expect_rw_uri): |
---|
4013 | data = simplejson.loads(res) |
---|
4014 | self.failUnlessEqual(data[0], "dirnode") |
---|
4015 | - f = data[1]["children"]["future"] |
---|
4016 | + f = data[1]["children"][name] |
---|
4017 | self.failUnlessEqual(f[0], "unknown") |
---|
4018 | - if expect_writecap: |
---|
4019 | - self.failUnlessEqual(f[1]["rw_uri"], future_writecap) |
---|
4020 | + if expect_rw_uri: |
---|
4021 | + self.failUnlessEqual(f[1]["rw_uri"], future_write_uri) |
---|
4022 | else: |
---|
4023 | self.failIfIn("rw_uri", f[1]) |
---|
4024 | - self.failUnlessEqual(f[1]["ro_uri"], future_readcap) |
---|
4025 | + self.failUnlessEqual(f[1]["ro_uri"], |
---|
4026 | + ("imm." if immutable else "ro.") + future_read_uri) |
---|
4027 | self.failUnless("metadata" in f[1]) |
---|
4028 | - d.addCallback(_check_json, expect_writecap=True) |
---|
4029 | - d.addCallback(lambda ign: self.GET(expected_info_url)) |
---|
4030 | - def _check_info(res, expect_readcap): |
---|
4031 | + d.addCallback(_check_json, expect_rw_uri=not immutable) |
---|
4032 | + |
---|
4033 | + def _check_info(res, expect_rw_uri, expect_ro_uri): |
---|
4034 | self.failUnlessIn("Object Type: <span>unknown</span>", res) |
---|
4035 | - self.failUnlessIn(future_writecap, res) |
---|
4036 | - if expect_readcap: |
---|
4037 | - self.failUnlessIn(future_readcap, res) |
---|
4038 | + if expect_rw_uri: |
---|
4039 | + self.failUnlessIn(future_write_uri, res) |
---|
4040 | + if expect_ro_uri: |
---|
4041 | + self.failUnlessIn(future_read_uri, res) |
---|
4042 | + else: |
---|
4043 | + self.failIfIn(future_read_uri, res) |
---|
4044 | self.failIfIn("Raw data as", res) |
---|
4045 | self.failIfIn("Directory writecap", res) |
---|
4046 | self.failIfIn("Checker Operations", res) |
---|
4047 | self.failIfIn("Mutable File Operations", res) |
---|
4048 | self.failIfIn("Directory Operations", res) |
---|
4049 | - d.addCallback(_check_info, expect_readcap=False) |
---|
4050 | - d.addCallback(lambda ign: self.GET(self.rooturl+"future?t=info")) |
---|
4051 | - d.addCallback(_check_info, expect_readcap=True) |
---|
4052 | + |
---|
4053 | + # Known bug: these should have expect_rw_uri=not immutable, but the |
---|
4054 | + # info pages are currently broken. Related to ticket #922. |
---|
4055 | + |
---|
4056 | + d.addCallback(lambda ign: self.GET(expected_info_url)) |
---|
4057 | + d.addCallback(_check_info, expect_rw_uri=False, expect_ro_uri=False) |
---|
4058 | + d.addCallback(lambda ign: self.GET("%s%s?t=info" % (self.rooturl, str(name)))) |
---|
4059 | + d.addCallback(_check_info, expect_rw_uri=False, expect_ro_uri=True) |
---|
4060 | |
---|
4061 | # and make sure that a read-only version of the directory can be |
---|
4062 | - # rendered too. This version will not have future_writecap |
---|
4063 | + # rendered too. This version will not have future_write_uri, whether |
---|
4064 | + # or not future_node was immutable. |
---|
4065 | d.addCallback(lambda ign: self.GET(self.rourl)) |
---|
4066 | d.addCallback(_check_html) |
---|
4067 | d.addCallback(lambda ign: self.GET(self.rourl+"?t=json")) |
---|
4068 | - d.addCallback(_check_json, expect_writecap=False) |
---|
4069 | + d.addCallback(_check_json, expect_rw_uri=False) |
---|
4070 | + return d |
---|
4071 | + |
---|
4072 | + def test_immutable_unknown(self): |
---|
4073 | + return self.test_unknown(immutable=True) |
---|
4074 | + |
---|
4075 | + def test_mutant_dirnodes_are_omitted(self): |
---|
4076 | + self.basedir = "web/Grid/mutant_dirnodes_are_omitted" |
---|
4077 | + |
---|
4078 | + self.set_up_grid() |
---|
4079 | + c = self.g.clients[0] |
---|
4080 | + nm = c.nodemaker |
---|
4081 | + self.uris = {} |
---|
4082 | + self.fileurls = {} |
---|
4083 | + |
---|
4084 | + lonely_uri = "URI:LIT:n5xgk" # LIT for "one" |
---|
4085 | + mut_write_uri = "URI:SSK:vfvcbdfbszyrsaxchgevhmmlii:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq" |
---|
4086 | + mut_read_uri = "URI:SSK-RO:e3mdrzfwhoq42hy5ubcz6rp3o4:ybyibhnp3vvwuq2vaw2ckjmesgkklfs6ghxleztqidihjyofgw7q" |
---|
4087 | + |
---|
4088 | + # This method tests mainly dirnode, but we'd have to duplicate code in order to |
---|
4089 | + # test the dirnode and web layers separately. |
---|
4090 | + |
---|
4091 | + # 'lonely' is a valid LIT child, 'ro' is a mutant child with an SSK-RO readcap, |
---|
4092 | + # and 'write-in-ro' is a mutant child with an SSK writecap in the ro_uri field. |
---|
4093 | + # When the directory is read, the mutants should be silently disposed of, leaving |
---|
4094 | + # their lonely sibling. |
---|
4095 | + # We don't test the case of a retrieving a cap from the encrypted rw_uri field, |
---|
4096 | + # because immutable directories don't have a writecap and therefore that field |
---|
4097 | + # isn't (and can't be) decrypted. |
---|
4098 | + # TODO: The field still exists in the netstring. Technically we should check what |
---|
4099 | + # happens if something is put there (it should be ignored), but that can wait. |
---|
4100 | + |
---|
4101 | + lonely_child = nm.create_from_cap(lonely_uri) |
---|
4102 | + mutant_ro_child = nm.create_from_cap(mut_read_uri) |
---|
4103 | + mutant_write_in_ro_child = nm.create_from_cap(mut_write_uri) |
---|
4104 | + |
---|
4105 | + def _by_hook_or_by_crook(): |
---|
4106 | + return True |
---|
4107 | + for n in [mutant_ro_child, mutant_write_in_ro_child]: |
---|
4108 | + n.is_allowed_in_immutable_directory = _by_hook_or_by_crook |
---|
4109 | + |
---|
4110 | + mutant_write_in_ro_child.get_write_uri = lambda: None |
---|
4111 | + mutant_write_in_ro_child.get_readonly_uri = lambda: mut_write_uri |
---|
4112 | + |
---|
4113 | + kids = {u"lonely": (lonely_child, {}), |
---|
4114 | + u"ro": (mutant_ro_child, {}), |
---|
4115 | + u"write-in-ro": (mutant_write_in_ro_child, {}), |
---|
4116 | + } |
---|
4117 | + d = c.create_immutable_dirnode(kids) |
---|
4118 | + |
---|
4119 | + def _created(dn): |
---|
4120 | + self.failUnless(isinstance(dn, dirnode.DirectoryNode)) |
---|
4121 | + self.failIf(dn.is_mutable()) |
---|
4122 | + self.failUnless(dn.is_readonly()) |
---|
4123 | + # This checks that if we somehow ended up calling dn._decrypt_rwcapdata, it would fail. |
---|
4124 | + self.failIf(hasattr(dn._node, 'get_writekey')) |
---|
4125 | + rep = str(dn) |
---|
4126 | + self.failUnless("RO-IMM" in rep) |
---|
4127 | + cap = dn.get_cap() |
---|
4128 | + self.failUnlessIn("CHK", cap.to_string()) |
---|
4129 | + self.cap = cap |
---|
4130 | + self.rootnode = dn |
---|
4131 | + self.rooturl = "uri/" + urllib.quote(dn.get_uri()) + "/" |
---|
4132 | + return download_to_data(dn._node) |
---|
4133 | + d.addCallback(_created) |
---|
4134 | + |
---|
4135 | + def _check_data(data): |
---|
4136 | + # Decode the netstring representation of the directory to check that all children |
---|
4137 | + # are present. This is a bit of an abstraction violation, but there's not really |
---|
4138 | + # any other way to do it given that the real DirectoryNode._unpack_contents would |
---|
4139 | + # strip the mutant children out (which is what we're trying to test, later). |
---|
4140 | + position = 0 |
---|
4141 | + numkids = 0 |
---|
4142 | + while position < len(data): |
---|
4143 | + entries, position = split_netstring(data, 1, position) |
---|
4144 | + entry = entries[0] |
---|
4145 | + (name, ro_uri, rwcapdata, metadata_s), subpos = split_netstring(entry, 4) |
---|
4146 | + name = name.decode("utf-8") |
---|
4147 | + self.failUnless(rwcapdata == "") |
---|
4148 | + ro_uri = ro_uri.strip() |
---|
4149 | + if name in kids: |
---|
4150 | + self.failIfEqual(ro_uri, "") |
---|
4151 | + (expected_child, ign) = kids[name] |
---|
4152 | + self.failUnlessEqual(ro_uri, expected_child.get_readonly_uri()) |
---|
4153 | + numkids += 1 |
---|
4154 | + |
---|
4155 | + self.failUnlessEqual(numkids, 3) |
---|
4156 | + return self.rootnode.list() |
---|
4157 | + d.addCallback(_check_data) |
---|
4158 | + |
---|
4159 | + # Now when we use the real directory listing code, the mutants should be absent. |
---|
4160 | + def _check_kids(children): |
---|
4161 | + self.failUnlessEqual(sorted(children.keys()), [u"lonely"]) |
---|
4162 | + lonely_node, lonely_metadata = children[u"lonely"] |
---|
4163 | + |
---|
4164 | + self.failUnlessEqual(lonely_node.get_write_uri(), None) |
---|
4165 | + self.failUnlessEqual(lonely_node.get_readonly_uri(), lonely_uri) |
---|
4166 | + d.addCallback(_check_kids) |
---|
4167 | + |
---|
4168 | + d.addCallback(lambda ign: nm.create_from_cap(self.cap.to_string())) |
---|
4169 | + d.addCallback(lambda n: n.list()) |
---|
4170 | + d.addCallback(_check_kids) # again with dirnode recreated from cap |
---|
4171 | + |
---|
4172 | + # Make sure the lonely child can be listed in HTML... |
---|
4173 | + d.addCallback(lambda ign: self.GET(self.rooturl)) |
---|
4174 | + def _check_html(res): |
---|
4175 | + self.failIfIn("URI:SSK", res) |
---|
4176 | + get_lonely = "".join([r'<td>FILE</td>', |
---|
4177 | + r'\s+<td>', |
---|
4178 | + r'<a href="[^"]+%s[^"]+">lonely</a>' % (urllib.quote(lonely_uri),), |
---|
4179 | + r'</td>', |
---|
4180 | + r'\s+<td>%d</td>' % len("one"), |
---|
4181 | + ]) |
---|
4182 | + self.failUnless(re.search(get_lonely, res), res) |
---|
4183 | + |
---|
4184 | + # find the More Info link for name, should be relative |
---|
4185 | + mo = re.search(r'<a href="([^"]+)">More Info</a>', res) |
---|
4186 | + info_url = mo.group(1) |
---|
4187 | + self.failUnless(info_url.endswith(urllib.quote(lonely_uri) + "?t=info"), info_url) |
---|
4188 | + d.addCallback(_check_html) |
---|
4189 | + |
---|
4190 | + # ... and in JSON. |
---|
4191 | + d.addCallback(lambda ign: self.GET(self.rooturl+"?t=json")) |
---|
4192 | + def _check_json(res): |
---|
4193 | + data = simplejson.loads(res) |
---|
4194 | + self.failUnlessEqual(data[0], "dirnode") |
---|
4195 | + listed_children = data[1]["children"] |
---|
4196 | + self.failUnlessEqual(sorted(listed_children.keys()), [u"lonely"]) |
---|
4197 | + ll_type, ll_data = listed_children[u"lonely"] |
---|
4198 | + self.failUnlessEqual(ll_type, "filenode") |
---|
4199 | + self.failIf("rw_uri" in ll_data) |
---|
4200 | + self.failUnlessEqual(ll_data["ro_uri"], lonely_uri) |
---|
4201 | + d.addCallback(_check_json) |
---|
4202 | return d |
---|
4203 | |
---|
4204 | def test_deep_check(self): |
---|
4205 | @@ -3156,10 +3540,10 @@ |
---|
4206 | |
---|
4207 | # this tests that deep-check and stream-manifest will ignore |
---|
4208 | # UnknownNode instances. Hopefully this will also cover deep-stats. |
---|
4209 | - future_writecap = "x-tahoe-crazy://I_am_from_the_future." |
---|
4210 | - future_readcap = "x-tahoe-crazy-readonly://I_am_from_the_future." |
---|
4211 | - future_node = UnknownNode(future_writecap, future_readcap) |
---|
4212 | - d.addCallback(lambda ign: self.rootnode.set_node(u"future",future_node)) |
---|
4213 | + future_write_uri = "x-tahoe-crazy://I_am_from_the_future." |
---|
4214 | + future_read_uri = "x-tahoe-crazy-readonly://I_am_from_the_future." |
---|
4215 | + future_node = UnknownNode(future_write_uri, future_read_uri) |
---|
4216 | + d.addCallback(lambda ign: self.rootnode.set_node(u"future", future_node)) |
---|
4217 | |
---|
4218 | def _clobber_shares(ignored): |
---|
4219 | self.delete_shares_numbered(self.uris["sick"], [0,1]) |
---|
4220 | diff -rN -u old-tahoe/src/allmydata/unknown.py new-tahoe/src/allmydata/unknown.py |
---|
4221 | --- old-tahoe/src/allmydata/unknown.py 2010-01-23 12:59:10.164000000 +0000 |
---|
4222 | +++ new-tahoe/src/allmydata/unknown.py 2010-01-23 12:59:12.153000000 +0000 |
---|
4223 | @@ -1,29 +1,146 @@ |
---|
4224 | + |
---|
4225 | from zope.interface import implements |
---|
4226 | from twisted.internet import defer |
---|
4227 | -from allmydata.interfaces import IFilesystemNode |
---|
4228 | +from allmydata.interfaces import IFilesystemNode, MustNotBeUnknownRWError |
---|
4229 | +from allmydata import uri |
---|
4230 | +from allmydata.uri import ALLEGED_READONLY_PREFIX, ALLEGED_IMMUTABLE_PREFIX |
---|
4231 | + |
---|
4232 | + |
---|
4233 | +# See ticket #833 for design rationale of UnknownNodes. |
---|
4234 | + |
---|
4235 | +"""Strip prefixes when storing an URI in a ro_uri field.""" |
---|
4236 | +def strip_prefix_for_ro(ro_uri, deep_immutable): |
---|
4237 | + # It is possible for an alleged-immutable URI to be put into a |
---|
4238 | + # mutable directory. In that case the ALLEGED_IMMUTABLE_PREFIX |
---|
4239 | + # should not be stripped. In other cases, the prefix can safely |
---|
4240 | + # be stripped because it is implied by the context. |
---|
4241 | + |
---|
4242 | + if ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX): |
---|
4243 | + if not deep_immutable: |
---|
4244 | + return ro_uri |
---|
4245 | + return ro_uri[len(ALLEGED_IMMUTABLE_PREFIX):] |
---|
4246 | + elif ro_uri.startswith(ALLEGED_READONLY_PREFIX): |
---|
4247 | + return ro_uri[len(ALLEGED_READONLY_PREFIX):] |
---|
4248 | + else: |
---|
4249 | + return ro_uri |
---|
4250 | |
---|
4251 | class UnknownNode: |
---|
4252 | implements(IFilesystemNode) |
---|
4253 | - def __init__(self, writecap, readcap): |
---|
4254 | - assert writecap is None or isinstance(writecap, str) |
---|
4255 | - self.writecap = writecap |
---|
4256 | - assert readcap is None or isinstance(readcap, str) |
---|
4257 | - self.readcap = readcap |
---|
4258 | + |
---|
4259 | + def __init__(self, rw_uri, ro_uri, deep_immutable=False, |
---|
4260 | + name=u"<unknown name>"): |
---|
4261 | + #traceback.print_stack() |
---|
4262 | + #print '%r.__init__(%r, %r, deep_immutable=%r, name=%r)' % (self, rw_uri, ro_uri, deep_immutable, name) |
---|
4263 | + assert rw_uri is None or isinstance(rw_uri, str) |
---|
4264 | + assert ro_uri is None or isinstance(ro_uri, str) |
---|
4265 | + |
---|
4266 | + # We don't raise errors when creating an UnknownNode; we instead create an |
---|
4267 | + # opaque node that records the error. This avoids breaking operations that |
---|
4268 | + # never store the opaque node. |
---|
4269 | + # Note that this means that if a stored dirnode has only a rw_uri, it |
---|
4270 | + # might be dropped. Any future "write-only" cap formats should have a dummy |
---|
4271 | + # unusable read cap to stop that from happening. |
---|
4272 | + |
---|
4273 | + self.error = None |
---|
4274 | + self.rw_uri = self.ro_uri = None |
---|
4275 | + if rw_uri is not None: |
---|
4276 | + if deep_immutable: |
---|
4277 | + self.error = MustNotBeUnknownRWError("cannot attach unknown rw cap as immutable child", |
---|
4278 | + name, True) |
---|
4279 | + return |
---|
4280 | + elif ro_uri is None: |
---|
4281 | + # If we have a single unknown cap (specified as a single cap |
---|
4282 | + # argument, or from a rw_uri slot when ro_uri has been omitted), |
---|
4283 | + # then we cannot tell whether it is a rw_uri, and we cannot |
---|
4284 | + # diminish it to a ro_uri. Prefixing it with ALLEGED_READONLY_PREFIX |
---|
4285 | + # would not be sufficient because we have no reason to believe |
---|
4286 | + # that it is a ro_uri, so that might grant excess authority. |
---|
4287 | + self.error = MustNotBeUnknownRWError("cannot attach unknown rw cap as child", |
---|
4288 | + name, False) |
---|
4289 | + return |
---|
4290 | + |
---|
4291 | + # If ro_uri definitely fails the constraint, it should be treated as opaque. |
---|
4292 | + if ro_uri is not None: |
---|
4293 | + read_cap = uri.from_string(ro_uri, deep_immutable=deep_immutable, name=name) |
---|
4294 | + if isinstance(read_cap, uri.UnknownURI): |
---|
4295 | + self.error = read_cap.get_error() |
---|
4296 | + if self.error: |
---|
4297 | + return |
---|
4298 | + |
---|
4299 | + if deep_immutable: |
---|
4300 | + # strengthen ro_uri to have ALLEGED_IMMUTABLE_PREFIX |
---|
4301 | + if ro_uri is not None: |
---|
4302 | + if ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX): |
---|
4303 | + self.ro_uri = ro_uri |
---|
4304 | + elif ro_uri.startswith(ALLEGED_READONLY_PREFIX): |
---|
4305 | + self.ro_uri = ALLEGED_IMMUTABLE_PREFIX + ro_uri[len(ALLEGED_READONLY_PREFIX):] |
---|
4306 | + else: |
---|
4307 | + self.ro_uri = ALLEGED_IMMUTABLE_PREFIX + ro_uri |
---|
4308 | + else: |
---|
4309 | + self.rw_uri = rw_uri |
---|
4310 | + # strengthen ro_uri to have ALLEGED_READONLY_PREFIX |
---|
4311 | + if ro_uri is not None: |
---|
4312 | + if (ro_uri.startswith(ALLEGED_READONLY_PREFIX) or |
---|
4313 | + ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX)): |
---|
4314 | + self.ro_uri = ro_uri |
---|
4315 | + else: |
---|
4316 | + self.ro_uri = ALLEGED_READONLY_PREFIX + ro_uri |
---|
4317 | + |
---|
4318 | + #print 'self.(error, rw_uri, ro_uri) = (%r, %r, %r)' % (self.error, self.rw_uri, self.ro_uri) |
---|
4319 | + |
---|
4320 | + def get_cap(self): |
---|
4321 | + return uri.UnknownURI(self.rw_uri or self.ro_uri) |
---|
4322 | + |
---|
4323 | + def get_readcap(self): |
---|
4324 | + return uri.UnknownURI(self.ro_uri) |
---|
4325 | + |
---|
4326 | + def is_readonly(self): |
---|
4327 | + raise AssertionError("an UnknownNode might be either read-only or " |
---|
4328 | + "read/write, so we shouldn't be calling is_readonly") |
---|
4329 | + |
---|
4330 | + def is_mutable(self): |
---|
4331 | + raise AssertionError("an UnknownNode might be either mutable or immutable, " |
---|
4332 | + "so we shouldn't be calling is_mutable") |
---|
4333 | + |
---|
4334 | + def is_unknown(self): |
---|
4335 | + return True |
---|
4336 | + |
---|
4337 | + def is_allowed_in_immutable_directory(self): |
---|
4338 | + # An UnknownNode consisting only of a ro_uri is allowed in an |
---|
4339 | + # immutable directory, even though we do not know that it is |
---|
4340 | + # immutable (or even read-only), provided that no error was detected. |
---|
4341 | + return not self.error and not self.rw_uri |
---|
4342 | + |
---|
4343 | + def raise_error(self): |
---|
4344 | + if self.error is not None: |
---|
4345 | + raise self.error |
---|
4346 | + |
---|
4347 | def get_uri(self): |
---|
4348 | - return self.writecap |
---|
4349 | + return self.rw_uri or self.ro_uri |
---|
4350 | + |
---|
4351 | + def get_write_uri(self): |
---|
4352 | + return self.rw_uri |
---|
4353 | + |
---|
4354 | def get_readonly_uri(self): |
---|
4355 | - return self.readcap |
---|
4356 | + return self.ro_uri |
---|
4357 | + |
---|
4358 | def get_storage_index(self): |
---|
4359 | return None |
---|
4360 | + |
---|
4361 | def get_verify_cap(self): |
---|
4362 | return None |
---|
4363 | + |
---|
4364 | def get_repair_cap(self): |
---|
4365 | return None |
---|
4366 | + |
---|
4367 | def get_size(self): |
---|
4368 | return None |
---|
4369 | + |
---|
4370 | def get_current_size(self): |
---|
4371 | return defer.succeed(None) |
---|
4372 | + |
---|
4373 | def check(self, monitor, verify, add_lease): |
---|
4374 | return defer.succeed(None) |
---|
4375 | + |
---|
4376 | def check_and_repair(self, monitor, verify, add_lease): |
---|
4377 | return defer.succeed(None) |
---|
4378 | diff -rN -u old-tahoe/src/allmydata/uri.py new-tahoe/src/allmydata/uri.py |
---|
4379 | --- old-tahoe/src/allmydata/uri.py 2010-01-23 12:59:10.175000000 +0000 |
---|
4380 | +++ new-tahoe/src/allmydata/uri.py 2010-01-23 12:59:12.157000000 +0000 |
---|
4381 | @@ -5,14 +5,16 @@ |
---|
4382 | from allmydata.storage.server import si_a2b, si_b2a |
---|
4383 | from allmydata.util import base32, hashutil |
---|
4384 | from allmydata.interfaces import IURI, IDirnodeURI, IFileURI, IImmutableFileURI, \ |
---|
4385 | - IVerifierURI, IMutableFileURI, IDirectoryURI, IReadonlyDirectoryURI |
---|
4386 | + IVerifierURI, IMutableFileURI, IDirectoryURI, IReadonlyDirectoryURI, \ |
---|
4387 | + MustBeDeepImmutableError, MustBeReadonlyError |
---|
4388 | |
---|
4389 | class BadURIError(Exception): |
---|
4390 | pass |
---|
4391 | |
---|
4392 | -# the URI shall be an ascii representation of the file. It shall contain |
---|
4393 | -# enough information to retrieve and validate the contents. It shall be |
---|
4394 | -# expressed in a limited character set (namely [TODO]). |
---|
4395 | +# The URI shall be an ASCII representation of a reference to the file/directory. |
---|
4396 | +# It shall contain enough information to retrieve and validate the contents. |
---|
4397 | +# It shall be expressed in a limited character set (currently base32 plus ':' and |
---|
4398 | +# capital letters, but future URIs might use a larger charset). |
---|
4399 | |
---|
4400 | BASE32STR_128bits = '(%s{25}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_3bits) |
---|
4401 | BASE32STR_256bits = '(%s{51}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_1bits) |
---|
4402 | @@ -39,6 +41,10 @@ |
---|
4403 | return self.to_string() != them.to_string() |
---|
4404 | else: |
---|
4405 | return True |
---|
4406 | + |
---|
4407 | + def is_unknown(self): |
---|
4408 | + return False |
---|
4409 | + |
---|
4410 | def to_human_encoding(self): |
---|
4411 | return 'http://127.0.0.1:3456/uri/'+self.to_string() |
---|
4412 | |
---|
4413 | @@ -97,8 +103,10 @@ |
---|
4414 | |
---|
4415 | def is_readonly(self): |
---|
4416 | return True |
---|
4417 | + |
---|
4418 | def is_mutable(self): |
---|
4419 | return False |
---|
4420 | + |
---|
4421 | def get_readonly(self): |
---|
4422 | return self |
---|
4423 | |
---|
4424 | @@ -157,6 +165,18 @@ |
---|
4425 | self.total_shares, |
---|
4426 | self.size)) |
---|
4427 | |
---|
4428 | + def is_readonly(self): |
---|
4429 | + return True |
---|
4430 | + |
---|
4431 | + def is_mutable(self): |
---|
4432 | + return False |
---|
4433 | + |
---|
4434 | + def get_readonly(self): |
---|
4435 | + return self |
---|
4436 | + |
---|
4437 | + def get_verify_cap(self): |
---|
4438 | + return self |
---|
4439 | + |
---|
4440 | |
---|
4441 | class LiteralFileURI(_BaseURI): |
---|
4442 | implements(IURI, IImmutableFileURI) |
---|
4443 | @@ -297,10 +317,13 @@ |
---|
4444 | |
---|
4445 | def is_readonly(self): |
---|
4446 | return True |
---|
4447 | + |
---|
4448 | def is_mutable(self): |
---|
4449 | return True |
---|
4450 | + |
---|
4451 | def get_readonly(self): |
---|
4452 | return self |
---|
4453 | + |
---|
4454 | def get_verify_cap(self): |
---|
4455 | return SSKVerifierURI(self.storage_index, self.fingerprint) |
---|
4456 | |
---|
4457 | @@ -334,6 +357,15 @@ |
---|
4458 | return 'URI:SSK-Verifier:%s:%s' % (si_b2a(self.storage_index), |
---|
4459 | base32.b2a(self.fingerprint)) |
---|
4460 | |
---|
4461 | + def is_readonly(self): |
---|
4462 | + return True |
---|
4463 | + def is_mutable(self): |
---|
4464 | + return False |
---|
4465 | + def get_readonly(self): |
---|
4466 | + return self |
---|
4467 | + def get_verify_cap(self): |
---|
4468 | + return self |
---|
4469 | + |
---|
4470 | class _DirectoryBaseURI(_BaseURI): |
---|
4471 | implements(IURI, IDirnodeURI) |
---|
4472 | def __init__(self, filenode_uri=None): |
---|
4473 | @@ -376,12 +408,12 @@ |
---|
4474 | def abbrev_si(self): |
---|
4475 | return base32.b2a(self._filenode_uri.storage_index)[:5] |
---|
4476 | |
---|
4477 | - def get_filenode_cap(self): |
---|
4478 | - return self._filenode_uri |
---|
4479 | - |
---|
4480 | def is_mutable(self): |
---|
4481 | return True |
---|
4482 | |
---|
4483 | + def get_filenode_cap(self): |
---|
4484 | + return self._filenode_uri |
---|
4485 | + |
---|
4486 | def get_verify_cap(self): |
---|
4487 | return DirectoryURIVerifier(self._filenode_uri.get_verify_cap()) |
---|
4488 | |
---|
4489 | @@ -432,12 +464,12 @@ |
---|
4490 | assert isinstance(filenode_uri, self.INNER_URI_CLASS), filenode_uri |
---|
4491 | _DirectoryBaseURI.__init__(self, filenode_uri) |
---|
4492 | |
---|
4493 | - def is_mutable(self): |
---|
4494 | - return False |
---|
4495 | - |
---|
4496 | def is_readonly(self): |
---|
4497 | return True |
---|
4498 | |
---|
4499 | + def is_mutable(self): |
---|
4500 | + return False |
---|
4501 | + |
---|
4502 | def get_readonly(self): |
---|
4503 | return self |
---|
4504 | |
---|
4505 | @@ -460,6 +492,7 @@ |
---|
4506 | # LIT caps have no verifier, since they aren't distributed |
---|
4507 | return None |
---|
4508 | |
---|
4509 | + |
---|
4510 | def wrap_dirnode_cap(filecap): |
---|
4511 | if isinstance(filecap, WriteableSSKFileURI): |
---|
4512 | return DirectoryURI(filecap) |
---|
4513 | @@ -469,7 +502,8 @@ |
---|
4514 | return ImmutableDirectoryURI(filecap) |
---|
4515 | if isinstance(filecap, LiteralFileURI): |
---|
4516 | return LiteralDirectoryURI(filecap) |
---|
4517 | - assert False, "cannot wrap a dirnode around %s" % filecap.__class__ |
---|
4518 | + assert False, "cannot interpret as a directory cap: %s" % filecap.__class__ |
---|
4519 | + |
---|
4520 | |
---|
4521 | class DirectoryURIVerifier(_DirectoryBaseURI): |
---|
4522 | implements(IVerifierURI) |
---|
4523 | @@ -487,6 +521,10 @@ |
---|
4524 | def get_filenode_cap(self): |
---|
4525 | return self._filenode_uri |
---|
4526 | |
---|
4527 | + def is_mutable(self): |
---|
4528 | + return False |
---|
4529 | + |
---|
4530 | + |
---|
4531 | class ImmutableDirectoryURIVerifier(DirectoryURIVerifier): |
---|
4532 | implements(IVerifierURI) |
---|
4533 | BASE_STRING='URI:DIR2-CHK-Verifier:' |
---|
4534 | @@ -494,68 +532,133 @@ |
---|
4535 | BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-CHK-VERIFIER'+SEP) |
---|
4536 | INNER_URI_CLASS=CHKFileVerifierURI |
---|
4537 | |
---|
4538 | + |
---|
4539 | class UnknownURI: |
---|
4540 | - def __init__(self, uri): |
---|
4541 | + def __init__(self, uri, error=None): |
---|
4542 | self._uri = uri |
---|
4543 | + self._error = error |
---|
4544 | + |
---|
4545 | def to_string(self): |
---|
4546 | return self._uri |
---|
4547 | |
---|
4548 | -def from_string(s): |
---|
4549 | - if not isinstance(s, str): |
---|
4550 | - raise TypeError("unknown URI type: %s.." % str(s)[:100]) |
---|
4551 | - elif s.startswith('URI:CHK:'): |
---|
4552 | + def get_readonly(self): |
---|
4553 | + return None |
---|
4554 | + |
---|
4555 | + def get_error(self): |
---|
4556 | + return self._error |
---|
4557 | + |
---|
4558 | + |
---|
4559 | +ALLEGED_READONLY_PREFIX = 'ro.' |
---|
4560 | +ALLEGED_IMMUTABLE_PREFIX = 'imm.' |
---|
4561 | + |
---|
4562 | +def from_string(u, deep_immutable=False, name=u"<unknown name>"): |
---|
4563 | + if not isinstance(u, str): |
---|
4564 | + raise TypeError("unknown URI type: %s.." % str(u)[:100]) |
---|
4565 | + |
---|
4566 | + # We allow and check ALLEGED_READONLY_PREFIX or ALLEGED_IMMUTABLE_PREFIX |
---|
4567 | + # on all URIs, even though we would only strictly need to do so for caps of |
---|
4568 | + # new formats (post Tahoe-LAFS 1.6). URIs that are not consistent with their |
---|
4569 | + # prefix are treated as unknown. This should be revisited when we add the |
---|
4570 | + # new cap formats. See <http://allmydata.org/trac/tahoe/ticket/833#comment:31>. |
---|
4571 | + s = u |
---|
4572 | + can_be_mutable = can_be_writeable = not deep_immutable |
---|
4573 | + if s.startswith(ALLEGED_IMMUTABLE_PREFIX): |
---|
4574 | + can_be_mutable = can_be_writeable = False |
---|
4575 | + s = s[len(ALLEGED_IMMUTABLE_PREFIX):] |
---|
4576 | + elif s.startswith(ALLEGED_READONLY_PREFIX): |
---|
4577 | + can_be_writeable = False |
---|
4578 | + s = s[len(ALLEGED_READONLY_PREFIX):] |
---|
4579 | + |
---|
4580 | + error = None |
---|
4581 | + if s.startswith('URI:CHK:'): |
---|
4582 | return CHKFileURI.init_from_string(s) |
---|
4583 | elif s.startswith('URI:CHK-Verifier:'): |
---|
4584 | return CHKFileVerifierURI.init_from_string(s) |
---|
4585 | elif s.startswith('URI:LIT:'): |
---|
4586 | return LiteralFileURI.init_from_string(s) |
---|
4587 | elif s.startswith('URI:SSK:'): |
---|
4588 | - return WriteableSSKFileURI.init_from_string(s) |
---|
4589 | + if can_be_writeable: |
---|
4590 | + return WriteableSSKFileURI.init_from_string(s) |
---|
4591 | + error = MustBeReadonlyError("URI:SSK file writecap used in a read-only context", |
---|
4592 | + name) |
---|
4593 | elif s.startswith('URI:SSK-RO:'): |
---|
4594 | - return ReadonlySSKFileURI.init_from_string(s) |
---|
4595 | + if can_be_mutable: |
---|
4596 | + return ReadonlySSKFileURI.init_from_string(s) |
---|
4597 | + error = MustBeDeepImmutableError("URI:SSK-RO readcap to a mutable file used in an immutable context", |
---|
4598 | + name) |
---|
4599 | elif s.startswith('URI:SSK-Verifier:'): |
---|
4600 | return SSKVerifierURI.init_from_string(s) |
---|
4601 | elif s.startswith('URI:DIR2:'): |
---|
4602 | - return DirectoryURI.init_from_string(s) |
---|
4603 | + if can_be_writeable: |
---|
4604 | + return DirectoryURI.init_from_string(s) |
---|
4605 | + error = MustBeReadonlyError("URI:DIR2 directory writecap used in a read-only context", |
---|
4606 | + name) |
---|
4607 | elif s.startswith('URI:DIR2-RO:'): |
---|
4608 | - return ReadonlyDirectoryURI.init_from_string(s) |
---|
4609 | + if can_be_mutable: |
---|
4610 | + return ReadonlyDirectoryURI.init_from_string(s) |
---|
4611 | + error = MustBeDeepImmutableError("URI:DIR2-RO readcap to a mutable directory used in an immutable context", |
---|
4612 | + name) |
---|
4613 | elif s.startswith('URI:DIR2-Verifier:'): |
---|
4614 | return DirectoryURIVerifier.init_from_string(s) |
---|
4615 | elif s.startswith('URI:DIR2-CHK:'): |
---|
4616 | return ImmutableDirectoryURI.init_from_string(s) |
---|
4617 | elif s.startswith('URI:DIR2-LIT:'): |
---|
4618 | return LiteralDirectoryURI.init_from_string(s) |
---|
4619 | - return UnknownURI(s) |
---|
4620 | + elif s.startswith('x-tahoe-future-test-writeable:') and not can_be_writeable: |
---|
4621 | + # For testing how future writeable caps would behave in read-only contexts. |
---|
4622 | + error = MustBeReadonlyError("x-tahoe-future-test-writeable: testing cap used in a read-only context", |
---|
4623 | + name) |
---|
4624 | + elif s.startswith('x-tahoe-future-test-mutable:') and not can_be_mutable: |
---|
4625 | + # For testing how future mutable readcaps would behave in immutable contexts. |
---|
4626 | + error = MustBeDeepImmutableError("x-tahoe-future-test-mutable: testing cap used in an immutable context", |
---|
4627 | + name) |
---|
4628 | + |
---|
4629 | + #if error: print error |
---|
4630 | + return UnknownURI(u, error=error) |
---|
4631 | |
---|
4632 | def is_uri(s): |
---|
4633 | try: |
---|
4634 | - from_string(s) |
---|
4635 | + from_string(s, deep_immutable=False) |
---|
4636 | return True |
---|
4637 | except (TypeError, AssertionError): |
---|
4638 | return False |
---|
4639 | |
---|
4640 | -def from_string_dirnode(s): |
---|
4641 | - u = from_string(s) |
---|
4642 | +def is_literal_file_uri(s): |
---|
4643 | + if not isinstance(s, str): |
---|
4644 | + return False |
---|
4645 | + return (s.startswith('URI:LIT:') or |
---|
4646 | + s.startswith(ALLEGED_READONLY_PREFIX + 'URI:LIT:') or |
---|
4647 | + s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:LIT:')) |
---|
4648 | + |
---|
4649 | +def has_uri_prefix(s): |
---|
4650 | + if not isinstance(s, str): |
---|
4651 | + return False |
---|
4652 | + return (s.startswith("URI:") or |
---|
4653 | + s.startswith(ALLEGED_READONLY_PREFIX + 'URI:') or |
---|
4654 | + s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:')) |
---|
4655 | + |
---|
4656 | +def from_string_dirnode(s, **kwargs): |
---|
4657 | + u = from_string(s, **kwargs) |
---|
4658 | assert IDirnodeURI.providedBy(u) |
---|
4659 | return u |
---|
4660 | |
---|
4661 | registerAdapter(from_string_dirnode, str, IDirnodeURI) |
---|
4662 | |
---|
4663 | -def from_string_filenode(s): |
---|
4664 | - u = from_string(s) |
---|
4665 | +def from_string_filenode(s, **kwargs): |
---|
4666 | + u = from_string(s, **kwargs) |
---|
4667 | assert IFileURI.providedBy(u) |
---|
4668 | return u |
---|
4669 | |
---|
4670 | registerAdapter(from_string_filenode, str, IFileURI) |
---|
4671 | |
---|
4672 | -def from_string_mutable_filenode(s): |
---|
4673 | - u = from_string(s) |
---|
4674 | +def from_string_mutable_filenode(s, **kwargs): |
---|
4675 | + u = from_string(s, **kwargs) |
---|
4676 | assert IMutableFileURI.providedBy(u) |
---|
4677 | return u |
---|
4678 | registerAdapter(from_string_mutable_filenode, str, IMutableFileURI) |
---|
4679 | |
---|
4680 | -def from_string_verifier(s): |
---|
4681 | - u = from_string(s) |
---|
4682 | +def from_string_verifier(s, **kwargs): |
---|
4683 | + u = from_string(s, **kwargs) |
---|
4684 | assert IVerifierURI.providedBy(u) |
---|
4685 | return u |
---|
4686 | registerAdapter(from_string_verifier, str, IVerifierURI) |
---|
4687 | diff -rN -u old-tahoe/src/allmydata/web/common.py new-tahoe/src/allmydata/web/common.py |
---|
4688 | --- old-tahoe/src/allmydata/web/common.py 2010-01-23 12:59:10.472000000 +0000 |
---|
4689 | +++ new-tahoe/src/allmydata/web/common.py 2010-01-23 12:59:12.357000000 +0000 |
---|
4690 | @@ -8,7 +8,8 @@ |
---|
4691 | from nevow.util import resource_filename |
---|
4692 | from allmydata.interfaces import ExistingChildError, NoSuchChildError, \ |
---|
4693 | FileTooLargeError, NotEnoughSharesError, NoSharesError, \ |
---|
4694 | - NotDeepImmutableError, EmptyPathnameComponentError |
---|
4695 | + EmptyPathnameComponentError, MustBeDeepImmutableError, \ |
---|
4696 | + MustBeReadonlyError, MustNotBeUnknownRWError |
---|
4697 | from allmydata.mutable.common import UnrecoverableFileError |
---|
4698 | from allmydata.util import abbreviate # TODO: consolidate |
---|
4699 | |
---|
4700 | @@ -181,9 +182,42 @@ |
---|
4701 | "failure, or disk corruption. You should perform a filecheck on " |
---|
4702 | "this object to learn more.") |
---|
4703 | return (t, http.GONE) |
---|
4704 | - if f.check(NotDeepImmutableError): |
---|
4705 | - t = ("NotDeepImmutableError: a mkdir-immutable operation was given " |
---|
4706 | - "a child that was not itself immutable: %s" % (f.value,)) |
---|
4707 | + if f.check(MustNotBeUnknownRWError): |
---|
4708 | + name = f.value.args[1] |
---|
4709 | + immutable = f.value.args[2] |
---|
4710 | + if immutable: |
---|
4711 | + t = ("MustNotBeUnknownRWError: an operation to add a child named " |
---|
4712 | + "'%s' to a directory was given an unknown cap in a write slot.\n" |
---|
4713 | + "If the cap is actually an immutable readcap, then using a " |
---|
4714 | + "webapi server that supports a later version of Tahoe may help.\n\n" |
---|
4715 | + "If you are using the webapi directly, then specifying an immutable " |
---|
4716 | + "readcap in the read slot (ro_uri) of the JSON PROPDICT, and " |
---|
4717 | + "omitting the write slot (rw_uri), would also work in this " |
---|
4718 | + "case.") % name.encode("utf-8") |
---|
4719 | + else: |
---|
4720 | + t = ("MustNotBeUnknownRWError: an operation to add a child named " |
---|
4721 | + "'%s' to a directory was given an unknown cap in a write slot.\n" |
---|
4722 | + "Using a webapi server that supports a later version of Tahoe " |
---|
4723 | + "may help.\n\n" |
---|
4724 | + "If you are using the webapi directly, specifying a readcap in " |
---|
4725 | + "the read slot (ro_uri) of the JSON PROPDICT, as well as a " |
---|
4726 | + "writecap in the write slot if desired, would also work in this " |
---|
4727 | + "case.") % name.encode("utf-8") |
---|
4728 | + return (t, http.BAD_REQUEST) |
---|
4729 | + if f.check(MustBeDeepImmutableError): |
---|
4730 | + name = f.value.args[1] |
---|
4731 | + t = ("MustBeDeepImmutableError: a cap passed to this operation for " |
---|
4732 | + "the child named '%s', needed to be immutable but was not. Either " |
---|
4733 | + "the cap is being added to an immutable directory, or it was " |
---|
4734 | + "originally retrieved from an immutable directory as an unknown " |
---|
4735 | + "cap." % name.encode("utf-8")) |
---|
4736 | + return (t, http.BAD_REQUEST) |
---|
4737 | + if f.check(MustBeReadonlyError): |
---|
4738 | + name = f.value.args[1] |
---|
4739 | + t = ("MustBeReadonlyError: a cap passed to this operation for " |
---|
4740 | + "the child named '%s', needed to be read-only but was not. " |
---|
4741 | + "The cap is being passed in a read slot (ro_uri), or was retrieved " |
---|
4742 | + "from a read slot as an unknown cap." % name.encode("utf-8")) |
---|
4743 | return (t, http.BAD_REQUEST) |
---|
4744 | if f.check(WebError): |
---|
4745 | return (f.value.text, f.value.code) |
---|
4746 | diff -rN -u old-tahoe/src/allmydata/web/directory.py new-tahoe/src/allmydata/web/directory.py |
---|
4747 | --- old-tahoe/src/allmydata/web/directory.py 2010-01-23 12:59:10.503000000 +0000 |
---|
4748 | +++ new-tahoe/src/allmydata/web/directory.py 2010-01-23 12:59:12.384000000 +0000 |
---|
4749 | @@ -351,7 +351,12 @@ |
---|
4750 | charset = get_arg(req, "_charset", "utf-8") |
---|
4751 | name = name.decode(charset) |
---|
4752 | replace = boolean_of_arg(get_arg(req, "replace", "true")) |
---|
4753 | - d = self.node.set_uri(name, childcap, childcap, overwrite=replace) |
---|
4754 | + |
---|
4755 | + # We mustn't pass childcap for the readcap argument because we don't |
---|
4756 | + # know whether it is a read cap. Passing a read cap as the writecap |
---|
4757 | + # argument will work (it ends up calling NodeMaker.create_from_cap, |
---|
4758 | + # which derives a readcap if necessary and possible). |
---|
4759 | + d = self.node.set_uri(name, childcap, None, overwrite=replace) |
---|
4760 | d.addCallback(lambda res: childcap) |
---|
4761 | return d |
---|
4762 | |
---|
4763 | @@ -362,9 +367,9 @@ |
---|
4764 | # won't show up in the resulting encoded form.. the 'name' |
---|
4765 | # field is completely missing. So to allow deletion of an |
---|
4766 | # empty file, we have to pretend that None means ''. The only |
---|
4767 | - # downide of this is a slightly confusing error message if |
---|
4768 | + # downside of this is a slightly confusing error message if |
---|
4769 | # someone does a POST without a name= field. For our own HTML |
---|
4770 | - # thisn't a big deal, because we create the 'delete' POST |
---|
4771 | + # this isn't a big deal, because we create the 'delete' POST |
---|
4772 | # buttons ourselves. |
---|
4773 | name = '' |
---|
4774 | charset = get_arg(req, "_charset", "utf-8") |
---|
4775 | @@ -584,7 +589,11 @@ |
---|
4776 | def render_title(self, ctx, data): |
---|
4777 | si_s = abbreviated_dirnode(self.node) |
---|
4778 | header = ["Tahoe-LAFS - Directory SI=%s" % si_s] |
---|
4779 | - if self.node.is_readonly(): |
---|
4780 | + if self.node.is_unknown(): |
---|
4781 | + header.append(" (unknown)") |
---|
4782 | + elif not self.node.is_mutable(): |
---|
4783 | + header.append(" (immutable)") |
---|
4784 | + elif self.node.is_readonly(): |
---|
4785 | header.append(" (read-only)") |
---|
4786 | else: |
---|
4787 | header.append(" (modifiable)") |
---|
4788 | @@ -593,7 +602,11 @@ |
---|
4789 | def render_header(self, ctx, data): |
---|
4790 | si_s = abbreviated_dirnode(self.node) |
---|
4791 | header = ["Tahoe-LAFS Directory SI=", T.span(class_="data-chars")[si_s]] |
---|
4792 | - if self.node.is_readonly(): |
---|
4793 | + if self.node.is_unknown(): |
---|
4794 | + header.append(" (unknown)") |
---|
4795 | + elif not self.node.is_mutable(): |
---|
4796 | + header.append(" (immutable)") |
---|
4797 | + elif self.node.is_readonly(): |
---|
4798 | header.append(" (read-only)") |
---|
4799 | return ctx.tag[header] |
---|
4800 | |
---|
4801 | @@ -602,7 +615,7 @@ |
---|
4802 | return T.div[T.a(href=link)["Return to Welcome page"]] |
---|
4803 | |
---|
4804 | def render_show_readonly(self, ctx, data): |
---|
4805 | - if self.node.is_readonly(): |
---|
4806 | + if self.node.is_unknown() or self.node.is_readonly(): |
---|
4807 | return "" |
---|
4808 | rocap = self.node.get_readonly_uri() |
---|
4809 | root = get_root(ctx) |
---|
4810 | @@ -629,7 +642,7 @@ |
---|
4811 | |
---|
4812 | root = get_root(ctx) |
---|
4813 | here = "%s/uri/%s/" % (root, urllib.quote(self.node.get_uri())) |
---|
4814 | - if self.node.is_readonly(): |
---|
4815 | + if self.node.is_unknown() or self.node.is_readonly(): |
---|
4816 | delete = "-" |
---|
4817 | rename = "-" |
---|
4818 | else: |
---|
4819 | @@ -677,8 +690,8 @@ |
---|
4820 | ctx.fillSlots("times", times) |
---|
4821 | |
---|
4822 | assert IFilesystemNode.providedBy(target), target |
---|
4823 | - writecap = target.get_uri() or "" |
---|
4824 | - quoted_uri = urllib.quote(writecap, safe="") # escape slashes too |
---|
4825 | + target_uri = target.get_uri() or "" |
---|
4826 | + quoted_uri = urllib.quote(target_uri, safe="") # escape slashes too |
---|
4827 | |
---|
4828 | if IMutableFileNode.providedBy(target): |
---|
4829 | # to prevent javascript in displayed .html files from stealing a |
---|
4830 | @@ -707,7 +720,7 @@ |
---|
4831 | |
---|
4832 | elif IDirectoryNode.providedBy(target): |
---|
4833 | # directory |
---|
4834 | - uri_link = "%s/uri/%s/" % (root, urllib.quote(writecap)) |
---|
4835 | + uri_link = "%s/uri/%s/" % (root, urllib.quote(target_uri)) |
---|
4836 | ctx.fillSlots("filename", |
---|
4837 | T.a(href=uri_link)[html.escape(name)]) |
---|
4838 | if not target.is_mutable(): |
---|
4839 | @@ -794,35 +807,30 @@ |
---|
4840 | kids = {} |
---|
4841 | for name, (childnode, metadata) in children.iteritems(): |
---|
4842 | assert IFilesystemNode.providedBy(childnode), childnode |
---|
4843 | - rw_uri = childnode.get_uri() |
---|
4844 | + rw_uri = childnode.get_write_uri() |
---|
4845 | ro_uri = childnode.get_readonly_uri() |
---|
4846 | if IFileNode.providedBy(childnode): |
---|
4847 | - if childnode.is_readonly(): |
---|
4848 | - rw_uri = None |
---|
4849 | kiddata = ("filenode", {'size': childnode.get_size(), |
---|
4850 | 'mutable': childnode.is_mutable(), |
---|
4851 | }) |
---|
4852 | elif IDirectoryNode.providedBy(childnode): |
---|
4853 | - if childnode.is_readonly(): |
---|
4854 | - rw_uri = None |
---|
4855 | kiddata = ("dirnode", {'mutable': childnode.is_mutable()}) |
---|
4856 | else: |
---|
4857 | kiddata = ("unknown", {}) |
---|
4858 | + |
---|
4859 | kiddata[1]["metadata"] = metadata |
---|
4860 | - if ro_uri: |
---|
4861 | - kiddata[1]["ro_uri"] = ro_uri |
---|
4862 | if rw_uri: |
---|
4863 | kiddata[1]["rw_uri"] = rw_uri |
---|
4864 | + if ro_uri: |
---|
4865 | + kiddata[1]["ro_uri"] = ro_uri |
---|
4866 | verifycap = childnode.get_verify_cap() |
---|
4867 | if verifycap: |
---|
4868 | kiddata[1]['verify_uri'] = verifycap.to_string() |
---|
4869 | + |
---|
4870 | kids[name] = kiddata |
---|
4871 | - if dirnode.is_readonly(): |
---|
4872 | - drw_uri = None |
---|
4873 | - dro_uri = dirnode.get_uri() |
---|
4874 | - else: |
---|
4875 | - drw_uri = dirnode.get_uri() |
---|
4876 | - dro_uri = dirnode.get_readonly_uri() |
---|
4877 | + |
---|
4878 | + drw_uri = dirnode.get_write_uri() |
---|
4879 | + dro_uri = dirnode.get_readonly_uri() |
---|
4880 | contents = { 'children': kids } |
---|
4881 | if dro_uri: |
---|
4882 | contents['ro_uri'] = dro_uri |
---|
4883 | @@ -833,13 +841,14 @@ |
---|
4884 | contents['verify_uri'] = verifycap.to_string() |
---|
4885 | contents['mutable'] = dirnode.is_mutable() |
---|
4886 | data = ("dirnode", contents) |
---|
4887 | - return simplejson.dumps(data, indent=1) + "\n" |
---|
4888 | + json = simplejson.dumps(data, indent=1) + "\n" |
---|
4889 | + #print json |
---|
4890 | + return json |
---|
4891 | d.addCallback(_got) |
---|
4892 | d.addCallback(text_plain, ctx) |
---|
4893 | return d |
---|
4894 | |
---|
4895 | |
---|
4896 | - |
---|
4897 | def DirectoryURI(ctx, dirnode): |
---|
4898 | return text_plain(dirnode.get_uri(), ctx) |
---|
4899 | |
---|
4900 | diff -rN -u old-tahoe/src/allmydata/web/filenode.py new-tahoe/src/allmydata/web/filenode.py |
---|
4901 | --- old-tahoe/src/allmydata/web/filenode.py 2010-01-23 12:59:10.572000000 +0000 |
---|
4902 | +++ new-tahoe/src/allmydata/web/filenode.py 2010-01-23 12:59:12.403000000 +0000 |
---|
4903 | @@ -6,10 +6,9 @@ |
---|
4904 | from nevow import url, rend |
---|
4905 | from nevow.inevow import IRequest |
---|
4906 | |
---|
4907 | -from allmydata.interfaces import ExistingChildError, CannotPackUnknownNodeError |
---|
4908 | +from allmydata.interfaces import ExistingChildError |
---|
4909 | from allmydata.monitor import Monitor |
---|
4910 | from allmydata.immutable.upload import FileHandle |
---|
4911 | -from allmydata.unknown import UnknownNode |
---|
4912 | from allmydata.util import log, base32 |
---|
4913 | |
---|
4914 | from allmydata.web.common import text_plain, WebError, RenderMixin, \ |
---|
4915 | @@ -20,7 +19,6 @@ |
---|
4916 | from allmydata.web.info import MoreInfo |
---|
4917 | |
---|
4918 | class ReplaceMeMixin: |
---|
4919 | - |
---|
4920 | def replace_me_with_a_child(self, req, client, replace): |
---|
4921 | # a new file is being uploaded in our place. |
---|
4922 | mutable = boolean_of_arg(get_arg(req, "mutable", "false")) |
---|
4923 | @@ -55,14 +53,7 @@ |
---|
4924 | def replace_me_with_a_childcap(self, req, client, replace): |
---|
4925 | req.content.seek(0) |
---|
4926 | childcap = req.content.read() |
---|
4927 | - childnode = client.create_node_from_uri(childcap, childcap+"readonly") |
---|
4928 | - if isinstance(childnode, UnknownNode): |
---|
4929 | - # don't be willing to pack unknown nodes: we might accidentally |
---|
4930 | - # put some write-authority into the rocap slot because we don't |
---|
4931 | - # know how to diminish the URI they gave us. We don't even know |
---|
4932 | - # if they gave us a readcap or a writecap. |
---|
4933 | - msg = "cannot attach unknown node as child %s" % str(self.name) |
---|
4934 | - raise CannotPackUnknownNodeError(msg) |
---|
4935 | + childnode = client.create_node_from_uri(childcap, None, name=self.name) |
---|
4936 | d = self.parentnode.set_node(self.name, childnode, overwrite=replace) |
---|
4937 | d.addCallback(lambda res: childnode.get_uri()) |
---|
4938 | return d |
---|
4939 | @@ -426,12 +417,8 @@ |
---|
4940 | |
---|
4941 | |
---|
4942 | def FileJSONMetadata(ctx, filenode, edge_metadata): |
---|
4943 | - if filenode.is_readonly(): |
---|
4944 | - rw_uri = None |
---|
4945 | - ro_uri = filenode.get_uri() |
---|
4946 | - else: |
---|
4947 | - rw_uri = filenode.get_uri() |
---|
4948 | - ro_uri = filenode.get_readonly_uri() |
---|
4949 | + rw_uri = filenode.get_write_uri() |
---|
4950 | + ro_uri = filenode.get_readonly_uri() |
---|
4951 | data = ("filenode", {}) |
---|
4952 | data[1]['size'] = filenode.get_size() |
---|
4953 | if ro_uri: |
---|
4954 | diff -rN -u old-tahoe/src/allmydata/web/info.py new-tahoe/src/allmydata/web/info.py |
---|
4955 | --- old-tahoe/src/allmydata/web/info.py 2010-01-23 12:59:10.609000000 +0000 |
---|
4956 | +++ new-tahoe/src/allmydata/web/info.py 2010-01-23 12:59:12.419000000 +0000 |
---|
4957 | @@ -21,6 +21,8 @@ |
---|
4958 | def get_type(self): |
---|
4959 | node = self.original |
---|
4960 | if IDirectoryNode.providedBy(node): |
---|
4961 | + if not node.is_mutable(): |
---|
4962 | + return "immutable directory" |
---|
4963 | return "directory" |
---|
4964 | if IFileNode.providedBy(node): |
---|
4965 | si = node.get_storage_index() |
---|
4966 | @@ -28,7 +30,7 @@ |
---|
4967 | if node.is_mutable(): |
---|
4968 | return "mutable file" |
---|
4969 | return "immutable file" |
---|
4970 | - return "LIT file" |
---|
4971 | + return "immutable LIT file" |
---|
4972 | return "unknown" |
---|
4973 | |
---|
4974 | def render_title(self, ctx, data): |
---|
4975 | @@ -68,10 +70,10 @@ |
---|
4976 | |
---|
4977 | def render_directory_writecap(self, ctx, data): |
---|
4978 | node = self.original |
---|
4979 | - if node.is_readonly(): |
---|
4980 | - return "" |
---|
4981 | if not IDirectoryNode.providedBy(node): |
---|
4982 | return "" |
---|
4983 | + if node.is_readonly(): |
---|
4984 | + return "" |
---|
4985 | return ctx.tag[node.get_uri()] |
---|
4986 | |
---|
4987 | def render_directory_readcap(self, ctx, data): |
---|
4988 | @@ -86,27 +88,24 @@ |
---|
4989 | return "" |
---|
4990 | return ctx.tag[node.get_verify_cap().to_string()] |
---|
4991 | |
---|
4992 | - |
---|
4993 | def render_file_writecap(self, ctx, data): |
---|
4994 | node = self.original |
---|
4995 | if IDirectoryNode.providedBy(node): |
---|
4996 | node = node._node |
---|
4997 | - if ((IDirectoryNode.providedBy(node) or IFileNode.providedBy(node)) |
---|
4998 | - and node.is_readonly()): |
---|
4999 | - return "" |
---|
5000 | - writecap = node.get_uri() |
---|
5001 | - if not writecap: |
---|
5002 | + write_uri = node.get_write_uri() |
---|
5003 | + #print "write_uri = %r, node = %r" % (write_uri, node) |
---|
5004 | + if not write_uri: |
---|
5005 | return "" |
---|
5006 | - return ctx.tag[writecap] |
---|
5007 | + return ctx.tag[write_uri] |
---|
5008 | |
---|
5009 | def render_file_readcap(self, ctx, data): |
---|
5010 | node = self.original |
---|
5011 | if IDirectoryNode.providedBy(node): |
---|
5012 | node = node._node |
---|
5013 | - readcap = node.get_readonly_uri() |
---|
5014 | - if not readcap: |
---|
5015 | + read_uri = node.get_readonly_uri() |
---|
5016 | + if not read_uri: |
---|
5017 | return "" |
---|
5018 | - return ctx.tag[readcap] |
---|
5019 | + return ctx.tag[read_uri] |
---|
5020 | |
---|
5021 | def render_file_verifycap(self, ctx, data): |
---|
5022 | node = self.original |
---|
5023 | diff -rN -u old-tahoe/src/allmydata/web/root.py new-tahoe/src/allmydata/web/root.py |
---|
5024 | --- old-tahoe/src/allmydata/web/root.py 2010-01-23 12:59:10.718000000 +0000 |
---|
5025 | +++ new-tahoe/src/allmydata/web/root.py 2010-01-23 12:59:12.488000000 +0000 |
---|
5026 | @@ -12,7 +12,7 @@ |
---|
5027 | from allmydata import get_package_versions_string |
---|
5028 | from allmydata import provisioning |
---|
5029 | from allmydata.util import idlib, log |
---|
5030 | -from allmydata.interfaces import IFileNode, UnhandledCapTypeError |
---|
5031 | +from allmydata.interfaces import IFileNode |
---|
5032 | from allmydata.web import filenode, directory, unlinked, status, operations |
---|
5033 | from allmydata.web import reliability, storage |
---|
5034 | from allmydata.web.common import abbreviate_size, getxmlfile, WebError, \ |
---|
5035 | @@ -85,7 +85,7 @@ |
---|
5036 | try: |
---|
5037 | node = self.client.create_node_from_uri(name) |
---|
5038 | return directory.make_handler_for(node, self.client) |
---|
5039 | - except (TypeError, UnhandledCapTypeError, AssertionError): |
---|
5040 | + except (TypeError, AssertionError): |
---|
5041 | raise WebError("'%s' is not a valid file- or directory- cap" |
---|
5042 | % name) |
---|
5043 | |
---|
5044 | @@ -104,7 +104,7 @@ |
---|
5045 | # 'name' must be a file URI |
---|
5046 | try: |
---|
5047 | node = self.client.create_node_from_uri(name) |
---|
5048 | - except (TypeError, UnhandledCapTypeError, AssertionError): |
---|
5049 | + except (TypeError, AssertionError): |
---|
5050 | # I think this can no longer be reached |
---|
5051 | raise WebError("'%s' is not a valid file- or directory- cap" |
---|
5052 | % name) |
---|
5053 | |
---|
5054 | diff -rN -u old-tahoe/contrib/fuse/impl_c/blackmatch.py new-tahoe/contrib/fuse/impl_c/blackmatch.py |
---|
5055 | --- old-tahoe/contrib/fuse/impl_c/blackmatch.py 2010-01-23 12:59:10.975000000 +0000 |
---|
5056 | +++ new-tahoe/contrib/fuse/impl_c/blackmatch.py 2010-01-23 12:59:12.773000000 +0000 |
---|
5057 | @@ -1,7 +1,7 @@ |
---|
5058 | #!/usr/bin/env python |
---|
5059 | |
---|
5060 | #----------------------------------------------------------------------------------------------- |
---|
5061 | -from allmydata.uri import CHKFileURI, DirectoryURI, LiteralFileURI |
---|
5062 | +from allmydata.uri import CHKFileURI, DirectoryURI, LiteralFileURI, is_literal_file_uri |
---|
5063 | from allmydata.scripts.common_http import do_http as do_http_req |
---|
5064 | from allmydata.util.hashutil import tagged_hash |
---|
5065 | from allmydata.util.assertutil import precondition |
---|
5066 | @@ -335,7 +335,7 @@ |
---|
5067 | self.fname = self.tfs.cache.tmp_file(os.urandom(20)) |
---|
5068 | if self.fnode is None: |
---|
5069 | log('TFF: [%s] open() for write: no file node, creating new File %s' % (self.name, self.fname, )) |
---|
5070 | - self.fnode = File(0, 'URI:LIT:') |
---|
5071 | + self.fnode = File(0, LiteralFileURI.BASE_STRING) |
---|
5072 | self.fnode.tmp_fname = self.fname # XXX kill this |
---|
5073 | self.parent.add_child(self.name, self.fnode, {}) |
---|
5074 | elif hasattr(self.fnode, 'tmp_fname'): |
---|
5075 | @@ -362,7 +362,7 @@ |
---|
5076 | self.fname = self.fnode.tmp_fname |
---|
5077 | log('TFF: reopening(%s) for reading' % self.fname) |
---|
5078 | else: |
---|
5079 | - if uri.startswith("URI:LIT") or not self.tfs.async: |
---|
5080 | + if is_literal_file_uri(uri) or not self.tfs.async: |
---|
5081 | log('TFF: synchronously fetching file from cache for reading') |
---|
5082 | self.fname = self.tfs.cache.get_file(uri) |
---|
5083 | else: |
---|
5084 | @@ -906,7 +906,7 @@ |
---|
5085 | |
---|
5086 | class TStat(fuse.Stat): |
---|
5087 | # in fuse 0.2, these are set by fuse.Stat.__init__ |
---|
5088 | - # in fuse 0.2-pre3 (hardy) they are not. badness unsues if they're missing |
---|
5089 | + # in fuse 0.2-pre3 (hardy) they are not. badness ensues if they're missing |
---|
5090 | st_mode = None |
---|
5091 | st_ino = 0 |
---|
5092 | st_dev = 0 |
---|
5093 | @@ -1237,7 +1237,7 @@ |
---|
5094 | |
---|
5095 | def get_file(self, uri): |
---|
5096 | self.log('get_file(%s)' % (uri,)) |
---|
5097 | - if uri.startswith("URI:LIT"): |
---|
5098 | + if is_literal_file_uri(uri): |
---|
5099 | return self.get_literal(uri) |
---|
5100 | else: |
---|
5101 | return self.get_chk(uri, async=False) |
---|