mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-03 03:36:44 +00:00
Sketch of advise_corrupt_share support for immutables.
This commit is contained in:
parent
f736683347
commit
922ee4feb1
@ -615,7 +615,7 @@ From RFC 7231::
|
|||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
Advise the server the data read from the indicated share was corrupt.
|
Advise the server the data read from the indicated share was corrupt.
|
||||||
The request body includes an human-meaningful string with details about the corruption.
|
The request body includes an human-meaningful (Unicode) string with details about the corruption.
|
||||||
It also includes potentially important details about the share.
|
It also includes potentially important details about the share.
|
||||||
|
|
||||||
For example::
|
For example::
|
||||||
@ -624,6 +624,8 @@ For example::
|
|||||||
|
|
||||||
.. share-type, storage-index, and share-number are inferred from the URL
|
.. share-type, storage-index, and share-number are inferred from the URL
|
||||||
|
|
||||||
|
The response code is OK, or 404 not found if the share couldn't be found.
|
||||||
|
|
||||||
Reading
|
Reading
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
|
@ -354,3 +354,31 @@ class StorageClientImmutables(object):
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
raise ClientException(response.code)
|
raise ClientException(response.code)
|
||||||
|
|
||||||
|
@inlineCallbacks
|
||||||
|
def advise_corrupt_share(
|
||||||
|
self,
|
||||||
|
storage_index: bytes,
|
||||||
|
share_number: int,
|
||||||
|
reason: str,
|
||||||
|
):
|
||||||
|
"""Indicate a share has been corrupted, with a human-readable message."""
|
||||||
|
assert isinstance(reason, str)
|
||||||
|
url = self._client.relative_url(
|
||||||
|
"/v1/immutable/{}/{}/corrupt".format(
|
||||||
|
_encode_si(storage_index), share_number
|
||||||
|
)
|
||||||
|
)
|
||||||
|
message = dumps({"reason": reason})
|
||||||
|
response = yield self._client.request(
|
||||||
|
"POST",
|
||||||
|
url,
|
||||||
|
data=message,
|
||||||
|
headers=Headers({"content-type": ["application/cbor"]}),
|
||||||
|
)
|
||||||
|
if response.code == http.OK:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
raise ClientException(
|
||||||
|
response.code,
|
||||||
|
)
|
||||||
|
@ -455,3 +455,22 @@ class HTTPServer(object):
|
|||||||
|
|
||||||
request.setResponseCode(http.NO_CONTENT)
|
request.setResponseCode(http.NO_CONTENT)
|
||||||
return b""
|
return b""
|
||||||
|
|
||||||
|
@_authorized_route(
|
||||||
|
_app,
|
||||||
|
set(),
|
||||||
|
"/v1/immutable/<storage_index:storage_index>/<int(signed=False):share_number>/corrupt",
|
||||||
|
methods=["POST"],
|
||||||
|
)
|
||||||
|
def advise_corrupt_share(self, request, authorization, storage_index, share_number):
|
||||||
|
"""Indicate that given share is corrupt, with a text reason."""
|
||||||
|
# TODO 3879 test success path
|
||||||
|
try:
|
||||||
|
bucket = self._storage_server.get_buckets(storage_index)[share_number]
|
||||||
|
except KeyError:
|
||||||
|
# TODO 3879 test this path
|
||||||
|
raise _HTTPError(http.NOT_FOUND)
|
||||||
|
|
||||||
|
info = loads(request.content.read())
|
||||||
|
bucket.advise_corrupt_share(info["reason"].encode("utf-8"))
|
||||||
|
return b""
|
||||||
|
@ -743,8 +743,9 @@ class StorageServer(service.MultiService):
|
|||||||
|
|
||||||
def advise_corrupt_share(self, share_type, storage_index, shnum,
|
def advise_corrupt_share(self, share_type, storage_index, shnum,
|
||||||
reason):
|
reason):
|
||||||
# This is a remote API, I believe, so this has to be bytes for legacy
|
# Previously this had to be bytes for legacy protocol backwards
|
||||||
# protocol backwards compatibility reasons.
|
# compatibility reasons. Now that Foolscap layer has been abstracted
|
||||||
|
# out, we can probably refactor this to be unicode...
|
||||||
assert isinstance(share_type, bytes)
|
assert isinstance(share_type, bytes)
|
||||||
assert isinstance(reason, bytes), "%r is not bytes" % (reason,)
|
assert isinstance(reason, bytes), "%r is not bytes" % (reason,)
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ from allmydata.util.hashutil import permute_server_hash
|
|||||||
from allmydata.util.dictutil import BytesKeyDict, UnicodeKeyDict
|
from allmydata.util.dictutil import BytesKeyDict, UnicodeKeyDict
|
||||||
from allmydata.storage.http_client import (
|
from allmydata.storage.http_client import (
|
||||||
StorageClient, StorageClientImmutables, StorageClientGeneral,
|
StorageClient, StorageClientImmutables, StorageClientGeneral,
|
||||||
ClientException as HTTPClientException,
|
ClientException as HTTPClientException
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1094,7 +1094,10 @@ class _HTTPBucketReader(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def advise_corrupt_share(self, reason):
|
def advise_corrupt_share(self, reason):
|
||||||
pass # TODO in later ticket
|
return self.client.advise_corrupt_share(
|
||||||
|
self.storage_index, self.share_number,
|
||||||
|
str(reason, "utf-8", errors="backslashreplace")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# WORK IN PROGRESS, for now it doesn't actually implement whole thing.
|
# WORK IN PROGRESS, for now it doesn't actually implement whole thing.
|
||||||
@ -1124,7 +1127,7 @@ class _HTTPStorageServer(object):
|
|||||||
cancel_secret,
|
cancel_secret,
|
||||||
sharenums,
|
sharenums,
|
||||||
allocated_size,
|
allocated_size,
|
||||||
canary,
|
canary
|
||||||
):
|
):
|
||||||
upload_secret = urandom(20)
|
upload_secret = urandom(20)
|
||||||
immutable_client = StorageClientImmutables(self._http_client)
|
immutable_client = StorageClientImmutables(self._http_client)
|
||||||
@ -1148,7 +1151,7 @@ class _HTTPStorageServer(object):
|
|||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_buckets(
|
def get_buckets(
|
||||||
self,
|
self,
|
||||||
storage_index,
|
storage_index
|
||||||
):
|
):
|
||||||
immutable_client = StorageClientImmutables(self._http_client)
|
immutable_client = StorageClientImmutables(self._http_client)
|
||||||
share_numbers = yield immutable_client.list_shares(
|
share_numbers = yield immutable_client.list_shares(
|
||||||
@ -1165,9 +1168,24 @@ class _HTTPStorageServer(object):
|
|||||||
self,
|
self,
|
||||||
storage_index,
|
storage_index,
|
||||||
renew_secret,
|
renew_secret,
|
||||||
cancel_secret,
|
cancel_secret
|
||||||
):
|
):
|
||||||
immutable_client = StorageClientImmutables(self._http_client)
|
immutable_client = StorageClientImmutables(self._http_client)
|
||||||
return immutable_client.add_or_renew_lease(
|
return immutable_client.add_or_renew_lease(
|
||||||
storage_index, renew_secret, cancel_secret
|
storage_index, renew_secret, cancel_secret
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def advise_corrupt_share(
|
||||||
|
self,
|
||||||
|
share_type,
|
||||||
|
storage_index,
|
||||||
|
shnum,
|
||||||
|
reason: bytes
|
||||||
|
):
|
||||||
|
if share_type == b"immutable":
|
||||||
|
imm_client = StorageClientImmutables(self._http_client)
|
||||||
|
return imm_client.advise_corrupt_share(
|
||||||
|
storage_index, shnum, str(reason, "utf-8", errors="backslashreplace")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError() # future tickets
|
||||||
|
@ -1149,12 +1149,6 @@ class HTTPImmutableAPIsTests(
|
|||||||
):
|
):
|
||||||
"""HTTP-specific tests for immutable ``IStorageServer`` APIs."""
|
"""HTTP-specific tests for immutable ``IStorageServer`` APIs."""
|
||||||
|
|
||||||
# These will start passing in future PRs as HTTP protocol is implemented.
|
|
||||||
SKIP_TESTS = {
|
|
||||||
"test_advise_corrupt_share",
|
|
||||||
"test_bucket_advise_corrupt_share",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class FoolscapMutableAPIsTests(
|
class FoolscapMutableAPIsTests(
|
||||||
_FoolscapMixin, IStorageServerMutableAPIsTestsMixin, AsyncTestCase
|
_FoolscapMixin, IStorageServerMutableAPIsTestsMixin, AsyncTestCase
|
||||||
|
Loading…
Reference in New Issue
Block a user